/[smeserver]/cdrom.image/sme9/updates/storage/devices.py
ViewVC logotype

Contents of /cdrom.image/sme9/updates/storage/devices.py

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.5 - (show annotations) (download) (as text)
Sun Dec 22 04:27:51 2013 UTC (10 years, 10 months ago) by wellsi
Branch: MAIN
CVS Tags: HEAD
Changes since 1.4: +0 -0 lines
Content type: text/x-python
FILE REMOVED
anaconda updates now handled via patches

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 time
100
101 # device backend modules
102 from devicelibs import mdraid
103 from devicelibs import lvm
104 from devicelibs import dm
105 import parted
106 import _ped
107 import platform
108 import block
109
110 from errors import *
111 from iutil import notify_kernel, numeric_type
112 from flags import flags
113 from .storage_log import log_method_call
114 from udev import *
115 from formats import get_device_format_class, getFormat, DeviceFormat
116
117 import gettext
118 _ = lambda x: gettext.ldgettext("anaconda", x)
119
120 import logging
121 log = logging.getLogger("storage")
122
123 def get_device_majors():
124 majors = {}
125 for line in open("/proc/devices").readlines():
126 try:
127 (major, device) = line.split()
128 except ValueError:
129 continue
130 try:
131 majors[int(major)] = device
132 except ValueError:
133 continue
134 return majors
135 device_majors = get_device_majors()
136
137
138 def devicePathToName(devicePath):
139 if devicePath.startswith("/dev/"):
140 name = devicePath[5:]
141 else:
142 name = devicePath
143
144 if name.startswith("mapper/"):
145 name = name[7:]
146
147 return name
148
149
150 def deviceNameToDiskByPath(deviceName=None):
151 if not deviceName:
152 return ""
153
154 ret = None
155 for dev in udev_get_block_devices():
156 if udev_device_get_name(dev) == deviceName:
157 ret = udev_device_get_by_path(dev)
158 break
159
160 if ret:
161 return ret
162 raise DeviceNotFoundError(deviceName)
163
164 class Device(object):
165 """ A generic device.
166
167 Device instances know which devices they depend upon (parents
168 attribute). They do not know which devices depend upon them, but
169 they do know whether or not they have any dependent devices
170 (isleaf attribute).
171
172 A Device's setup method should set up all parent devices as well
173 as the device itself. It should not run the resident format's
174 setup method.
175
176 Which Device types rely on their parents' formats being active?
177 DMCryptDevice
178
179 A Device's teardown method should accept the keyword argument
180 recursive, which takes a boolean value and indicates whether or
181 not to recursively close parent devices.
182
183 A Device's create method should create all parent devices as well
184 as the device itself. It should also run the Device's setup method
185 after creating the device. The create method should not create a
186 device's resident format.
187
188 Which device type rely on their parents' formats to be created
189 before they can be created/assembled?
190 VolumeGroup
191 DMCryptDevice
192
193 A Device's destroy method should destroy any resident format
194 before destroying the device itself.
195
196 """
197
198 # This is a counter for generating unique ids for Devices.
199 _id = 0
200
201 _type = "generic device"
202 _packages = []
203 _services = []
204
205 def __init__(self, name, parents=None):
206 """ Create a Device instance.
207
208 Arguments:
209
210 name -- the device name (generally a device node's basename)
211
212 Keyword Arguments:
213
214 parents -- a list of required Device instances
215
216 """
217 self._name = name
218 if parents is None:
219 parents = []
220 elif not isinstance(parents, list):
221 raise ValueError("parents must be a list of Device instances")
222 self.parents = parents
223 self.kids = 0
224
225 # Set this instance's id and increment the counter.
226 self.id = Device._id
227 Device._id += 1
228
229 for parent in self.parents:
230 parent.addChild()
231
232 def __deepcopy__(self, memo):
233 """ Create a deep copy of a Device instance.
234
235 We can't do copy.deepcopy on parted objects, which is okay.
236 For these parted objects, we just do a shallow copy.
237 """
238 new = self.__class__.__new__(self.__class__)
239 memo[id(self)] = new
240 dont_copy_attrs = ('_raidSet',)
241 shallow_copy_attrs = ('_partedDevice', '_partedPartition')
242 for (attr, value) in self.__dict__.items():
243 if attr in dont_copy_attrs:
244 setattr(new, attr, value)
245 elif attr in shallow_copy_attrs:
246 setattr(new, attr, copy.copy(value))
247 else:
248 setattr(new, attr, copy.deepcopy(value, memo))
249
250 return new
251
252 def __str__(self):
253 s = ("%(type)s instance (%(id)s) --\n"
254 " name = %(name)s status = %(status)s"
255 " parents = %(parents)s\n"
256 " kids = %(kids)s\n"
257 " id = %(dev_id)s\n" %
258 {"type": self.__class__.__name__, "id": "%#x" % id(self),
259 "name": self.name, "parents": self.parents, "kids": self.kids,
260 "status": self.status, "dev_id": self.id})
261 return s
262
263 @property
264 def dict(self):
265 d = {"type": self.type, "name": self.name,
266 "parents": [p.name for p in self.parents]}
267 return d
268
269 def writeKS(self, f, preexisting=False, noformat=False, s=None):
270 return
271
272 def removeChild(self):
273 log_method_call(self, name=self.name, kids=self.kids)
274 self.kids -= 1
275
276 def addChild(self):
277 log_method_call(self, name=self.name, kids=self.kids)
278 self.kids += 1
279
280 def setup(self, intf=None):
281 """ Open, or set up, a device. """
282 raise NotImplementedError("setup method not defined for Device")
283
284 def teardown(self, recursive=None):
285 """ Close, or tear down, a device. """
286 raise NotImplementedError("teardown method not defined for Device")
287
288 def create(self, intf=None):
289 """ Create the device. """
290 raise NotImplementedError("create method not defined for Device")
291
292 def destroy(self):
293 """ Destroy the device. """
294 raise NotImplementedError("destroy method not defined for Device")
295
296 def setupParents(self, orig=False):
297 """ Run setup method of all parent devices. """
298 log_method_call(self, name=self.name, orig=orig, kids=self.kids)
299 for parent in self.parents:
300 parent.setup(orig=orig)
301
302 def teardownParents(self, recursive=None):
303 """ Run teardown method of all parent devices. """
304 for parent in self.parents:
305 parent.teardown(recursive=recursive)
306
307 def createParents(self):
308 """ Run create method of all parent devices. """
309 log.info("NOTE: recursive device creation disabled")
310 for parent in self.parents:
311 if not parent.exists:
312 raise DeviceError("parent device does not exist", self.name)
313 #parent.create()
314
315 def dependsOn(self, dep):
316 """ Return True if this device depends on dep. """
317 # XXX does a device depend on itself?
318 if dep in self.parents:
319 return True
320
321 for parent in self.parents:
322 if parent.dependsOn(dep):
323 return True
324
325 return False
326
327 def dracutSetupArgs(self):
328 return set()
329
330 @property
331 def status(self):
332 """ This device's status.
333
334 For now, this should return a boolean:
335 True the device is open and ready for use
336 False the device is not open
337 """
338 return False
339
340 @property
341 def name(self):
342 """ This device's name. """
343 return self._name
344
345 @property
346 def isleaf(self):
347 """ True if this device has no children. """
348 return self.kids == 0
349
350 @property
351 def typeDescription(self):
352 """ String describing the device type. """
353 return self._type
354
355 @property
356 def type(self):
357 """ Device type. """
358 return self._type
359
360 @property
361 def packages(self):
362 """ List of packages required to manage devices of this type.
363
364 This list includes the packages required by its parent devices.
365 """
366 packages = self._packages
367 for parent in self.parents:
368 for package in parent.packages:
369 if package not in packages:
370 packages.append(package)
371
372 return packages
373
374 @property
375 def services(self):
376 """ List of services required to manage devices of this type.
377
378 This list includes the services required by its parent devices."
379 """
380 services = self._services
381 for parent in self.parents:
382 for service in parent.services:
383 if service not in services:
384 services.append(service)
385
386 return services
387
388 @property
389 def mediaPresent(self):
390 return True
391
392
393 class NetworkStorageDevice(object):
394 """ Virtual base class for network backed storage devices """
395
396 def __init__(self, host_address=None, nic=None):
397 """ Create a NetworkStorage Device instance. Note this class is only
398 to be used as a baseclass and then only with multiple inheritance.
399 The only correct use is:
400 class MyStorageDevice(StorageDevice, NetworkStorageDevice):
401
402 The sole purpose of this class is to:
403 1) Be able to check if a StorageDevice is network backed
404 (using isinstance).
405 2) To be able to get the host address of the host (server) backing
406 the storage *or* the NIC through which the storage is connected
407
408 Arguments:
409
410 host_address -- host address of the backing server
411 nic -- nic to which the storage is bound
412 """
413 self.host_address = host_address
414 self.nic = nic
415
416
417 class StorageDevice(Device):
418 """ A generic storage device.
419
420 A fully qualified path to the device node can be obtained via the
421 path attribute, although it is not guaranteed to be useful, or
422 even present, unless the StorageDevice's setup method has been
423 run.
424
425 StorageDevice instances can optionally contain a filesystem,
426 represented by an FS instance. A StorageDevice's create method
427 should create a filesystem if one has been specified.
428 """
429 _type = "storage device"
430 _devDir = "/dev"
431 sysfsBlockDir = "class/block"
432 _resizable = False
433 _partitionable = False
434 _isDisk = False
435
436 def __init__(self, device, format=None,
437 size=None, major=None, minor=None,
438 sysfsPath='', parents=None, exists=None, serial=None,
439 vendor="", model="", bus=""):
440 """ Create a StorageDevice instance.
441
442 Arguments:
443
444 device -- the device name (generally a device node's basename)
445
446 Keyword Arguments:
447
448 size -- the device's size (units/format TBD)
449 major -- the device major
450 minor -- the device minor
451 sysfsPath -- sysfs device path
452 format -- a DeviceFormat instance
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, device, parents=parents)
466
467 self.uuid = None
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.immutable = None
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 @property
489 def packages(self):
490 """ List of packages required to manage devices of this type.
491
492 This list includes the packages required by this device's
493 format type as well those required by all of its parent
494 devices.
495 """
496 packages = super(StorageDevice, self).packages
497 packages.extend(self.format.packages)
498 for parent in self.parents:
499 for package in parent.format.packages:
500 if package not in packages:
501 packages.append(package)
502
503 return packages
504
505 @property
506 def services(self):
507 """ List of services required to manage devices of this type.
508
509 This list includes the services required by this device's
510 format type as well those required by all of its parent
511 devices.
512 """
513 services = super(StorageDevice, self).services
514 services.extend(self.format.services)
515 for parent in self.parents:
516 for service in parent.format.services:
517 if service not in services:
518 services.append(service)
519
520 return services
521
522 @property
523 def partedDevice(self):
524 if self.exists and self.status and not self._partedDevice:
525 log.debug("looking up parted Device: %s" % self.path)
526
527 # We aren't guaranteed to be able to get a device. In
528 # particular, built-in USB flash readers show up as devices but
529 # do not always have any media present, so parted won't be able
530 # to find a device.
531 try:
532 self._partedDevice = parted.Device(path=self.path)
533 except (_ped.IOException, _ped.DeviceException):
534 pass
535
536 return self._partedDevice
537
538 def _getTargetSize(self):
539 return self._targetSize
540
541 def _setTargetSize(self, newsize):
542 self._targetSize = newsize
543
544 targetSize = property(lambda s: s._getTargetSize(),
545 lambda s, v: s._setTargetSize(v),
546 doc="Target size of this device")
547
548 def __str__(self):
549 s = Device.__str__(self)
550 s += (" uuid = %(uuid)s format = %(format)r size = %(size)s\n"
551 " major = %(major)s minor = %(minor)r exists = %(exists)s\n"
552 " sysfs path = %(sysfs)s partedDevice = %(partedDevice)r\n"
553 " target size = %(targetSize)s path = %(path)s\n"
554 " format args = %(formatArgs)s originalFormat = %(origFmt)s" %
555 {"uuid": self.uuid, "format": self.format, "size": self.size,
556 "major": self.major, "minor": self.minor, "exists": self.exists,
557 "sysfs": self.sysfsPath, "partedDevice": self.partedDevice,
558 "targetSize": self.targetSize, "path": self.path,
559 "formatArgs": self.formatArgs, "origFmt": self.originalFormat})
560 return s
561
562 @property
563 def dict(self):
564 d = super(StorageDevice, self).dict
565 d.update({"uuid": self.uuid, "size": self.size,
566 "format": self.format.dict, "removable": self.removable,
567 "major": self.major, "minor": self.minor,
568 "exists": self.exists, "sysfs": self.sysfsPath,
569 "targetSize": self.targetSize, "path": self.path})
570 return d
571
572 @property
573 def path(self):
574 """ Device node representing this device. """
575 return "%s/%s" % (self._devDir, self.name)
576
577 def updateSysfsPath(self):
578 """ Update this device's sysfs path. """
579 log_method_call(self, self.name, status=self.status)
580 sysfsName = self.name.replace("/", "!")
581 path = os.path.join("/sys", self.sysfsBlockDir, sysfsName)
582 self.sysfsPath = os.path.realpath(path)[4:]
583 log.debug("%s sysfsPath set to %s" % (self.name, self.sysfsPath))
584
585 @property
586 def formatArgs(self):
587 """ Device-specific arguments to format creation program. """
588 return []
589
590 @property
591 def resizable(self):
592 """ Can this type of device be resized? """
593 return self._resizable and self.exists and \
594 ((self.format and self.format.resizable) or not self.format)
595
596 def notifyKernel(self):
597 """ Send a 'change' uevent to the kernel for this device. """
598 log_method_call(self, self.name, status=self.status)
599 if not self.exists:
600 log.debug("not sending change uevent for non-existent device")
601 return
602
603 if not self.status:
604 log.debug("not sending change uevent for inactive device")
605 return
606
607 path = os.path.normpath("/sys/%s" % self.sysfsPath)
608 try:
609 notify_kernel(path, action="change")
610 except Exception, e:
611 log.warning("failed to notify kernel of change: %s" % e)
612
613 @property
614 def fstabSpec(self):
615 spec = self.path
616 if self.format and self.format.uuid:
617 spec = "UUID=%s" % self.format.uuid
618 return spec
619
620 def resize(self, intf=None):
621 """ Resize the device.
622
623 New size should already be set.
624 """
625 raise NotImplementedError("resize method not defined for StorageDevice")
626
627 def setup(self, intf=None, orig=False):
628 """ Open, or set up, a device. """
629 log_method_call(self, self.name, orig=orig, status=self.status)
630 if not self.exists:
631 raise DeviceError("device has not been created", self.name)
632
633 self.setupParents(orig=orig)
634 for parent in self.parents:
635 if orig:
636 parent.originalFormat.setup()
637 else:
638 parent.format.setup()
639
640 def teardown(self, recursive=None):
641 """ Close, or tear down, a device. """
642 log_method_call(self, self.name, status=self.status)
643 if not self.exists and not recursive:
644 raise DeviceError("device has not been created", self.name)
645
646 if self.status:
647 if self.originalFormat.exists:
648 self.originalFormat.teardown()
649 self.format.cacheMajorminor()
650 if self.format.exists:
651 self.format.teardown()
652 udev_settle()
653
654 if recursive:
655 self.teardownParents(recursive=recursive)
656
657 def _getSize(self):
658 """ Get the device's size in MB, accounting for pending changes. """
659 if self.exists and not self.mediaPresent:
660 return 0
661
662 if self.exists and self.partedDevice:
663 self._size = self.currentSize
664
665 size = self._size
666 if self.exists and self.resizable and self.targetSize != size:
667 size = self.targetSize
668
669 return size
670
671 def _setSize(self, newsize):
672 """ Set the device's size to a new value. """
673 if newsize > self.maxSize:
674 raise DeviceError("device cannot be larger than %s MB" %
675 (self.maxSize(),), self.name)
676 self._size = newsize
677
678 size = property(lambda x: x._getSize(),
679 lambda x, y: x._setSize(y),
680 doc="The device's size in MB, accounting for pending changes")
681
682 @property
683 def currentSize(self):
684 """ The device's actual size. """
685 size = 0
686 if self.exists and self.partedDevice:
687 size = self.partedDevice.getSize()
688 elif self.exists:
689 size = self._size
690 return size
691
692 @property
693 def minSize(self):
694 """ The minimum size this device can be. """
695 if self.format.minSize:
696 return self.format.minSize
697 else:
698 return self.size
699
700 @property
701 def maxSize(self):
702 """ The maximum size this device can be. """
703 if self.format.maxSize > self.currentSize:
704 return self.currentSize
705 else:
706 return self.format.maxSize
707
708 @property
709 def status(self):
710 """ This device's status.
711
712 For now, this should return a boolean:
713 True the device is open and ready for use
714 False the device is not open
715 """
716 if not self.exists:
717 return False
718 return os.access(self.path, os.W_OK)
719
720 def _setFormat(self, format):
721 """ Set the Device's format. """
722 if not format:
723 format = getFormat(None, device=self.path, exists=self.exists)
724 log_method_call(self, self.name, type=format.type,
725 current=getattr(self._format, "type", None))
726 if self._format and self._format.status:
727 # FIXME: self.format.status doesn't mean much
728 raise DeviceError("cannot replace active format", self.name)
729
730 self._format = format
731 self._format.device = self.path
732
733 def _getFormat(self):
734 return self._format
735
736 format = property(lambda d: d._getFormat(),
737 lambda d,f: d._setFormat(f),
738 doc="The device's formatting.")
739
740 def preCommitFixup(self, *args, **kwargs):
741 """ Do any necessary pre-commit fixups."""
742 pass
743
744 def create(self, intf=None):
745 """ Create the device. """
746 log_method_call(self, self.name, status=self.status)
747 if self.exists:
748 raise DeviceError("device has already been created", self.name)
749
750 self.createParents()
751 self.setupParents()
752 self.exists = True
753 self.setup()
754
755 def destroy(self):
756 """ Destroy the device. """
757 log_method_call(self, self.name, status=self.status)
758 if not self.exists:
759 raise DeviceError("device has not been created", self.name)
760
761 if not self.isleaf:
762 raise DeviceError("Cannot destroy non-leaf device", self.name)
763
764 self.exists = False
765 # we already did this in DeviceTree._removeDevice
766 #for parent in self.parents:
767 # parent.removeChild()
768
769 @property
770 def removable(self):
771 devpath = os.path.normpath("/sys/%s" % self.sysfsPath)
772 remfile = os.path.normpath("%s/removable" % devpath)
773 return (self.sysfsPath and os.path.exists(devpath) and
774 os.access(remfile, os.R_OK) and
775 open(remfile).readline().strip() == "1")
776
777 @property
778 def isDisk(self):
779 return self._isDisk
780
781 @property
782 def partitionable(self):
783 return self._partitionable
784
785 @property
786 def partitioned(self):
787 return self.format.type == "disklabel" and self.partitionable
788
789 @property
790 def serial(self):
791 return self._serial
792
793 @property
794 def model(self):
795 if not self._model:
796 self._model = getattr(self.partedDevice, "model", "")
797 return self._model
798
799 @property
800 def vendor(self):
801 return self._vendor
802
803 @property
804 def growable(self):
805 """ True if this device or it's component devices are growable. """
806 grow = getattr(self, "req_grow", False)
807 if not grow:
808 for parent in self.parents:
809 grow = parent.growable
810 if grow:
811 break
812 return grow
813
814 def checkSize(self):
815 """ Check to make sure the size of the device is allowed by the
816 format used.
817
818 return None is all is ok
819 return large or small depending on the problem
820 """
821 problem = None
822 if self.format.maxSize and self.size > self.format.maxSize:
823 problem = _("large")
824 elif self.format.minSize and self.size < self.format.minSize:
825 problem = _("small")
826 return problem
827
828 class DiskDevice(StorageDevice):
829 """ A disk """
830 _type = "disk"
831 _partitionable = True
832 _isDisk = True
833
834 def __init__(self, device, format=None,
835 size=None, major=None, minor=None, sysfsPath='',
836 parents=None, serial=None, vendor="", model="", bus="",
837 exists=True):
838 """ Create a DiskDevice instance.
839
840 Arguments:
841
842 device -- the device name (generally a device node's basename)
843
844 Keyword Arguments:
845
846 size -- the device's size (units/format TBD)
847 major -- the device major
848 minor -- the device minor
849 sysfsPath -- sysfs device path
850 format -- a DeviceFormat instance
851 parents -- a list of required Device instances
852 removable -- whether or not this is a removable device
853 serial -- the ID_SERIAL_SHORT for this device
854 vendor -- the manufacturer of this Device
855 model -- manufacturer's device model string
856 bus -- the interconnect this device uses
857
858
859 DiskDevices always exist.
860 """
861 StorageDevice.__init__(self, device, format=format, size=size,
862 major=major, minor=minor, exists=exists,
863 sysfsPath=sysfsPath, parents=parents,
864 serial=serial, model=model,
865 vendor=vendor, bus=bus)
866
867 def __str__(self):
868 s = StorageDevice.__str__(self)
869 s += (" removable = %(removable)s partedDevice = %(partedDevice)r" %
870 {"removable": self.removable, "partedDevice": self.partedDevice})
871 return s
872
873 @property
874 def mediaPresent(self):
875 if not self.partedDevice:
876 return False
877
878 # Some drivers (cpqarray <blegh>) make block device nodes for
879 # controllers with no disks attached and then report a 0 size,
880 # treat this as no media present
881 return self.partedDevice.getSize() != 0
882
883 @property
884 def description(self):
885 return self.model
886
887 @property
888 def size(self):
889 """ The disk's size in MB """
890 return super(DiskDevice, self).size
891 #size = property(StorageDevice._getSize)
892
893 def probe(self):
894 """ Probe for any missing information about this device.
895
896 pyparted should be able to tell us anything we want to know.
897 size, disklabel type, maybe even partition layout
898 """
899 log_method_call(self, self.name, size=self.size, partedDevice=self.partedDevice)
900
901 def destroy(self):
902 """ Destroy the device. """
903 log_method_call(self, self.name, status=self.status)
904 if not self.mediaPresent:
905 raise DeviceError("cannot destroy disk with no media", self.name)
906
907 self.teardown()
908
909 def setup(self, intf=None, orig=False):
910 """ Open, or set up, a device. """
911 log_method_call(self, self.name, orig=orig, status=self.status)
912 if not os.path.exists(self.path):
913 raise DeviceError("device does not exist", self.name)
914
915
916 class PartitionDevice(StorageDevice):
917 """ A disk partition.
918
919 On types and flags...
920
921 We don't need to deal with numerical partition types at all. The
922 only type we are concerned with is primary/logical/extended. Usage
923 specification is accomplished through the use of flags, which we
924 will set according to the partition's format.
925 """
926 _type = "partition"
927 _resizable = True
928
929 def __init__(self, name, format=None,
930 size=None, grow=False, maxsize=None,
931 major=None, minor=None, bootable=None,
932 sysfsPath='', parents=None, exists=None,
933 partType=None, primary=False, weight=0):
934 """ Create a PartitionDevice instance.
935
936 Arguments:
937
938 name -- the device name (generally a device node's basename)
939
940 Keyword Arguments:
941
942 exists -- indicates whether this is an existing device
943 format -- the device's format (DeviceFormat instance)
944
945 For existing partitions:
946
947 parents -- the disk that contains this partition
948 major -- the device major
949 minor -- the device minor
950 sysfsPath -- sysfs device path
951
952 For new partitions:
953
954 partType -- primary,extended,&c (as parted constant)
955 grow -- whether or not to grow the partition
956 maxsize -- max size for growable partitions (in MB)
957 size -- the device's size (in MB)
958 bootable -- whether the partition is bootable
959 parents -- a list of potential containing disks
960 weight -- an initial sorting weight to assign
961 """
962 self.req_disks = []
963 self.req_partType = None
964 self.req_primary = None
965 self.req_grow = None
966 self.req_bootable = None
967 self.req_size = 0
968 self.req_base_size = 0
969 self.req_max_size = 0
970 self.req_base_weight = 0
971
972 self._bootable = False
973
974 StorageDevice.__init__(self, name, format=format, size=size,
975 major=major, minor=minor, exists=exists,
976 sysfsPath=sysfsPath, parents=parents)
977
978 if not exists:
979 # this is a request, not a partition -- it has no parents
980 self.req_disks = self.parents[:]
981 for dev in self.parents:
982 dev.removeChild()
983 self.parents = []
984
985 # FIXME: Validate partType, but only if this is a new partition
986 # Otherwise, overwrite it with the partition's type.
987 self._partType = None
988 self.partedFlags = {}
989 self._partedPartition = None
990 self._origPath = None
991 self._currentSize = 0
992
993 # FIXME: Validate size, but only if this is a new partition.
994 # For existing partitions we will get the size from
995 # parted.
996
997 if self.exists:
998 log.debug("looking up parted Partition: %s" % self.path)
999 self._partedPartition = self.disk.format.partedDisk.getPartitionByPath(self.path)
1000 if not self._partedPartition:
1001 raise DeviceError("cannot find parted partition instance", self.name)
1002
1003 self._origPath = self.path
1004 # collect information about the partition from parted
1005 self.probe()
1006 if self.getFlag(parted.PARTITION_PREP):
1007 # the only way to identify a PPC PReP Boot partition is to
1008 # check the partition type/flags, so do it here.
1009 self.format = getFormat("prepboot", device=self.path, exists=True)
1010 else:
1011 # XXX It might be worthwhile to create a shit-simple
1012 # PartitionRequest class and pass one to this constructor
1013 # for new partitions.
1014 self.req_name = name
1015 self.req_partType = partType
1016 self.req_primary = primary
1017 self.req_max_size = numeric_type(maxsize)
1018 self.req_grow = grow
1019 self.req_bootable = bootable
1020
1021 # req_size may be manipulated in the course of partitioning
1022 self.req_size = self._size
1023
1024 # req_base_size will always remain constant
1025 self.req_base_size = self._size
1026
1027 self.req_base_weight = weight
1028
1029 def __str__(self):
1030 s = StorageDevice.__str__(self)
1031 s += (" grow = %(grow)s max size = %(maxsize)s bootable = %(bootable)s\n"
1032 " part type = %(partType)s primary = %(primary)s\n"
1033 " partedPartition = %(partedPart)r disk = %(disk)r\n" %
1034 {"grow": self.req_grow, "maxsize": self.req_max_size,
1035 "bootable": self.bootable, "partType": self.partType,
1036 "primary": self.req_primary,
1037 "partedPart": self.partedPartition, "disk": self.disk})
1038
1039 if self.partedPartition:
1040 s += (" start = %(start)s end = %(end)s length = %(length)s\n"
1041 " flags = %(flags)s" %
1042 {"length": self.partedPartition.geometry.length,
1043 "start": self.partedPartition.geometry.start,
1044 "end": self.partedPartition.geometry.end,
1045 "flags": self.partedPartition.getFlagsAsString()})
1046
1047 return s
1048
1049 @property
1050 def dict(self):
1051 d = super(PartitionDevice, self).dict
1052 d.update({"type": self.partType})
1053 if not self.exists:
1054 d.update({"grow": self.req_grow, "maxsize": self.req_max_size,
1055 "bootable": self.bootable,
1056 "primary": self.req_primary})
1057
1058 if self.partedPartition:
1059 d.update({"length": self.partedPartition.geometry.length,
1060 "start": self.partedPartition.geometry.start,
1061 "end": self.partedPartition.geometry.end,
1062 "flags": self.partedPartition.getFlagsAsString()})
1063 return d
1064
1065 def writeKS(self, f, preexisting=False, noformat=False, s=None):
1066 args = []
1067
1068 if self.isExtended:
1069 return
1070
1071 if self.req_grow:
1072 args.append("--grow")
1073 if self.req_max_size:
1074 args.append("--maxsize=%s" % self.req_max_size)
1075 if self.req_primary:
1076 args.append("--asprimary")
1077 if self.req_size:
1078 args.append("--size=%s" % (self.req_size or 1))
1079 if preexisting:
1080 if len(self.req_disks) == 1:
1081 args.append("--ondisk=%s" % self.req_disks[0].name)
1082 else:
1083 args.append("--onpart=%s" % self.name)
1084 if noformat:
1085 args.append("--noformat")
1086
1087 f.write("#part ")
1088 self.format.writeKS(f)
1089 f.write(" %s" % " ".join(args))
1090 if s:
1091 f.write(" %s" % s)
1092
1093 def _setTargetSize(self, newsize):
1094 if newsize != self.currentSize:
1095 # change this partition's geometry in-memory so that other
1096 # partitioning operations can complete (e.g., autopart)
1097 self._targetSize = newsize
1098 disk = self.disk.format.partedDisk
1099
1100 # resize the partition's geometry in memory
1101 (constraint, geometry) = self._computeResize(self.partedPartition)
1102 disk.setPartitionGeometry(partition=self.partedPartition,
1103 constraint=constraint,
1104 start=geometry.start, end=geometry.end)
1105
1106 @property
1107 def path(self):
1108 if not self.parents:
1109 devDir = StorageDevice._devDir
1110 else:
1111 devDir = self.parents[0]._devDir
1112
1113 return "%s/%s" % (devDir, self.name)
1114
1115 @property
1116 def partType(self):
1117 """ Get the partition's type (as parted constant). """
1118 try:
1119 ptype = self.partedPartition.type
1120 except AttributeError:
1121 ptype = self._partType
1122
1123 if not self.exists and ptype is None:
1124 ptype = self.req_partType
1125
1126 return ptype
1127
1128 @property
1129 def isExtended(self):
1130 return (self.partType is not None and
1131 self.partType & parted.PARTITION_EXTENDED)
1132
1133 @property
1134 def isLogical(self):
1135 return (self.partType is not None and
1136 self.partType & parted.PARTITION_LOGICAL)
1137
1138 @property
1139 def isPrimary(self):
1140 return (self.partType is not None and
1141 self.partType == parted.PARTITION_NORMAL)
1142
1143 @property
1144 def isProtected(self):
1145 return (self.partType is not None and
1146 self.partType & parted.PARTITION_PROTECTED)
1147
1148 @property
1149 def fstabSpec(self):
1150 spec = self.path
1151 if self.disk and self.disk.type == 'dasd':
1152 spec = deviceNameToDiskByPath(self.name)
1153 elif self.format and self.format.uuid:
1154 spec = "UUID=%s" % self.format.uuid
1155 return spec
1156
1157 def _getPartedPartition(self):
1158 return self._partedPartition
1159
1160 def _setPartedPartition(self, partition):
1161 """ Set this PartitionDevice's parted Partition instance. """
1162 log_method_call(self, self.name)
1163 if partition is None:
1164 path = None
1165 elif isinstance(partition, parted.Partition):
1166 path = partition.path
1167 else:
1168 raise ValueError("partition must be a parted.Partition instance")
1169
1170 log.debug("device %s new partedPartition %s has path %s" % (self.name,
1171 partition,
1172 path))
1173 self._partedPartition = partition
1174 self.updateName()
1175
1176 partedPartition = property(lambda d: d._getPartedPartition(),
1177 lambda d,p: d._setPartedPartition(p))
1178
1179 def preCommitFixup(self, *args, **kwargs):
1180 """ Re-get self.partedPartition from the original disklabel. """
1181 log_method_call(self, self.name)
1182 if not self.exists:
1183 return
1184
1185 # find the correct partition on the original parted.Disk since the
1186 # name/number we're now using may no longer match
1187 _disklabel = self.disk.originalFormat
1188
1189 if self.isExtended:
1190 # getPartitionBySector doesn't work on extended partitions
1191 _partition = _disklabel.extendedPartition
1192 log.debug("extended lookup found partition %s"
1193 % devicePathToName(getattr(_partition, "path", None)))
1194 else:
1195 # lookup the partition by sector to avoid the renumbering
1196 # nonsense entirely
1197 _sector = self.partedPartition.geometry.start
1198 _partition = _disklabel.partedDisk.getPartitionBySector(_sector)
1199 log.debug("sector-based lookup found partition %s"
1200 % devicePathToName(getattr(_partition, "path", None)))
1201
1202 self.partedPartition = _partition
1203
1204 def _getWeight(self):
1205 return self.req_base_weight
1206
1207 def _setWeight(self, weight):
1208 self.req_base_weight = weight
1209
1210 weight = property(lambda d: d._getWeight(),
1211 lambda d,w: d._setWeight(w))
1212
1213 def updateSysfsPath(self):
1214 """ Update this device's sysfs path. """
1215 log_method_call(self, self.name, status=self.status)
1216 if not self.parents:
1217 self.sysfsPath = ''
1218
1219 elif self.parents[0]._devDir == "/dev/mapper":
1220 dm_node = dm.dm_node_from_name(self.name)
1221 path = os.path.join("/sys", self.sysfsBlockDir, dm_node)
1222 self.sysfsPath = os.path.realpath(path)[4:]
1223
1224 else:
1225 StorageDevice.updateSysfsPath(self)
1226
1227 def updateName(self):
1228 if self.partedPartition is None:
1229 self._name = self.req_name
1230 else:
1231 self._name = \
1232 devicePathToName(self.partedPartition.getDeviceNodeName())
1233
1234 def dependsOn(self, dep):
1235 """ Return True if this device depends on dep. """
1236 if isinstance(dep, PartitionDevice) and dep.isExtended and \
1237 self.isLogical and self.disk == dep.disk:
1238 return True
1239
1240 return Device.dependsOn(self, dep)
1241
1242 def _setFormat(self, format):
1243 """ Set the Device's format. """
1244 log_method_call(self, self.name)
1245 StorageDevice._setFormat(self, format)
1246
1247 def _setBootable(self, bootable):
1248 """ Set the bootable flag for this partition. """
1249 if self.partedPartition:
1250 if iutil.isS390():
1251 return
1252 if self.flagAvailable(parted.PARTITION_BOOT):
1253 if bootable:
1254 self.setFlag(parted.PARTITION_BOOT)
1255 else:
1256 self.unsetFlag(parted.PARTITION_BOOT)
1257 else:
1258 raise DeviceError("boot flag not available for this partition", self.name)
1259
1260 self._bootable = bootable
1261 else:
1262 self.req_bootable = bootable
1263
1264 def _getBootable(self):
1265 return self._bootable or self.req_bootable
1266
1267 bootable = property(_getBootable, _setBootable)
1268
1269 def flagAvailable(self, flag):
1270 log_method_call(self, path=self.path, flag=flag)
1271 if not self.partedPartition:
1272 return
1273
1274 return self.partedPartition.isFlagAvailable(flag)
1275
1276 def getFlag(self, flag):
1277 log_method_call(self, path=self.path, flag=flag)
1278 if not self.partedPartition or not self.flagAvailable(flag):
1279 return
1280
1281 return self.partedPartition.getFlag(flag)
1282
1283 def setFlag(self, flag):
1284 log_method_call(self, path=self.path, flag=flag)
1285 if not self.partedPartition or not self.flagAvailable(flag):
1286 return
1287
1288 self.partedPartition.setFlag(flag)
1289
1290 def unsetFlag(self, flag):
1291 log_method_call(self, path=self.path, flag=flag)
1292 if not self.partedPartition or not self.flagAvailable(flag):
1293 return
1294
1295 self.partedPartition.unsetFlag(flag)
1296
1297 def probe(self):
1298 """ Probe for any missing information about this device.
1299
1300 size, partition type, flags
1301 """
1302 log_method_call(self, self.name, exists=self.exists)
1303 if not self.exists:
1304 return
1305
1306 # this is in MB
1307 self._size = self.partedPartition.getSize()
1308 self._currentSize = self._size
1309 self.targetSize = self._size
1310
1311 self._partType = self.partedPartition.type
1312
1313 self._bootable = self.getFlag(parted.PARTITION_BOOT)
1314
1315 def create(self, intf=None):
1316 """ Create the device. """
1317 log_method_call(self, self.name, status=self.status)
1318 if self.exists:
1319 raise DeviceError("device already exists", self.name)
1320
1321 w = None
1322 if intf:
1323 w = intf.waitWindow(_("Creating"),
1324 _("Creating device %s") % (self.path,))
1325
1326 try:
1327 self.createParents()
1328 self.setupParents()
1329
1330 self.disk.format.addPartition(self.partedPartition)
1331
1332 try:
1333 self.disk.format.commit()
1334 except DiskLabelCommitError:
1335 part = self.disk.format.partedDisk.getPartitionByPath(self.path)
1336 self.disk.format.removePartition(part)
1337 raise
1338
1339 if not self.isExtended:
1340 # Ensure old metadata which lived in freespace so did not get
1341 # explictly destroyed by a destroyformat action gets wiped
1342 DeviceFormat(device=self.path, exists=True).destroy()
1343 except Exception:
1344 raise
1345 else:
1346 self.partedPartition = self.disk.format.partedDisk.getPartitionByPath(self.path)
1347
1348 self.exists = True
1349 self._currentSize = self.partedPartition.getSize()
1350 self.setup()
1351 finally:
1352 if w:
1353 w.pop()
1354
1355 def _computeResize(self, partition):
1356 log_method_call(self, self.name, status=self.status)
1357
1358 # compute new size for partition
1359 currentGeom = partition.geometry
1360 currentDev = currentGeom.device
1361 newLen = long(self.targetSize * 1024 * 1024) / currentDev.sectorSize
1362 newGeometry = parted.Geometry(device=currentDev,
1363 start=currentGeom.start,
1364 length=newLen)
1365 # and align the end sector
1366 newGeometry.end = self.disk.format.endAlignment.alignDown(newGeometry,
1367 newGeometry.end)
1368 constraint = parted.Constraint(exactGeom=newGeometry)
1369
1370 return (constraint, newGeometry)
1371
1372 def resize(self, intf=None):
1373 """ Resize the device.
1374
1375 self.targetSize must be set to the new size.
1376 """
1377 log_method_call(self, self.name, status=self.status)
1378
1379 if self.targetSize != self.currentSize:
1380 # partedDisk has been restored to _origPartedDisk, so
1381 # recalculate resize geometry because we may have new
1382 # partitions on the disk, which could change constraints
1383 partedDisk = self.disk.format.partedDisk
1384 partition = partedDisk.getPartitionByPath(self.path)
1385 (constraint, geometry) = self._computeResize(partition)
1386
1387 partedDisk.setPartitionGeometry(partition=partition,
1388 constraint=constraint,
1389 start=geometry.start,
1390 end=geometry.end)
1391
1392 self.disk.format.commit()
1393 self._currentSize = partition.getSize()
1394
1395 def destroy(self):
1396 """ Destroy the device. """
1397 log_method_call(self, self.name, status=self.status)
1398 if not self.exists:
1399 raise DeviceError("device has not been created", self.name)
1400
1401 if not self.sysfsPath:
1402 return
1403
1404 if not self.isleaf:
1405 raise DeviceError("Cannot destroy non-leaf device", self.name)
1406
1407 self.setupParents(orig=True)
1408
1409 # we should have already set self.partedPartition to point to the
1410 # partition on the original disklabel
1411 self.disk.originalFormat.removePartition(self.partedPartition)
1412 try:
1413 self.disk.originalFormat.commit()
1414 except DiskLabelCommitError:
1415 self.disk.originalFormat.addPartition(self.partedPartition)
1416 self.partedPartition = self.disk.originalFormat.partedDisk.getPartitionByPath(self.path)
1417 raise
1418
1419 self.exists = False
1420
1421 def teardown(self, recursive=None):
1422 """ Close, or tear down, a device. """
1423 log_method_call(self, self.name, status=self.status)
1424 if not self.exists and not recursive:
1425 raise DeviceError("device has not been created", self.name)
1426
1427 if self.status:
1428 if self.originalFormat.exists:
1429 self.originalFormat.teardown()
1430 if self.format.exists:
1431 self.format.teardown()
1432
1433 StorageDevice.teardown(self, recursive=recursive)
1434
1435 def deactivate(self):
1436 """
1437 This is never called. For instructional purposes only.
1438
1439 We do not want multipath partitions disappearing upon their teardown().
1440 """
1441 if self.parents[0].type == 'dm-multipath':
1442 devmap = block.getMap(major=self.major, minor=self.minor)
1443 if devmap:
1444 try:
1445 block.removeDeviceMap(devmap)
1446 except Exception as e:
1447 raise DeviceTeardownError("failed to tear down device-mapper partition %s: %s" % (self.name, e))
1448 udev_settle()
1449
1450 def _getSize(self):
1451 """ Get the device's size. """
1452 size = self._size
1453 if self.partedPartition:
1454 # this defaults to MB
1455 size = self.partedPartition.getSize()
1456 return size
1457
1458 def _setSize(self, newsize):
1459 """ Set the device's size (for resize, not creation).
1460
1461 Arguments:
1462
1463 newsize -- the new size (in MB)
1464
1465 """
1466 log_method_call(self, self.name,
1467 status=self.status, size=self._size, newsize=newsize)
1468 if not self.exists:
1469 raise DeviceError("device does not exist", self.name)
1470
1471 if newsize > self.disk.size:
1472 raise ValueError("partition size would exceed disk size")
1473
1474 # this defaults to MB
1475 maxAvailableSize = self.partedPartition.getMaxAvailableSize()
1476
1477 if newsize > maxAvailableSize:
1478 raise ValueError("new size is greater than available space")
1479
1480 # now convert the size to sectors and update the geometry
1481 geometry = self.partedPartition.geometry
1482 physicalSectorSize = geometry.device.physicalSectorSize
1483
1484 new_length = (newsize * (1024 * 1024)) / physicalSectorSize
1485 geometry.length = new_length
1486
1487 def _getDisk(self):
1488 """ The disk that contains this partition."""
1489 try:
1490 disk = self.parents[0]
1491 except IndexError:
1492 disk = None
1493 return disk
1494
1495 def _setDisk(self, disk):
1496 """Change the parent.
1497
1498 Setting up a disk is not trivial. It has the potential to change
1499 the underlying object. If necessary we must also change this object.
1500 """
1501 log_method_call(self, self.name, old=self.disk, new=disk)
1502 if self.disk:
1503 self.disk.removeChild()
1504
1505 if disk:
1506 self.parents = [disk]
1507 disk.addChild()
1508 else:
1509 self.parents = []
1510
1511 disk = property(lambda p: p._getDisk(), lambda p,d: p._setDisk(d))
1512
1513 @property
1514 def maxSize(self):
1515 """ The maximum size this partition can be. """
1516 # XXX: this is MB by default
1517 maxPartSize = self.partedPartition.getMaxAvailableSize()
1518
1519 if self.format.maxSize > maxPartSize:
1520 return maxPartSize
1521 else:
1522 return self.format.maxSize
1523
1524 @property
1525 def currentSize(self):
1526 """ The device's actual size. """
1527 if self.exists:
1528 return self._currentSize
1529 else:
1530 return 0
1531
1532 @property
1533 def resizable(self):
1534 """ Can this type of device be resized? """
1535 return super(PartitionDevice, self).resizable and \
1536 self.disk.type != 'dasd'
1537
1538 def checkSize(self):
1539 """ Check to make sure the size of the device is allowed by the
1540 format used.
1541
1542 return None is all is ok
1543 return large or small depending on the problem
1544 """
1545 problem = None
1546 if self.format.maxSize and self.size > self.format.maxSize:
1547 problem = _("large")
1548 elif (self.format.minSize and
1549 (not self.req_grow and
1550 self.size < self.format.minSize) or
1551 (self.req_grow and self.req_max_size and
1552 self.req_max_size < self.format.minSize)):
1553 problem = _("small")
1554 return problem
1555
1556 class DMDevice(StorageDevice):
1557 """ A device-mapper device """
1558 _type = "dm"
1559 _devDir = "/dev/mapper"
1560
1561 def __init__(self, name, format=None, size=None, dmUuid=None,
1562 target=None, exists=None, parents=None, sysfsPath=''):
1563 """ Create a DMDevice instance.
1564
1565 Arguments:
1566
1567 name -- the device name (generally a device node's basename)
1568
1569 Keyword Arguments:
1570
1571 target -- the device-mapper target type (string)
1572 size -- the device's size (units/format TBD)
1573 dmUuid -- the device's device-mapper UUID
1574 sysfsPath -- sysfs device path
1575 format -- a DeviceFormat instance
1576 parents -- a list of required Device instances
1577 exists -- indicates whether this is an existing device
1578 """
1579 StorageDevice.__init__(self, name, format=format, size=size,
1580 exists=exists,
1581 parents=parents, sysfsPath=sysfsPath)
1582 self.target = target
1583 self.dmUuid = dmUuid
1584
1585 def __str__(self):
1586 s = StorageDevice.__str__(self)
1587 s += (" target = %(target)s dmUuid = %(dmUuid)s" %
1588 {"target": self.target, "dmUuid": self.dmUuid})
1589 return s
1590
1591 @property
1592 def dict(self):
1593 d = super(DMDevice, self).dict
1594 d.update({"target": self.target, "dmUuid": self.dmUuid})
1595 return d
1596
1597 @property
1598 def fstabSpec(self):
1599 """ Return the device specifier for use in /etc/fstab. """
1600 return self.path
1601
1602 @property
1603 def mapName(self):
1604 """ This device's device-mapper map name """
1605 return self.name
1606
1607 @property
1608 def status(self):
1609 _status = False
1610 for map in block.dm.maps():
1611 if map.name == self.mapName:
1612 _status = map.live_table and not map.suspended
1613 break
1614
1615 return _status
1616
1617 def updateSysfsPath(self):
1618 """ Update this device's sysfs path. """
1619 log_method_call(self, self.name, status=self.status)
1620 if not self.exists:
1621 raise DeviceError("device has not been created", self.name)
1622
1623 if self.status:
1624 dm_node = self.getDMNode()
1625 path = os.path.join("/sys", self.sysfsBlockDir, dm_node)
1626 self.sysfsPath = os.path.realpath(path)[4:]
1627 else:
1628 self.sysfsPath = ''
1629
1630 #def getTargetType(self):
1631 # return dm.getDmTarget(name=self.name)
1632
1633 def getDMNode(self):
1634 """ Return the dm-X (eg: dm-0) device node for this device. """
1635 log_method_call(self, self.name, status=self.status)
1636 if not self.exists:
1637 raise DeviceError("device has not been created", self.name)
1638
1639 return dm.dm_node_from_name(self.name)
1640
1641 def _setName(self, name):
1642 """ Set the device's map name. """
1643 log_method_call(self, self.name, status=self.status)
1644 if self.status:
1645 raise DeviceError("cannot rename active device", self.name)
1646
1647 self._name = name
1648 #self.sysfsPath = "/dev/disk/by-id/dm-name-%s" % self.name
1649
1650 name = property(lambda d: d._name,
1651 lambda d,n: d._setName(n))
1652
1653
1654 class DMCryptDevice(DMDevice):
1655 """ A dm-crypt device """
1656 _type = "dm-crypt"
1657
1658 def __init__(self, name, format=None, size=None, uuid=None,
1659 exists=None, sysfsPath='', parents=None):
1660 """ Create a DMCryptDevice instance.
1661
1662 Arguments:
1663
1664 name -- the device name (generally a device node's basename)
1665
1666 Keyword Arguments:
1667
1668 size -- the device's size (units/format TBD)
1669 sysfsPath -- sysfs device path
1670 format -- a DeviceFormat instance
1671 parents -- a list of required Device instances
1672 exists -- indicates whether this is an existing device
1673 """
1674 DMDevice.__init__(self, name, format=format, size=size,
1675 parents=parents, sysfsPath=sysfsPath,
1676 exists=exists, target="crypt")
1677
1678 class LUKSDevice(DMCryptDevice):
1679 """ A mapped LUKS device. """
1680 _type = "luks/dm-crypt"
1681 _packages = ["cryptsetup-luks"]
1682
1683 def __init__(self, name, format=None, size=None, uuid=None,
1684 exists=None, sysfsPath='', parents=None):
1685 """ Create a LUKSDevice instance.
1686
1687 Arguments:
1688
1689 name -- the device name
1690
1691 Keyword Arguments:
1692
1693 size -- the device's size in MB
1694 uuid -- the device's UUID
1695 sysfsPath -- sysfs device path
1696 format -- a DeviceFormat instance
1697 parents -- a list of required Device instances
1698 exists -- indicates whether this is an existing device
1699 """
1700 DMCryptDevice.__init__(self, name, format=format, size=size,
1701 parents=parents, sysfsPath=sysfsPath,
1702 uuid=None, exists=exists)
1703
1704 def writeKS(self, f, preexisting=False, noformat=False, s=None):
1705 # XXX This is a bad hack, but there's no better alternative.
1706 # The self.format here is a filesystem object, and returns
1707 # the mountpoint. The self.slave.format is a LUKS object,
1708 # which just returns "--encrypted". We need to swith these two
1709 # because the mountpoint should go right after the ks command,
1710 # like part or raid, and not at the end.
1711 # With this switch, we get something like:
1712 # "#raid <mountpoint> --fstype ... --encrypted"
1713 # Changing just the order of the writeKS methods does not help.
1714 # The result would be "<mountpoint> --fstype #raid --encrypted ...".
1715 # We need to get the mountpoint *inside* the string.
1716 __self_format = self._format
1717 __slave_format = self.slave._format
1718
1719 # exchange format devices
1720 self._format = __slave_format
1721 self.slave._format = __self_format
1722
1723 self.slave.writeKS(f, preexisting=preexisting, noformat=noformat, s=s)
1724 f.write(" ")
1725 self.format.writeKS(f)
1726 if s:
1727 f.write(" %s" % s)
1728
1729 # restore format devices
1730 self._format = __self_format
1731 self.slave._format = __slave_format
1732
1733 @property
1734 def size(self):
1735 if not self.exists or not self.partedDevice:
1736 # the LUKS metadata area is 2MB
1737 size = float(self.slave.size) - 2.0
1738 else:
1739 size = self.partedDevice.getSize()
1740 return size
1741
1742 def create(self, intf=None):
1743 """ Create the device. """
1744 log_method_call(self, self.name, status=self.status)
1745 if self.exists:
1746 raise DeviceError("device already exists", self.name)
1747
1748 self.createParents()
1749 self.setupParents()
1750
1751 #if not self.slave.format.exists:
1752 # self.slave.format.create()
1753 self._name = self.slave.format.mapName
1754 self.exists = True
1755 self.setup()
1756
1757 def setup(self, intf=None, orig=False):
1758 """ Open, or set up, a device. """
1759 log_method_call(self, self.name, orig=orig, status=self.status)
1760 if not self.exists:
1761 raise DeviceError("device has not been created", self.name)
1762
1763 self.slave.setup(orig=orig)
1764 if orig:
1765 self.slave.originalFormat.setup()
1766 else:
1767 self.slave.format.setup()
1768
1769 udev_settle()
1770
1771 # we always probe since the device may not be set up when we want
1772 # information about it
1773 self._size = self.currentSize
1774
1775 def teardown(self, recursive=False):
1776 """ Close, or tear down, a device. """
1777 log_method_call(self, self.name, status=self.status)
1778 if not self.exists and not recursive:
1779 raise DeviceError("device has not been created", self.name)
1780
1781 if self.status:
1782 if self.originalFormat.exists:
1783 self.originalFormat.teardown()
1784 if self.format.exists:
1785 self.format.teardown()
1786 udev_settle()
1787
1788 if self.slave.originalFormat.exists:
1789 self.slave.originalFormat.teardown()
1790 udev_settle()
1791
1792 if self.slave.format.exists:
1793 self.slave.format.teardown()
1794 udev_settle()
1795
1796 if recursive:
1797 self.teardownParents(recursive=recursive)
1798
1799 def destroy(self):
1800 log_method_call(self, self.name, status=self.status)
1801 self.format.teardown()
1802 udev_settle()
1803 self.teardown()
1804
1805 @property
1806 def slave(self):
1807 """ This device's backing device. """
1808 return self.parents[0]
1809
1810 def dracutSetupArgs(self):
1811 return set(["rd_LUKS_UUID=luks-%s" % self.slave.format.uuid])
1812
1813
1814 class LVMVolumeGroupDevice(DMDevice):
1815 """ An LVM Volume Group
1816
1817 XXX Maybe this should inherit from StorageDevice instead of
1818 DMDevice since there's no actual device.
1819 """
1820 _type = "lvmvg"
1821 _packages = ["lvm2"]
1822
1823 def __init__(self, name, parents, size=None, free=None,
1824 peSize=None, peCount=None, peFree=None, pvCount=None,
1825 uuid=None, exists=None, sysfsPath=''):
1826 """ Create a LVMVolumeGroupDevice instance.
1827
1828 Arguments:
1829
1830 name -- the device name (generally a device node's basename)
1831 parents -- a list of physical volumes (StorageDevice)
1832
1833 Keyword Arguments:
1834
1835 peSize -- physical extent size (in MB)
1836 exists -- indicates whether this is an existing device
1837 sysfsPath -- sysfs device path
1838
1839 For existing VG's only:
1840
1841 size -- the VG's size (in MB)
1842 free -- amount of free space in the VG
1843 peFree -- number of free extents
1844 peCount -- total number of extents
1845 pvCount -- number of PVs in this VG
1846 uuid -- the VG's UUID
1847
1848 """
1849 self.pvClass = get_device_format_class("lvmpv")
1850 if not self.pvClass:
1851 raise StorageError("cannot find 'lvmpv' class")
1852
1853 if isinstance(parents, list):
1854 for dev in parents:
1855 if not isinstance(dev.format, self.pvClass):
1856 raise ValueError("constructor requires a list of PVs")
1857 elif not isinstance(parents.format, self.pvClass):
1858 raise ValueError("constructor requires a list of PVs")
1859
1860 DMDevice.__init__(self, name, parents=parents,
1861 exists=exists, sysfsPath=sysfsPath)
1862
1863 self.uuid = uuid
1864 self.free = numeric_type(free)
1865 self.peSize = numeric_type(peSize)
1866 self.peCount = numeric_type(peCount)
1867 self.peFree = numeric_type(peFree)
1868 self.pvCount = numeric_type(pvCount)
1869 self.lv_names = []
1870 self.lv_uuids = []
1871 self.lv_sizes = []
1872 self.lv_attr = []
1873 self.hasDuplicate = False
1874 self.reserved_percent = 0
1875 self.reserved_space = 0
1876
1877 # circular references, here I come
1878 self._lvs = []
1879
1880 # TODO: validate peSize if given
1881 if not self.peSize:
1882 self.peSize = 32.0 # MB
1883
1884 if not self.exists:
1885 self.pvCount = len(self.parents)
1886
1887 # Some snapshots don't have a proper LV as an origin (--vorigin).
1888 # They still occupy space in the VG.
1889 self.voriginSnapshots = {}
1890
1891 #self.probe()
1892
1893 def __str__(self):
1894 s = DMDevice.__str__(self)
1895 s += (" free = %(free)s PE Size = %(peSize)s PE Count = %(peCount)s\n"
1896 " PE Free = %(peFree)s PV Count = %(pvCount)s\n"
1897 " LV Names = %(lv_names)s modified = %(modified)s\n"
1898 " extents = %(extents)s free space = %(freeSpace)s\n"
1899 " free extents = %(freeExtents)s"
1900 " reserved percent = %(rpct)s reserved space = %(res)s\n"
1901 " PVs = %(pvs)s\n"
1902 " LVs = %(lvs)s" %
1903 {"free": self.free, "peSize": self.peSize, "peCount": self.peCount,
1904 "peFree": self.peFree, "pvCount": self.pvCount,
1905 "lv_names": self.lv_names, "modified": self.isModified,
1906 "extents": self.extents, "freeSpace": self.freeSpace,
1907 "freeExtents": self.freeExtents, "pvs": self.pvs, "lvs": self.lvs,
1908 "rpct": self.reserved_percent, "res": self.reserved_space})
1909 return s
1910
1911 @property
1912 def dict(self):
1913 d = super(LVMVolumeGroupDevice, self).dict
1914 d.update({"free": self.free, "peSize": self.peSize,
1915 "peCount": self.peCount, "peFree": self.peFree,
1916 "pvCount": self.pvCount, "extents": self.extents,
1917 "freeSpace": self.freeSpace,
1918 "freeExtents": self.freeExtents,
1919 "lv_names": self.lv_names,
1920 "lv_uuids": self.lv_uuids,
1921 "lv_sizes": self.lv_sizes,
1922 "lv_attr": self.lv_attr,
1923 "reserved_percent": self.reserved_percent,
1924 "reserved_space": self.reserved_space,
1925 "lvNames": [lv.name for lv in self.lvs]})
1926 return d
1927
1928 def writeKS(self, f, preexisting=False, noformat=False, s=None):
1929 args = ["--pesize=%s" % int(self.peSize * 1024)]
1930 pvs = []
1931
1932 for pv in self.pvs:
1933 pvs.append("pv.%s" % pv.format.majorminor)
1934
1935 if preexisting:
1936 args.append("--useexisting")
1937 if noformat:
1938 args.append("--noformat")
1939
1940 if self.reserved_space:
1941 args.append("--reserved-space=%d" % self.reserved_space)
1942 elif self.reserved_percent:
1943 args.append("--reserved-percent=%d" % self.reserved_percent)
1944
1945 f.write("#volgroup %s %s %s" % (self.name, " ".join(args), " ".join(pvs)))
1946 if s:
1947 f.write(" %s" % s)
1948
1949 def probe(self):
1950 """ Probe for any information about this device. """
1951 log_method_call(self, self.name, status=self.status)
1952 if not self.exists:
1953 raise DeviceError("device has not been created", self.name)
1954
1955 @property
1956 def mapName(self):
1957 """ This device's device-mapper map name """
1958 # Thank you lvm for this lovely hack.
1959 return self.name.replace("-","--")
1960
1961 @property
1962 def path(self):
1963 """ Device node representing this device. """
1964 return "%s/%s" % (self._devDir, self.mapName)
1965
1966 def updateSysfsPath(self):
1967 """ Update this device's sysfs path. """
1968 log_method_call(self, self.name, status=self.status)
1969 if not self.exists:
1970 raise DeviceError("device has not been created", self.name)
1971
1972 self.sysfsPath = ''
1973
1974 @property
1975 def status(self):
1976 """ The device's status (True means active). """
1977 if not self.exists:
1978 return False
1979
1980 # certainly if any of this VG's LVs are active then so are we
1981 for lv in self.lvs:
1982 if lv.status:
1983 return True
1984
1985 # if any of our PVs are not active then we cannot be
1986 for pv in self.pvs:
1987 if not pv.status:
1988 return False
1989
1990 # if we are missing some of our PVs we cannot be active
1991 if not self.complete:
1992 return False
1993
1994 return True
1995
1996 def _addDevice(self, device):
1997 """ Add a new physical volume device to the volume group.
1998
1999 XXX This is for use by device probing routines and is not
2000 intended for modification of the VG.
2001 """
2002 log_method_call(self,
2003 self.name,
2004 device=device.name,
2005 status=self.status)
2006 if not self.exists:
2007 raise DeviceError("device does not exist", self.name)
2008
2009 if not isinstance(device.format, self.pvClass):
2010 raise ValueError("addDevice requires a PV arg")
2011
2012 if self.uuid and device.format.vgUuid != self.uuid:
2013 # this means there is another vg with the same name on the system
2014 # set hasDuplicate which will make complete return False
2015 # and let devicetree._handleInconsistencies() further handle this.
2016 # Note we still add the device to our parents for use by
2017 # devicetree._handleInconsistencies()
2018 self.hasDuplicate = True
2019
2020 if device in self.pvs:
2021 raise ValueError("device is already a member of this VG")
2022
2023 self.parents.append(device)
2024 device.addChild()
2025
2026 # now see if the VG can be activated
2027 if self.complete:
2028 self.setup()
2029
2030 def _removeDevice(self, device):
2031 """ Remove a physical volume from the volume group.
2032
2033 This is for cases like clearing of preexisting partitions.
2034 """
2035 log_method_call(self,
2036 self.name,
2037 device=device.name,
2038 status=self.status)
2039 try:
2040 self.parents.remove(device)
2041 except ValueError, e:
2042 raise ValueError("cannot remove non-member PV device from VG")
2043
2044 device.removeChild()
2045
2046 def setup(self, intf=None, orig=False):
2047 """ Open, or set up, a device.
2048
2049 XXX we don't do anything like "vgchange -ay" because we don't
2050 want all of the LVs activated, just the VG itself.
2051 """
2052 log_method_call(self, self.name, orig=orig, status=self.status)
2053 if not self.exists:
2054 raise DeviceError("device has not been created", self.name)
2055
2056 if self.status:
2057 return
2058
2059 if not self.complete:
2060 raise DeviceError("cannot activate VG with missing PV(s)", self.name)
2061
2062 self.setupParents(orig=orig)
2063
2064 def teardown(self, recursive=None):
2065 """ Close, or tear down, a device. """
2066 log_method_call(self, self.name, status=self.status)
2067 if not self.exists and not recursive:
2068 raise DeviceError("device has not been created", self.name)
2069
2070 if self.status:
2071 lvm.vgdeactivate(self.name)
2072
2073 if recursive:
2074 self.teardownParents(recursive=recursive)
2075
2076 def create(self, intf=None):
2077 """ Create the device. """
2078 log_method_call(self, self.name, status=self.status)
2079 if self.exists:
2080 raise DeviceError("device already exists", self.name)
2081
2082 w = None
2083 if intf:
2084 w = intf.progressWindow(_("Creating"),
2085 _("Creating device %s")
2086 % (self.path,),
2087 100, pulse = True)
2088 try:
2089 self.createParents()
2090 self.setupParents()
2091
2092 pv_list = [pv.path for pv in self.parents]
2093 lvm.vgcreate(self.name, pv_list, self.peSize, progress=w)
2094 except Exception:
2095 raise
2096 else:
2097 # FIXME set / update self.uuid here
2098 self.exists = True
2099 self.setup()
2100 finally:
2101 if w:
2102 w.pop()
2103
2104 def destroy(self):
2105 """ Destroy the device. """
2106 log_method_call(self, self.name, status=self.status)
2107 if not self.exists:
2108 raise DeviceError("device has not been created", self.name)
2109
2110 # set up the pvs since lvm needs access to them to do the vgremove
2111 self.setupParents(orig=True)
2112
2113 # this sometimes fails for some reason.
2114 try:
2115 lvm.vgreduce(self.name, [], rm=True)
2116 lvm.vgremove(self.name)
2117 except lvm.LVMError:
2118 raise DeviceError("Could not completely remove VG", self.name)
2119 finally:
2120 self.exists = False
2121
2122 def reduce(self, pv_list):
2123 """ Remove the listed PVs from the VG. """
2124 log_method_call(self, self.name, status=self.status)
2125 if not self.exists:
2126 raise DeviceError("device has not been created", self.name)
2127
2128 lvm.vgreduce(self.name, pv_list)
2129 # XXX do we need to notify the kernel?
2130
2131 def _addLogVol(self, lv):
2132 """ Add an LV to this VG. """
2133 if lv in self._lvs:
2134 raise ValueError("lv is already part of this vg")
2135
2136 # verify we have the space, then add it
2137 # do not verify for growing vg (because of ks)
2138 if not lv.exists and not self.growable and lv.size > self.freeSpace:
2139 raise DeviceError("new lv is too large to fit in free space", self.name)
2140
2141 log.debug("Adding %s/%dMB to %s" % (lv.name, lv.size, self.name))
2142 self._lvs.append(lv)
2143
2144 def _removeLogVol(self, lv):
2145 """ Remove an LV from this VG. """
2146 if lv not in self.lvs:
2147 raise ValueError("specified lv is not part of this vg")
2148
2149 self._lvs.remove(lv)
2150
2151 def _addPV(self, pv):
2152 """ Add a PV to this VG. """
2153 if pv in self.pvs:
2154 raise ValueError("pv is already part of this vg")
2155
2156 # for the time being we will not allow vgextend
2157 if self.exists:
2158 raise DeviceError("cannot add pv to existing vg", self.name)
2159
2160 self.parents.append(pv)
2161 pv.addChild()
2162
2163 # and update our pv count
2164 self.pvCount = len(self.parents)
2165
2166 def _removePV(self, pv):
2167 """ Remove an PV from this VG. """
2168 if not pv in self.pvs:
2169 raise ValueError("specified pv is not part of this vg")
2170
2171 # for the time being we will not allow vgreduce
2172 if self.exists:
2173 raise DeviceError("cannot remove pv from existing vg", self.name)
2174
2175 self.parents.remove(pv)
2176 pv.removeChild()
2177
2178 # and update our pv count
2179 self.pvCount = len(self.parents)
2180
2181 # We can't rely on lvm to tell us about our size, free space, &c
2182 # since we could have modifications queued, unless the VG and all of
2183 # its PVs already exist.
2184 #
2185 # -- liblvm may contain support for in-memory devices
2186
2187 @property
2188 def isModified(self):
2189 """ Return True if the VG has changes queued that LVM is unaware of. """
2190 modified = True
2191 if self.exists and not filter(lambda d: not d.exists, self.pvs):
2192 modified = False
2193
2194 return modified
2195
2196 @property
2197 def snapshotSpace(self):
2198 """ Total space used by snapshots in this volume group. """
2199 used = 0
2200 for lv in self.lvs:
2201 log.debug("lv %s uses %dMB for snapshots" % (lv.lvname,
2202 lv.snapshotSpace))
2203 used += self.align(lv.snapshotSpace, roundup=True)
2204
2205 for (vname, vsize) in self.voriginSnapshots.items():
2206 log.debug("snapshot %s with vorigin uses %dMB" % (vname, vsize))
2207 used += self.align(vsize, roundup=True)
2208
2209 return used
2210
2211 @property
2212 def reservedSpace(self):
2213 """ Reserved space in this VG, in MB """
2214 reserved = 0
2215 if self.reserved_percent > 0:
2216 reserved = self.reserved_percent * 0.01 * self.size
2217 elif self.reserved_space > 0:
2218 reserved = self.reserved_space
2219
2220 return self.align(reserved, roundup=True)
2221
2222 @property
2223 def size(self):
2224 """ The size of this VG """
2225 # TODO: just ask lvm if isModified returns False
2226
2227 # sum up the sizes of the PVs and align to pesize
2228 size = 0
2229 for pv in self.pvs:
2230 log.debug("PV size == %s" % pv.size)
2231 size += max(0, self.align(pv.size - pv.format.peStart))
2232
2233 return size
2234
2235 @property
2236 def extents(self):
2237 """ Number of extents in this VG """
2238 # TODO: just ask lvm if isModified returns False
2239
2240 return self.size / self.peSize
2241
2242 @property
2243 def freeSpace(self):
2244 """ The amount of free space in this VG (in MB). """
2245 # TODO: just ask lvm if isModified returns False
2246
2247 # total the sizes of any LVs
2248 log.debug("%s size is %dMB" % (self.name, self.size))
2249 used = sum(lv.vgSpaceUsed for lv in self.lvs) + self.snapshotSpace
2250 used += self.reservedSpace
2251 free = self.size - used
2252
2253 pad = self.peSize * 2 * len(self.pvs)
2254 if free >= pad:
2255 free -= pad
2256
2257 log.debug("vg %s has %dMB free" % (self.name, free))
2258 return free
2259
2260 @property
2261 def freeExtents(self):
2262 """ The number of free extents in this VG. """
2263 # TODO: just ask lvm if isModified returns False
2264 return self.freeSpace / self.peSize
2265
2266 def align(self, size, roundup=None):
2267 """ Align a size to a multiple of physical extent size. """
2268 size = numeric_type(size)
2269
2270 if roundup:
2271 round = math.ceil
2272 else:
2273 round = math.floor
2274
2275 # we want Kbytes as a float for our math
2276 size *= 1024.0
2277 pesize = self.peSize * 1024.0
2278 return long((round(size / pesize) * pesize) / 1024)
2279
2280 @property
2281 def pvs(self):
2282 """ A list of this VG's PVs """
2283 return self.parents[:] # we don't want folks changing our list
2284
2285 @property
2286 def lvs(self):
2287 """ A list of this VG's LVs """
2288 return self._lvs[:] # we don't want folks changing our list
2289
2290 @property
2291 def complete(self):
2292 """Check if the vg has all its pvs in the system
2293 Return True if complete.
2294 """
2295 # vgs with duplicate names are overcomplete, which is not what we want
2296 if self.hasDuplicate:
2297 return False
2298
2299 return len(self.pvs) == self.pvCount or not self.exists
2300
2301
2302 class LVMLogicalVolumeDevice(DMDevice):
2303 """ An LVM Logical Volume """
2304 _type = "lvmlv"
2305 _resizable = True
2306 _packages = ["lvm2"]
2307
2308 def __init__(self, name, vgdev, size=None, uuid=None,
2309 stripes=1, logSize=0, snapshotSpace=0,
2310 format=None, exists=None, sysfsPath='',
2311 grow=None, maxsize=None, percent=None,
2312 singlePV=False):
2313 """ Create a LVMLogicalVolumeDevice instance.
2314
2315 Arguments:
2316
2317 name -- the device name (generally a device node's basename)
2318 vgdev -- volume group (LVMVolumeGroupDevice instance)
2319
2320 Keyword Arguments:
2321
2322 size -- the device's size (in MB)
2323 uuid -- the device's UUID
2324 stripes -- number of copies in the vg (>1 for mirrored lvs)
2325 logSize -- size of log volume (for mirrored lvs)
2326 snapshotSpace -- sum of sizes of snapshots of this lv
2327 sysfsPath -- sysfs device path
2328 format -- a DeviceFormat instance
2329 exists -- indicates whether this is an existing device
2330 singlePV -- if true, maps this lv to a single pv
2331
2332 For new (non-existent) LVs only:
2333
2334 grow -- whether to grow this LV
2335 maxsize -- maximum size for growable LV (in MB)
2336 percent -- percent of VG space to take
2337
2338 """
2339 if isinstance(vgdev, list):
2340 if len(vgdev) != 1:
2341 raise ValueError("constructor requires a single LVMVolumeGroupDevice instance")
2342 elif not isinstance(vgdev[0], LVMVolumeGroupDevice):
2343 raise ValueError("constructor requires a LVMVolumeGroupDevice instance")
2344 elif not isinstance(vgdev, LVMVolumeGroupDevice):
2345 raise ValueError("constructor requires a LVMVolumeGroupDevice instance")
2346 DMDevice.__init__(self, name, size=size, format=format,
2347 sysfsPath=sysfsPath, parents=vgdev,
2348 exists=exists)
2349
2350 self.singlePVerr = ("%(mountpoint)s is restricted to a single "
2351 "physical volume on this platform. No physical "
2352 "volumes available in volume group %(vgname)s "
2353 "with %(size)d MB of available space." %
2354 {'mountpoint': getattr(self.format, "mountpoint",
2355 "A proposed logical volume"),
2356 'vgname': self.vg.name,
2357 'size': self.size})
2358
2359 self.uuid = uuid
2360 self.snapshotSpace = snapshotSpace
2361 self.stripes = stripes
2362 self.logSize = logSize
2363 self.singlePV = singlePV
2364
2365 self.req_grow = None
2366 self.req_max_size = 0
2367 self.req_size = 0
2368 self.req_percent = 0
2369
2370 if not self.exists:
2371 self.req_grow = grow
2372 self.req_max_size = numeric_type(maxsize)
2373 # XXX should we enforce that req_size be pe-aligned?
2374 self.req_size = self._size
2375 self.req_percent = numeric_type(percent)
2376
2377 if self.singlePV:
2378 # make sure there is at least one PV that can hold this LV
2379 validpvs = filter(lambda x: float(x.size) >= self.req_size,
2380 self.vg.pvs)
2381 if not validpvs:
2382 raise SinglePhysicalVolumeError(self.singlePVerr)
2383
2384 # here we go with the circular references
2385 self.vg._addLogVol(self)
2386
2387 def __str__(self):
2388 s = DMDevice.__str__(self)
2389 s += (" VG device = %(vgdev)r percent = %(percent)s\n"
2390 " mirrored = %(mirrored)s stripes = %(stripes)d"
2391 " snapshot total = %(snapshots)dMB\n"
2392 " VG space used = %(vgspace)dMB" %
2393 {"vgdev": self.vg, "percent": self.req_percent,
2394 "mirrored": self.mirrored, "stripes": self.stripes,
2395 "snapshots": self.snapshotSpace, "vgspace": self.vgSpaceUsed })
2396 return s
2397
2398 @property
2399 def dict(self):
2400 d = super(LVMLogicalVolumeDevice, self).dict
2401 if self.exists:
2402 d.update({"mirrored": self.mirrored, "stripes": self.stripes,
2403 "snapshots": self.snapshotSpace,
2404 "vgspace": self.vgSpaceUsed})
2405 else:
2406 d.update({"percent": self.req_percent})
2407
2408 return d
2409
2410 def writeKS(self, f, preexisting=False, noformat=False, s=None):
2411 args = ["--name=%s" % self.lvname,
2412 "--vgname=%s" % self.vg.name]
2413
2414 if self.req_grow:
2415 args.extend(["--grow", "--size=%s" % (self.req_size or 1)])
2416
2417 if self.req_max_size > 0:
2418 args.append("--maxsize=%s" % self.req_max_size)
2419 else:
2420 if self.req_percent > 0:
2421 args.append("--percent=%s" % self.req_percent)
2422 elif self.req_size > 0:
2423 args.append("--size=%s" % self.req_size)
2424
2425 if preexisting:
2426 args.append("--useexisting")
2427 if noformat:
2428 args.append("--noformat")
2429
2430 f.write("#logvol ")
2431 self.format.writeKS(f)
2432 f.write(" %s" % " ".join(args))
2433 if s:
2434 f.write(" %s" % s)
2435
2436 @property
2437 def mirrored(self):
2438 return self.stripes > 1
2439
2440 def _setSize(self, size):
2441 size = self.vg.align(numeric_type(size))
2442 log.debug("trying to set lv %s size to %dMB" % (self.name, size))
2443 if size <= (self.vg.freeSpace + self._size):
2444 self._size = size
2445 self.targetSize = size
2446 else:
2447 log.debug("failed to set size: %dMB short" % (size - (self.vg.freeSpace + self._size),))
2448 raise ValueError("not enough free space in volume group")
2449
2450 size = property(StorageDevice._getSize, _setSize)
2451
2452 @property
2453 def vgSpaceUsed(self):
2454 """ Space occupied by this LV, not including snapshots. """
2455 return (self.vg.align(self.size, roundup=True) * self.stripes
2456 + self.logSize)
2457
2458 @property
2459 def vg(self):
2460 """ This Logical Volume's Volume Group. """
2461 return self.parents[0]
2462
2463 @property
2464 def mapName(self):
2465 """ This device's device-mapper map name """
2466 # Thank you lvm for this lovely hack.
2467 return "%s-%s" % (self.vg.mapName, self._name.replace("-","--"))
2468
2469 @property
2470 def path(self):
2471 """ Device node representing this device. """
2472 return "%s/%s" % (self._devDir, self.mapName)
2473
2474 def getDMNode(self):
2475 """ Return the dm-X (eg: dm-0) device node for this device. """
2476 log_method_call(self, self.name, status=self.status)
2477 if not self.exists:
2478 raise DeviceError("device has not been created", self.name)
2479
2480 return dm.dm_node_from_name(self.mapName)
2481
2482 @property
2483 def name(self):
2484 """ This device's name. """
2485 return "%s-%s" % (self.vg.name, self._name)
2486
2487 @property
2488 def lvname(self):
2489 """ The LV's name (not including VG name). """
2490 return self._name
2491
2492 @property
2493 def complete(self):
2494 """ Test if vg exits and if it has all pvs. """
2495 return self.vg.complete
2496
2497 def setup(self, intf=None, orig=False):
2498 """ Open, or set up, a device. """
2499 log_method_call(self, self.name, orig=orig, status=self.status)
2500 if not self.exists:
2501 raise DeviceError("device has not been created", self.name)
2502
2503 if self.status:
2504 return
2505
2506 self.vg.setup(orig=orig)
2507 lvm.lvactivate(self.vg.name, self._name)
2508
2509 # we always probe since the device may not be set up when we want
2510 # information about it
2511 self._size = self.currentSize
2512
2513 def teardown(self, recursive=None):
2514 """ Close, or tear down, a device. """
2515 log_method_call(self, self.name, status=self.status)
2516 if not self.exists and not recursive:
2517 raise DeviceError("device has not been created", self.name)
2518
2519 if self.status:
2520 if self.originalFormat.exists:
2521 self.originalFormat.teardown()
2522 if self.format.exists:
2523 self.format.teardown()
2524 udev_settle()
2525
2526 if self.status:
2527 lvm.lvdeactivate(self.vg.name, self._name)
2528
2529 if recursive:
2530 # It's likely that teardown of a VG will fail due to other
2531 # LVs being active (filesystems mounted, &c), so don't let
2532 # it bring everything down.
2533 try:
2534 self.vg.teardown(recursive=recursive)
2535 except Exception as e:
2536 log.debug("vg %s teardown failed; continuing" % self.vg.name)
2537
2538 def _getSinglePV(self):
2539 validpvs = filter(lambda x: float(x.size) >= self.size, self.vg.pvs)
2540
2541 if not validpvs:
2542 raise SinglePhysicalVolumeError(self.singlePVerr)
2543
2544 return [validpvs[0].path]
2545
2546 def create(self, intf=None):
2547 """ Create the device. """
2548 log_method_call(self, self.name, status=self.status)
2549 if self.exists:
2550 raise DeviceError("device already exists", self.name)
2551
2552 w = None
2553 if intf:
2554 w = intf.progressWindow(_("Creating"),
2555 _("Creating device %s")
2556 % (self.path,),
2557 100, pulse = True)
2558 try:
2559 self.createParents()
2560 self.setupParents()
2561
2562 # should we use --zero for safety's sake?
2563 if self.singlePV:
2564 lvm.lvcreate(self.vg.name, self._name, self.size, progress=w,
2565 pvs=self._getSinglePV())
2566 else:
2567 lvm.lvcreate(self.vg.name, self._name, self.size, progress=w)
2568 except Exception:
2569 raise
2570 else:
2571 # FIXME set / update self.uuid here
2572 self.exists = True
2573 self.setup()
2574 finally:
2575 if w:
2576 w.pop()
2577
2578 def destroy(self):
2579 """ Destroy the device. """
2580 log_method_call(self, self.name, status=self.status)
2581 if not self.exists:
2582 raise DeviceError("device has not been created", self.name)
2583
2584 self.teardown()
2585 # set up the vg's pvs so lvm can remove the lv
2586 self.vg.setupParents(orig=True)
2587 lvm.lvremove(self.vg.name, self._name)
2588 self.exists = False
2589
2590 def resize(self, intf=None):
2591 # XXX resize format probably, right?
2592 log_method_call(self, self.name, status=self.status)
2593 if not self.exists:
2594 raise DeviceError("device has not been created", self.name)
2595
2596 # Setup VG parents (in case they are dmraid partitions for example)
2597 self.vg.setupParents(orig=True)
2598
2599 if self.originalFormat.exists:
2600 self.originalFormat.teardown()
2601 if self.format.exists:
2602 self.format.teardown()
2603
2604 udev_settle()
2605 lvm.lvresize(self.vg.name, self._name, self.size)
2606
2607 def dracutSetupArgs(self):
2608 # Note no mapName usage here, this is a lvm cmdline name, which
2609 # is different (ofcourse)
2610 return set(["rd_LVM_LV=%s/%s" % (self.vg.name, self._name)])
2611
2612 def checkSize(self):
2613 """ Check to make sure the size of the device is allowed by the
2614 format used.
2615
2616 return None is all is ok
2617 return large or small depending on the problem
2618 """
2619 problem = None
2620 if self.format.maxSize and self.size > self.format.maxSize:
2621 problem = _("large")
2622 elif (self.format.minSize and
2623 (not self.req_grow and
2624 self.size < self.format.minSize) or
2625 (self.req_grow and self.req_max_size and
2626 self.req_max_size < self.format.minSize)):
2627 problem = _("small")
2628 return problem
2629
2630 class MDRaidArrayDevice(StorageDevice):
2631 """ An mdraid (Linux RAID) device. """
2632 _type = "mdarray"
2633 _packages = ["mdadm"]
2634
2635 def __init__(self, name, level=None, major=None, minor=None, size=None,
2636 memberDevices=None, totalDevices=None,
2637 uuid=None, format=None, exists=None, metadataVersion=None,
2638 parents=None, sysfsPath=''):
2639 """ Create a MDRaidArrayDevice instance.
2640
2641 Arguments:
2642
2643 name -- the device name (generally a device node's basename)
2644
2645 Keyword Arguments:
2646
2647 level -- the device's RAID level (a string, eg: '1' or 'raid1')
2648 metadataVersion -- the version of the device's md metadata
2649 parents -- list of member devices (StorageDevice instances)
2650 size -- the device's size (units/format TBD)
2651 uuid -- the device's UUID
2652 minor -- the device minor
2653 sysfsPath -- sysfs device path
2654 format -- a DeviceFormat instance
2655 exists -- indicates whether this is an existing device
2656 """
2657 StorageDevice.__init__(self, name, format=format, exists=exists,
2658 major=major, minor=minor, size=size,
2659 parents=parents, sysfsPath=sysfsPath)
2660
2661 self.level = level
2662 if level == "container":
2663 self._type = "mdcontainer"
2664 elif level is not None:
2665 self.level = mdraid.raidLevel(level)
2666
2667 # For new arrays check if we have enough members
2668 if (not exists and parents and
2669 len(parents) < mdraid.get_raid_min_members(self.level)):
2670 raise ValueError, _("A RAID%d set requires atleast %d members") % (
2671 self.level, mdraid.get_raid_min_members(self.level))
2672
2673 self.uuid = uuid
2674 self._totalDevices = numeric_type(totalDevices)
2675 self._memberDevices = numeric_type(memberDevices)
2676 self.sysfsPath = "/devices/virtual/block/%s" % name
2677 self.chunkSize = 512.0 / 1024.0 # chunk size in MB
2678
2679 if not isinstance(metadataVersion, str):
2680 self.metadataVersion = "1.1"
2681 else:
2682 self.metadataVersion = metadataVersion
2683
2684 # bitmaps are not meaningful on raid0 according to mdadm-3.0.3
2685 self.createBitmap = self.level != 0
2686
2687 # For container members probe size now, as we cannot determine it
2688 # when teared down.
2689 if self.parents and self.parents[0].type == "mdcontainer":
2690 self._size = self.currentSize
2691 self._type = "mdbiosraidarray"
2692
2693 self.formatClass = get_device_format_class("mdmember")
2694 if not self.formatClass:
2695 raise DeviceError("cannot find class for 'mdmember'", self.name)
2696
2697 if self.exists and self.uuid:
2698 # this is a hack to work around mdadm's insistence on giving
2699 # really high minors to arrays it has no config entry for
2700 open("/etc/mdadm.conf", "a").write("ARRAY %s UUID=%s\n"
2701 % (self.path, self.uuid))
2702
2703 @property
2704 def rawArraySize(self):
2705 """ Calculate the raw array size without taking into account space
2706 reserved for metadata or chunkSize alignment.
2707
2708 This is used to calculate the superBlockSize for v1.1 and v1.2
2709 metadata.
2710
2711 Returns the raw size in MB
2712 """
2713 smallestMemberSize = self.smallestMember.size
2714 if self.level == mdraid.RAID0:
2715 size = self.memberDevices * smallestMemberSize
2716 elif self.level == mdraid.RAID1:
2717 size = smallestMemberSize
2718 elif self.level == mdraid.RAID4:
2719 size = (self.memberDevices - 1) * smallestMemberSize
2720 elif self.level == mdraid.RAID5:
2721 size = (self.memberDevices - 1) * smallestMemberSize
2722 elif self.level == mdraid.RAID6:
2723 size = (self.memberDevices - 2) * smallestMemberSize
2724 elif self.level == mdraid.RAID10:
2725 size = (self.memberDevices / 2.0) * smallestMemberSize
2726 else:
2727 size = smallestMemberSize
2728 log.error("unknown RAID level %s" % (self.level))
2729 log.debug("raw RAID %s size == %s" % (self.level, size))
2730 return size
2731
2732 @property
2733 def superBlockSize(self):
2734 """ mdadm has different amounts of space reserved for its use depending
2735 on the metadata type and size of the array.
2736
2737 0.9 use 2.0 MB
2738 1.0 use 2.0 MB
2739 1.1 or 1.2 use the formula lifted from mdadm/super1.c to calculate it
2740 based on the array size.
2741 """
2742 # mdadm 3.2.4 made a major change in the amount of space used for 1.1 and 1.2
2743 # in order to reserve space for reshaping. See commit 508a7f16 in the
2744 # upstream mdadm repository.
2745 if self.metadataVersion not in ["1.1", "1.2"]:
2746 return 2.0
2747
2748 array_size = self.rawArraySize
2749 # MDADM: We try to leave 0.1% at the start for reshape
2750 # MDADM: operations, but limit this to 128Meg (0.1% of 10Gig)
2751 # MDADM: which is plenty for efficient reshapes
2752 # NOTE: In the mdadm code this is in 512b sectors. Converted to use MB
2753 headroom = 128
2754 while headroom << 10 > array_size:
2755 headroom >>= 1
2756 log.info("Using %sMB superBlockSize" % (headroom))
2757 return headroom
2758
2759 @property
2760 def smallestMember(self):
2761 try:
2762 smallest = sorted(self.devices, key=lambda d: d.size)[0]
2763 except IndexError:
2764 smallest = None
2765 return smallest
2766
2767 @property
2768 def size(self):
2769 if not self.devices:
2770 return 0
2771
2772 # For container members return probed size, as we cannot determine it
2773 # when teared down.
2774 if self.type == "mdbiosraidarray":
2775 return self._size
2776
2777 size = 0
2778 smallestMemberSize = self.smallestMember.size - self.superBlockSize
2779 if not self.exists or not self.partedDevice:
2780 if self.level == mdraid.RAID0:
2781 size = self.memberDevices * smallestMemberSize
2782 size -= size % self.chunkSize
2783 elif self.level == mdraid.RAID1:
2784 size = smallestMemberSize
2785 elif self.level == mdraid.RAID4:
2786 size = (self.memberDevices - 1) * smallestMemberSize
2787 size -= size % self.chunkSize
2788 elif self.level == mdraid.RAID5:
2789 size = (self.memberDevices - 1) * smallestMemberSize
2790 size -= size % self.chunkSize
2791 elif self.level == mdraid.RAID6:
2792 size = (self.memberDevices - 2) * smallestMemberSize
2793 size -= size % self.chunkSize
2794 elif self.level == mdraid.RAID10:
2795 size = (self.memberDevices / 2.0) * smallestMemberSize
2796 size -= size % self.chunkSize
2797 log.debug("non-existant RAID %s size == %s" % (self.level, size))
2798 else:
2799 size = self.partedDevice.getSize()
2800 log.debug("existing RAID %s size == %s" % (self.level, size))
2801
2802 return size
2803
2804 @property
2805 def description(self):
2806 if self.level == mdraid.RAID0:
2807 levelstr = "stripe"
2808 elif self.level == mdraid.RAID1:
2809 levelstr = "mirror"
2810 else:
2811 levelstr = "raid%s" % self.level
2812
2813 if self.type == "mdcontainer":
2814 return "BIOS RAID container"
2815 elif self.type == "mdbiosraidarray":
2816 return "BIOS RAID set (%s)" % levelstr
2817 else:
2818 return "MDRAID set (%s)" % levelstr
2819
2820 def __str__(self):
2821 s = StorageDevice.__str__(self)
2822 s += (" level = %(level)s spares = %(spares)s\n"
2823 " members = %(memberDevices)s\n"
2824 " total devices = %(totalDevices)s"
2825 " metadata version = %(metadataVersion)s" %
2826 {"level": self.level, "spares": self.spares,
2827 "memberDevices": self.memberDevices,
2828 "totalDevices": self.totalDevices,
2829 "metadataVersion": self.metadataVersion})
2830 return s
2831
2832 @property
2833 def dict(self):
2834 d = super(MDRaidArrayDevice, self).dict
2835 d.update({"level": self.level,
2836 "spares": self.spares, "memberDevices": self.memberDevices,
2837 "totalDevices": self.totalDevices,
2838 "metadataVersion": self.metadataVersion})
2839 return d
2840
2841 def writeKS(self, f, preexisting=False, noformat=False, s=None):
2842 args = ["--level=%s" % self.level,
2843 "--device=%s" % self.name]
2844 mems = []
2845
2846 if self.spares > 0:
2847 args.append("--spares=%s" % self.spares)
2848 if preexisting:
2849 args.append("--useexisting")
2850 if noformat:
2851 args.append("--noformat")
2852
2853 for mem in self.parents:
2854 mems.append("raid.%s" % mem.format.majorminor)
2855
2856 f.write("#raid ")
2857 self.format.writeKS(f)
2858 f.write(" %s" % " ".join(args))
2859 f.write(" %s" % " ".join(mems))
2860 if s:
2861 f.write(" %s" % s)
2862
2863 @property
2864 def mdadmConfEntry(self):
2865 """ This array's mdadm.conf entry. """
2866 if self.level is None or self.memberDevices is None or not self.uuid:
2867 raise DeviceError("array is not fully defined", self.name)
2868
2869 # containers and the sets within must only have a UUID= parameter
2870 if self.type == "mdcontainer" or self.type == "mdbiosraidarray":
2871 fmt = "ARRAY %s UUID=%s\n"
2872 return fmt % (self.path, self.uuid)
2873
2874 fmt = "ARRAY %s level=raid%d num-devices=%d UUID=%s\n"
2875 return fmt % (self.path, self.level, self.memberDevices, self.uuid)
2876
2877 @property
2878 def totalDevices(self):
2879 """ Total number of devices in the array, including spares. """
2880 count = len(self.parents)
2881 if not self.exists:
2882 count = self._totalDevices
2883 return count
2884
2885 def _getMemberDevices(self):
2886 return self._memberDevices
2887
2888 def _setMemberDevices(self, number):
2889 if not isinstance(number, int):
2890 raise ValueError("memberDevices is an integer")
2891
2892 if number > self.totalDevices:
2893 raise ValueError("memberDevices cannot be greater than totalDevices")
2894 self._memberDevices = number
2895
2896 memberDevices = property(_getMemberDevices, _setMemberDevices,
2897 doc="number of member devices")
2898
2899 def _getSpares(self):
2900 spares = 0
2901 if self.memberDevices is not None:
2902 if self.totalDevices is not None and \
2903 self.totalDevices > self.memberDevices:
2904 spares = self.totalDevices - self.memberDevices
2905 elif self.totalDevices is None:
2906 spares = self.memberDevices
2907 self._totalDevices = self.memberDevices
2908 return spares
2909
2910 def _setSpares(self, spares):
2911 # FIXME: this is too simple to be right
2912 if self.totalDevices > spares:
2913 self.memberDevices = self.totalDevices - spares
2914
2915 spares = property(_getSpares, _setSpares)
2916
2917 def probe(self):
2918 """ Probe for any missing information about this device.
2919
2920 I'd like to avoid paying any attention to "Preferred Minor"
2921 as it seems problematic.
2922 """
2923 log_method_call(self, self.name, status=self.status)
2924 if not self.exists:
2925 raise DeviceError("device has not been created", self.name)
2926
2927 try:
2928 self.devices[0].setup()
2929 except Exception:
2930 return
2931
2932 info = mdraid.mdexamine(self.devices[0].path)
2933 if self.level is None:
2934 self.level = mdraid.raidLevel(info['level'])
2935
2936 def updateSysfsPath(self):
2937 """ Update this device's sysfs path. """
2938 log_method_call(self, self.name, status=self.status)
2939 if not self.exists:
2940 raise DeviceError("device has not been created", self.name)
2941
2942 if self.status:
2943 self.sysfsPath = "/devices/virtual/block/%s" % self.name
2944 else:
2945 self.sysfsPath = ''
2946
2947 def _addDevice(self, device):
2948 """ Add a new member device to the array.
2949
2950 XXX This is for use when probing devices, not for modification
2951 of arrays.
2952 """
2953 log_method_call(self,
2954 self.name,
2955 device=device.name,
2956 status=self.status)
2957 if not self.exists:
2958 raise DeviceError("device has not been created", self.name)
2959
2960 if not isinstance(device.format, self.formatClass):
2961 raise ValueError("invalid device format for mdraid member")
2962
2963 if self.uuid and device.format.mdUuid != self.uuid:
2964 raise ValueError("cannot add member with non-matching UUID")
2965
2966 if device in self.devices:
2967 raise ValueError("device is already a member of this array")
2968
2969 # we added it, so now set up the relations
2970 self.devices.append(device)
2971 device.addChild()
2972
2973 device.setup()
2974 udev_settle()
2975
2976 if self.spares > 0:
2977 # mdadm doesn't like it when you try to incrementally add spares
2978 return
2979
2980 try:
2981 mdraid.mdadd(device.path)
2982 # mdadd causes udev events
2983 udev_settle()
2984 except MDRaidError as e:
2985 log.warning("failed to add member %s to md array %s: %s"
2986 % (device.path, self.path, e))
2987
2988 if self.status:
2989 # we always probe since the device may not be set up when we want
2990 # information about it
2991 self._size = self.currentSize
2992
2993 def _removeDevice(self, device):
2994 """ Remove a component device from the array.
2995
2996 XXX This is for use by clearpart, not for reconfiguration.
2997 """
2998 log_method_call(self,
2999 self.name,
3000 device=device.name,
3001 status=self.status)
3002
3003 if device not in self.devices:
3004 raise ValueError("cannot remove non-member device from array")
3005
3006 self.devices.remove(device)
3007 device.removeChild()
3008
3009 @property
3010 def status(self):
3011 """ This device's status.
3012
3013 For now, this should return a boolean:
3014 True the device is open and ready for use
3015 False the device is not open
3016 """
3017 # check the status in sysfs
3018 status = False
3019 if not self.exists:
3020 return status
3021
3022 state_file = "/sys/%s/md/array_state" % self.sysfsPath
3023 if os.access(state_file, os.R_OK):
3024 state = open(state_file).read().strip()
3025 log.debug("%s state is %s" % (self.name, state))
3026 if state in ("clean", "active", "active-idle", "readonly", "read-auto"):
3027 status = True
3028 # mdcontainers have state inactive when started (clear if stopped)
3029 if self.type == "mdcontainer" and state == "inactive":
3030 status = True
3031
3032 return status
3033
3034 @property
3035 def degraded(self):
3036 """ Return True if the array is running in degraded mode. """
3037 rc = False
3038 degraded_file = "/sys/%s/md/degraded" % self.sysfsPath
3039 if os.access(degraded_file, os.R_OK):
3040 val = open(degraded_file).read().strip()
3041 log.debug("%s degraded is %s" % (self.name, val))
3042 if val == "1":
3043 rc = True
3044
3045 return rc
3046
3047 @property
3048 def devices(self):
3049 """ Return a list of this array's member device instances. """
3050 return self.parents
3051
3052 def setup(self, intf=None, orig=False):
3053 """ Open, or set up, a device. """
3054 log_method_call(self, self.name, orig=orig, status=self.status)
3055 if not self.exists:
3056 raise DeviceError("device has not been created", self.name)
3057
3058 if self.status:
3059 return
3060
3061 disks = []
3062 for member in self.devices:
3063 member.setup(orig=orig)
3064 disks.append(member.path)
3065
3066 update_super_minor = self.metadataVersion in ("0", "0.90")
3067
3068 mdraid.mdactivate(self.path,
3069 members=disks,
3070 super_minor=self.minor,
3071 update_super_minor=update_super_minor,
3072 uuid=self.uuid)
3073
3074 udev_settle()
3075
3076 # we always probe since the device may not be set up when we want
3077 # information about it
3078 self._size = self.currentSize
3079
3080 def teardown(self, recursive=None):
3081 """ Close, or tear down, a device. """
3082 log_method_call(self, self.name, status=self.status)
3083 if not self.exists and not recursive:
3084 raise DeviceError("device has not been created", self.name)
3085
3086 if self.status:
3087 if self.originalFormat.exists:
3088 self.originalFormat.teardown()
3089 self.format.cacheMajorminor()
3090 if self.format.exists:
3091 self.format.teardown()
3092 udev_settle()
3093
3094 # Since BIOS RAID sets (containers in mdraid terminology) never change
3095 # there is no need to stop them and later restart them. Not stopping
3096 # (and thus also not starting) them also works around bug 523334
3097 if self.type == "mdcontainer" or self.type == "mdbiosraidarray":
3098 return
3099
3100 # We don't really care what the array's state is. If the device
3101 # file exists, we want to deactivate it. mdraid has too many
3102 # states.
3103 if self.exists and os.path.exists(self.path):
3104 mdraid.mddeactivate(self.path)
3105
3106 if recursive:
3107 self.teardownParents(recursive=recursive)
3108
3109 def preCommitFixup(self, *args, **kwargs):
3110 """ Determine create parameters for this set """
3111 mountpoints = kwargs.pop("mountpoints")
3112 log_method_call(self, self.name, mountpoints)
3113
3114 if "/boot" in mountpoints:
3115 bootmountpoint = "/boot"
3116 else:
3117 bootmountpoint = "/"
3118
3119 # If we are used to boot from we cannot use 1.1 metadata
3120 if getattr(self.format, "mountpoint", None) == bootmountpoint or \
3121 getattr(self.format, "mountpoint", None) == "/boot/efi" or \
3122 self.format.type == "prepboot":
3123 self.metadataVersion = "1.0"
3124
3125 # Bitmaps are not useful for swap and small partitions
3126 if self.size < 1000 or self.format.type == "swap":
3127 self.createBitmap = False
3128
3129 def create(self, intf=None):
3130 """ Create the device. """
3131 log_method_call(self, self.name, status=self.status)
3132 if self.exists:
3133 raise DeviceError("device already exists", self.name)
3134
3135 w = None
3136 if intf:
3137 w = intf.progressWindow(_("Creating"),
3138 _("Creating device %s")
3139 % (self.path,),
3140 100, pulse = True)
3141 try:
3142 self.createParents()
3143 self.setupParents()
3144
3145 disks = [disk.path for disk in self.devices]
3146 spares = len(self.devices) - self.memberDevices
3147
3148 # allow creation of degraded arrays
3149 if len(disks) == 1:
3150 disks.append("missing")
3151
3152 mdraid.mdcreate(self.path,
3153 self.level,
3154 disks,
3155 spares,
3156 metadataVer=self.metadataVersion,
3157 bitmap=self.createBitmap,
3158 progress=w)
3159 except Exception:
3160 raise
3161 else:
3162 self.exists = True
3163 # the array is automatically activated upon creation, but...
3164 self.setup()
3165 udev_settle()
3166 self.updateSysfsPath()
3167 info = udev_get_block_device(self.sysfsPath)
3168 self.uuid = udev_device_get_md_uuid(info)
3169 for member in self.devices:
3170 member.mdUuid = self.uuid
3171 finally:
3172 if w:
3173 w.pop()
3174
3175 @property
3176 def formatArgs(self):
3177 formatArgs = []
3178 if self.format.type == "ext2":
3179 if self.level == mdraid.RAID5:
3180 formatArgs = ['-R',
3181 'stride=%d' % ((self.memberDevices - 1) * 16)]
3182 if self.level == mdraid.RAID4:
3183 formatArgs = ['-R',
3184 'stride=%d' % ((self.memberDevices - 1) * 16)]
3185 elif self.level == mdraid.RAID0:
3186 formatArgs = ['-R',
3187 'stride=%d' % (self.memberDevices * 16)]
3188
3189 def destroy(self):
3190 """ Destroy the device. """
3191 log_method_call(self, self.name, status=self.status)
3192 if not self.exists:
3193 raise DeviceError("device has not been created", self.name)
3194
3195 self.teardown()
3196
3197 # The destruction of the formatting on the member devices does the
3198 # real work, but it isn't our place to do it from here.
3199 self.exists = False
3200
3201 @property
3202 def mediaPresent(self):
3203 # Containers should not get any format handling done
3204 # (the device node does not allow read / write calls)
3205 if self.type == "mdcontainer":
3206 return False
3207 # BIOS RAID sets should show as present even when teared down
3208 elif self.type == "mdbiosraidarray":
3209 return True
3210 else:
3211 return self.partedDevice is not None
3212
3213 @property
3214 def model(self):
3215 return self.description
3216
3217 @property
3218 def partitionable(self):
3219 return self.type == "mdbiosraidarray"
3220
3221 @property
3222 def isDisk(self):
3223 return self.type == "mdbiosraidarray"
3224
3225 def dracutSetupArgs(self):
3226 return set(["rd_MD_UUID=%s" % self.uuid])
3227
3228
3229 class DMRaidArrayDevice(DMDevice):
3230 """ A dmraid (device-mapper RAID) device """
3231 _type = "dm-raid array"
3232 _packages = ["dmraid"]
3233 _partitionable = True
3234 _isDisk = True
3235
3236 def __init__(self, name, raidSet=None, format=None,
3237 size=None, parents=None, sysfsPath=''):
3238 """ Create a DMRaidArrayDevice instance.
3239
3240 Arguments:
3241
3242 name -- the dmraid name also the device node's basename
3243
3244 Keyword Arguments:
3245
3246 raidSet -- the RaidSet object from block
3247 parents -- a list of the member devices
3248 sysfsPath -- sysfs device path
3249 size -- the device's size
3250 format -- a DeviceFormat instance
3251 """
3252 if isinstance(parents, list):
3253 for parent in parents:
3254 if not parent.format or parent.format.type != "dmraidmember":
3255 raise ValueError("parent devices must contain dmraidmember format")
3256 DMDevice.__init__(self, name, format=format, size=size,
3257 parents=parents, sysfsPath=sysfsPath, exists=True)
3258
3259 self.formatClass = get_device_format_class("dmraidmember")
3260 if not self.formatClass:
3261 raise StorageError("cannot find class for 'dmraidmember'")
3262
3263 self._raidSet = raidSet
3264
3265 @property
3266 def raidSet(self):
3267 return self._raidSet
3268
3269 def _addDevice(self, device):
3270 """ Add a new member device to the array.
3271
3272 XXX This is for use when probing devices, not for modification
3273 of arrays.
3274 """
3275 log_method_call(self, self.name, device=device.name, status=self.status)
3276
3277 if not self.exists:
3278 raise DeviceError("device has not been created", self.name)
3279
3280 if not isinstance(device.format, self.formatClass):
3281 raise ValueError("invalid device format for dmraid member")
3282
3283 if device in self.members:
3284 raise ValueError("device is already a member of this array")
3285
3286 # we added it, so now set up the relations
3287 self.devices.append(device)
3288 device.addChild()
3289
3290 @property
3291 def members(self):
3292 return self.parents
3293
3294 @property
3295 def devices(self):
3296 """ Return a list of this array's member device instances. """
3297 return self.parents
3298
3299 def deactivate(self):
3300 """ Deactivate the raid set. """
3301 log_method_call(self, self.name, status=self.status)
3302 # This call already checks if the set is not active.
3303 self._raidSet.deactivate()
3304
3305 def activate(self):
3306 """ Activate the raid set. """
3307 log_method_call(self, self.name, status=self.status)
3308 # This call already checks if the set is active.
3309 self._raidSet.activate(mknod=True)
3310 udev_settle()
3311
3312 def setup(self, intf=None, orig=False):
3313 """ Open, or set up, a device. """
3314 log_method_call(self, self.name, orig=orig, status=self.status)
3315 StorageDevice.setup(self, intf=intf, orig=orig)
3316 self.activate()
3317
3318 def teardown(self, recursive=None):
3319 """ Close, or tear down, a device. """
3320 log_method_call(self, self.name, status=self.status)
3321 if not self.exists and not recursive:
3322 raise DeviceError("device has not been created", self.name)
3323
3324 log.debug("not tearing down dmraid device %s" % self.name)
3325
3326 @property
3327 def description(self):
3328 return "BIOS RAID set (%s)" % self._raidSet.rs.set_type
3329
3330 @property
3331 def model(self):
3332 return self.description
3333
3334 def dracutSetupArgs(self):
3335 return set(["rd_DM_UUID=%s" % self.name])
3336
3337 class MultipathDevice(DMDevice):
3338 """ A multipath device """
3339 _type = "dm-multipath"
3340 _packages = ["device-mapper-multipath"]
3341 _services = ["multipathd"]
3342 _partitionable = True
3343 _isDisk = True
3344
3345 def __init__(self, name, info, format=None, size=None,
3346 parents=None, sysfsPath=''):
3347 """ Create a MultipathDevice instance.
3348
3349 Arguments:
3350
3351 name -- the device name (generally a device node's basename)
3352 info -- the udev info for this device
3353
3354 Keyword Arguments:
3355
3356 sysfsPath -- sysfs device path
3357 size -- the device's size
3358 format -- a DeviceFormat instance
3359 parents -- a list of the backing devices (Device instances)
3360 """
3361
3362 self._info = info
3363 self.setupIdentity()
3364 DMDevice.__init__(self, name, format=format, size=size,
3365 parents=parents, sysfsPath=sysfsPath,
3366 exists=True)
3367
3368 self.config = {
3369 'wwid' : self.identity,
3370 'mode' : '0600',
3371 'uid' : '0',
3372 'gid' : '0',
3373 }
3374
3375 def setupIdentity(self):
3376 """ Adds identifying remarks to MultipathDevice object.
3377
3378 May be overridden by a sub-class for e.g. RDAC handling.
3379 """
3380 self._identity = self._info.get("ID_SERIAL_RAW", self._info.get("ID_SERIAL_SHORT"))
3381
3382 @property
3383 def identity(self):
3384 """ Get identity set with setupIdentityFromInfo()
3385
3386 May be overridden by a sub-class for e.g. RDAC handling.
3387 """
3388 if not hasattr(self, "_identity"):
3389 raise RuntimeError, "setupIdentityFromInfo() has not been called."
3390 return self._identity
3391
3392 @property
3393 def wwid(self):
3394 identity = self.identity
3395 ret = []
3396 while identity:
3397 ret.append(identity[:2])
3398 identity = identity[2:]
3399 return ":".join(ret)
3400
3401 @property
3402 def model(self):
3403 if not self.parents:
3404 return ""
3405 return self.parents[0].model
3406
3407 @property
3408 def vendor(self):
3409 if not self.parents:
3410 return ""
3411 return self.parents[0].vendor
3412
3413 @property
3414 def description(self):
3415 return "WWID %s" % (self.wwid,)
3416
3417 def addParent(self, parent):
3418 """ Add a parent device to the mpath. """
3419 log_method_call(self, self.name, status=self.status)
3420 if self.status:
3421 self.teardown()
3422 self.parents.append(parent)
3423 self.setup()
3424 else:
3425 self.parents.append(parent)
3426
3427 def setupPartitions(self):
3428 log_method_call(self, name=self.name, kids=self.kids)
3429 rc = iutil.execWithRedirect("kpartx",
3430 ["-a", "-p", "p", "/dev/mapper/%s" % self.name],
3431 stdout = "/dev/tty5",
3432 stderr = "/dev/tty5")
3433 if rc:
3434 raise MPathError("multipath partition activation failed for '%s'" %
3435 self.name)
3436 udev_settle()
3437
3438 def teardown(self, recursive=None):
3439 """ Tear down the mpath device. """
3440 log_method_call(self, self.name, status=self.status)
3441
3442 if not self.exists and not recursive:
3443 raise DeviceError("device has not been created", self.name)
3444
3445 if self.status:
3446 # in case format is not a disklabel but a filesystem
3447 if self.originalFormat.exists:
3448 self.originalFormat.teardown()
3449 if self.format.exists:
3450 self.format.teardown()
3451 udev_settle()
3452
3453 if recursive:
3454 self.teardownParents(recursive=recursive)
3455
3456 def deactivate(self):
3457 """
3458 This is never called, included just for documentation.
3459
3460 If we called this during teardown(), we wouldn't be able to get parted
3461 object because /dev/mapper/mpathX wouldn't exist.
3462 """
3463 if self.exists and os.path.exists(self.path):
3464 #self.teardownPartitions()
3465 #rc = iutil.execWithRedirect("multipath",
3466 # ['-f', self.name],
3467 # stdout = "/dev/tty5",
3468 # stderr = "/dev/tty5")
3469 #if rc:
3470 # raise MPathError("multipath deactivation failed for '%s'" %
3471 # self.name)
3472 bdev = block.getDevice(self.name)
3473 devmap = block.getMap(major=bdev[0], minor=bdev[1])
3474 if devmap.open_count:
3475 return
3476 try:
3477 block.removeDeviceMap(devmap)
3478 except Exception as e:
3479 raise MPathError("failed to tear down multipath device %s: %s"
3480 % (self.name, e))
3481
3482 def setup(self, intf=None, orig=False):
3483 """ Open, or set up, a device. """
3484 log_method_call(self, self.name, orig=orig, status=self.status)
3485
3486 if self.status:
3487 return
3488
3489 StorageDevice.setup(self, intf=intf, orig=orig)
3490 udev_settle()
3491 rc = iutil.execWithRedirect("multipath",
3492 [self.name],
3493 stdout = "/dev/tty5",
3494 stderr = "/dev/tty5")
3495 if rc:
3496 raise MPathError("multipath activation failed for '%s'" %
3497 self.name, hardware_fault=True)
3498 udev_settle()
3499 self.setupPartitions()
3500 udev_settle()
3501
3502 class NoDevice(StorageDevice):
3503 """ A nodev device for nodev filesystems like tmpfs. """
3504 _type = "nodev"
3505
3506 def __init__(self, format=None):
3507 """ Create a NoDevice instance.
3508
3509 Arguments:
3510
3511 Keyword Arguments:
3512
3513 format -- a DeviceFormat instance
3514 """
3515 if format:
3516 name = format.type
3517 else:
3518 name = "none"
3519
3520 StorageDevice.__init__(self, name, format=format)
3521
3522 @property
3523 def path(self):
3524 """ Device node representing this device. """
3525 return self.name
3526
3527 def probe(self):
3528 """ Probe for any missing information about this device. """
3529 log_method_call(self, self.name, status=self.status)
3530
3531 def setup(self, intf=None, orig=False):
3532 """ Open, or set up, a device. """
3533 log_method_call(self, self.name, orig=orig, status=self.status)
3534
3535 def teardown(self, recursive=False):
3536 """ Close, or tear down, a device. """
3537 log_method_call(self, self.name, status=self.status)
3538
3539 def create(self, intf=None):
3540 """ Create the device. """
3541 log_method_call(self, self.name, status=self.status)
3542 self.setupParents()
3543
3544 def destroy(self):
3545 """ Destroy the device. """
3546 log_method_call(self, self.name, status=self.status)
3547
3548
3549 class FileDevice(StorageDevice):
3550 """ A file on a filesystem.
3551
3552 This exists because of swap files.
3553 """
3554 _type = "file"
3555 _devDir = ""
3556
3557 def __init__(self, path, format=None, size=None,
3558 exists=None, parents=None):
3559 """ Create a FileDevice instance.
3560
3561 Arguments:
3562
3563 path -- full path to the file
3564
3565 Keyword Arguments:
3566
3567 format -- a DeviceFormat instance
3568 size -- the file size (units TBD)
3569 parents -- a list of required devices (Device instances)
3570 exists -- indicates whether this is an existing device
3571 """
3572 StorageDevice.__init__(self, path, format=format, size=size,
3573 exists=exists, parents=parents)
3574
3575 def probe(self):
3576 """ Probe for any missing information about this device. """
3577 pass
3578
3579 @property
3580 def fstabSpec(self):
3581 return self.name
3582
3583 @property
3584 def path(self):
3585 path = self.name
3586 root = ""
3587 try:
3588 status = self.parents[0].format.status
3589 except (AttributeError, IndexError):
3590 status = False
3591
3592 if status:
3593 # this is the actual active mountpoint
3594 root = self.parents[0].format._mountpoint
3595 # trim the mountpoint down to the chroot since we already have
3596 # the otherwise fully-qualified path
3597 mountpoint = self.parents[0].format.mountpoint
3598 if mountpoint.endswith("/"):
3599 mountpoint = mountpoint[:-1]
3600 if mountpoint:
3601 root = root[:-len(mountpoint)]
3602
3603 return os.path.normpath("%s/%s" % (root, path))
3604
3605 def setup(self, intf=None, orig=False):
3606 StorageDevice.setup(self, orig=orig)
3607 if self.format and self.format.exists and not self.format.status:
3608 self.format.device = self.path
3609
3610 for parent in self.parents:
3611 if orig:
3612 parent.originalFormat.setup()
3613 else:
3614 parent.format.setup()
3615
3616 def teardown(self, recursive=None):
3617 StorageDevice.teardown(self)
3618 if self.format and self.format.exists and not self.format.status:
3619 self.format.device = self.path
3620
3621 def create(self, intf=None):
3622 """ Create the device. """
3623 log_method_call(self, self.name, status=self.status)
3624 if self.exists:
3625 raise DeviceError("device already exists", self.name)
3626
3627 w = None
3628 if intf:
3629 w = intf.waitWindow(_("Creating"),
3630 _("Creating file %s") % (self.path,))
3631
3632 try:
3633 # this only checks that parents exist
3634 self.createParents()
3635 self.setupParents()
3636
3637 fd = os.open(self.path, os.O_RDWR)
3638 buf = '\0' * 1024 * 1024 * self.size
3639 os.write(fd, buf)
3640 except (OSError, TypeError) as e:
3641 log.error("error writing out %s: %s" % (self.path, e))
3642 raise DeviceError(e, self.name)
3643 else:
3644 self.exists = True
3645 finally:
3646 os.close(fd)
3647 if w:
3648 w.pop()
3649
3650 def destroy(self):
3651 """ Destroy the device. """
3652 log_method_call(self, self.name, status=self.status)
3653 if not self.exists:
3654 raise DeviceError("device has not been created", self.name)
3655
3656 os.unlink(self.path)
3657 self.exists = False
3658
3659
3660 class DirectoryDevice(FileDevice):
3661 """ A directory on a filesystem.
3662
3663 This exists because of bind mounts.
3664 """
3665 _type = "directory"
3666
3667 def create(self):
3668 """ Create the device. """
3669 log_method_call(self, self.name, status=self.status)
3670 if self.exists:
3671 raise DeviceError("device already exists", self.name)
3672
3673 self.createParents()
3674 self.setupParents()
3675 try:
3676 iutil.mkdirChain(self.path)
3677 except Exception, e:
3678 raise DeviceError(e, self.name)
3679
3680 self.exists = True
3681
3682 def destroy(self):
3683 """ Destroy the device. """
3684 log_method_call(self, self.name, status=self.status)
3685 if not self.exists:
3686 raise DeviceError("device has not been created", self.name)
3687
3688 os.unlink(self.path)
3689 self.exists = False
3690
3691
3692 class iScsiDiskDevice(DiskDevice, NetworkStorageDevice):
3693 """ An iSCSI disk. """
3694 _type = "iscsi"
3695 _packages = ["iscsi-initiator-utils", "dracut-network"]
3696
3697 def __init__(self, device, **kwargs):
3698 self.node = kwargs.pop("node")
3699 self.ibft = kwargs.pop("ibft")
3700 self.nic = kwargs.pop("nic")
3701 self.initiator = kwargs.pop("initiator")
3702
3703 if self.node is None:
3704 # qla4xxx partial offload
3705 name = kwargs.pop("fw_name")
3706 address = kwargs.pop("fw_address")
3707 port = kwargs.pop("fw_port")
3708 DiskDevice.__init__(self, device, **kwargs)
3709 NetworkStorageDevice.__init__(self,
3710 host_address=address,
3711 nic=self.nic)
3712 log.debug("created new iscsi disk %s %s:%s using fw initiator %s"
3713 % (name, address, port, self.initiator))
3714 else:
3715 DiskDevice.__init__(self, device, **kwargs)
3716 NetworkStorageDevice.__init__(self, host_address=self.node.address,
3717 nic=self.nic)
3718 log.debug("created new iscsi disk %s %s:%d via %s:%s" % (self.node.name,
3719 self.node.address,
3720 self.node.port,
3721 self.node.iface,
3722 self.nic))
3723
3724 def dracutSetupArgs(self):
3725 if self.ibft:
3726 return set(["iscsi_firmware"])
3727
3728 # qla4xxx partial offload
3729 if self.node is None:
3730 return set()
3731
3732 address = self.node.address
3733 # surround ipv6 addresses with []
3734 if ":" in address:
3735 address = "[%s]" % address
3736
3737 netroot="netroot=iscsi:"
3738 auth = self.node.getAuth()
3739 if auth:
3740 netroot += "%s:%s" % (auth.username, auth.password)
3741 if len(auth.reverse_username) or len(auth.reverse_password):
3742 netroot += ":%s:%s" % (auth.reverse_username,
3743 auth.reverse_password)
3744
3745 iface_spec = ""
3746 if self.nic != "default":
3747 iface_spec = ":%s:%s" % (self.node.iface, self.nic)
3748 netroot += "@%s::%d%s::%s" % (address,
3749 self.node.port,
3750 iface_spec,
3751 self.node.name)
3752
3753 initiator = "iscsi_initiator=%s" % self.initiator
3754
3755 return set([netroot, initiator])
3756
3757 class FcoeDiskDevice(DiskDevice, NetworkStorageDevice):
3758 """ An FCoE disk. """
3759 _type = "fcoe"
3760 _packages = ["fcoe-utils", "dracut-network"]
3761
3762 def __init__(self, device, **kwargs):
3763 self.nic = kwargs.pop("nic")
3764 self.identifier = kwargs.pop("identifier")
3765 DiskDevice.__init__(self, device, **kwargs)
3766 NetworkStorageDevice.__init__(self, nic=self.nic)
3767 log.debug("created new fcoe disk %s (%s) @ %s" %
3768 (device, self.identifier, self.nic))
3769
3770 def dracutSetupArgs(self):
3771 dcb = True
3772
3773 from .fcoe import fcoe
3774 for nic, dcb, auto_vlan in fcoe().nics:
3775 if nic == self.nic:
3776 break
3777
3778 if dcb:
3779 dcbOpt = "dcb"
3780 else:
3781 dcbOpt = "nodcb"
3782
3783 if nic in fcoe().ksnics:
3784 return set(["fcoe=%s:%s" % (nic, dcbOpt)])
3785 else:
3786 return set(["fcoe=edd:%s" % dcbOpt])
3787
3788
3789 class OpticalDevice(StorageDevice):
3790 """ An optical drive, eg: cdrom, dvd+r, &c.
3791
3792 XXX Is this useful?
3793 """
3794 _type = "cdrom"
3795
3796 def __init__(self, name, major=None, minor=None, exists=None,
3797 format=None, parents=None, sysfsPath='', vendor="",
3798 model=""):
3799 StorageDevice.__init__(self, name, format=format,
3800 major=major, minor=minor, exists=True,
3801 parents=parents, sysfsPath=sysfsPath,
3802 vendor=vendor, model=model)
3803
3804 @property
3805 def mediaPresent(self):
3806 """ Return a boolean indicating whether or not the device contains
3807 media.
3808 """
3809 log_method_call(self, self.name, status=self.status)
3810 if not self.exists:
3811 raise DeviceError("device has not been created", self.name)
3812
3813 try:
3814 fd = os.open(self.path, os.O_RDONLY)
3815 except OSError as e:
3816 # errno 123 = No medium found
3817 if e.errno == 123:
3818 return False
3819 else:
3820 return True
3821 else:
3822 os.close(fd)
3823 return True
3824
3825 def eject(self):
3826 """ Eject the drawer. """
3827 import _isys
3828
3829 log_method_call(self, self.name, status=self.status)
3830 if not self.exists:
3831 raise DeviceError("device has not been created", self.name)
3832
3833 #try to umount and close device before ejecting
3834 self.teardown()
3835
3836 if flags.noeject:
3837 log.info("noeject in effect, not ejecting cdrom")
3838 return
3839
3840 # Make a best effort attempt to do the eject. If it fails, it's not
3841 # critical.
3842 fd = os.open(self.path, os.O_RDONLY | os.O_NONBLOCK)
3843
3844 try:
3845 _isys.ejectcdrom(fd)
3846 except SystemError as e:
3847 log.warning("error ejecting cdrom %s: %s" % (self.name, e))
3848
3849 os.close(fd)
3850
3851
3852 class ZFCPDiskDevice(DiskDevice):
3853 """ A mainframe ZFCP disk. """
3854 _type = "zfcp"
3855
3856 def __init__(self, device, **kwargs):
3857 self.hba_id = kwargs.pop("hba_id")
3858 self.wwpn = kwargs.pop("wwpn")
3859 self.fcp_lun = kwargs.pop("fcp_lun")
3860 DiskDevice.__init__(self, device, **kwargs)
3861
3862 def __str__(self):
3863 s = DiskDevice.__str__(self)
3864 s += (" hba_id = %(hba_id)s wwpn = %(wwpn)s fcp_lun = %(fcp_lun)s" %
3865 {"hba_id": self.hba_id,
3866 "wwpn": self.wwpn,
3867 "fcp_lun": self.fcp_lun})
3868 return s
3869
3870 @property
3871 def description(self):
3872 return "FCP device %(device)s with WWPN %(wwpn)s and LUN %(lun)s" \
3873 % {'device': self.hba_id,
3874 'wwpn': self.wwpn,
3875 'lun': self.fcp_lun}
3876
3877 def dracutSetupArgs(self):
3878 return set(["rd_ZFCP=%s,%s,%s" % (self.hba_id, self.wwpn, self.fcp_lun,)])
3879
3880
3881 class DASDDevice(DiskDevice):
3882 """ A mainframe DASD. """
3883 _type = "dasd"
3884
3885 def __init__(self, device, **kwargs):
3886 self.busid = kwargs.pop('busid')
3887 self.opts = kwargs.pop('opts')
3888 self.dasd = kwargs.pop('dasd')
3889 DiskDevice.__init__(self, device, **kwargs)
3890
3891 if self.dasd:
3892 self.dasd.addDASD(self)
3893
3894 @property
3895 def description(self):
3896 return "DASD device %s" % self.busid
3897
3898 def getOpts(self):
3899 return map(lambda (k, v): "%s=%s" % (k, v,), self.opts.items())
3900
3901 def dracutSetupArgs(self):
3902 conf = "/etc/dasd.conf"
3903 opts = {}
3904
3905 if os.path.isfile(conf):
3906 f = open(conf)
3907 lines = filter(lambda y: not y.startswith('#') and y != '',
3908 map(lambda x: x.strip(), f.readlines()))
3909 f.close()
3910
3911 for line in lines:
3912 parts = line.split()
3913 if parts != []:
3914 opts[parts[0]] = parts
3915
3916 if self.busid in opts.keys():
3917 return set(["rd_DASD=%s" % ",".join(opts[self.busid])])
3918 else:
3919 return set(["rd_DASD=%s" % ",".join([self.busid] + self.getOpts())])
3920
3921 class NFSDevice(StorageDevice, NetworkStorageDevice):
3922 """ An NFS device """
3923 _type = "nfs"
3924 _packages = ["dracut-network"]
3925
3926 def __init__(self, device, format=None, parents=None):
3927 # we could make host/ip, path, &c but will anything use it?
3928 StorageDevice.__init__(self, device, format=format, parents=parents)
3929 NetworkStorageDevice.__init__(self, device.split(":")[0])
3930
3931 @property
3932 def path(self):
3933 """ Device node representing this device. """
3934 return self.name
3935
3936 def setup(self, intf=None, orig=False):
3937 """ Open, or set up, a device. """
3938 log_method_call(self, self.name, orig=orig, status=self.status)
3939
3940 def teardown(self, recursive=None):
3941 """ Close, or tear down, a device. """
3942 log_method_call(self, self.name, status=self.status)
3943
3944 def create(self, intf=None):
3945 """ Create the device. """
3946 log_method_call(self, self.name, status=self.status)
3947 self.createParents()
3948 self.setupParents()
3949
3950 def destroy(self):
3951 """ Destroy the device. """
3952 log_method_call(self, self.name, status=self.status)

admin@koozali.org
ViewVC Help
Powered by ViewVC 1.2.1 RSS 2.0 feed