Package rtslib :: Module tcm
[hide private]
[frames] | no frames]

Source Code for Module rtslib.tcm

  1  ''' 
  2  Implements the RTS Target backstore and storage object classes. 
  3   
  4  This file is part of RTSLib. 
  5  Copyright (c) 2011-2013 by Datera, Inc 
  6   
  7  Licensed under the Apache License, Version 2.0 (the "License"); you may 
  8  not use this file except in compliance with the License. You may obtain 
  9  a copy of the License at 
 10   
 11      http://www.apache.org/licenses/LICENSE-2.0 
 12   
 13  Unless required by applicable law or agreed to in writing, software 
 14  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
 15  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
 16  License for the specific language governing permissions and limitations 
 17  under the License. 
 18  ''' 
 19   
 20  import os 
 21  import stat 
 22  import re 
 23  import glob 
 24  import resource 
 25   
 26  from node import CFSNode 
 27  from utils import fread, fwrite, RTSLibError, generate_wwn 
 28  from utils import convert_scsi_path_to_hctl, convert_scsi_hctl_to_path 
 29  from utils import is_dev_in_use, get_blockdev_type, get_blockdev_size 
30 31 32 -class StorageObject(CFSNode):
33 ''' 34 This is an interface to storage objects in configFS. A StorageObject is 35 identified by its backstore and its name. 36 ''' 37 # StorageObject private stuff 38
39 - def __repr__(self):
40 return "<%s %s/%s>" % (self.__class__.__name__, self.plugin, self.name)
41
42 - def __init__(self, name, mode):
43 super(StorageObject, self).__init__() 44 if "/" in name or " " in name or "\t" in name or "\n" in name: 45 raise RTSLibError("A storage object's name cannot contain " 46 " /, newline or spaces/tabs.") 47 else: 48 self._name = name 49 self._backstore = _Backstore(name, type(self), mode) 50 self._path = "%s/%s" % (self._backstore.path, self.name) 51 self.plugin = self._backstore.plugin 52 try: 53 self._create_in_cfs_ine(mode) 54 except: 55 self._backstore.delete() 56 raise
57
58 - def _configure(self, wwn=None):
59 if not wwn: 60 wwn = generate_wwn('unit_serial') 61 self.wwn = wwn 62 63 self._config_pr_aptpl()
64
65 - def __eq__(self, other):
66 return self.plugin == other.plugin and self.name == other.name
67
68 - def __ne__(self, other):
69 return not self == other
70
71 - def _config_pr_aptpl(self):
72 """ 73 LIO actually *writes* pr aptpl info to the filesystem, so we 74 need to read it in and squirt it back into configfs when we configure 75 the storage object. BLEH. 76 """ 77 aptpl_dir = "/var/target/pr" 78 79 try: 80 lines = fread("%s/aptpl_%s" % (aptpl_dir, self.wwn)).split() 81 except: 82 return 83 84 if not lines[0].startswith("PR_REG_START:"): 85 return 86 87 reservations = [] 88 for line in lines: 89 if line.startswith("PR_REG_START:"): 90 res_list = [] 91 elif line.startswith("PR_REG_END:"): 92 reservations.append(res_list) 93 else: 94 res_list.append(line.strip()) 95 96 for res in reservations: 97 fwrite(self.path + "/pr/res_aptpl_metadata", ",".join(res))
98 99 @classmethod
100 - def all(cls):
101 for so_dir in glob.glob("%s/core/*_*/*" % cls.configfs_dir): 102 if os.path.isdir(so_dir): 103 yield cls.so_from_path(so_dir)
104 105 @classmethod
106 - def so_from_path(cls, path):
107 ''' 108 Build a StorageObject of the correct type from a configfs path. 109 ''' 110 mapping = dict( 111 fileio=FileIOStorageObject, 112 pscsi=PSCSIStorageObject, 113 iblock=BlockStorageObject, 114 rd_mcp=RDMCPStorageObject, 115 ) 116 so_name = os.path.basename(path) 117 so_type = path.split("/")[-2].rsplit("_", 1)[0] 118 return mapping[so_type](so_name)
119
120 - def _get_wwn(self):
121 self._check_self() 122 if self.is_configured(): 123 path = "%s/wwn/vpd_unit_serial" % self.path 124 return fread(path).partition(":")[2].strip() 125 else: 126 raise RTSLibError("Cannot read a T10 WWN Unit Serial from " 127 + "an unconfigured StorageObject.")
128
129 - def _set_wwn(self, wwn):
130 self._check_self() 131 if self.is_configured(): 132 path = "%s/wwn/vpd_unit_serial" % self.path 133 fwrite(path, "%s\n" % wwn) 134 else: 135 raise RTSLibError("Cannot write a T10 WWN Unit Serial to " 136 + "an unconfigured StorageObject.")
137
138 - def _set_udev_path(self, udev_path):
139 self._check_self() 140 path = "%s/udev_path" % self.path 141 fwrite(path, "%s" % udev_path)
142
143 - def _get_udev_path(self):
144 self._check_self() 145 path = "%s/udev_path" % self.path 146 udev_path = fread(path) 147 if not udev_path and self._backstore.plugin == "fileio": 148 udev_path = self._parse_info('File').strip() 149 return udev_path
150
151 - def _get_version(self):
152 return self._backstore.version
153
154 - def _get_name(self):
155 return self._name
156
157 - def _enable(self):
158 self._check_self() 159 path = "%s/enable" % self.path 160 fwrite(path, "1\n")
161
162 - def _control(self, command):
163 self._check_self() 164 path = "%s/control" % self.path 165 fwrite(path, "%s" % str(command).strip())
166
167 - def _write_fd(self, contents):
168 self._check_self() 169 path = "%s/fd" % self.path 170 fwrite(path, "%s" % str(contents).strip())
171
172 - def _parse_info(self, key):
173 self._check_self() 174 info = fread("%s/info" % self.path) 175 return re.search(".*%s: ([^: ]+).*" \ 176 % key, ' '.join(info.split())).group(1).lower()
177
178 - def _get_status(self):
179 self._check_self() 180 return self._parse_info('Status')
181
182 - def _gen_attached_luns(self):
183 ''' 184 Fast scan of luns attached to a storage object. This is an order of 185 magnitude faster than using root.luns and matching path on them. 186 ''' 187 isdir = os.path.isdir 188 islink = os.path.islink 189 listdir = os.listdir 190 realpath = os.path.realpath 191 path = self.path 192 from root import RTSRoot 193 from target import LUN, TPG, Target 194 from fabric import target_names_excludes 195 196 for base, fm in ((fm.path, fm) for fm in RTSRoot().fabric_modules if fm.exists): 197 for tgt_dir in listdir(base): 198 if tgt_dir not in target_names_excludes: 199 tpgts_base = "%s/%s" % (base, tgt_dir) 200 for tpgt_dir in listdir(tpgts_base): 201 luns_base = "%s/%s/lun" % (tpgts_base, tpgt_dir) 202 if isdir(luns_base): 203 for lun_dir in listdir(luns_base): 204 links_base = "%s/%s" % (luns_base, lun_dir) 205 for lun_file in listdir(links_base): 206 link = "%s/%s" % (links_base, lun_file) 207 if islink(link) and realpath(link) == path: 208 val = (tpgt_dir + "_" + lun_dir) 209 val = val.split('_') 210 target = Target(fm, tgt_dir) 211 yield LUN(TPG(target, val[1]), val[3])
212
213 - def _list_attached_luns(self):
214 ''' 215 Generates all luns attached to a storage object. 216 ''' 217 self._check_self() 218 for lun in self._gen_attached_luns(): 219 yield lun
220 221 # StorageObject public stuff 222
223 - def delete(self):
224 ''' 225 Recursively deletes a StorageObject object. 226 This will delete all attached LUNs currently using the StorageObject 227 object, and then the StorageObject itself. The underlying file and 228 block storages will not be touched, but all ramdisk data will be lost. 229 ''' 230 self._check_self() 231 232 # If we are called after a configure error, we can skip this 233 if self.is_configured(): 234 for lun in self._gen_attached_luns(): 235 if self.status != 'activated': 236 break 237 else: 238 lun.delete() 239 240 super(StorageObject, self).delete() 241 self._backstore.delete()
242
243 - def is_configured(self):
244 ''' 245 @return: True if the StorageObject is configured, else returns False 246 ''' 247 248 self._check_self() 249 path = "%s/enable" % self.path 250 try: 251 configured = fread(path) 252 except IOError: 253 return True 254 255 return bool(int(configured))
256 257 version = property(_get_version, 258 doc="Get the version of the StorageObject's backstore") 259 name = property(_get_name, 260 doc="Get the StorageObject name as a string.") 261 udev_path = property(_get_udev_path, 262 doc="Get the StorageObject udev_path as a string.") 263 wwn = property(_get_wwn, _set_wwn, 264 doc="Get or set the StorageObject T10 WWN Serial as a string.") 265 status = property(_get_status, 266 doc="Get the storage object status, depending on whether or not it"\ 267 + "is used by any LUN") 268 attached_luns = property(_list_attached_luns, 269 doc="Get the list of all LUN objects attached.") 270
271 - def dump(self):
272 d = super(StorageObject, self).dump() 273 d['name'] = self.name 274 d['plugin'] = self.plugin 275 return d
276
277 278 -class PSCSIStorageObject(StorageObject):
279 ''' 280 An interface to configFS storage objects for pscsi backstore. 281 ''' 282 283 # PSCSIStorageObject private stuff 284
285 - def __init__(self, name, dev=None):
286 ''' 287 A PSCSIStorageObject can be instantiated in two ways: 288 - B{Creation mode}: If I{dev} is specified, the underlying configFS 289 object will be created with that parameter. No PSCSIStorageObject 290 with the same I{name} can pre-exist in the parent PSCSIBackstore 291 in that mode, or instantiation will fail. 292 - B{Lookup mode}: If I{dev} is not set, then the PSCSIStorageObject 293 will be bound to the existing configFS object in the parent 294 PSCSIBackstore having the specified I{name}. The underlying 295 configFS object must already exist in that mode, or instantiation 296 will fail. 297 298 @param name: The name of the PSCSIStorageObject. 299 @type name: string 300 @param dev: You have two choices: 301 - Use the SCSI id of the device: I{dev="H:C:T:L"}. 302 - Use the path to the SCSI device: I{dev="/path/to/dev"}. 303 @type dev: string 304 @return: A PSCSIStorageObject object. 305 ''' 306 if dev is not None: 307 super(PSCSIStorageObject, self).__init__(name, 'create') 308 try: 309 self._configure(dev) 310 except: 311 self.delete() 312 raise 313 else: 314 super(PSCSIStorageObject, self).__init__(name, 'lookup')
315
316 - def _configure(self, dev):
317 self._check_self() 318 319 # Use H:C:T:L format or preserve the path given by the user. 320 try: 321 (hostid, channelid, targetid, lunid) = \ 322 convert_scsi_path_to_hctl(dev) 323 except TypeError: 324 try: 325 (hostid, channelid, targetid, lunid) = dev.split(':') 326 hostid = int(hostid) 327 channelid = int(channelid) 328 targetid = int(targetid) 329 lunid = int(lunid) 330 except ValueError: 331 raise RTSLibError("Cannot find SCSI device by " 332 + "path, and dev " 333 + "parameter not in H:C:T:L " 334 + "format: %s." % dev) 335 else: 336 udev_path = convert_scsi_hctl_to_path(hostid, 337 channelid, 338 targetid, 339 lunid) 340 if not udev_path: 341 raise RTSLibError("SCSI device does not exist.") 342 else: 343 udev_path = dev.strip() 344 345 if is_dev_in_use(udev_path): 346 raise RTSLibError("Cannot configure StorageObject because " 347 + "device %s (SCSI %d:%d:%d:%d) " 348 % (udev_path, hostid, channelid, 349 targetid, lunid) 350 + "is already in use.") 351 352 self._control("scsi_host_id=%d," % hostid \ 353 + "scsi_channel_id=%d," % channelid \ 354 + "scsi_target_id=%d," % targetid \ 355 + "scsi_lun_id=%d" % lunid) 356 self._set_udev_path(udev_path) 357 self._enable() 358 359 super(PSCSIStorageObject, self)._configure()
360
361 - def _set_wwn(self, wwn):
362 # pscsi doesn't support setting wwn 363 pass
364
365 - def _get_model(self):
366 self._check_self() 367 info = fread("%s/info" % self.path) 368 return str(re.search(".*Model:(.*)Rev:", 369 ' '.join(info.split())).group(1)).strip()
370
371 - def _get_vendor(self):
372 self._check_self() 373 info = fread("%s/info" % self.path) 374 return str(re.search(".*Vendor:(.*)Model:", 375 ' '.join(info.split())).group(1)).strip()
376
377 - def _get_revision(self):
378 self._check_self() 379 return self._parse_info('Rev')
380
381 - def _get_channel_id(self):
382 self._check_self() 383 return int(self._parse_info('Channel ID'))
384
385 - def _get_target_id(self):
386 self._check_self() 387 return int(self._parse_info('Target ID'))
388
389 - def _get_lun(self):
390 self._check_self() 391 return int(self._parse_info('LUN'))
392
393 - def _get_host_id(self):
394 self._check_self() 395 return int(self._parse_info('Host ID'))
396 397 # PSCSIStorageObject public stuff 398 399 wwn = property(StorageObject._get_wwn, _set_wwn, 400 doc="Get the StorageObject T10 WWN Unit Serial as a string." 401 + " You cannot set it for pscsi-backed StorageObjects.") 402 model = property(_get_model, 403 doc="Get the SCSI device model string") 404 vendor = property(_get_vendor, 405 doc="Get the SCSI device vendor string") 406 revision = property(_get_revision, 407 doc="Get the SCSI device revision string") 408 host_id = property(_get_host_id, 409 doc="Get the SCSI device host id") 410 channel_id = property(_get_channel_id, 411 doc="Get the SCSI device channel id") 412 target_id = property(_get_target_id, 413 doc="Get the SCSI device target id") 414 lun = property(_get_lun, 415 doc="Get the SCSI device LUN") 416
417 - def dump(self):
418 d = super(PSCSIStorageObject, self).dump() 419 d['dev'] = self.udev_path 420 return d
421
422 423 -class RDMCPStorageObject(StorageObject):
424 ''' 425 An interface to configFS storage objects for rd_mcp backstore. 426 ''' 427 428 # RDMCPStorageObject private stuff 429
430 - def __init__(self, name, size=None, wwn=None, nullio=False):
431 ''' 432 A RDMCPStorageObject can be instantiated in two ways: 433 - B{Creation mode}: If I{size} is specified, the underlying 434 configFS object will be created with that parameter. 435 No RDMCPStorageObject with the same I{name} can pre-exist in the 436 parent Backstore in that mode, or instantiation will fail. 437 - B{Lookup mode}: If I{size} is not set, then the 438 RDMCPStorageObject will be bound to the existing configFS object 439 in the parent Backstore having the specified I{name}. 440 The underlying configFS object must already exist in that mode, 441 or instantiation will fail. 442 443 @param name: The name of the RDMCPStorageObject. 444 @type name: string 445 @param size: The size of the ramdrive to create, in bytes. 446 @type size: int 447 @param wwn: T10 WWN Unit Serial, will generate if None 448 @type wwn: string 449 @param nullio: If rd should be created w/o backing page store. 450 @type nullio: boolean 451 @return: A RDMCPStorageObject object. 452 ''' 453 454 if size is not None: 455 super(RDMCPStorageObject, self).__init__(name, 'create') 456 try: 457 self._configure(size, wwn, nullio) 458 except: 459 self.delete() 460 raise 461 else: 462 super(RDMCPStorageObject, self).__init__(name, 'lookup')
463
464 - def _configure(self, size, wwn, nullio):
465 self._check_self() 466 # convert to pages 467 size = round(float(size)/resource.getpagesize()) 468 if size == 0: 469 size = 1 470 471 self._control("rd_pages=%d" % size) 472 if nullio: 473 self._control("rd_nullio=1") 474 self._enable() 475 476 super(RDMCPStorageObject, self)._configure(wwn)
477
478 - def _get_page_size(self):
479 self._check_self() 480 return int(self._parse_info("PAGES/PAGE_SIZE").split('*')[1])
481
482 - def _get_pages(self):
483 self._check_self() 484 return int(self._parse_info("PAGES/PAGE_SIZE").split('*')[0])
485
486 - def _get_size(self):
487 self._check_self() 488 size = self._get_page_size() * self._get_pages() 489 return size
490
491 - def _get_nullio(self):
492 self._check_self() 493 # nullio not present before 3.10 494 try: 495 return bool(int(self._parse_info('nullio'))) 496 except AttributeError: 497 return False
498 499 # RDMCPStorageObject public stuff 500 501 page_size = property(_get_page_size, 502 doc="Get the ramdisk page size.") 503 pages = property(_get_pages, 504 doc="Get the ramdisk number of pages.") 505 size = property(_get_size, 506 doc="Get the ramdisk size in bytes.") 507 nullio = property(_get_nullio, 508 doc="Get the nullio status.") 509
510 - def dump(self):
511 d = super(RDMCPStorageObject, self).dump() 512 d['wwn'] = self.wwn 513 d['size'] = self.size 514 # only dump nullio if enabled 515 if self.nullio: 516 d['nullio'] = True 517 return d
518
519 520 -class FileIOStorageObject(StorageObject):
521 ''' 522 An interface to configFS storage objects for fileio backstore. 523 ''' 524 525 # FileIOStorageObject private stuff 526
527 - def __init__(self, name, dev=None, size=None, 528 wwn=None, write_back=False):
529 ''' 530 A FileIOStorageObject can be instantiated in two ways: 531 - B{Creation mode}: If I{dev} and I{size} are specified, the 532 underlying configFS object will be created with those parameters. 533 No FileIOStorageObject with the same I{name} can pre-exist in the 534 parent Backstore in that mode, or instantiation will fail. 535 - B{Lookup mode}: If I{dev} and I{size} are not set, then the 536 FileIOStorageObject will be bound to the existing configFS object 537 in the parent Backstore having the specified I{name}. 538 The underlying configFS object must already exist in that mode, 539 or instantiation will fail. 540 541 @param name: The name of the FileIOStorageObject. 542 @type name: string 543 @param dev: The path to the backend file or block device to be used. 544 - Examples: I{dev="/dev/sda"}, I{dev="/tmp/myfile"} 545 - The only block device type that is accepted I{TYPE_DISK}, or 546 partitions of a I{TYPE_DISK} device. 547 For other device types, use pscsi. 548 @type dev: string 549 @param size: Size of the object, if not a block device 550 @type size: int 551 @param wwn: T10 WWN Unit Serial, will generate if None 552 @type wwn: string 553 @param write_back: Should we create the StorageObject with 554 write caching enabled? Disabled by default 555 @type write_back: bool 556 @return: A FileIOStorageObject object. 557 ''' 558 559 if dev is not None: 560 super(FileIOStorageObject, self).__init__(name, 'create') 561 try: 562 self._configure(dev, size, wwn, write_back) 563 except: 564 self.delete() 565 raise 566 else: 567 super(FileIOStorageObject, self).__init__(name, 'lookup')
568
569 - def _configure(self, dev, size, wwn, write_back):
570 self._check_self() 571 572 block_type = get_blockdev_type(dev) 573 if block_type is None: # a file 574 if os.path.exists(os.path.realpath(dev)) and not os.path.isfile(dev): 575 raise RTSLibError("Path not to a file or block device.") 576 577 if size is None: 578 raise RTSLibError("Path is to a file, size needed.") 579 580 self._control("fd_dev_name=%s,fd_dev_size=%d" % (dev, size)) 581 582 else: # a block device 583 # size is ignored but we can't raise an exception because 584 # dump() saves it and thus restore() will call us with it. 585 586 if block_type != 0: 587 raise RTSLibError("Device is not a TYPE_DISK block device.") 588 589 if is_dev_in_use(dev): 590 raise RTSLibError("Device %s is already in use." % dev) 591 592 self._control("fd_dev_name=%s" % dev) 593 594 if write_back: 595 self.set_attribute("emulate_write_cache", 1) 596 self._control("fd_buffered_io=%d" % write_back) 597 598 self._set_udev_path(dev) 599 600 self._enable() 601 602 super(FileIOStorageObject, self)._configure(wwn)
603
604 - def _get_wb_enabled(self):
605 self._check_self() 606 return bool(int(self.get_attribute("emulate_write_cache")))
607
608 - def _get_size(self):
609 self._check_self() 610 611 if self.is_block: 612 return (get_blockdev_size(self._parse_info('File')) * 613 int(self._parse_info('SectorSize'))) 614 else: 615 return int(self._parse_info('Size'))
616
617 - def _is_block(self):
618 return get_blockdev_type(self.udev_path) is not None
619 620 # FileIOStorageObject public stuff 621 622 write_back = property(_get_wb_enabled, 623 doc="True if write-back, False if write-through (write cache disabled)") 624 size = property(_get_size, 625 doc="Get the current FileIOStorage size in bytes") 626 is_block = property(_is_block, 627 doc="True if FileIoStorage is backed by a block device instead of a file") 628
629 - def dump(self):
630 d = super(FileIOStorageObject, self).dump() 631 d['write_back'] = self.write_back 632 d['wwn'] = self.wwn 633 d['dev'] = self.udev_path 634 d['size'] = self.size 635 return d
636
637 638 -class BlockStorageObject(StorageObject):
639 ''' 640 An interface to configFS storage objects for block backstore. 641 ''' 642 643 # BlockStorageObject private stuff 644
645 - def __init__(self, name, dev=None, wwn=None, readonly=False, 646 write_back=False):
647 ''' 648 A BlockIOStorageObject can be instantiated in two ways: 649 - B{Creation mode}: If I{dev} is specified, the underlying configFS 650 object will be created with that parameter. 651 No BlockIOStorageObject with the same I{name} can pre-exist in 652 the parent Backstore in that mode. 653 - B{Lookup mode}: If I{dev} is not set, then the 654 BlockIOStorageObject will be bound to the existing configFS 655 object in the parent Backstore having the specified 656 I{name}. The underlying configFS object must already exist in 657 that mode, or instantiation will fail. 658 659 @param name: The name of the BlockIOStorageObject. 660 @type name: string 661 @param dev: The path to the backend block device to be used. 662 - Example: I{dev="/dev/sda"}. 663 - The only device type that is accepted I{TYPE_DISK}. 664 For other device types, use pscsi. 665 @type dev: string 666 @param wwn: T10 WWN Unit Serial, will generate if None 667 @type wwn: string 668 @return: A BlockIOStorageObject object. 669 ''' 670 671 if dev is not None: 672 super(BlockStorageObject, self).__init__(name, 'create') 673 try: 674 self._configure(dev, wwn, readonly, write_back) 675 except: 676 self.delete() 677 raise 678 else: 679 super(BlockStorageObject, self).__init__(name, 'lookup')
680
681 - def _configure(self, dev, wwn, readonly, write_back):
682 self._check_self() 683 if get_blockdev_type(dev) != 0: 684 raise RTSLibError("Device is not a TYPE_DISK block device.") 685 if is_dev_in_use(dev): 686 raise RTSLibError("Cannot configure StorageObject because " 687 + "device %s is already in use." % dev) 688 self._set_udev_path(dev) 689 self._control("udev_path=%s" % dev) 690 self._control("readonly=%d" % readonly) 691 self._enable() 692 693 if write_back: 694 self.set_attribute("emulate_write_cache", 1) 695 696 super(BlockStorageObject, self)._configure(wwn)
697
698 - def _get_major(self):
699 self._check_self() 700 return int(self._parse_info('Major'))
701
702 - def _get_minor(self):
703 self._check_self() 704 return int(self._parse_info('Minor'))
705
706 - def _get_size(self):
707 # udev_path doesn't work here, what if LV gets renamed? 708 return get_blockdev_size(self._parse_info('device')) * int(self._parse_info('SectorSize'))
709
710 - def _get_wb_enabled(self):
711 self._check_self() 712 return bool(int(self.get_attribute("emulate_write_cache")))
713
714 - def _get_readonly(self):
715 self._check_self() 716 # 'readonly' not present before kernel 3.6 717 try: 718 return bool(int(self._parse_info('readonly'))) 719 except AttributeError: 720 return False
721 722 # BlockStorageObject public stuff 723 724 major = property(_get_major, 725 doc="Get the block device major number") 726 minor = property(_get_minor, 727 doc="Get the block device minor number") 728 size = property(_get_size, 729 doc="Get the block device size") 730 write_back = property(_get_wb_enabled, 731 doc="True if write-back, False if write-through (write cache disabled)") 732 readonly = property(_get_readonly, 733 doc="True if the device is read-only, False if read/write") 734
735 - def dump(self):
736 d = super(BlockStorageObject, self).dump() 737 d['write_back'] = self.write_back 738 d['readonly'] = self.readonly 739 d['wwn'] = self.wwn 740 d['dev'] = self.udev_path 741 return d
742
743 744 -class StorageObjectFactory(object):
745 """ 746 Create a storage object based on a given path. 747 Only works for file & block. 748 """ 749
750 - def __new__(cls, path):
751 path = os.path.realpath(path) 752 name = path.strip("/").replace("/", "-") 753 if os.path.exists(path): 754 s = os.stat(path) 755 if stat.S_ISBLK(s.st_mode): 756 return BlockStorageObject(name=name, dev=path) 757 elif stat.S_ISREG(s.st_mode): 758 return FileIOStorageObject(name=name, dev=path, size=s.st_size) 759 760 raise RTSLibError("Can't create storageobject from path: %s" % path)
761 762 763 bs_params = { 764 PSCSIStorageObject: dict(name='pscsi'), 765 RDMCPStorageObject: dict(name='ramdisk', alt_dirprefix='rd_mcp'), 766 FileIOStorageObject: dict(name='fileio'), 767 BlockStorageObject: dict(name='block', alt_dirprefix='iblock'), 768 } 769 770 bs_cache = {}
771 772 -class _Backstore(CFSNode):
773 """ 774 Backstore is needed as a level in the configfs hierarchy, but otherwise useless. 775 1:1 so:backstore. 776 Created by storageobject ctor before SO configfs entry. 777 """ 778
779 - def __init__(self, name, storage_object_cls, mode):
780 super(_Backstore, self).__init__() 781 self._so_cls = storage_object_cls 782 self._plugin = bs_params[self._so_cls]['name'] 783 784 dirp = bs_params[self._so_cls].get("alt_dirprefix", self._plugin) 785 786 global bs_cache 787 if not bs_cache: 788 for dir in glob.iglob("%s/core/*_*/*/" % self.configfs_dir): 789 parts = dir.split("/") 790 bs_name = parts[-2] 791 bs_dirp, bs_index = parts[-3].rsplit("_", 1) 792 current_key = "%s/%s" % (bs_dirp, bs_name) 793 bs_cache[current_key] = int(bs_index) 794 795 # mapping in cache? 796 self._lookup_key = "%s/%s" % (dirp, name) 797 self._index = bs_cache.get(self._lookup_key, None) 798 799 if self._index != None and mode == 'create': 800 raise RTSLibError("Storage object %s/%s exists" % 801 (self._plugin, name)) 802 elif self._index == None: 803 if mode == 'lookup': 804 raise RTSLibError("Storage object %s/%s not found" % 805 (self._plugin, name)) 806 else: 807 # Allocate new index value 808 for index in xrange(1048576): 809 if index not in bs_cache.values(): 810 self._index = index 811 bs_cache[self._lookup_key] = self._index 812 break 813 else: 814 raise RTSLibError("No available backstore index") 815 816 self._path = "%s/core/%s_%d" % (self.configfs_dir, 817 dirp, 818 self._index) 819 self._create_in_cfs_ine(mode)
820
821 - def delete(self):
822 super(_Backstore, self).delete() 823 del bs_cache[self._lookup_key]
824
825 - def _get_index(self):
826 return self._index
827
828 - def _parse_info(self, key):
829 self._check_self() 830 info = fread("%s/hba_info" % self.path) 831 return re.search(".*%s: ([^: ]+).*" \ 832 % key, ' '.join(info.split())).group(1).lower()
833
834 - def _get_version(self):
835 self._check_self() 836 return self._parse_info("version")
837
838 - def _get_plugin(self):
839 self._check_self() 840 return self._plugin
841
842 - def _get_name(self):
843 self._check_self() 844 return "%s%d" % (self.plugin, self.index)
845 846 plugin = property(_get_plugin, 847 doc="Get the backstore plugin name.") 848 index = property(_get_index, 849 doc="Get the backstore index as an int.") 850 version = property(_get_version, 851 doc="Get the Backstore plugin version string.") 852 name = property(_get_name, 853 doc="Get the backstore name.")
854
855 856 -def _test():
857 import doctest 858 doctest.testmod()
859 860 if __name__ == "__main__": 861 _test() 862