Package pyanaconda :: Package storage :: Module devices
[hide private]
[frames] | no frames]

Source Code for Module pyanaconda.storage.devices

   1  # devices.py 
   2  # Device classes for anaconda's storage configuration module. 
   3  #  
   4  # Copyright (C) 2009  Red Hat, Inc. 
   5  # 
   6  # This copyrighted material is made available to anyone wishing to use, 
   7  # modify, copy, or redistribute it subject to the terms and conditions of 
   8  # the GNU General Public License v.2, or (at your option) any later version. 
   9  # This program is distributed in the hope that it will be useful, but WITHOUT 
  10  # ANY WARRANTY expressed or implied, including the implied warranties of 
  11  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General 
  12  # Public License for more details.  You should have received a copy of the 
  13  # GNU General Public License along with this program; if not, write to the 
  14  # Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
  15  # 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the 
  16  # source code or documentation are not subject to the GNU General Public 
  17  # License and may only be used or replicated with the express permission of 
  18  # Red Hat, Inc. 
  19  # 
  20  # Red Hat Author(s): Dave Lehman <dlehman@redhat.com> 
  21  # 
  22   
  23   
  24  """ 
  25      Device classes for use by anaconda. 
  26   
  27      This is the hierarchy of device objects that anaconda will use for 
  28      managing storage devices in the system. These classes will 
  29      individually make use of external support modules as needed to 
  30      perform operations specific to the type of device they represent. 
  31   
  32      TODO: 
  33          - see how to do network devices (NetworkManager may help) 
  34            - perhaps just a wrapper here 
  35          - document return values of all methods/functions 
  36          - find out what other kinds of wild and crazy devices we need to 
  37            represent here (iseries? xen? more mainframe? mac? ps?) 
  38              - PReP 
  39                - this is a prime candidate for a PseudoDevice 
  40              - DASD 
  41              - ZFCP 
  42              - XEN 
  43   
  44      What specifications do we allow?              new        existing 
  45          partitions                               
  46              usage                                  +            + 
  47                  filesystem, partition type are implicit 
  48              mountpoint                             +            + 
  49              size 
  50                  exact                              +            - 
  51                  range                              +            - 
  52                  resize                             -            + 
  53              format                                 -            + 
  54              encryption                             +            + 
  55   
  56              disk                                                  
  57                  exact                              +            - 
  58                  set                                +            - 
  59                      how will we specify this? 
  60                          partition w/ multiple parents cannot otherwise occur 
  61              primary                                +            - 
  62   
  63          mdraid sets 
  64              filesystem (*)                         +            + 
  65              mountpoint                             +            + 
  66              size?                                                 
  67              format                                 -            + 
  68              encryption                             +            + 
  69   
  70              level                                  +            ?  
  71              device minor                           +            ?  
  72              member devices                         +            ?  
  73              spares                                 +            ?  
  74              name? 
  75              bitmap? (boolean)                      +            - 
  76   
  77          volume groups 
  78              name                                   +            -  
  79              member pvs                             +            + 
  80              pesize                                 +            ? 
  81   
  82          logical volumes 
  83              filesystem                             +            + 
  84              mountpoint                             +            + 
  85              size 
  86                  exact                              +            ? 
  87              format                                 -            + 
  88              encryption                             +            + 
  89   
  90              name                                   +            ? 
  91              vgname                                 +            ? 
  92   
  93   
  94  """ 
  95   
  96  import os 
  97  import math 
  98  import copy 
  99  import pprint 
 100  import tempfile 
 101   
 102  # device backend modules 
 103  from devicelibs import mdraid 
 104  from devicelibs import lvm 
 105  from devicelibs import dm 
 106  from devicelibs import loop 
 107  from devicelibs import btrfs 
 108  import parted 
 109  import _ped 
 110  import block 
 111   
 112  from errors import * 
 113  from pyanaconda.iutil import notify_kernel, numeric_type 
 114  from pyanaconda.flags import flags 
 115  from pyanaconda.anaconda_log import log_method_call 
 116  from udev import * 
 117  from formats import get_device_format_class, getFormat, DeviceFormat 
 118   
 119  import gettext 
 120  _ = lambda x: gettext.ldgettext("anaconda", x) 
 121  P_ = lambda x, y, z: gettext.ldngettext("anaconda", x, y, z) 
 122   
 123  import logging 
 124  log = logging.getLogger("storage") 
