/[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.3 - (show annotations) (download) (as text)
Mon Nov 18 18:07:33 2013 UTC (10 years, 10 months ago) by charliebrady
Branch: MAIN
Changes since 1.2: +1 -1 lines
Content type: text/x-python
Update default peSize (4.0MB to 32.0MB). SME bug 8009, RH bugzilla 895982.

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 log.debug("vg %s has %dMB free" % (self.name, free))
2253 return free
2254
2255 @property
2256 def freeExtents(self):
2257 """ The number of free extents in this VG. """
2258 # TODO: just ask lvm if isModified returns False
2259 return self.freeSpace / self.peSize
2260
2261 def align(self, size, roundup=None):
2262 """ Align a size to a multiple of physical extent size. """
2263 size = numeric_type(size)
2264
2265 if roundup:
2266 round = math.ceil
2267 else:
2268 round = math.floor
2269
2270 # we want Kbytes as a float for our math
2271 size *= 1024.0
2272 pesize = self.peSize * 1024.0
2273 return long((round(size / pesize) * pesize) / 1024)
2274
2275 @property
2276 def pvs(self):
2277 """ A list of this VG's PVs """
2278 return self.parents[:] # we don't want folks changing our list
2279
2280 @property
2281 def lvs(self):
2282 """ A list of this VG's LVs """
2283 return self._lvs[:] # we don't want folks changing our list
2284
2285 @property
2286 def complete(self):
2287 """Check if the vg has all its pvs in the system
2288 Return True if complete.
2289 """
2290 # vgs with duplicate names are overcomplete, which is not what we want
2291 if self.hasDuplicate:
2292 return False
2293
2294 return len(self.pvs) == self.pvCount or not self.exists
2295
2296
2297 class LVMLogicalVolumeDevice(DMDevice):
2298 """ An LVM Logical Volume """
2299 _type = "lvmlv"
2300 _resizable = True
2301 _packages = ["lvm2"]
2302
2303 def __init__(self, name, vgdev, size=None, uuid=None,
2304 stripes=1, logSize=0, snapshotSpace=0,
2305 format=None, exists=None, sysfsPath='',
2306 grow=None, maxsize=None, percent=None,
2307 singlePV=False):
2308 """ Create a LVMLogicalVolumeDevice instance.
2309
2310 Arguments:
2311
2312 name -- the device name (generally a device node's basename)
2313 vgdev -- volume group (LVMVolumeGroupDevice instance)
2314
2315 Keyword Arguments:
2316
2317 size -- the device's size (in MB)
2318 uuid -- the device's UUID
2319 stripes -- number of copies in the vg (>1 for mirrored lvs)
2320 logSize -- size of log volume (for mirrored lvs)
2321 snapshotSpace -- sum of sizes of snapshots of this lv
2322 sysfsPath -- sysfs device path
2323 format -- a DeviceFormat instance
2324 exists -- indicates whether this is an existing device
2325 singlePV -- if true, maps this lv to a single pv
2326
2327 For new (non-existent) LVs only:
2328
2329 grow -- whether to grow this LV
2330 maxsize -- maximum size for growable LV (in MB)
2331 percent -- percent of VG space to take
2332
2333 """
2334 if isinstance(vgdev, list):
2335 if len(vgdev) != 1:
2336 raise ValueError("constructor requires a single LVMVolumeGroupDevice instance")
2337 elif not isinstance(vgdev[0], LVMVolumeGroupDevice):
2338 raise ValueError("constructor requires a LVMVolumeGroupDevice instance")
2339 elif not isinstance(vgdev, LVMVolumeGroupDevice):
2340 raise ValueError("constructor requires a LVMVolumeGroupDevice instance")
2341 DMDevice.__init__(self, name, size=size, format=format,
2342 sysfsPath=sysfsPath, parents=vgdev,
2343 exists=exists)
2344
2345 self.singlePVerr = ("%(mountpoint)s is restricted to a single "
2346 "physical volume on this platform. No physical "
2347 "volumes available in volume group %(vgname)s "
2348 "with %(size)d MB of available space." %
2349 {'mountpoint': getattr(self.format, "mountpoint",
2350 "A proposed logical volume"),
2351 'vgname': self.vg.name,
2352 'size': self.size})
2353
2354 self.uuid = uuid
2355 self.snapshotSpace = snapshotSpace
2356 self.stripes = stripes
2357 self.logSize = logSize
2358 self.singlePV = singlePV
2359
2360 self.req_grow = None
2361 self.req_max_size = 0
2362 self.req_size = 0
2363 self.req_percent = 0
2364
2365 if not self.exists:
2366 self.req_grow = grow
2367 self.req_max_size = numeric_type(maxsize)
2368 # XXX should we enforce that req_size be pe-aligned?
2369 self.req_size = self._size
2370 self.req_percent = numeric_type(percent)
2371
2372 if self.singlePV:
2373 # make sure there is at least one PV that can hold this LV
2374 validpvs = filter(lambda x: float(x.size) >= self.req_size,
2375 self.vg.pvs)
2376 if not validpvs:
2377 raise SinglePhysicalVolumeError(self.singlePVerr)
2378
2379 # here we go with the circular references
2380 self.vg._addLogVol(self)
2381
2382 def __str__(self):
2383 s = DMDevice.__str__(self)
2384 s += (" VG device = %(vgdev)r percent = %(percent)s\n"
2385 " mirrored = %(mirrored)s stripes = %(stripes)d"
2386 " snapshot total = %(snapshots)dMB\n"
2387 " VG space used = %(vgspace)dMB" %
2388 {"vgdev": self.vg, "percent": self.req_percent,
2389 "mirrored": self.mirrored, "stripes": self.stripes,
2390 "snapshots": self.snapshotSpace, "vgspace": self.vgSpaceUsed })
2391 return s
2392
2393 @property
2394 def dict(self):
2395 d = super(LVMLogicalVolumeDevice, self).dict
2396 if self.exists:
2397 d.update({"mirrored": self.mirrored, "stripes": self.stripes,
2398 "snapshots": self.snapshotSpace,
2399 "vgspace": self.vgSpaceUsed})
2400 else:
2401 d.update({"percent": self.req_percent})
2402
2403 return d
2404
2405 def writeKS(self, f, preexisting=False, noformat=False, s=None):
2406 args = ["--name=%s" % self.lvname,
2407 "--vgname=%s" % self.vg.name]
2408
2409 if self.req_grow:
2410 args.extend(["--grow", "--size=%s" % (self.req_size or 1)])
2411
2412 if self.req_max_size > 0:
2413 args.append("--maxsize=%s" % self.req_max_size)
2414 else:
2415 if self.req_percent > 0:
2416 args.append("--percent=%s" % self.req_percent)
2417 elif self.req_size > 0:
2418 args.append("--size=%s" % self.req_size)
2419
2420 if preexisting:
2421 args.append("--useexisting")
2422 if noformat:
2423 args.append("--noformat")
2424
2425 f.write("#logvol ")
2426 self.format.writeKS(f)
2427 f.write(" %s" % " ".join(args))
2428 if s:
2429 f.write(" %s" % s)
2430
2431 @property
2432 def mirrored(self):
2433 return self.stripes > 1
2434
2435 def _setSize(self, size):
2436 size = self.vg.align(numeric_type(size))
2437 log.debug("trying to set lv %s size to %dMB" % (self.name, size))
2438 if size <= (self.vg.freeSpace + self._size):
2439 self._size = size
2440 self.targetSize = size
2441 else:
2442 log.debug("failed to set size: %dMB short" % (size - (self.vg.freeSpace + self._size),))
2443 raise ValueError("not enough free space in volume group")
2444
2445 size = property(StorageDevice._getSize, _setSize)
2446
2447 @property
2448 def vgSpaceUsed(self):
2449 """ Space occupied by this LV, not including snapshots. """
2450 return (self.vg.align(self.size, roundup=True) * self.stripes
2451 + self.logSize)
2452
2453 @property
2454 def vg(self):
2455 """ This Logical Volume's Volume Group. """
2456 return self.parents[0]
2457
2458 @property
2459 def mapName(self):
2460 """ This device's device-mapper map name """
2461 # Thank you lvm for this lovely hack.
2462 return "%s-%s" % (self.vg.mapName, self._name.replace("-","--"))
2463
2464 @property
2465 def path(self):
2466 """ Device node representing this device. """
2467 return "%s/%s" % (self._devDir, self.mapName)
2468
2469 def getDMNode(self):
2470 """ Return the dm-X (eg: dm-0) device node for this device. """
2471 log_method_call(self, self.name, status=self.status)
2472 if not self.exists:
2473 raise DeviceError("device has not been created", self.name)
2474
2475 return dm.dm_node_from_name(self.mapName)
2476
2477 @property
2478 def name(self):
2479 """ This device's name. """
2480 return "%s-%s" % (self.vg.name, self._name)
2481
2482 @property
2483 def lvname(self):
2484 """ The LV's name (not including VG name). """
2485 return self._name
2486
2487 @property
2488 def complete(self):
2489 """ Test if vg exits and if it has all pvs. """
2490 return self.vg.complete
2491
2492 def setup(self, intf=None, orig=False):
2493 """ Open, or set up, a device. """
2494 log_method_call(self, self.name, orig=orig, status=self.status)
2495 if not self.exists:
2496 raise DeviceError("device has not been created", self.name)
2497
2498 if self.status:
2499 return
2500
2501 self.vg.setup(orig=orig)
2502 lvm.lvactivate(self.vg.name, self._name)
2503
2504 # we always probe since the device may not be set up when we want
2505 # information about it
2506 self._size = self.currentSize
2507
2508 def teardown(self, recursive=None):
2509 """ Close, or tear down, a device. """
2510 log_method_call(self, self.name, status=self.status)
2511 if not self.exists and not recursive:
2512 raise DeviceError("device has not been created", self.name)
2513
2514 if self.status:
2515 if self.originalFormat.exists:
2516 self.originalFormat.teardown()
2517 if self.format.exists:
2518 self.format.teardown()
2519 udev_settle()
2520
2521 if self.status:
2522 lvm.lvdeactivate(self.vg.name, self._name)
2523
2524 if recursive:
2525 # It's likely that teardown of a VG will fail due to other
2526 # LVs being active (filesystems mounted, &c), so don't let
2527 # it bring everything down.
2528 try:
2529 self.vg.teardown(recursive=recursive)
2530 except Exception as e:
2531 log.debug("vg %s teardown failed; continuing" % self.vg.name)
2532
2533 def _getSinglePV(self):
2534 validpvs = filter(lambda x: float(x.size) >= self.size, self.vg.pvs)
2535
2536 if not validpvs:
2537 raise SinglePhysicalVolumeError(self.singlePVerr)
2538
2539 return [validpvs[0].path]
2540
2541 def create(self, intf=None):
2542 """ Create the device. """
2543 log_method_call(self, self.name, status=self.status)
2544 if self.exists:
2545 raise DeviceError("device already exists", self.name)
2546
2547 w = None
2548 if intf:
2549 w = intf.progressWindow(_("Creating"),
2550 _("Creating device %s")
2551 % (self.path,),
2552 100, pulse = True)
2553 try:
2554 self.createParents()
2555 self.setupParents()
2556
2557 # should we use --zero for safety's sake?
2558 if self.singlePV:
2559 lvm.lvcreate(self.vg.name, self._name, self.size, progress=w,
2560 pvs=self._getSinglePV())
2561 else:
2562 lvm.lvcreate(self.vg.name, self._name, self.size, progress=w)
2563 except Exception:
2564 raise
2565 else:
2566 # FIXME set / update self.uuid here
2567 self.exists = True
2568 self.setup()
2569 finally:
2570 if w:
2571 w.pop()
2572
2573 def destroy(self):
2574 """ Destroy the device. """
2575 log_method_call(self, self.name, status=self.status)
2576 if not self.exists:
2577 raise DeviceError("device has not been created", self.name)
2578
2579 self.teardown()
2580 # set up the vg's pvs so lvm can remove the lv
2581 self.vg.setupParents(orig=True)
2582 lvm.lvremove(self.vg.name, self._name)
2583 self.exists = False
2584
2585 def resize(self, intf=None):
2586 # XXX resize format probably, right?
2587 log_method_call(self, self.name, status=self.status)
2588 if not self.exists:
2589 raise DeviceError("device has not been created", self.name)
2590
2591 # Setup VG parents (in case they are dmraid partitions for example)
2592 self.vg.setupParents(orig=True)
2593
2594 if self.originalFormat.exists:
2595 self.originalFormat.teardown()
2596 if self.format.exists:
2597 self.format.teardown()
2598
2599 udev_settle()
2600 lvm.lvresize(self.vg.name, self._name, self.size)
2601
2602 def dracutSetupArgs(self):
2603 # Note no mapName usage here, this is a lvm cmdline name, which
2604 # is different (ofcourse)
2605 return set(["rd_LVM_LV=%s/%s" % (self.vg.name, self._name)])
2606
2607 def checkSize(self):
2608 """ Check to make sure the size of the device is allowed by the
2609 format used.
2610
2611 return None is all is ok
2612 return large or small depending on the problem
2613 """
2614 problem = None
2615 if self.format.maxSize and self.size > self.format.maxSize:
2616 problem = _("large")
2617 elif (self.format.minSize and
2618 (not self.req_grow and
2619 self.size < self.format.minSize) or
2620 (self.req_grow and self.req_max_size and
2621 self.req_max_size < self.format.minSize)):
2622 problem = _("small")
2623 return problem
2624
2625 class MDRaidArrayDevice(StorageDevice):
2626 """ An mdraid (Linux RAID) device. """
2627 _type = "mdarray"
2628 _packages = ["mdadm"]
2629
2630 def __init__(self, name, level=None, major=None, minor=None, size=None,
2631 memberDevices=None, totalDevices=None,
2632 uuid=None, format=None, exists=None, metadataVersion=None,
2633 parents=None, sysfsPath=''):
2634 """ Create a MDRaidArrayDevice instance.
2635
2636 Arguments:
2637
2638 name -- the device name (generally a device node's basename)
2639
2640 Keyword Arguments:
2641
2642 level -- the device's RAID level (a string, eg: '1' or 'raid1')
2643 metadataVersion -- the version of the device's md metadata
2644 parents -- list of member devices (StorageDevice instances)
2645 size -- the device's size (units/format TBD)
2646 uuid -- the device's UUID
2647 minor -- the device minor
2648 sysfsPath -- sysfs device path
2649 format -- a DeviceFormat instance
2650 exists -- indicates whether this is an existing device
2651 """
2652 StorageDevice.__init__(self, name, format=format, exists=exists,
2653 major=major, minor=minor, size=size,
2654 parents=parents, sysfsPath=sysfsPath)
2655
2656 self.level = level
2657 if level == "container":
2658 self._type = "mdcontainer"
2659 elif level is not None:
2660 self.level = mdraid.raidLevel(level)
2661
2662 # For new arrays check if we have enough members
2663 if (not exists and parents and
2664 len(parents) < mdraid.get_raid_min_members(self.level)):
2665 raise ValueError, _("A RAID%d set requires atleast %d members") % (
2666 self.level, mdraid.get_raid_min_members(self.level))
2667
2668 self.uuid = uuid
2669 self._totalDevices = numeric_type(totalDevices)
2670 self._memberDevices = numeric_type(memberDevices)
2671 self.sysfsPath = "/devices/virtual/block/%s" % name
2672 self.chunkSize = 512.0 / 1024.0 # chunk size in MB
2673
2674 if not isinstance(metadataVersion, str):
2675 self.metadataVersion = "1.1"
2676 else:
2677 self.metadataVersion = metadataVersion
2678
2679 # bitmaps are not meaningful on raid0 according to mdadm-3.0.3
2680 self.createBitmap = self.level != 0
2681
2682 # For container members probe size now, as we cannot determine it
2683 # when teared down.
2684 if self.parents and self.parents[0].type == "mdcontainer":
2685 self._size = self.currentSize
2686 self._type = "mdbiosraidarray"
2687
2688 self.formatClass = get_device_format_class("mdmember")
2689 if not self.formatClass:
2690 raise DeviceError("cannot find class for 'mdmember'", self.name)
2691
2692 if self.exists and self.uuid:
2693 # this is a hack to work around mdadm's insistence on giving
2694 # really high minors to arrays it has no config entry for
2695 open("/etc/mdadm.conf", "a").write("ARRAY %s UUID=%s\n"
2696 % (self.path, self.uuid))
2697
2698 @property
2699 def rawArraySize(self):
2700 """ Calculate the raw array size without taking into account space
2701 reserved for metadata or chunkSize alignment.
2702
2703 This is used to calculate the superBlockSize for v1.1 and v1.2
2704 metadata.
2705
2706 Returns the raw size in MB
2707 """
2708 smallestMemberSize = self.smallestMember.size
2709 if self.level == mdraid.RAID0:
2710 size = self.memberDevices * smallestMemberSize
2711 elif self.level == mdraid.RAID1:
2712 size = smallestMemberSize
2713 elif self.level == mdraid.RAID4:
2714 size = (self.memberDevices - 1) * smallestMemberSize
2715 elif self.level == mdraid.RAID5:
2716 size = (self.memberDevices - 1) * smallestMemberSize
2717 elif self.level == mdraid.RAID6:
2718 size = (self.memberDevices - 2) * smallestMemberSize
2719 elif self.level == mdraid.RAID10:
2720 size = (self.memberDevices / 2.0) * smallestMemberSize
2721 else:
2722 size = smallestMemberSize
2723 log.error("unknown RAID level %s" % (self.level))
2724 log.debug("raw RAID %s size == %s" % (self.level, size))
2725 return size
2726
2727 @property
2728 def superBlockSize(self):
2729 """ mdadm has different amounts of space reserved for its use depending
2730 on the metadata type and size of the array.
2731
2732 0.9 use 2.0 MB
2733 1.0 use 2.0 MB
2734 1.1 or 1.2 use the formula lifted from mdadm/super1.c to calculate it
2735 based on the array size.
2736 """
2737 # mdadm 3.2.4 made a major change in the amount of space used for 1.1 and 1.2
2738 # in order to reserve space for reshaping. See commit 508a7f16 in the
2739 # upstream mdadm repository.
2740 if self.metadataVersion not in ["1.1", "1.2"]:
2741 return 2.0
2742
2743 array_size = self.rawArraySize
2744 # MDADM: We try to leave 0.1% at the start for reshape
2745 # MDADM: operations, but limit this to 128Meg (0.1% of 10Gig)
2746 # MDADM: which is plenty for efficient reshapes
2747 # NOTE: In the mdadm code this is in 512b sectors. Converted to use MB
2748 headroom = 128
2749 while headroom << 10 > array_size:
2750 headroom >>= 1
2751 log.info("Using %sMB superBlockSize" % (headroom))
2752 return headroom
2753
2754 @property
2755 def smallestMember(self):
2756 try:
2757 smallest = sorted(self.devices, key=lambda d: d.size)[0]
2758 except IndexError:
2759 smallest = None
2760 return smallest
2761
2762 @property
2763 def size(self):
2764 if not self.devices:
2765 return 0
2766
2767 # For container members return probed size, as we cannot determine it
2768 # when teared down.
2769 if self.type == "mdbiosraidarray":
2770 return self._size
2771
2772 size = 0
2773 smallestMemberSize = self.smallestMember.size - self.superBlockSize
2774 if not self.exists or not self.partedDevice:
2775 if self.level == mdraid.RAID0:
2776 size = self.memberDevices * smallestMemberSize
2777 size -= size % self.chunkSize
2778 elif self.level == mdraid.RAID1:
2779 size = smallestMemberSize
2780 elif self.level == mdraid.RAID4:
2781 size = (self.memberDevices - 1) * smallestMemberSize
2782 size -= size % self.chunkSize
2783 elif self.level == mdraid.RAID5:
2784 size = (self.memberDevices - 1) * smallestMemberSize
2785 size -= size % self.chunkSize
2786 elif self.level == mdraid.RAID6:
2787 size = (self.memberDevices - 2) * smallestMemberSize
2788 size -= size % self.chunkSize
2789 elif self.level == mdraid.RAID10:
2790 size = (self.memberDevices / 2.0) * smallestMemberSize
2791 size -= size % self.chunkSize
2792 log.debug("non-existant RAID %s size == %s" % (self.level, size))
2793 else:
2794 size = self.partedDevice.getSize()
2795 log.debug("existing RAID %s size == %s" % (self.level, size))
2796
2797 return size
2798
2799 @property
2800 def description(self):
2801 if self.level == mdraid.RAID0:
2802 levelstr = "stripe"
2803 elif self.level == mdraid.RAID1:
2804 levelstr = "mirror"
2805 else:
2806 levelstr = "raid%s" % self.level
2807
2808 if self.type == "mdcontainer":
2809 return "BIOS RAID container"
2810 elif self.type == "mdbiosraidarray":
2811 return "BIOS RAID set (%s)" % levelstr
2812 else:
2813 return "MDRAID set (%s)" % levelstr
2814
2815 def __str__(self):
2816 s = StorageDevice.__str__(self)
2817 s += (" level = %(level)s spares = %(spares)s\n"
2818 " members = %(memberDevices)s\n"
2819 " total devices = %(totalDevices)s"
2820 " metadata version = %(metadataVersion)s" %
2821 {"level": self.level, "spares": self.spares,
2822 "memberDevices": self.memberDevices,
2823 "totalDevices": self.totalDevices,
2824 "metadataVersion": self.metadataVersion})
2825 return s
2826
2827 @property
2828 def dict(self):
2829 d = super(MDRaidArrayDevice, self).dict
2830 d.update({"level": self.level,
2831 "spares": self.spares, "memberDevices": self.memberDevices,
2832 "totalDevices": self.totalDevices,
2833 "metadataVersion": self.metadataVersion})
2834 return d
2835
2836 def writeKS(self, f, preexisting=False, noformat=False, s=None):
2837 args = ["--level=%s" % self.level,
2838 "--device=%s" % self.name]
2839 mems = []
2840
2841 if self.spares > 0:
2842 args.append("--spares=%s" % self.spares)
2843 if preexisting:
2844 args.append("--useexisting")
2845 if noformat:
2846 args.append("--noformat")
2847
2848 for mem in self.parents:
2849 mems.append("raid.%s" % mem.format.majorminor)
2850
2851 f.write("#raid ")
2852 self.format.writeKS(f)
2853 f.write(" %s" % " ".join(args))
2854 f.write(" %s" % " ".join(mems))
2855 if s:
2856 f.write(" %s" % s)
2857
2858 @property
2859 def mdadmConfEntry(self):
2860 """ This array's mdadm.conf entry. """
2861 if self.level is None or self.memberDevices is None or not self.uuid:
2862 raise DeviceError("array is not fully defined", self.name)
2863
2864 # containers and the sets within must only have a UUID= parameter
2865 if self.type == "mdcontainer" or self.type == "mdbiosraidarray":
2866 fmt = "ARRAY %s UUID=%s\n"
2867 return fmt % (self.path, self.uuid)
2868
2869 fmt = "ARRAY %s level=raid%d num-devices=%d UUID=%s\n"
2870 return fmt % (self.path, self.level, self.memberDevices, self.uuid)
2871
2872 @property
2873 def totalDevices(self):
2874 """ Total number of devices in the array, including spares. """
2875 count = len(self.parents)
2876 if not self.exists:
2877 count = self._totalDevices
2878 return count
2879
2880 def _getMemberDevices(self):
2881 return self._memberDevices
2882
2883 def _setMemberDevices(self, number):
2884 if not isinstance(number, int):
2885 raise ValueError("memberDevices is an integer")
2886
2887 if number > self.totalDevices:
2888 raise ValueError("memberDevices cannot be greater than totalDevices")
2889 self._memberDevices = number
2890
2891 memberDevices = property(_getMemberDevices, _setMemberDevices,
2892 doc="number of member devices")
2893
2894 def _getSpares(self):
2895 spares = 0
2896 if self.memberDevices is not None:
2897 if self.totalDevices is not None and \
2898 self.totalDevices > self.memberDevices:
2899 spares = self.totalDevices - self.memberDevices
2900 elif self.totalDevices is None:
2901 spares = self.memberDevices
2902 self._totalDevices = self.memberDevices
2903 return spares
2904
2905 def _setSpares(self, spares):
2906 # FIXME: this is too simple to be right
2907 if self.totalDevices > spares:
2908 self.memberDevices = self.totalDevices - spares
2909
2910 spares = property(_getSpares, _setSpares)
2911
2912 def probe(self):
2913 """ Probe for any missing information about this device.
2914
2915 I'd like to avoid paying any attention to "Preferred Minor"
2916 as it seems problematic.
2917 """
2918 log_method_call(self, self.name, status=self.status)
2919 if not self.exists:
2920 raise DeviceError("device has not been created", self.name)
2921
2922 try:
2923 self.devices[0].setup()
2924 except Exception:
2925 return
2926
2927 info = mdraid.mdexamine(self.devices[0].path)
2928 if self.level is None:
2929 self.level = mdraid.raidLevel(info['level'])
2930
2931 def updateSysfsPath(self):
2932 """ Update this device's sysfs path. """
2933 log_method_call(self, self.name, status=self.status)
2934 if not self.exists:
2935 raise DeviceError("device has not been created", self.name)
2936
2937 if self.status:
2938 self.sysfsPath = "/devices/virtual/block/%s" % self.name
2939 else:
2940 self.sysfsPath = ''
2941
2942 def _addDevice(self, device):
2943 """ Add a new member device to the array.
2944
2945 XXX This is for use when probing devices, not for modification
2946 of arrays.
2947 """
2948 log_method_call(self,
2949 self.name,
2950 device=device.name,
2951 status=self.status)
2952 if not self.exists:
2953 raise DeviceError("device has not been created", self.name)
2954
2955 if not isinstance(device.format, self.formatClass):
2956 raise ValueError("invalid device format for mdraid member")
2957
2958 if self.uuid and device.format.mdUuid != self.uuid:
2959 raise ValueError("cannot add member with non-matching UUID")
2960
2961 if device in self.devices:
2962 raise ValueError("device is already a member of this array")
2963
2964 # we added it, so now set up the relations
2965 self.devices.append(device)
2966 device.addChild()
2967
2968 device.setup()
2969 udev_settle()
2970
2971 if self.spares > 0:
2972 # mdadm doesn't like it when you try to incrementally add spares
2973 return
2974
2975 try:
2976 mdraid.mdadd(device.path)
2977 # mdadd causes udev events
2978 udev_settle()
2979 except MDRaidError as e:
2980 log.warning("failed to add member %s to md array %s: %s"
2981 % (device.path, self.path, e))
2982
2983 if self.status:
2984 # we always probe since the device may not be set up when we want
2985 # information about it
2986 self._size = self.currentSize
2987
2988 def _removeDevice(self, device):
2989 """ Remove a component device from the array.
2990
2991 XXX This is for use by clearpart, not for reconfiguration.
2992 """
2993 log_method_call(self,
2994 self.name,
2995 device=device.name,
2996 status=self.status)
2997
2998 if device not in self.devices:
2999 raise ValueError("cannot remove non-member device from array")
3000
3001 self.devices.remove(device)
3002 device.removeChild()
3003
3004 @property
3005 def status(self):
3006 """ This device's status.
3007
3008 For now, this should return a boolean:
3009 True the device is open and ready for use
3010 False the device is not open
3011 """
3012 # check the status in sysfs
3013 status = False
3014 if not self.exists:
3015 return status
3016
3017 state_file = "/sys/%s/md/array_state" % self.sysfsPath
3018 if os.access(state_file, os.R_OK):
3019 state = open(state_file).read().strip()
3020 log.debug("%s state is %s" % (self.name, state))
3021 if state in ("clean", "active", "active-idle", "readonly", "read-auto"):
3022 status = True
3023 # mdcontainers have state inactive when started (clear if stopped)
3024 if self.type == "mdcontainer" and state == "inactive":
3025 status = True
3026
3027 return status
3028
3029 @property
3030 def degraded(self):
3031 """ Return True if the array is running in degraded mode. """
3032 rc = False
3033 degraded_file = "/sys/%s/md/degraded" % self.sysfsPath
3034 if os.access(degraded_file, os.R_OK):
3035 val = open(degraded_file).read().strip()
3036 log.debug("%s degraded is %s" % (self.name, val))
3037 if val == "1":
3038 rc = True
3039
3040 return rc
3041
3042 @property
3043 def devices(self):
3044 """ Return a list of this array's member device instances. """
3045 return self.parents
3046
3047 def setup(self, intf=None, orig=False):
3048 """ Open, or set up, a device. """
3049 log_method_call(self, self.name, orig=orig, status=self.status)
3050 if not self.exists:
3051 raise DeviceError("device has not been created", self.name)
3052
3053 if self.status:
3054 return
3055
3056 disks = []
3057 for member in self.devices:
3058 member.setup(orig=orig)
3059 disks.append(member.path)
3060
3061 update_super_minor = self.metadataVersion in ("0", "0.90")
3062
3063 mdraid.mdactivate(self.path,
3064 members=disks,
3065 super_minor=self.minor,
3066 update_super_minor=update_super_minor,
3067 uuid=self.uuid)
3068
3069 udev_settle()
3070
3071 # we always probe since the device may not be set up when we want
3072 # information about it
3073 self._size = self.currentSize
3074
3075 def teardown(self, recursive=None):
3076 """ Close, or tear down, a device. """
3077 log_method_call(self, self.name, status=self.status)
3078 if not self.exists and not recursive:
3079 raise DeviceError("device has not been created", self.name)
3080
3081 if self.status:
3082 if self.originalFormat.exists:
3083 self.originalFormat.teardown()
3084 self.format.cacheMajorminor()
3085 if self.format.exists:
3086 self.format.teardown()
3087 udev_settle()
3088
3089 # Since BIOS RAID sets (containers in mdraid terminology) never change
3090 # there is no need to stop them and later restart them. Not stopping
3091 # (and thus also not starting) them also works around bug 523334
3092 if self.type == "mdcontainer" or self.type == "mdbiosraidarray":
3093 return
3094
3095 # We don't really care what the array's state is. If the device
3096 # file exists, we want to deactivate it. mdraid has too many
3097 # states.
3098 if self.exists and os.path.exists(self.path):
3099 mdraid.mddeactivate(self.path)
3100
3101 if recursive:
3102 self.teardownParents(recursive=recursive)
3103
3104 def preCommitFixup(self, *args, **kwargs):
3105 """ Determine create parameters for this set """
3106 mountpoints = kwargs.pop("mountpoints")
3107 log_method_call(self, self.name, mountpoints)
3108
3109 if "/boot" in mountpoints:
3110 bootmountpoint = "/boot"
3111 else:
3112 bootmountpoint = "/"
3113
3114 # If we are used to boot from we cannot use 1.1 metadata
3115 if getattr(self.format, "mountpoint", None) == bootmountpoint or \
3116 getattr(self.format, "mountpoint", None) == "/boot/efi" or \
3117 self.format.type == "prepboot":
3118 self.metadataVersion = "1.0"
3119
3120 # Bitmaps are not useful for swap and small partitions
3121 if self.size < 1000 or self.format.type == "swap":
3122 self.createBitmap = False
3123
3124 def create(self, intf=None):
3125 """ Create the device. """
3126 log_method_call(self, self.name, status=self.status)
3127 if self.exists:
3128 raise DeviceError("device already exists", self.name)
3129
3130 w = None
3131 if intf:
3132 w = intf.progressWindow(_("Creating"),
3133 _("Creating device %s")
3134 % (self.path,),
3135 100, pulse = True)
3136 try:
3137 self.createParents()
3138 self.setupParents()
3139
3140 disks = [disk.path for disk in self.devices]
3141 spares = len(self.devices) - self.memberDevices
3142
3143 # allow creation of degraded arrays
3144 if len(disks) == 1:
3145 disks.append("missing")
3146
3147 mdraid.mdcreate(self.path,
3148 self.level,
3149 disks,
3150 spares,
3151 metadataVer=self.metadataVersion,
3152 bitmap=self.createBitmap,
3153 progress=w)
3154 except Exception:
3155 raise
3156 else:
3157 self.exists = True
3158 # the array is automatically activated upon creation, but...
3159 self.setup()
3160 udev_settle()
3161 self.updateSysfsPath()
3162 info = udev_get_block_device(self.sysfsPath)
3163 self.uuid = udev_device_get_md_uuid(info)
3164 for member in self.devices:
3165 member.mdUuid = self.uuid
3166 finally:
3167 if w:
3168 w.pop()
3169
3170 @property
3171 def formatArgs(self):
3172 formatArgs = []
3173 if self.format.type == "ext2":
3174 if self.level == mdraid.RAID5:
3175 formatArgs = ['-R',
3176 'stride=%d' % ((self.memberDevices - 1) * 16)]
3177 if self.level == mdraid.RAID4:
3178 formatArgs = ['-R',
3179 'stride=%d' % ((self.memberDevices - 1) * 16)]
3180 elif self.level == mdraid.RAID0:
3181 formatArgs = ['-R',
3182 'stride=%d' % (self.memberDevices * 16)]
3183
3184 def destroy(self):
3185 """ Destroy the device. """
3186 log_method_call(self, self.name, status=self.status)
3187 if not self.exists:
3188 raise DeviceError("device has not been created", self.name)
3189
3190 self.teardown()
3191
3192 # The destruction of the formatting on the member devices does the
3193 # real work, but it isn't our place to do it from here.
3194 self.exists = False
3195
3196 @property
3197 def mediaPresent(self):
3198 # Containers should not get any format handling done
3199 # (the device node does not allow read / write calls)
3200 if self.type == "mdcontainer":
3201 return False
3202 # BIOS RAID sets should show as present even when teared down
3203 elif self.type == "mdbiosraidarray":
3204 return True
3205 else:
3206 return self.partedDevice is not None
3207
3208 @property
3209 def model(self):
3210 return self.description
3211
3212 @property
3213 def partitionable(self):
3214 return self.type == "mdbiosraidarray"
3215
3216 @property
3217 def isDisk(self):
3218 return self.type == "mdbiosraidarray"
3219
3220 def dracutSetupArgs(self):
3221 return set(["rd_MD_UUID=%s" % self.uuid])
3222
3223
3224 class DMRaidArrayDevice(DMDevice):
3225 """ A dmraid (device-mapper RAID) device """
3226 _type = "dm-raid array"
3227 _packages = ["dmraid"]
3228 _partitionable = True
3229 _isDisk = True
3230
3231 def __init__(self, name, raidSet=None, format=None,
3232 size=None, parents=None, sysfsPath=''):
3233 """ Create a DMRaidArrayDevice instance.
3234
3235 Arguments:
3236
3237 name -- the dmraid name also the device node's basename
3238
3239 Keyword Arguments:
3240
3241 raidSet -- the RaidSet object from block
3242 parents -- a list of the member devices
3243 sysfsPath -- sysfs device path
3244 size -- the device's size
3245 format -- a DeviceFormat instance
3246 """
3247 if isinstance(parents, list):
3248 for parent in parents:
3249 if not parent.format or parent.format.type != "dmraidmember":
3250 raise ValueError("parent devices must contain dmraidmember format")
3251 DMDevice.__init__(self, name, format=format, size=size,
3252 parents=parents, sysfsPath=sysfsPath, exists=True)
3253
3254 self.formatClass = get_device_format_class("dmraidmember")
3255 if not self.formatClass:
3256 raise StorageError("cannot find class for 'dmraidmember'")
3257
3258 self._raidSet = raidSet
3259
3260 @property
3261 def raidSet(self):
3262 return self._raidSet
3263
3264 def _addDevice(self, device):
3265 """ Add a new member device to the array.
3266
3267 XXX This is for use when probing devices, not for modification
3268 of arrays.
3269 """
3270 log_method_call(self, self.name, device=device.name, status=self.status)
3271
3272 if not self.exists:
3273 raise DeviceError("device has not been created", self.name)
3274
3275 if not isinstance(device.format, self.formatClass):
3276 raise ValueError("invalid device format for dmraid member")
3277
3278 if device in self.members:
3279 raise ValueError("device is already a member of this array")
3280
3281 # we added it, so now set up the relations
3282 self.devices.append(device)
3283 device.addChild()
3284
3285 @property
3286 def members(self):
3287 return self.parents
3288
3289 @property
3290 def devices(self):
3291 """ Return a list of this array's member device instances. """
3292 return self.parents
3293
3294 def deactivate(self):
3295 """ Deactivate the raid set. """
3296 log_method_call(self, self.name, status=self.status)
3297 # This call already checks if the set is not active.
3298 self._raidSet.deactivate()
3299
3300 def activate(self):
3301 """ Activate the raid set. """
3302 log_method_call(self, self.name, status=self.status)
3303 # This call already checks if the set is active.
3304 self._raidSet.activate(mknod=True)
3305 udev_settle()
3306
3307 def setup(self, intf=None, orig=False):
3308 """ Open, or set up, a device. """
3309 log_method_call(self, self.name, orig=orig, status=self.status)
3310 StorageDevice.setup(self, intf=intf, orig=orig)
3311 self.activate()
3312
3313 def teardown(self, recursive=None):
3314 """ Close, or tear down, a device. """
3315 log_method_call(self, self.name, status=self.status)
3316 if not self.exists and not recursive:
3317 raise DeviceError("device has not been created", self.name)
3318
3319 log.debug("not tearing down dmraid device %s" % self.name)
3320
3321 @property
3322 def description(self):
3323 return "BIOS RAID set (%s)" % self._raidSet.rs.set_type
3324
3325 @property
3326 def model(self):
3327 return self.description
3328
3329 def dracutSetupArgs(self):
3330 return set(["rd_DM_UUID=%s" % self.name])
3331
3332 class MultipathDevice(DMDevice):
3333 """ A multipath device """
3334 _type = "dm-multipath"
3335 _packages = ["device-mapper-multipath"]
3336 _services = ["multipathd"]
3337 _partitionable = True
3338 _isDisk = True
3339
3340 def __init__(self, name, info, format=None, size=None,
3341 parents=None, sysfsPath=''):
3342 """ Create a MultipathDevice instance.
3343
3344 Arguments:
3345
3346 name -- the device name (generally a device node's basename)
3347 info -- the udev info for this device
3348
3349 Keyword Arguments:
3350
3351 sysfsPath -- sysfs device path
3352 size -- the device's size
3353 format -- a DeviceFormat instance
3354 parents -- a list of the backing devices (Device instances)
3355 """
3356
3357 self._info = info
3358 self.setupIdentity()
3359 DMDevice.__init__(self, name, format=format, size=size,
3360 parents=parents, sysfsPath=sysfsPath,
3361 exists=True)
3362
3363 self.config = {
3364 'wwid' : self.identity,
3365 'mode' : '0600',
3366 'uid' : '0',
3367 'gid' : '0',
3368 }
3369
3370 def setupIdentity(self):
3371 """ Adds identifying remarks to MultipathDevice object.
3372
3373 May be overridden by a sub-class for e.g. RDAC handling.
3374 """
3375 self._identity = self._info.get("ID_SERIAL_RAW", self._info.get("ID_SERIAL_SHORT"))
3376
3377 @property
3378 def identity(self):
3379 """ Get identity set with setupIdentityFromInfo()
3380
3381 May be overridden by a sub-class for e.g. RDAC handling.
3382 """
3383 if not hasattr(self, "_identity"):
3384 raise RuntimeError, "setupIdentityFromInfo() has not been called."
3385 return self._identity
3386
3387 @property
3388 def wwid(self):
3389 identity = self.identity
3390 ret = []
3391 while identity:
3392 ret.append(identity[:2])
3393 identity = identity[2:]
3394 return ":".join(ret)
3395
3396 @property
3397 def model(self):
3398 if not self.parents:
3399 return ""
3400 return self.parents[0].model
3401
3402 @property
3403 def vendor(self):
3404 if not self.parents:
3405 return ""
3406 return self.parents[0].vendor
3407
3408 @property
3409 def description(self):
3410 return "WWID %s" % (self.wwid,)
3411
3412 def addParent(self, parent):
3413 """ Add a parent device to the mpath. """
3414 log_method_call(self, self.name, status=self.status)
3415 if self.status:
3416 self.teardown()
3417 self.parents.append(parent)
3418 self.setup()
3419 else:
3420 self.parents.append(parent)
3421
3422 def setupPartitions(self):
3423 log_method_call(self, name=self.name, kids=self.kids)
3424 rc = iutil.execWithRedirect("kpartx",
3425 ["-a", "-p", "p", "/dev/mapper/%s" % self.name],
3426 stdout = "/dev/tty5",
3427 stderr = "/dev/tty5")
3428 if rc:
3429 raise MPathError("multipath partition activation failed for '%s'" %
3430 self.name)
3431 udev_settle()
3432
3433 def teardown(self, recursive=None):
3434 """ Tear down the mpath device. """
3435 log_method_call(self, self.name, status=self.status)
3436
3437 if not self.exists and not recursive:
3438 raise DeviceError("device has not been created", self.name)
3439
3440 if self.status:
3441 # in case format is not a disklabel but a filesystem
3442 if self.originalFormat.exists:
3443 self.originalFormat.teardown()
3444 if self.format.exists:
3445 self.format.teardown()
3446 udev_settle()
3447
3448 if recursive:
3449 self.teardownParents(recursive=recursive)
3450
3451 def deactivate(self):
3452 """
3453 This is never called, included just for documentation.
3454
3455 If we called this during teardown(), we wouldn't be able to get parted
3456 object because /dev/mapper/mpathX wouldn't exist.
3457 """
3458 if self.exists and os.path.exists(self.path):
3459 #self.teardownPartitions()
3460 #rc = iutil.execWithRedirect("multipath",
3461 # ['-f', self.name],
3462 # stdout = "/dev/tty5",
3463 # stderr = "/dev/tty5")
3464 #if rc:
3465 # raise MPathError("multipath deactivation failed for '%s'" %
3466 # self.name)
3467 bdev = block.getDevice(self.name)
3468 devmap = block.getMap(major=bdev[0], minor=bdev[1])
3469 if devmap.open_count:
3470 return
3471 try:
3472 block.removeDeviceMap(devmap)
3473 except Exception as e:
3474 raise MPathError("failed to tear down multipath device %s: %s"
3475 % (self.name, e))
3476
3477 def setup(self, intf=None, orig=False):
3478 """ Open, or set up, a device. """
3479 log_method_call(self, self.name, orig=orig, status=self.status)
3480
3481 if self.status:
3482 return
3483
3484 StorageDevice.setup(self, intf=intf, orig=orig)
3485 udev_settle()
3486 rc = iutil.execWithRedirect("multipath",
3487 [self.name],
3488 stdout = "/dev/tty5",
3489 stderr = "/dev/tty5")
3490 if rc:
3491 raise MPathError("multipath activation failed for '%s'" %
3492 self.name, hardware_fault=True)
3493 udev_settle()
3494 self.setupPartitions()
3495 udev_settle()
3496
3497 class NoDevice(StorageDevice):
3498 """ A nodev device for nodev filesystems like tmpfs. """
3499 _type = "nodev"
3500
3501 def __init__(self, format=None):
3502 """ Create a NoDevice instance.
3503
3504 Arguments:
3505
3506 Keyword Arguments:
3507
3508 format -- a DeviceFormat instance
3509 """
3510 if format:
3511 name = format.type
3512 else:
3513 name = "none"
3514
3515 StorageDevice.__init__(self, name, format=format)
3516
3517 @property
3518 def path(self):
3519 """ Device node representing this device. """
3520 return self.name
3521
3522 def probe(self):
3523 """ Probe for any missing information about this device. """
3524 log_method_call(self, self.name, status=self.status)
3525
3526 def setup(self, intf=None, orig=False):
3527 """ Open, or set up, a device. """
3528 log_method_call(self, self.name, orig=orig, status=self.status)
3529
3530 def teardown(self, recursive=False):
3531 """ Close, or tear down, a device. """
3532 log_method_call(self, self.name, status=self.status)
3533
3534 def create(self, intf=None):
3535 """ Create the device. """
3536 log_method_call(self, self.name, status=self.status)
3537 self.setupParents()
3538
3539 def destroy(self):
3540 """ Destroy the device. """
3541 log_method_call(self, self.name, status=self.status)
3542
3543
3544 class FileDevice(StorageDevice):
3545 """ A file on a filesystem.
3546
3547 This exists because of swap files.
3548 """
3549 _type = "file"
3550 _devDir = ""
3551
3552 def __init__(self, path, format=None, size=None,
3553 exists=None, parents=None):
3554 """ Create a FileDevice instance.
3555
3556 Arguments:
3557
3558 path -- full path to the file
3559
3560 Keyword Arguments:
3561
3562 format -- a DeviceFormat instance
3563 size -- the file size (units TBD)
3564 parents -- a list of required devices (Device instances)
3565 exists -- indicates whether this is an existing device
3566 """
3567 StorageDevice.__init__(self, path, format=format, size=size,
3568 exists=exists, parents=parents)
3569
3570 def probe(self):
3571 """ Probe for any missing information about this device. """
3572 pass
3573
3574 @property
3575 def fstabSpec(self):
3576 return self.name
3577
3578 @property
3579 def path(self):
3580 path = self.name
3581 root = ""
3582 try:
3583 status = self.parents[0].format.status
3584 except (AttributeError, IndexError):
3585 status = False
3586
3587 if status:
3588 # this is the actual active mountpoint
3589 root = self.parents[0].format._mountpoint
3590 # trim the mountpoint down to the chroot since we already have
3591 # the otherwise fully-qualified path
3592 mountpoint = self.parents[0].format.mountpoint
3593 if mountpoint.endswith("/"):
3594 mountpoint = mountpoint[:-1]
3595 if mountpoint:
3596 root = root[:-len(mountpoint)]
3597
3598 return os.path.normpath("%s/%s" % (root, path))
3599
3600 def setup(self, intf=None, orig=False):
3601 StorageDevice.setup(self, orig=orig)
3602 if self.format and self.format.exists and not self.format.status:
3603 self.format.device = self.path
3604
3605 for parent in self.parents:
3606 if orig:
3607 parent.originalFormat.setup()
3608 else:
3609 parent.format.setup()
3610
3611 def teardown(self, recursive=None):
3612 StorageDevice.teardown(self)
3613 if self.format and self.format.exists and not self.format.status:
3614 self.format.device = self.path
3615
3616 def create(self, intf=None):
3617 """ Create the device. """
3618 log_method_call(self, self.name, status=self.status)
3619 if self.exists:
3620 raise DeviceError("device already exists", self.name)
3621
3622 w = None
3623 if intf:
3624 w = intf.waitWindow(_("Creating"),
3625 _("Creating file %s") % (self.path,))
3626
3627 try:
3628 # this only checks that parents exist
3629 self.createParents()
3630 self.setupParents()
3631
3632 fd = os.open(self.path, os.O_RDWR)
3633 buf = '\0' * 1024 * 1024 * self.size
3634 os.write(fd, buf)
3635 except (OSError, TypeError) as e:
3636 log.error("error writing out %s: %s" % (self.path, e))
3637 raise DeviceError(e, self.name)
3638 else:
3639 self.exists = True
3640 finally:
3641 os.close(fd)
3642 if w:
3643 w.pop()
3644
3645 def destroy(self):
3646 """ Destroy the device. """
3647 log_method_call(self, self.name, status=self.status)
3648 if not self.exists:
3649 raise DeviceError("device has not been created", self.name)
3650
3651 os.unlink(self.path)
3652 self.exists = False
3653
3654
3655 class DirectoryDevice(FileDevice):
3656 """ A directory on a filesystem.
3657
3658 This exists because of bind mounts.
3659 """
3660 _type = "directory"
3661
3662 def create(self):
3663 """ Create the device. """
3664 log_method_call(self, self.name, status=self.status)
3665 if self.exists:
3666 raise DeviceError("device already exists", self.name)
3667
3668 self.createParents()
3669 self.setupParents()
3670 try:
3671 iutil.mkdirChain(self.path)
3672 except Exception, e:
3673 raise DeviceError(e, self.name)
3674
3675 self.exists = True
3676
3677 def destroy(self):
3678 """ Destroy the device. """
3679 log_method_call(self, self.name, status=self.status)
3680 if not self.exists:
3681 raise DeviceError("device has not been created", self.name)
3682
3683 os.unlink(self.path)
3684 self.exists = False
3685
3686
3687 class iScsiDiskDevice(DiskDevice, NetworkStorageDevice):
3688 """ An iSCSI disk. """
3689 _type = "iscsi"
3690 _packages = ["iscsi-initiator-utils", "dracut-network"]
3691
3692 def __init__(self, device, **kwargs):
3693 self.node = kwargs.pop("node")
3694 self.ibft = kwargs.pop("ibft")
3695 self.nic = kwargs.pop("nic")
3696 self.initiator = kwargs.pop("initiator")
3697
3698 if self.node is None:
3699 # qla4xxx partial offload
3700 name = kwargs.pop("fw_name")
3701 address = kwargs.pop("fw_address")
3702 port = kwargs.pop("fw_port")
3703 DiskDevice.__init__(self, device, **kwargs)
3704 NetworkStorageDevice.__init__(self,
3705 host_address=address,
3706 nic=self.nic)
3707 log.debug("created new iscsi disk %s %s:%s using fw initiator %s"
3708 % (name, address, port, self.initiator))
3709 else:
3710 DiskDevice.__init__(self, device, **kwargs)
3711 NetworkStorageDevice.__init__(self, host_address=self.node.address,
3712 nic=self.nic)
3713 log.debug("created new iscsi disk %s %s:%d via %s:%s" % (self.node.name,
3714 self.node.address,
3715 self.node.port,
3716 self.node.iface,
3717 self.nic))
3718
3719 def dracutSetupArgs(self):
3720 if self.ibft:
3721 return set(["iscsi_firmware"])
3722
3723 # qla4xxx partial offload
3724 if self.node is None:
3725 return set()
3726
3727 address = self.node.address
3728 # surround ipv6 addresses with []
3729 if ":" in address:
3730 address = "[%s]" % address
3731
3732 netroot="netroot=iscsi:"
3733 auth = self.node.getAuth()
3734 if auth:
3735 netroot += "%s:%s" % (auth.username, auth.password)
3736 if len(auth.reverse_username) or len(auth.reverse_password):
3737 netroot += ":%s:%s" % (auth.reverse_username,
3738 auth.reverse_password)
3739
3740 iface_spec = ""
3741 if self.nic != "default":
3742 iface_spec = ":%s:%s" % (self.node.iface, self.nic)
3743 netroot += "@%s::%d%s::%s" % (address,
3744 self.node.port,
3745 iface_spec,
3746 self.node.name)
3747
3748 initiator = "iscsi_initiator=%s" % self.initiator
3749
3750 return set([netroot, initiator])
3751
3752 class FcoeDiskDevice(DiskDevice, NetworkStorageDevice):
3753 """ An FCoE disk. """
3754 _type = "fcoe"
3755 _packages = ["fcoe-utils", "dracut-network"]
3756
3757 def __init__(self, device, **kwargs):
3758 self.nic = kwargs.pop("nic")
3759 self.identifier = kwargs.pop("identifier")
3760 DiskDevice.__init__(self, device, **kwargs)
3761 NetworkStorageDevice.__init__(self, nic=self.nic)
3762 log.debug("created new fcoe disk %s (%s) @ %s" %
3763 (device, self.identifier, self.nic))
3764
3765 def dracutSetupArgs(self):
3766 dcb = True
3767
3768 from .fcoe import fcoe
3769 for nic, dcb, auto_vlan in fcoe().nics:
3770 if nic == self.nic:
3771 break
3772
3773 if dcb:
3774 dcbOpt = "dcb"
3775 else:
3776 dcbOpt = "nodcb"
3777
3778 if nic in fcoe().ksnics:
3779 return set(["fcoe=%s:%s" % (nic, dcbOpt)])
3780 else:
3781 return set(["fcoe=edd:%s" % dcbOpt])
3782
3783
3784 class OpticalDevice(StorageDevice):
3785 """ An optical drive, eg: cdrom, dvd+r, &c.
3786
3787 XXX Is this useful?
3788 """
3789 _type = "cdrom"
3790
3791 def __init__(self, name, major=None, minor=None, exists=None,
3792 format=None, parents=None, sysfsPath='', vendor="",
3793 model=""):
3794 StorageDevice.__init__(self, name, format=format,
3795 major=major, minor=minor, exists=True,
3796 parents=parents, sysfsPath=sysfsPath,
3797 vendor=vendor, model=model)
3798
3799 @property
3800 def mediaPresent(self):
3801 """ Return a boolean indicating whether or not the device contains
3802 media.
3803 """
3804 log_method_call(self, self.name, status=self.status)
3805 if not self.exists:
3806 raise DeviceError("device has not been created", self.name)
3807
3808 try:
3809 fd = os.open(self.path, os.O_RDONLY)
3810 except OSError as e:
3811 # errno 123 = No medium found
3812 if e.errno == 123:
3813 return False
3814 else:
3815 return True
3816 else:
3817 os.close(fd)
3818 return True
3819
3820 def eject(self):
3821 """ Eject the drawer. """
3822 import _isys
3823
3824 log_method_call(self, self.name, status=self.status)
3825 if not self.exists:
3826 raise DeviceError("device has not been created", self.name)
3827
3828 #try to umount and close device before ejecting
3829 self.teardown()
3830
3831 if flags.noeject:
3832 log.info("noeject in effect, not ejecting cdrom")
3833 return
3834
3835 # Make a best effort attempt to do the eject. If it fails, it's not
3836 # critical.
3837 fd = os.open(self.path, os.O_RDONLY | os.O_NONBLOCK)
3838
3839 try:
3840 _isys.ejectcdrom(fd)
3841 except SystemError as e:
3842 log.warning("error ejecting cdrom %s: %s" % (self.name, e))
3843
3844 os.close(fd)
3845
3846
3847 class ZFCPDiskDevice(DiskDevice):
3848 """ A mainframe ZFCP disk. """
3849 _type = "zfcp"
3850
3851 def __init__(self, device, **kwargs):
3852 self.hba_id = kwargs.pop("hba_id")
3853 self.wwpn = kwargs.pop("wwpn")
3854 self.fcp_lun = kwargs.pop("fcp_lun")
3855 DiskDevice.__init__(self, device, **kwargs)
3856
3857 def __str__(self):
3858 s = DiskDevice.__str__(self)
3859 s += (" hba_id = %(hba_id)s wwpn = %(wwpn)s fcp_lun = %(fcp_lun)s" %
3860 {"hba_id": self.hba_id,
3861 "wwpn": self.wwpn,
3862 "fcp_lun": self.fcp_lun})
3863 return s
3864
3865 @property
3866 def description(self):
3867 return "FCP device %(device)s with WWPN %(wwpn)s and LUN %(lun)s" \
3868 % {'device': self.hba_id,
3869 'wwpn': self.wwpn,
3870 'lun': self.fcp_lun}
3871
3872 def dracutSetupArgs(self):
3873 return set(["rd_ZFCP=%s,%s,%s" % (self.hba_id, self.wwpn, self.fcp_lun,)])
3874
3875
3876 class DASDDevice(DiskDevice):
3877 """ A mainframe DASD. """
3878 _type = "dasd"
3879
3880 def __init__(self, device, **kwargs):
3881 self.busid = kwargs.pop('busid')
3882 self.opts = kwargs.pop('opts')
3883 self.dasd = kwargs.pop('dasd')
3884 DiskDevice.__init__(self, device, **kwargs)
3885
3886 if self.dasd:
3887 self.dasd.addDASD(self)
3888
3889 @property
3890 def description(self):
3891 return "DASD device %s" % self.busid
3892
3893 def getOpts(self):
3894 return map(lambda (k, v): "%s=%s" % (k, v,), self.opts.items())
3895
3896 def dracutSetupArgs(self):
3897 conf = "/etc/dasd.conf"
3898 opts = {}
3899
3900 if os.path.isfile(conf):
3901 f = open(conf)
3902 lines = filter(lambda y: not y.startswith('#') and y != '',
3903 map(lambda x: x.strip(), f.readlines()))
3904 f.close()
3905
3906 for line in lines:
3907 parts = line.split()
3908 if parts != []:
3909 opts[parts[0]] = parts
3910
3911 if self.busid in opts.keys():
3912 return set(["rd_DASD=%s" % ",".join(opts[self.busid])])
3913 else:
3914 return set(["rd_DASD=%s" % ",".join([self.busid] + self.getOpts())])
3915
3916 class NFSDevice(StorageDevice, NetworkStorageDevice):
3917 """ An NFS device """
3918 _type = "nfs"
3919 _packages = ["dracut-network"]
3920
3921 def __init__(self, device, format=None, parents=None):
3922 # we could make host/ip, path, &c but will anything use it?
3923 StorageDevice.__init__(self, device, format=format, parents=parents)
3924 NetworkStorageDevice.__init__(self, device.split(":")[0])
3925
3926 @property
3927 def path(self):
3928 """ Device node representing this device. """
3929 return self.name
3930
3931 def setup(self, intf=None, orig=False):
3932 """ Open, or set up, a device. """
3933 log_method_call(self, self.name, orig=orig, status=self.status)
3934
3935 def teardown(self, recursive=None):
3936 """ Close, or tear down, a device. """
3937 log_method_call(self, self.name, status=self.status)
3938
3939 def create(self, intf=None):
3940 """ Create the device. """
3941 log_method_call(self, self.name, status=self.status)
3942 self.createParents()
3943 self.setupParents()
3944
3945 def destroy(self):
3946 """ Destroy the device. """
3947 log_method_call(self, self.name, status=self.status)

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