125 126 -def get_device_majors():
127 majors = {} 128 for line in open("/proc/devices").readlines(): 129 try: 130 (major, device) = line.split() 131 except ValueError: 132 continue 133 try: 134 majors[int(major)] = device 135 except ValueError: 136 continue 137 return majors
138 device_majors = get_device_majors()
139 140 141 -def devicePathToName(devicePath):
142 if devicePath.startswith("/dev/"): 143 name = devicePath[5:] 144 else: 145 name = devicePath 146 147 if name.startswith("mapper/"): 148 name = name[7:] 149 150 return name
151
152 153 -def deviceNameToDiskByPath(deviceName=None):
154 if not deviceName: 155 return "" 156 157 ret = None 158 for dev in udev_get_block_devices(): 159 if udev_device_get_name(dev) == deviceName: 160 ret = udev_device_get_by_path(dev) 161 break 162 163 if ret: 164 return ret 165 raise DeviceNotFoundError(deviceName)
166
167 -class Device(object):
168 """ A generic device. 169 170 Device instances know which devices they depend upon (parents 171 attribute). They do not know which devices depend upon them, but 172 they do know whether or not they have any dependent devices 173 (isleaf attribute). 174 175 A Device's setup method should set up all parent devices as well 176 as the device itself. It should not run the resident format's 177 setup method. 178 179 Which Device types rely on their parents' formats being active? 180 DMCryptDevice 181 182 A Device's teardown method should accept the keyword argument 183 recursive, which takes a boolean value and indicates whether or 184 not to recursively close parent devices. 185 186 A Device's create method should create all parent devices as well 187 as the device itself. It should also run the Device's setup method 188 after creating the device. The create method should not create a 189 device's resident format. 190 191 Which device type rely on their parents' formats to be created 192 before they can be created/assembled? 193 VolumeGroup 194 DMCryptDevice 195 196 A Device's destroy method should destroy any resident format 197 before destroying the device itself. 198 199 """ 200 201 # This is a counter for generating unique ids for Devices. 202 _id = 0 203 204 _type = "device" 205 _packages = [] 206 _services = [] 207
208 - def __init__(self, name, parents=None):
209 """ Create a Device instance. 210 211 Arguments: 212 213 name -- the device name (generally a device node's basename) 214 215 Keyword Arguments: 216 217 parents -- a list of required Device instances 218 219 """ 220 self._name = name 221 if parents is None: 222 parents = [] 223 elif not isinstance(parents, list): 224 raise ValueError("parents must be a list of Device instances") 225 self.parents = parents 226 self.kids = 0 227 228 # Set this instance's id and increment the counter. 229 self.id = Device._id 230 Device._id += 1 231 232 for parent in self.parents: 233 parent.addChild()
234
235 - def __deepcopy__(self, memo):
236 """ Create a deep copy of a Device instance. 237 238 We can't do copy.deepcopy on parted objects, which is okay. 239 For these parted objects, we just do a shallow copy. 240 """ 241 new = self.__class__.__new__(self.__class__) 242 memo[id(self)] = new 243 dont_copy_attrs = ('_raidSet',) 244 shallow_copy_attrs = ('_partedDevice', '_partedPartition') 245 for (attr, value) in self.__dict__.items(): 246 if attr in dont_copy_attrs: 247 setattr(new, attr, value) 248 elif attr in shallow_copy_attrs: 249 setattr(new, attr, copy.copy(value)) 250 else: 251 setattr(new, attr, copy.deepcopy(value, memo)) 252 253 return new
254
255 - def __repr__(self):
256 s = ("%(type)s instance (%(id)s) --\n" 257 " name = %(name)s status = %(status)s" 258 " kids = %(kids)s id = %(dev_id)s\n" 259 " parents = %(parents)s\n" % 260 {"type": self.__class__.__name__, "id": "%#x" % id(self), 261 "name": self.name, "kids": self.kids, "status": self.status, 262 "dev_id": self.id, 263 "parents": pprint.pformat([str(p) for p in self.parents])}) 264 return s
265
266 - def __str__(self):
267 s = "%s %s (%d)" % (self.type, self.name, self.id) 268 return s
269 270 @property
271 - def dict(self):
272 d = {"type": self.type, "name": self.name, 273 "parents": [p.name for p in self.parents]} 274 return d
275
276 - def writeKS(self, f, preexisting=False, noformat=False, s=None):
277 return
278
279 - def removeChild(self):
280 log_method_call(self, name=self.name, kids=self.kids) 281 self.kids -= 1
282
283 - def addChild(self):
284 log_method_call(self, name=self.name, kids=self.kids) 285 self.kids += 1
286
287 - def setup(self, intf=None):
288 """ Open, or set up, a device. """ 289 raise NotImplementedError("setup method not defined for Device")
290
291 - def teardown(self, recursive=None):
292 """ Close, or tear down, a device. """ 293 raise NotImplementedError("teardown method not defined for Device")
294
295 - def create(self, intf=None):
296 """ Create the device. """ 297 raise NotImplementedError("create method not defined for Device")
298
299 - def destroy(self):
300 """ Destroy the device. """ 301 raise NotImplementedError("destroy method not defined for Device")
302
303 - def setupParents(self, orig=False):
304 """ Run setup method of all parent devices. """ 305 log_method_call(self, name=self.name, orig=orig, kids=self.kids) 306 for parent in self.parents: 307 parent.setup(orig=orig)
308
309 - def teardownParents(self, recursive=None):
310 """ Run teardown method of all parent devices. """ 311 for parent in self.parents: 312 parent.teardown(recursive=recursive)
313
314 - def dependsOn(self, dep):
315 """ Return True if this device depends on dep. """ 316 # XXX does a device depend on itself? 317 if dep in self.parents: 318 return True 319 320 for parent in self.parents: 321 if parent.dependsOn(dep): 322 return True 323 324 return False
325
326 - def dracutSetupArgs(self):
327 return set()
328 329 @property
330 - def status(self):
331 """ This device's status. 332 333 For now, this should return a boolean: 334 True the device is open and ready for use 335 False the device is not open 336 """ 337 return False
338 339 @property
340 - def name(self):
341 """ This device's name. """ 342 return self._name
343 344 @property
345 - def isleaf(self):
346 """ True if this device has no children. """ 347 return self.kids == 0
348 349 @property
350 - def typeDescription(self):
351 """ String describing the device type. """ 352 return self._type
353 354 @property
355 - def type(self):
356 """ Device type. """ 357 return self._type
358 359 @property
360 - def packages(self):
361 """ List of packages required to manage devices of this type. 362 363 This list includes the packages required by its parent devices. 364 """ 365 packages = self._packages 366 for parent in self.parents: 367 for package in parent.packages: 368 if package not in packages: 369 packages.append(package) 370 371 return packages
372 373 @property
374 - def services(self):
375 """ List of services required to manage devices of this type. 376 377 This list includes the services required by its parent devices." 378 """ 379 services = self._services 380 for parent in self.parents: 381 for service in parent.services: 382 if service not in services: 383 services.append(service) 384 385 return services
386 387 @property
388 - def mediaPresent(self):
389 return True
390
391 392 -class NetworkStorageDevice(object):
393 """ Virtual base class for network backed storage devices """ 394
395 - def __init__(self, host_address=None, nic=None):
396 """ Create a NetworkStorage Device instance. Note this class is only 397 to be used as a baseclass and then only with multiple inheritance. 398 The only correct use is: 399 class MyStorageDevice(StorageDevice, NetworkStorageDevice): 400 401 The sole purpose of this class is to: 402 1) Be able to check if a StorageDevice is network backed 403 (using isinstance). 404 2) To be able to get the host address of the host (server) backing 405 the storage *or* the NIC through which the storage is connected 406 407 Arguments: 408 409 host_address -- host address of the backing server 410 nic -- nic to which the storage is bound 411 """ 412 self.host_address = host_address 413 self.nic = nic
414
415 416 -class StorageDevice(Device):
417 """ A generic storage device. 418 419 A fully qualified path to the device node can be obtained via the 420 path attribute, although it is not guaranteed to be useful, or 421 even present, unless the StorageDevice's setup method has been 422 run. 423 424 StorageDevice instances can optionally contain a filesystem, 425 represented by an FS instance. A StorageDevice's create method 426 should create a filesystem if one has been specified. 427 """ 428 _type = "storage" 429 _devDir = "/dev" 430 sysfsBlockDir = "class/block" 431 _resizable = False 432 _partitionable = False 433 _isDisk = False 434
435 - def __init__(self, name, format=None, uuid=None, 436 size=None, major=None, minor=None, 437 sysfsPath='', parents=None, exists=None, serial=None, 438 vendor="", model="", bus=""):
439 """ Create a StorageDevice instance. 440 441 Arguments: 442 443 name -- the device name (generally a device node's basename) 444 445 Keyword Arguments: 446 447 size -- the device's size (units/format TBD) 448 major -- the device major 449 minor -- the device minor 450 sysfsPath -- sysfs device path 451 format -- a DeviceFormat instance 452 uuid -- universally unique identifier 453 parents -- a list of required Device instances 454 serial -- the ID_SERIAL_SHORT for this device 455 vendor -- the manufacturer of this Device 456 model -- manufacturer's device model string 457 bus -- the interconnect this device uses 458 459 """ 460 # allow specification of individual parents 461 if isinstance(parents, Device): 462 parents = [parents] 463 464 self.exists = exists 465 Device.__init__(self, name, parents=parents) 466 467 self.uuid = uuid 468 self._format = None 469 self._size = numeric_type(size) 470 self.major = numeric_type(major) 471 self.minor = numeric_type(minor) 472 self.sysfsPath = sysfsPath 473 self._serial = serial 474 self._vendor = vendor 475 self._model = model 476 self.bus = bus 477 478 self.protected = False 479 self.controllable = True 480 481 self.format = format 482 self.originalFormat = self.format 483 self.fstabComment = "" 484 self._targetSize = self._size 485 486 self._partedDevice = None
487
488 - def __str__(self):
489 exist = "existing" 490 if not self.exists: 491 exist = "non-existent" 492 s = "%s %dMB %s" % (exist, self.size, super(StorageDevice, self).__str__()) 493 if self.format.type: 494 s += " with %s" % self.format 495 496 return s
497 498 @property
499 - def packages(self):
500 """ List of packages required to manage devices of this type. 501 502 This list includes the packages required by this device's 503 format type as well those required by all of its parent 504 devices. 505 """ 506 packages = super(StorageDevice, self).packages 507 packages.extend(self.format.packages) 508 for parent in self.parents: 509 for package in parent.format.packages: 510 if package not in packages: 511 packages.append(package) 512 513 return packages
514 515 @property
516 - def services(self):
517 """ List of services required to manage devices of this type. 518 519 This list includes the services required by this device's 520 format type as well those required by all of its parent 521 devices. 522 """ 523 services = super(StorageDevice, self).services 524 services.extend(self.format.services) 525 for parent in self.parents: 526 for service in parent.format.services: 527 if service not in services: 528 services.append(service) 529 530 return services
531 532 @property
533 - def disks(self):
534 """ A list of all disks this device depends on, including itself. """ 535 _disks = [] 536 for parent in self.parents: 537 for disk in parent.disks: 538 if disk not in _disks: 539 _disks.append(disk) 540 541 if self.isDisk and not self.format.hidden: 542 _disks.append(self) 543 544 return _disks
545 546 @property
547 - def encrypted(self):
548 """ True if this device, or any it requires, is encrypted. """ 549 crypted = False 550 for parent in self.parents: 551 if parent.encrypted: 552 crypted = True 553 break 554 555 if not crypted and isinstance(self, DMCryptDevice): 556 crypted = True 557 558 return crypted
559 560 @property
561 - def partedDevice(self):
562 if self.exists and self.status and not self._partedDevice: 563 log.debug("looking up parted Device: %s" % self.path) 564 565 # We aren't guaranteed to be able to get a device. In 566 # particular, built-in USB flash readers show up as devices but 567 # do not always have any media present, so parted won't be able 568 # to find a device. 569 try: 570 self._partedDevice = parted.Device(path=self.path) 571 except (_ped.IOException, _ped.DeviceException): 572 pass 573 574 return self._partedDevice
575
576 - def _getTargetSize(self):
577 return self._targetSize
578
579 - def _setTargetSize(self, newsize):
580 self._targetSize = newsize
581 582 targetSize = property(lambda s: s._getTargetSize(), 583 lambda s, v: s._setTargetSize(v), 584 doc="Target size of this device") 585
586 - def __repr__(self):
587 s = Device.__repr__(self) 588 s += (" uuid = %(uuid)s size = %(size)s\n" 589 " format = %(format)s\n" 590 " major = %(major)s minor = %(minor)s exists = %(exists)s" 591 " protected = %(protected)s\n" 592 " sysfs path = %(sysfs)s partedDevice = %(partedDevice)s\n" 593 " target size = %(targetSize)s path = %(path)s\n" 594 " format args = %(formatArgs)s originalFormat = %(origFmt)s" % 595 {"uuid": self.uuid, "format": self.format, "size": self.size, 596 "major": self.major, "minor": self.minor, "exists": self.exists, 597 "sysfs": self.sysfsPath, "partedDevice": self.partedDevice, 598 "targetSize": self.targetSize, "path": self.path, 599 "protected": self.protected, 600 "formatArgs": self.formatArgs, "origFmt": self.originalFormat.type}) 601 return s
602 603 @property
604 - def dict(self):
605 d = super(StorageDevice, self).dict 606 d.update({"uuid": self.uuid, "size": self.size, 607 "format": self.format.dict, "removable": self.removable, 608 "major": self.major, "minor": self.minor, 609 "exists": self.exists, "sysfs": self.sysfsPath, 610 "targetSize": self.targetSize, "path": self.path}) 611 return d
612 613 @property
614 - def path(self):
615 """ Device node representing this device. """ 616 return "%s/%s" % (self._devDir, self.name)
617
618 - def updateSysfsPath(self):
619 """ Update this device's sysfs path. """ 620 log_method_call(self, self.name, status=self.status) 621 sysfsName = self.name.replace("/", "!") 622 path = os.path.join("/sys", self.sysfsBlockDir, sysfsName) 623 self.sysfsPath = os.path.realpath(path)[4:] 624 log.debug("%s sysfsPath set to %s" % (self.name, self.sysfsPath))
625 626 @property
627 - def formatArgs(self):
628 """ Device-specific arguments to format creation program. """ 629 return []
630 631 @property
632 - def resizable(self):
633 """ Can this type of device be resized? """ 634 return (self._resizable and self.exists and 635 (self.format.resizable or not self.format.type))
636
637 - def notifyKernel(self):
638 """ Send a 'change' uevent to the kernel for this device. """ 639 log_method_call(self, self.name, status=self.status) 640 if not self.exists: 641 log.debug("not sending change uevent for non-existent device") 642 return 643 644 if not self.status: 645 log.debug("not sending change uevent for inactive device") 646 return 647 648 path = os.path.normpath("/sys/%s" % self.sysfsPath) 649 try: 650 notify_kernel(path, action="change") 651 except (ValueError, IOError) as e: 652 log.warning("failed to notify kernel of change: %s" % e)
653 654 @property
655 - def fstabSpec(self):
656 spec = self.path 657 if self.format and self.format.uuid: 658 spec = "UUID=%s" % self.format.uuid 659 return spec
660
661 - def resize(self, intf=None):
662 """ Resize the device. 663 664 New size should already be set. 665 """ 666 raise NotImplementedError("resize method not defined for StorageDevice")
667 668 # 669 # progress/status ui 670 # 671 # We define methods to create a wait window or a pulse progress window. 672 # Device classes can define a _statusWindow method that calls the 673 # appropriate method for display during device operations. Currently, 674 # the only thing we display progress/status windows for is device creation. 675 # MD and LVM devices can pass pulse progress windows down into the 676 # devicelibs functions, but all other devices display a wait window. 677 #
678 - def _progressWindow(self, intf=None, title="", msg=""):
679 w = None 680 if intf: 681 w = intf.progressWindow(title, msg, 100, pulse=True) 682 return w
683
684 - def _waitWindow(self, intf=None, title="", msg=""):
685 w = None 686 if intf: 687 w = intf.waitWindow(title, msg) 688 return w
689
690 - def _statusWindow(self, intf=None, title="", msg=""):
691 return self._waitWindow(intf=intf, title=title, msg=msg)
692 693 # 694 # setup 695 #
696 - def _preSetup(self, orig=False):
697 """ Preparation and pre-condition checking for device setup. 698 699 Return True if setup should proceed or False if not. 700 """ 701 if not self.exists: 702 raise DeviceError("device has not been created", self.name) 703 704 if self.status or not self.controllable: 705 return False 706 707 self.setupParents(orig=orig) 708 return True
709
710 - def _setup(self, intf=None, orig=False):
711 """ Perform device-specific setup operations. """ 712 pass
713
714 - def setup(self, intf=None, orig=False):
715 """ Open, or set up, a device. """ 716 log_method_call(self, self.name, orig=orig, status=self.status, 717 controllable=self.controllable) 718 if not self._preSetup(orig=orig): 719 return 720 721 self._setup(intf=intf, orig=orig) 722 self._postSetup()
723
724 - def _postSetup(self):
725 """ Perform post-setup operations. """ 726 udev_settle() 727 # we always probe since the device may not be set up when we want 728 # information about it 729 self._size = self.currentSize
730 731 # 732 # teardown 733 #
734 - def _preTeardown(self, recursive=None):
735 """ Preparation and pre-condition checking for device teardown. 736 737 Return True if teardown should proceed or False if not. 738 """ 739 if not self.exists and not recursive: 740 raise DeviceError("device has not been created", self.name) 741 742 if not self.status or not self.controllable: 743 return False 744 745 if self.originalFormat.exists: 746 self.originalFormat.teardown() 747 self.format.cacheMajorminor() 748 if self.format.exists: 749 self.format.teardown() 750 udev_settle() 751 return True
752
753 - def _teardown(self, recursive=None):
754 """ Perform device-specific teardown operations. """ 755 pass
756
757 - def teardown(self, recursive=None):
758 """ Close, or tear down, a device. """ 759 log_method_call(self, self.name, status=self.status, 760 controllable=self.controllable) 761 if not self._preTeardown(recursive=recursive): 762 return 763 764 self._teardown(recursive=recursive) 765 self._postTeardown(recursive=recursive)
766
767 - def _postTeardown(self, recursive=None):
768 """ Perform post-teardown operations. """ 769 if recursive: 770 self.teardownParents(recursive=recursive)
771 772 # 773 # create 774 #
775 - def _preCreate(self):
776 """ Preparation and pre-condition checking for device creation. """ 777 if self.exists: 778 raise DeviceError("device has already been created", self.name) 779 780 self.setupParents()
781
782 - def _create(self, w):
783 """ Perform device-specific create operations. """ 784 pass
785
786 - def create(self, intf=None):
787 """ Create the device. """ 788 log_method_call(self, self.name, status=self.status) 789 self._preCreate() 790 w = self._statusWindow(intf=intf, 791 title=_("Creating"), 792 msg=_("Creating device %s") % self.path) 793 try: 794 self._create(w) 795 except Exception as e: 796 raise DeviceCreateError(str(e), self.name) 797 else: 798 self._postCreate() 799 finally: 800 if w: 801 w.pop()
802
803 - def _postCreate(self):
804 """ Perform post-create operations. """ 805 self.exists = True 806 self.setup() 807 self.updateSysfsPath() 808 udev_settle()
809 810 # 811 # destroy 812 #
813 - def _preDestroy(self):
814 """ Preparation and precondition checking for device destruction. """ 815 if not self.exists: 816 raise DeviceError("device has not been created", self.name) 817 818 if not self.isleaf: 819 raise DeviceError("Cannot destroy non-leaf device", self.name) 820 821 self.teardown()
822
823 - def _destroy(self):
824 """ Perform device-specific destruction operations. """ 825 pass
826
827 - def destroy(self):
828 """ Destroy the device. """ 829 log_method_call(self, self.name, status=self.status) 830 self._preDestroy() 831 self._destroy() 832 self._postDestroy()
833
834 - def _postDestroy(self):
835 """ Perform post-destruction operations. """ 836 self.exists = False
837
838 - def setupParents(self, orig=False):
839 """ Run setup method of all parent devices. """ 840 log_method_call(self, name=self.name, orig=orig, kids=self.kids) 841 for parent in self.parents: 842 parent.setup(orig=orig) 843 if orig: 844 _format = parent.originalFormat 845 else: 846 _format = parent.format 847 848 # set up the formatting, if present 849 if _format.type and _format.exists: 850 _format.setup()
851
852 - def _getSize(self):
853 """ Get the device's size in MB, accounting for pending changes. """ 854 if self.exists and not self.mediaPresent: 855 return 0 856 857 if self.exists and self.partedDevice: 858 self._size = self.currentSize 859 860 size = self._size 861 if self.exists and self.resizable and self.targetSize != size: 862 size = self.targetSize 863 864 return size
865
866 - def _setSize(self, newsize):
867 """ Set the device's size to a new value. """ 868 if newsize > self.maxSize: 869 raise DeviceError("device cannot be larger than %s MB" % 870 (self.maxSize(),), self.name) 871 self._size = newsize
872 873 size = property(lambda x: x._getSize(), 874 lambda x, y: x._setSize(y), 875 doc="The device's size in MB, accounting for pending changes") 876 877 @property
878 - def currentSize(self):
879 """ The device's actual size. """ 880 size = 0 881 if self.exists and self.partedDevice: 882 size = self.partedDevice.getSize() 883 elif self.exists: 884 size = self._size 885 return size
886 887 @property
888 - def minSize(self):
889 """ The minimum size this device can be. """ 890 if self.format.minSize: 891 return self.format.minSize 892 else: 893 return self.size
894 895 @property
896 - def maxSize(self):
897 """ The maximum size this device can be. """ 898 if self.format.maxSize > self.currentSize: 899 return self.currentSize 900 else: 901 return self.format.maxSize
902 903 @property
904 - def status(self):
905 """ This device's status. 906 907 For now, this should return a boolean: 908 True the device is open and ready for use 909 False the device is not open 910 """ 911 if not self.exists: 912 return False 913 return os.access(self.path, os.W_OK)
914
915 - def _setFormat(self, format):
916 """ Set the Device's format. """ 917 if not format: 918 format = getFormat(None, device=self.path, exists=self.exists) 919 log_method_call(self, self.name, type=format.type, 920 current=getattr(self._format, "type", None)) 921 if self._format and self._format.status: 922 # FIXME: self.format.status doesn't mean much 923 raise DeviceError("cannot replace active format", self.name) 924 925 self._format = format 926 self._format.device = self.path
927
928 - def _getFormat(self):
929 return self._format
930 931 format = property(lambda d: d._getFormat(), 932 lambda d,f: d._setFormat(f), 933 doc="The device's formatting.") 934
935 - def preCommitFixup(self, *args, **kwargs):
936 """ Do any necessary pre-commit fixups.""" 937 pass
938 939 @property
940 - def removable(self):
941 devpath = os.path.normpath("/sys/%s" % self.sysfsPath) 942 remfile = os.path.normpath("%s/removable" % devpath) 943 return (self.sysfsPath and os.path.exists(devpath) and 944 os.access(remfile, os.R_OK) and 945 open(remfile).readline().strip() == "1")
946 947 @property
948 - def isDisk(self):
949 return self._isDisk
950 951 @property
952 - def partitionable(self):
953 return self._partitionable
954 955 @property
956 - def partitioned(self):
957 return self.format.type == "disklabel" and self.partitionable
958 959 @property
960 - def serial(self):
961 return self._serial
962 963 @property
964 - def model(self):
965 if not self._model: 966 self._model = getattr(self.partedDevice, "model", "") 967 return self._model
968 969 @property
970 - def vendor(self):
971 return self._vendor
972 973 @property
974 - def growable(self):
975 """ True if this device or it's component devices are growable. """ 976 grow = getattr(self, "req_grow", False) 977 if not grow: 978 for parent in self.parents: 979 grow = parent.growable 980 if grow: 981 break 982 return grow
983
984 - def checkSize(self):
985 """ Check to make sure the size of the device is allowed by the 986 format used. 987 988 Returns: 989 0 - ok 990 1 - Too large 991 -1 - Too small 992 """ 993 if self.format.maxSize and self.size > self.format.maxSize: 994 return 1 995 elif self.format.minSize and self.size < self.format.minSize: 996 return -1 997 return 0
998
999 -class DiskDevice(StorageDevice):
1000 """ A disk """ 1001 _type = "disk" 1002 _partitionable = True 1003 _isDisk = True 1004
1005 - def __init__(self, name, format=None, 1006 size=None, major=None, minor=None, sysfsPath='', 1007 parents=None, serial=None, vendor="", model="", bus="", 1008 exists=True):
1009 """ Create a DiskDevice instance. 1010 1011 Arguments: 1012 1013 name -- the device name (generally a device node's basename) 1014 1015 Keyword Arguments: 1016 1017 size -- the device's size (units/format TBD) 1018 major -- the device major 1019 minor -- the device minor 1020 sysfsPath -- sysfs device path 1021 format -- a DeviceFormat instance 1022 parents -- a list of required Device instances 1023 removable -- whether or not this is a removable device 1024 serial -- the ID_SERIAL_SHORT for this device 1025 vendor -- the manufacturer of this Device 1026 model -- manufacturer's device model string 1027 bus -- the interconnect this device uses 1028 1029 1030 DiskDevices always exist. 1031 """ 1032 StorageDevice.__init__(self, name, format=format, size=size, 1033 major=major, minor=minor, exists=exists, 1034 sysfsPath=sysfsPath, parents=parents, 1035 serial=serial, model=model, 1036 vendor=vendor, bus=bus)
1037
1038 - def __repr__(self):
1039 s = StorageDevice.__repr__(self) 1040 s += (" removable = %(removable)s partedDevice = %(partedDevice)r" % 1041 {"removable": self.removable, "partedDevice": self.partedDevice}) 1042 return s
1043 1044 @property
1045 - def mediaPresent(self):
1046 if not self.partedDevice: 1047 return False 1048 1049 # Some drivers (cpqarray <blegh>) make block device nodes for 1050 # controllers with no disks attached and then report a 0 size, 1051 # treat this as no media present 1052 return self.partedDevice.getSize() != 0
1053 1054 @property
1055 - def description(self):
1056 return self.model
1057 1058 @property
1059 - def size(self):
1060 """ The disk's size in MB """ 1061 return super(DiskDevice, self).size
1062 #size = property(StorageDevice._getSize) 1063
1064 - def _preDestroy(self):
1065 """ Destroy the device. """ 1066 log_method_call(self, self.name, status=self.status) 1067 if not self.mediaPresent: 1068 raise DeviceError("cannot destroy disk with no media", self.name) 1069 1070 StorageDevice._preDestroy(self)
1071
1072 1073 -class PartitionDevice(StorageDevice):
1074 """ A disk partition. 1075 1076 On types and flags... 1077 1078 We don't need to deal with numerical partition types at all. The 1079 only type we are concerned with is primary/logical/extended. Usage 1080 specification is accomplished through the use of flags, which we 1081 will set according to the partition's format. 1082 """ 1083 _type = "partition" 1084 _resizable = True 1085 defaultSize = 500 1086
1087 - def __init__(self, name, format=None, 1088 size=None, grow=False, maxsize=None, 1089 major=None, minor=None, bootable=None, 1090 sysfsPath='', parents=None, exists=None, 1091 partType=None, primary=False, weight=0):
1092 """ Create a PartitionDevice instance. 1093 1094 Arguments: 1095 1096 name -- the device name (generally a device node's basename) 1097 1098 Keyword Arguments: 1099 1100 exists -- indicates whether this is an existing device 1101 format -- the device's format (DeviceFormat instance) 1102 1103 For existing partitions: 1104 1105 parents -- the disk that contains this partition 1106 major -- the device major 1107 minor -- the device minor 1108 sysfsPath -- sysfs device path 1109 1110 For new partitions: 1111 1112 partType -- primary,extended,&c (as parted constant) 1113 grow -- whether or not to grow the partition 1114 maxsize -- max size for growable partitions (in MB) 1115 size -- the device's size (in MB) 1116 bootable -- whether the partition is bootable 1117 parents -- a list of potential containing disks 1118 weight -- an initial sorting weight to assign 1119 """ 1120 self.req_disks = [] 1121 self.req_partType = None 1122 self.req_primary = None 1123 self.req_grow = None 1124 self.req_bootable = None 1125 self.req_size = 0 1126 self.req_base_size = 0 1127 self.req_max_size = 0 1128 self.req_base_weight = 0 1129 1130 self._bootable = False 1131 1132 StorageDevice.__init__(self, name, format=format, size=size, 1133 major=major, minor=minor, exists=exists, 1134 sysfsPath=sysfsPath, parents=parents) 1135 1136 if not exists: 1137 # this is a request, not a partition -- it has no parents 1138 self.req_disks = self.parents[:] 1139 for dev in self.parents: 1140 dev.removeChild() 1141 self.parents = [] 1142 1143 # FIXME: Validate partType, but only if this is a new partition 1144 # Otherwise, overwrite it with the partition's type. 1145 self._partType = None 1146 self.partedFlags = {} 1147 self._partedPartition = None 1148 self._origPath = None 1149 self._currentSize = 0 1150 1151 # FIXME: Validate size, but only if this is a new partition. 1152 # For existing partitions we will get the size from 1153 # parted. 1154 1155 if self.exists: 1156 log.debug("looking up parted Partition: %s" % self.path) 1157 self._partedPartition = self.disk.format.partedDisk.getPartitionByPath(self.path) 1158 if not self._partedPartition: 1159 raise DeviceError("cannot find parted partition instance", self.name) 1160 1161 self._origPath = self.path 1162 # collect information about the partition from parted 1163 self.probe() 1164 if self.getFlag(parted.PARTITION_PREP): 1165 # the only way to identify a PPC PReP Boot partition is to 1166 # check the partition type/flags, so do it here. 1167 self.format = getFormat("prepboot", device=self.path, exists=True) 1168 elif self.getFlag(parted.PARTITION_BIOS_GRUB): 1169 # the only way to identify a BIOS Boot partition is to 1170 # check the partition type/flags, so do it here. 1171 self.format = getFormat("biosboot", device=self.path, exists=True) 1172 else: 1173 # XXX It might be worthwhile to create a shit-simple 1174 # PartitionRequest class and pass one to this constructor 1175 # for new partitions. 1176 if not self._size: 1177 # default size for new partition requests 1178 self._size = self.defaultSize 1179 self.req_name = name 1180 self.req_partType = partType 1181 self.req_primary = primary 1182 self.req_max_size = numeric_type(maxsize) 1183 self.req_grow = grow 1184 self.req_bootable = bootable 1185 1186 # req_size may be manipulated in the course of partitioning 1187 self.req_size = self._size 1188 1189 # req_base_size will always remain constant 1190 self.req_base_size = self._size 1191 1192 self.req_base_weight = weight
1193
1194 - def __repr__(self):
1195 s = StorageDevice.__repr__(self) 1196 s += (" grow = %(grow)s max size = %(maxsize)s bootable = %(bootable)s\n" 1197 " part type = %(partType)s primary = %(primary)s\n" 1198 " partedPartition = %(partedPart)s\n" 1199 " disk = %(disk)s\n" % 1200 {"grow": self.req_grow, "maxsize": self.req_max_size, 1201 "bootable": self.bootable, "partType": self.partType, 1202 "primary": self.req_primary, 1203 "partedPart": self.partedPartition, "disk": self.disk}) 1204 1205 if self.partedPartition: 1206 s += (" start = %(start)s end = %(end)s length = %(length)s\n" 1207 " flags = %(flags)s" % 1208 {"length": self.partedPartition.geometry.length, 1209 "start": self.partedPartition.geometry.start, 1210 "end": self.partedPartition.geometry.end, 1211 "flags": self.partedPartition.getFlagsAsString()}) 1212 1213 return s
1214 1215 @property
1216 - def dict(self):
1217 d = super(PartitionDevice, self).dict 1218 d.update({"type": self.partType}) 1219 if not self.exists: 1220 d.update({"grow": self.req_grow, "maxsize": self.req_max_size, 1221 "bootable": self.bootable, 1222 "primary": self.req_primary}) 1223 1224 if self.partedPartition: 1225 d.update({"length": self.partedPartition.geometry.length, 1226 "start": self.partedPartition.geometry.start, 1227 "end": self.partedPartition.geometry.end, 1228 "flags": self.partedPartition.getFlagsAsString()}) 1229 return d
1230
1231 - def writeKS(self, f, preexisting=False, noformat=False, s=None):
1232 args = [] 1233 1234 if self.isExtended: 1235 return 1236 1237 if self.req_grow: 1238 args.append("--grow") 1239 if self.req_max_size: 1240 args.append("--maxsize=%s" % self.req_max_size) 1241 if self.req_primary: 1242 args.append("--asprimary") 1243 if self.req_size: 1244 args.append("--size=%s" % (self.req_size or self.defaultSize)) 1245 if preexisting: 1246 if len(self.req_disks) == 1: 1247 args.append("--ondisk=%s" % self.req_disks[0].name) 1248 else: 1249 args.append("--onpart=%s" % self.name) 1250 if noformat: 1251 args.append("--noformat") 1252 1253 f.write("#part ") 1254 self.format.writeKS(f) 1255 f.write(" %s" % " ".join(args)) 1256 if s: 1257 f.write(" %s" % s)
1258
1259 - def _setTargetSize(self, newsize):
1260 if newsize != self.currentSize: 1261 # change this partition's geometry in-memory so that other 1262 # partitioning operations can complete (e.g., autopart) 1263 self._targetSize = newsize 1264 disk = self.disk.format.partedDisk 1265 1266 # resize the partition's geometry in memory 1267 (constraint, geometry) = self._computeResize(self.partedPartition) 1268 disk.setPartitionGeometry(partition=self.partedPartition, 1269 constraint=constraint, 1270 start=geometry.start, end=geometry.end)
1271 1272 @property
1273 - def path(self):
1274 if not self.parents: 1275 devDir = StorageDevice._devDir 1276 else: 1277 devDir = self.parents[0]._devDir 1278 1279 return "%s/%s" % (devDir, self.name)
1280 1281 @property
1282 - def partType(self):
1283 """ Get the partition's type (as parted constant). """ 1284 try: 1285 ptype = self.partedPartition.type 1286 except AttributeError: 1287 ptype = self._partType 1288 1289 if not self.exists and ptype is None: 1290 ptype = self.req_partType 1291 1292 return ptype
1293 1294 @property
1295 - def isExtended(self):
1296 return (self.partType is not None and 1297 self.partType & parted.PARTITION_EXTENDED)
1298 1299 @property
1300 - def isLogical(self):
1301 return (self.partType is not None and 1302 self.partType & parted.PARTITION_LOGICAL)
1303 1304 @property
1305 - def isPrimary(self):
1306 return (self.partType is not None and 1307 self.partType == parted.PARTITION_NORMAL)
1308 1309 @property
1310 - def isProtected(self):
1311 return (self.partType is not None and 1312 self.partType & parted.PARTITION_PROTECTED)
1313 1314 @property
1315 - def fstabSpec(self):
1316 spec = self.path 1317 if self.disk and self.disk.type == 'dasd': 1318 spec = deviceNameToDiskByPath(self.name) 1319 elif self.format and self.format.uuid: 1320 spec = "UUID=%s" % self.format.uuid 1321 return spec
1322
1323 - def _getPartedPartition(self):
1324 return self._partedPartition
1325
1326 - def _setPartedPartition(self, partition):
1327 """ Set this PartitionDevice's parted Partition instance. """ 1328 log_method_call(self, self.name) 1329 if partition is None: 1330 path = None 1331 elif isinstance(partition, parted.Partition): 1332 path = partition.path 1333 else: 1334 raise ValueError("partition must be a parted.Partition instance") 1335 1336 log.debug("device %s new partedPartition %s" % (self.name, partition)) 1337 self._partedPartition = partition 1338 self.updateName()
1339 1340 partedPartition = property(lambda d: d._getPartedPartition(), 1341 lambda d,p: d._setPartedPartition(p)) 1342
1343 - def preCommitFixup(self, *args, **kwargs):
1344 """ Re-get self.partedPartition from the original disklabel. """ 1345 log_method_call(self, self.name) 1346 if not self.exists: 1347 return 1348 1349 # find the correct partition on the original parted.Disk since the 1350 # name/number we're now using may no longer match 1351 _disklabel = self.disk.originalFormat 1352 1353 if self.isExtended: 1354 # getPartitionBySector doesn't work on extended partitions 1355 _partition = _disklabel.extendedPartition 1356 log.debug("extended lookup found partition %s" 1357 % devicePathToName(getattr(_partition, "path", None))) 1358 else: 1359 # lookup the partition by sector to avoid the renumbering 1360 # nonsense entirely 1361 _sector = self.partedPartition.geometry.start 1362 _partition = _disklabel.partedDisk.getPartitionBySector(_sector) 1363 log.debug("sector-based lookup found partition %s" 1364 % devicePathToName(getattr(_partition, "path", None))) 1365 1366 self.partedPartition = _partition
1367
1368 - def _getWeight(self):
1369 return self.req_base_weight
1370
1371 - def _setWeight(self, weight):
1372 self.req_base_weight = weight
1373 1374 weight = property(lambda d: d._getWeight(), 1375 lambda d,w: d._setWeight(w)) 1376
1377 - def updateSysfsPath(self):
1378 """ Update this device's sysfs path. """ 1379 log_method_call(self, self.name, status=self.status) 1380 if not self.parents: 1381 self.sysfsPath = '' 1382 1383 elif isinstance(self.parents[0], DMDevice): 1384 dm_node = dm.dm_node_from_name(self.name) 1385 path = os.path.join("/sys", self.sysfsBlockDir, dm_node) 1386 self.sysfsPath = os.path.realpath(path)[4:] 1387 else: 1388 StorageDevice.updateSysfsPath(self)
1389
1390 - def updateName(self):
1391 if self.partedPartition is None: 1392 self._name = self.req_name 1393 else: 1394 self._name = \ 1395 devicePathToName(self.partedPartition.getDeviceNodeName())
1396
1397 - def dependsOn(self, dep):
1398 """ Return True if this device depends on dep. """ 1399 if isinstance(dep, PartitionDevice) and dep.isExtended and \ 1400 self.isLogical and self.disk == dep.disk: 1401 return True 1402 1403 return Device.dependsOn(self, dep)
1404
1405 - def _setFormat(self, format):
1406 """ Set the Device's format. """ 1407 log_method_call(self, self.name) 1408 StorageDevice._setFormat(self, format)
1409
1410 - def _setBootable(self, bootable):
1411 """ Set the bootable flag for this partition. """ 1412 if self.partedPartition: 1413 if iutil.isS390(): 1414 return 1415 if self.flagAvailable(parted.PARTITION_BOOT): 1416 if bootable: 1417 self.setFlag(parted.PARTITION_BOOT) 1418 else: 1419 self.unsetFlag(parted.PARTITION_BOOT) 1420 else: 1421 raise DeviceError("boot flag not available for this partition", self.name) 1422 1423 self._bootable = bootable 1424 else: 1425 self.req_bootable = bootable
1426
1427 - def _getBootable(self):
1428 return self._bootable or self.req_bootable
1429 1430 bootable = property(_getBootable, _setBootable) 1431
1432 - def flagAvailable(self, flag):
1433 if not self.partedPartition: 1434 return 1435 1436 return self.partedPartition.isFlagAvailable(flag)
1437
1438 - def getFlag(self, flag):
1439 log_method_call(self, path=self.path, flag=flag) 1440 if not self.partedPartition or not self.flagAvailable(flag): 1441 return 1442 1443 return self.partedPartition.getFlag(flag)
1444
1445 - def setFlag(self, flag):
1446 log_method_call(self, path=self.path, flag=flag) 1447 if not self.partedPartition or not self.flagAvailable(flag): 1448 return 1449 1450 self.partedPartition.setFlag(flag)
1451
1452 - def unsetFlag(self, flag):
1453 log_method_call(self, path=self.path, flag=flag) 1454 if not self.partedPartition or not self.flagAvailable(flag): 1455 return 1456 1457 self.partedPartition.unsetFlag(flag)
1458
1459 - def probe(self):
1460 """ Probe for any missing information about this device. 1461 1462 size, partition type, flags 1463 """ 1464 log_method_call(self, self.name, exists=self.exists) 1465 if not self.exists: 1466 return 1467 1468 # this is in MB 1469 self._size = self.partedPartition.getSize() 1470 self._currentSize = self._size 1471 self.targetSize = self._size 1472 1473 self._partType = self.partedPartition.type 1474 1475 self._bootable = self.getFlag(parted.PARTITION_BOOT)
1476
1477 - def _create(self, w):
1478 """ Create the device. """ 1479 log_method_call(self, self.name, status=self.status) 1480 self.disk.format.addPartition(self.partedPartition) 1481 1482 try: 1483 self.disk.format.commit() 1484 except DiskLabelCommitError: 1485 part = self.disk.format.partedDisk.getPartitionByPath(self.path) 1486 self.disk.format.removePartition(part) 1487 raise
1488
1489 - def _postCreate(self):
1490 if self.isExtended: 1491 partition = self.disk.format.extendedPartition 1492 else: 1493 start = self.partedPartition.geometry.start 1494 partition = self.disk.format.partedDisk.getPartitionBySector(start) 1495 1496 log.debug("post-commit partition path is %s" % getattr(partition, 1497 "path", None)) 1498 self.partedPartition = partition 1499 if not self.isExtended: 1500 # Ensure old metadata which lived in freespace so did not get 1501 # explictly destroyed by a destroyformat action gets wiped 1502 DeviceFormat(device=self.path, exists=True).destroy() 1503 1504 StorageDevice._postCreate(self) 1505 self._currentSize = self.partedPartition.getSize()
1506
1507 - def _computeResize(self, partition):
1508 log_method_call(self, self.name, status=self.status) 1509 1510 # compute new size for partition 1511 currentGeom = partition.geometry 1512 currentDev = currentGeom.device 1513 newLen = long(self.targetSize * 1024 * 1024) / currentDev.sectorSize 1514 newGeometry = parted.Geometry(device=currentDev, 1515 start=currentGeom.start, 1516 length=newLen) 1517 # and align the end sector 1518 newGeometry.end = self.disk.format.endAlignment.alignDown(newGeometry, 1519 newGeometry.end) 1520 constraint = parted.Constraint(exactGeom=newGeometry) 1521 1522 return (constraint, newGeometry)
1523
1524 - def resize(self, intf=None):
1525 """ Resize the device. 1526 1527 self.targetSize must be set to the new size. 1528 """ 1529 log_method_call(self, self.name, status=self.status) 1530 self._preDestroy() 1531 if self.targetSize != self.currentSize: 1532 # partedDisk has been restored to _origPartedDisk, so 1533 # recalculate resize geometry because we may have new 1534 # partitions on the disk, which could change constraints 1535 partedDisk = self.disk.format.partedDisk 1536 partition = partedDisk.getPartitionByPath(self.path) 1537 (constraint, geometry) = self._computeResize(partition) 1538 1539 partedDisk.setPartitionGeometry(partition=partition, 1540 constraint=constraint, 1541 start=geometry.start, 1542 end=geometry.end) 1543 1544 self.disk.format.commit() 1545 self._currentSize = partition.getSize()
1546
1547 - def _preDestroy(self):
1548 StorageDevice._preDestroy(self) 1549 if not self.sysfsPath: 1550 return 1551 1552 self.setupParents(orig=True)
1553
1554 - def _destroy(self):
1555 """ Destroy the device. """ 1556 log_method_call(self, self.name, status=self.status) 1557 # we should have already set self.partedPartition to point to the 1558 # partition on the original disklabel 1559 self.disk.originalFormat.removePartition(self.partedPartition) 1560 try: 1561 self.disk.originalFormat.commit() 1562 except DiskLabelCommitError: 1563 self.disk.originalFormat.addPartition(self.partedPartition) 1564 self.partedPartition = self.disk.originalFormat.partedDisk.getPartitionByPath(self.path) 1565 raise
1566
1567 - def deactivate(self):
1568 """ 1569 This is never called. For instructional purposes only. 1570 1571 We do not want multipath partitions disappearing upon their teardown(). 1572 """ 1573 if self.parents[0].type == 'dm-multipath': 1574 devmap = block.getMap(major=self.major, minor=self.minor) 1575 if devmap: 1576 try: 1577 block.removeDeviceMap(devmap) 1578 except Exception as e: 1579 raise DeviceTeardownError("failed to tear down device-mapper partition %s: %s" % (self.name, e)) 1580 udev_settle()
1581
1582 - def _getSize(self):
1583 """ Get the device's size. """ 1584 size = self._size 1585 if self.partedPartition: 1586 # this defaults to MB 1587 size = self.partedPartition.getSize() 1588 return size
1589
1590 - def _setSize(self, newsize):
1591 """ Set the device's size (for resize, not creation). 1592 1593 Arguments: 1594 1595 newsize -- the new size (in MB) 1596 1597 """ 1598 log_method_call(self, self.name, 1599 status=self.status, size=self._size, newsize=newsize) 1600 if not self.exists: 1601 raise DeviceError("device does not exist", self.name) 1602 1603 if newsize > self.disk.size: 1604 raise ValueError("partition size would exceed disk size") 1605 1606 # this defaults to MB 1607 maxAvailableSize = self.partedPartition.getMaxAvailableSize() 1608 1609 if newsize > maxAvailableSize: 1610 raise ValueError("new size is greater than available space") 1611 1612 # now convert the size to sectors and update the geometry 1613 geometry = self.partedPartition.geometry 1614 physicalSectorSize = geometry.device.physicalSectorSize 1615 1616 new_length = (newsize * (1024 * 1024)) / physicalSectorSize 1617 geometry.length = new_length
1618
1619 - def _getDisk(self):
1620 """ The disk that contains this partition.""" 1621 try: 1622 disk = self.parents[0] 1623 except IndexError: 1624 disk = None 1625 return disk
1626
1627 - def _setDisk(self, disk):
1628 """Change the parent. 1629 1630 Setting up a disk is not trivial. It has the potential to change 1631 the underlying object. If necessary we must also change this object. 1632 """ 1633 log_method_call(self, self.name, old=getattr(self.disk, "name", None), 1634 new=getattr(disk, "name", None)) 1635 if self.disk: 1636 self.disk.removeChild() 1637 1638 if disk: 1639 self.parents = [disk] 1640 disk.addChild() 1641 else: 1642 self.parents = []
1643 1644 disk = property(lambda p: p._getDisk(), lambda p,d: p._setDisk(d)) 1645 1646 @property
1647 - def maxSize(self):
1648 """ The maximum size this partition can be. """ 1649 # XXX: this is MB by default 1650 maxPartSize = self.partedPartition.getMaxAvailableSize() 1651 1652 if self.format.maxSize > maxPartSize: 1653 return maxPartSize 1654 else: 1655 return self.format.maxSize
1656 1657 @property
1658 - def currentSize(self):
1659 """ The device's actual size. """ 1660 if self.exists: 1661 return self._currentSize 1662 else: 1663 return 0
1664 1665 @property
1666 - def resizable(self):
1667 """ Can this type of device be resized? """ 1668 return super(PartitionDevice, self).resizable and \ 1669 self.disk.type != 'dasd'
1670
1671 - def checkSize(self):
1672 """ Check to make sure the size of the device is allowed by the 1673 format used. 1674 1675 Returns: 1676 0 - ok 1677 1 - Too large 1678 -1 - Too small 1679 """ 1680 if self.format.maxSize and self.size > self.format.maxSize: 1681 return 1 1682 elif (self.format.minSize and 1683 (not self.req_grow and 1684 self.size < self.format.minSize) or 1685 (self.req_grow and self.req_max_size and 1686 self.req_max_size < self.format.minSize)): 1687 return -1 1688 return 0
1689
1690 -class DMDevice(StorageDevice):
1691 """ A device-mapper device """ 1692 _type = "dm" 1693 _devDir = "/dev/mapper" 1694
1695 - def __init__(self, name, format=None, size=None, dmUuid=None, 1696 target=None, exists=None, parents=None, sysfsPath=''):
1697 """ Create a DMDevice instance. 1698 1699 Arguments: 1700 1701 name -- the device name (generally a device node's basename) 1702 1703 Keyword Arguments: 1704 1705 target -- the device-mapper target type (string) 1706 size -- the device's size (units/format TBD) 1707 dmUuid -- the device's device-mapper UUID 1708 sysfsPath -- sysfs device path 1709 format -- a DeviceFormat instance 1710 parents -- a list of required Device instances 1711 exists -- indicates whether this is an existing device 1712 """ 1713 StorageDevice.__init__(self, name, format=format, size=size, 1714 exists=exists, 1715 parents=parents, sysfsPath=sysfsPath) 1716 self.target = target 1717 self.dmUuid = dmUuid
1718
1719 - def __repr__(self):
1720 s = StorageDevice.__repr__(self) 1721 s += (" target = %(target)s dmUuid = %(dmUuid)s" % 1722 {"target": self.target, "dmUuid": self.dmUuid}) 1723 return s
1724 1725 @property
1726 - def dict(self):
1727 d = super(DMDevice, self).dict 1728 d.update({"target": self.target, "dmUuid": self.dmUuid}) 1729 return d
1730 1731 @property
1732 - def fstabSpec(self):
1733 """ Return the device specifier for use in /etc/fstab. """ 1734 return self.path
1735 1736 @property
1737 - def mapName(self):
1738 """ This device's device-mapper map name """ 1739 return self.name
1740 1741 @property
1742 - def status(self):
1743 _status = False 1744 for map in block.dm.maps(): 1745 if map.name == self.mapName: 1746 _status = map.live_table and not map.suspended 1747 break 1748 1749 return _status
1750
1751 - def updateSysfsPath(self):
1752 """ Update this device's sysfs path. """ 1753 log_method_call(self, self.name, status=self.status) 1754 if not self.exists: 1755 raise DeviceError("device has not been created", self.name) 1756 1757 if self.status: 1758 dm_node = self.getDMNode() 1759 path = os.path.join("/sys", self.sysfsBlockDir, dm_node) 1760 self.sysfsPath = os.path.realpath(path)[4:] 1761 else: 1762 self.sysfsPath = ''
1763 1764 #def getTargetType(self): 1765 # return dm.getDmTarget(name=self.name) 1766
1767 - def getDMNode(self):
1768 """ Return the dm-X (eg: dm-0) device node for this device. """ 1769 log_method_call(self, self.name, status=self.status) 1770 if not self.exists: 1771 raise DeviceError("device has not been created", self.name) 1772 1773 return dm.dm_node_from_name(self.name)
1774
1775 - def setupPartitions(self):
1776 log_method_call(self, name=self.name, kids=self.kids) 1777 rc = iutil.execWithRedirect("kpartx", 1778 ["-a", "-p", "p", self.path], 1779 stdout = "/dev/tty5", 1780 stderr = "/dev/tty5") 1781 if rc: 1782 raise DMError("partition activation failed for '%s'" % self.name) 1783 udev_settle()
1784
1785 - def teardownPartitions(self):
1786 log_method_call(self, name=self.name, kids=self.kids) 1787 rc = iutil.execWithRedirect("kpartx", 1788 ["-d", "-p", "p", self.path], 1789 stdout = "/dev/tty5", 1790 stderr = "/dev/tty5") 1791 if rc: 1792 raise DMError("partition deactivation failed for '%s'" % self.name) 1793 udev_settle()
1794
1795 - def _setName(self, name):
1796 """ Set the device's map name. """ 1797 log_method_call(self, self.name, status=self.status) 1798 if self.status: 1799 raise DeviceError("cannot rename active device", self.name) 1800 1801 self._name = name
1802 #self.sysfsPath = "/dev/disk/by-id/dm-name-%s" % self.name 1803 1804 name = property(lambda d: d._name, 1805 lambda d,n: d._setName(n)) 1806 1807 @property
1808 - def slave(self):
1809 """ This device's backing device. """ 1810 return self.parents[0]
1811
1812 1813 -class DMLinearDevice(DMDevice):
1814 _type = "dm-linear" 1815 _partitionable = True 1816 _isDisk = True 1817
1818 - def __init__(self, name, format=None, size=None, dmUuid=None, 1819 exists=None, parents=None, sysfsPath=''):
1820 """ Create a DMLinearDevice instance. 1821 1822 Arguments: 1823 1824 name -- the device name (generally a device node's basename) 1825 1826 Keyword Arguments: 1827 1828 size -- the device's size (units/format TBD) 1829 dmUuid -- the device's device-mapper UUID 1830 sysfsPath -- sysfs device path 1831 format -- a DeviceFormat instance 1832 parents -- a list of required Device instances 1833 exists -- indicates whether this is an existing device 1834 """ 1835 if not parents: 1836 raise ValueError("DMLinearDevice requires a backing block device") 1837 1838 DMDevice.__init__(self, name, format=format, size=size, 1839 parents=parents, sysfsPath=sysfsPath, 1840 exists=exists, target="linear", dmUuid=dmUuid)
1841
1842 - def _setup(self, intf=None, orig=False):
1843 """ Open, or set up, a device. """ 1844 log_method_call(self, self.name, orig=orig, status=self.status, 1845 controllable=self.controllable) 1846 slave_length = self.slave.partedDevice.length 1847 dm.dm_create_linear(self.name, self.slave.path, slave_length, 1848 self.dmUuid)
1849
1850 - def _postSetup(self):
1851 StorageDevice._postSetup(self) 1852 self.setupPartitions() 1853 udev_settle()
1854
1855 - def _teardown(self, recursive=False):
1856 self.teardownPartitions() 1857 udev_settle() 1858 dm.dm_remove(self.name) 1859 udev_settle()
1860
1861 - def deactivate(self, recursive=False):
1862 StorageDevice.teardown(self, recursive=recursive)
1863
1864 - def teardown(self, recursive=None):
1865 """ Close, or tear down, a device. """ 1866 log_method_call(self, self.name, status=self.status, 1867 controllable=self.controllable) 1868 if not self._preTeardown(recursive=recursive): 1869 return 1870 1871 log.debug("not tearing down dm-linear device %s" % self.name)
1872 1873 @property
1874 - def description(self):
1875 return self.model
1876
1877 1878 -class DMCryptDevice(DMDevice):
1879 """ A dm-crypt device """ 1880 _type = "dm-crypt" 1881
1882 - def __init__(self, name, format=None, size=None, uuid=None, 1883 exists=None, sysfsPath='', parents=None):
1884 """ Create a DMCryptDevice instance. 1885 1886 Arguments: 1887 1888 name -- the device name (generally a device node's basename) 1889 1890 Keyword Arguments: 1891 1892 size -- the device's size (units/format TBD) 1893 sysfsPath -- sysfs device path 1894 format -- a DeviceFormat instance 1895 parents -- a list of required Device instances 1896 exists -- indicates whether this is an existing device 1897 """ 1898 DMDevice.__init__(self, name, format=format, size=size, 1899 parents=parents, sysfsPath=sysfsPath, 1900 exists=exists, target="crypt")
1901
1902 -class LUKSDevice(DMCryptDevice):
1903 """ A mapped LUKS device. """ 1904 _type = "luks/dm-crypt" 1905 _packages = ["cryptsetup-luks"] 1906
1907 - def __init__(self, name, format=None, size=None, uuid=None, 1908 exists=None, sysfsPath='', parents=None):
1909 """ Create a LUKSDevice instance. 1910 1911 Arguments: 1912 1913 name -- the device name 1914 1915 Keyword Arguments: 1916 1917 size -- the device's size in MB 1918 uuid -- the device's UUID 1919 sysfsPath -- sysfs device path 1920 format -- a DeviceFormat instance 1921 parents -- a list of required Device instances 1922 exists -- indicates whether this is an existing device 1923 """ 1924 DMCryptDevice.__init__(self, name, format=format, size=size, 1925 parents=parents, sysfsPath=sysfsPath, 1926 uuid=None, exists=exists)
1927
1928 - def writeKS(self, f, preexisting=False, noformat=False, s=None):
1929 self.slave.writeKS(f, preexisting=preexisting, noformat=noformat, s=s) 1930 f.write(" ") 1931 self.format.writeKS(f) 1932 if s: 1933 f.write(" %s" % s)
1934 1935 @property
1936 - def size(self):
1937 if not self.exists or not self.partedDevice: 1938 # the LUKS metadata area is 2MB 1939 size = float(self.slave.size) - 2.0 1940 else: 1941 size = self.partedDevice.getSize() 1942 return size
1943
1944 - def _postCreate(self):
1945 self._name = self.slave.format.mapName 1946 StorageDevice._postCreate(self)
1947
1948 - def _postTeardown(self, recursive=False):
1949 if not recursive: 1950 # this is handled by StorageDevice._postTeardown if recursive 1951 # is True 1952 self.teardownParents(recursive=recursive) 1953 1954 StorageDevice._postTeardown(self, recursive=recursive)
1955
1956 - def dracutSetupArgs(self):
1957 return set(["rd.luks.uuid=luks-%s" % self.slave.format.uuid])
1958
1959 -class LVMVolumeGroupDevice(DMDevice):
1960 """ An LVM Volume Group 1961 1962 XXX Maybe this should inherit from StorageDevice instead of 1963 DMDevice since there's no actual device. 1964 """ 1965 _type = "lvmvg" 1966 _packages = ["lvm2"] 1967
1968 - def __init__(self, name, parents, size=None, free=None, 1969 peSize=None, peCount=None, peFree=None, pvCount=None, 1970 uuid=None, exists=None, sysfsPath=''):
1971 """ Create a LVMVolumeGroupDevice instance. 1972 1973 Arguments: 1974 1975 name -- the device name (generally a device node's basename) 1976 parents -- a list of physical volumes (StorageDevice) 1977 1978 Keyword Arguments: 1979 1980 peSize -- physical extent size (in MB) 1981 exists -- indicates whether this is an existing device 1982 sysfsPath -- sysfs device path 1983 1984 For existing VG's only: 1985 1986 size -- the VG's size (in MB) 1987 free -- amount of free space in the VG 1988 peFree -- number of free extents 1989 peCount -- total number of extents 1990 pvCount -- number of PVs in this VG 1991 uuid -- the VG's UUID 1992 1993 """ 1994 self.pvClass = get_device_format_class("lvmpv") 1995 if not self.pvClass: 1996 raise StorageError("cannot find 'lvmpv' class") 1997 1998 if isinstance(parents, list): 1999 for dev in parents: 2000 if not isinstance(dev.format, self.pvClass): 2001 raise ValueError("constructor requires a list of PVs") 2002 elif not isinstance(parents.format, self.pvClass): 2003 raise ValueError("constructor requires a list of PVs") 2004 2005 DMDevice.__init__(self, name, parents=parents, 2006 exists=exists, sysfsPath=sysfsPath) 2007 2008 self.uuid = uuid 2009 self.free = numeric_type(free) 2010 self.peSize = numeric_type(peSize) 2011 self.peCount = numeric_type(peCount) 2012 self.peFree = numeric_type(peFree) 2013 self.pvCount = numeric_type(pvCount) 2014 self.lv_names = [] 2015 self.lv_uuids = [] 2016 self.lv_sizes = [] 2017 self.lv_attr = [] 2018 self.hasDuplicate = False 2019 self.reserved_percent = 0 2020 self.reserved_space = 0 2021 2022 # circular references, here I come 2023 self._lvs = [] 2024 2025 # TODO: validate peSize if given 2026 if not self.peSize: 2027 self.peSize = 32.0 # MB 2028 2029 if not self.exists: 2030 self.pvCount = len(self.parents) 2031 2032 # Some snapshots don't have a proper LV as an origin (--vorigin). 2033 # They still occupy space in the VG. 2034 self.voriginSnapshots = {}
2035
2036 - def __repr__(self):
2037 s = DMDevice.__repr__(self) 2038 s += (" free = %(free)s PE Size = %(peSize)s PE Count = %(peCount)s\n" 2039 " PE Free = %(peFree)s PV Count = %(pvCount)s\n" 2040 " LV Names = %(lv_names)s modified = %(modified)s\n" 2041 " extents = %(extents)s free space = %(freeSpace)s\n" 2042 " free extents = %(freeExtents)s" 2043 " reserved percent = %(rpct)s reserved space = %(res)s\n" 2044 " PVs = %(pvs)s\n" 2045 " LVs = %(lvs)s" % 2046 {"free": self.free, "peSize": self.peSize, "peCount": self.peCount, 2047 "peFree": self.peFree, "pvCount": self.pvCount, 2048 "lv_names": self.lv_names, "modified": self.isModified, 2049 "extents": self.extents, "freeSpace": self.freeSpace, 2050 "freeExtents": self.freeExtents, 2051 "rpct": self.reserved_percent, "res": self.reserved_space, 2052 "pvs": pprint.pformat([str(p) for p in self.pvs]), 2053 "lvs": pprint.pformat([str(l) for l in self.lvs])}) 2054 return s
2055 2056 @property
2057 - def dict(self):
2058 d = super(LVMVolumeGroupDevice, self).dict 2059 d.update({"free": self.free, "peSize": self.peSize, 2060 "peCount": self.peCount, "peFree": self.peFree, 2061 "pvCount": self.pvCount, "extents": self.extents, 2062 "freeSpace": self.freeSpace, 2063 "freeExtents": self.freeExtents, 2064 "lv_names": self.lv_names, 2065 "lv_uuids": self.lv_uuids, 2066 "lv_sizes": self.lv_sizes, 2067 "lv_attr": self.lv_attr, 2068 "reserved_percent": self.reserved_percent, 2069 "reserved_space": self.reserved_space, 2070 "lvNames": [lv.name for lv in self.lvs]}) 2071 return d
2072
2073 - def writeKS(self, f, preexisting=False, noformat=False, s=None):
2074 args = ["--pesize=%s" % int(self.peSize * 1024)] 2075 pvs = [] 2076 2077 for pv in self.pvs: 2078 pvs.append("pv.%s" % pv.format.majorminor) 2079 2080 if preexisting: 2081 args.append("--useexisting") 2082 if noformat: 2083 args.append("--noformat") 2084 2085 if self.reserved_space: 2086 args.append("--reserved-space=%d" % self.reserved_space) 2087 elif self.reserved_percent: 2088 args.append("--reserved-percent=%d" % self.reserved_percent) 2089 2090 f.write("#volgroup %s %s %s" % (self.name, " ".join(args), " ".join(pvs))) 2091 if s: 2092 f.write(" %s" % s)
2093 2094 @property
2095 - def mapName(self):
2096 """ This device's device-mapper map name """ 2097 # Thank you lvm for this lovely hack. 2098 return self.name.replace("-","--")
2099 2100 @property
2101 - def path(self):
2102 """ Device node representing this device. """ 2103 return "%s/%s" % (self._devDir, self.mapName)
2104
2105 - def updateSysfsPath(self):
2106 """ Update this device's sysfs path. """ 2107 log_method_call(self, self.name, status=self.status) 2108 if not self.exists: 2109 raise DeviceError("device has not been created", self.name) 2110 2111 self.sysfsPath = ''
2112 2113 @property
2114 - def status(self):
2115 """ The device's status (True means active). """ 2116 if not self.exists: 2117 return False 2118 2119 # certainly if any of this VG's LVs are active then so are we 2120 for lv in self.lvs: 2121 if lv.status: 2122 return True 2123 2124 # if any of our PVs are not active then we cannot be 2125 for pv in self.pvs: 2126 if not pv.status: 2127 return False 2128 2129 # if we are missing some of our PVs we cannot be active 2130 if not self.complete: 2131 return False 2132 2133 return True
2134
2135 - def _addDevice(self, device):
2136 """ Add a new physical volume device to the volume group. 2137 2138 XXX This is for use by device probing routines and is not 2139 intended for modification of the VG. 2140 """ 2141 log_method_call(self, 2142 self.name, 2143 device=device.name, 2144 status=self.status) 2145 if not self.exists: 2146 raise DeviceError("device does not exist", self.name) 2147 2148 if not isinstance(device.format, self.pvClass): 2149 raise ValueError("addDevice requires a PV arg") 2150 2151 if self.uuid and device.format.vgUuid != self.uuid: 2152 # this means there is another vg with the same name on the system 2153 # set hasDuplicate which will make complete return False 2154 # and let devicetree._handleInconsistencies() further handle this. 2155 # Note we still add the device to our parents for use by 2156 # devicetree._handleInconsistencies() 2157 self.hasDuplicate = True 2158 2159 if device in self.pvs: 2160 raise ValueError("device is already a member of this VG") 2161 2162 self.parents.append(device) 2163 device.addChild() 2164 2165 # now see if the VG can be activated 2166 if self.complete: 2167 self.setup()
2168
2169 - def _removeDevice(self, device):
2170 """ Remove a physical volume from the volume group. 2171 2172 This is for cases like clearing of preexisting partitions. 2173 """ 2174 log_method_call(self, 2175 self.name, 2176 device=device.name, 2177 status=self.status) 2178 try: 2179 self.parents.remove(device) 2180 except ValueError: 2181 raise ValueError("cannot remove non-member PV device from VG") 2182 2183 device.removeChild()
2184