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

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

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


Revision 1.1 - (hide annotations) (download) (as text)
Tue Jul 30 21:01:52 2013 UTC (11 years, 3 months ago) by charliebrady
Branch: MAIN
Content type: text/x-python
Add storage module from anaconda to updates directory, so that parts of it can be modified.

1 charliebrady 1.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 = 4.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     mdraid.mdcreate(self.path,
3143     self.level,
3144     disks,
3145     spares,
3146     metadataVer=self.metadataVersion,
3147     bitmap=self.createBitmap,
3148     progress=w)
3149     except Exception:
3150     raise
3151     else:
3152     self.exists = True
3153     # the array is automatically activated upon creation, but...
3154     self.setup()
3155     udev_settle()
3156     self.updateSysfsPath()
3157     info = udev_get_block_device(self.sysfsPath)
3158     self.uuid = udev_device_get_md_uuid(info)
3159     for member in self.devices:
3160     member.mdUuid = self.uuid
3161     finally:
3162     if w:
3163     w.pop()
3164    
3165     @property
3166     def formatArgs(self):
3167     formatArgs = []
3168     if self.format.type == "ext2":
3169     if self.level == mdraid.RAID5:
3170     formatArgs = ['-R',
3171     'stride=%d' % ((self.memberDevices - 1) * 16)]
3172     if self.level == mdraid.RAID4:
3173     formatArgs = ['-R',
3174     'stride=%d' % ((self.memberDevices - 1) * 16)]
3175     elif self.level == mdraid.RAID0:
3176     formatArgs = ['-R',
3177     'stride=%d' % (self.memberDevices * 16)]
3178    
3179     def destroy(self):
3180     """ Destroy the device. """
3181     log_method_call(self, self.name, status=self.status)
3182     if not self.exists:
3183     raise DeviceError("device has not been created", self.name)
3184    
3185     self.teardown()
3186    
3187     # The destruction of the formatting on the member devices does the
3188     # real work, but it isn't our place to do it from here.
3189     self.exists = False
3190    
3191     @property
3192     def mediaPresent(self):
3193     # Containers should not get any format handling done
3194     # (the device node does not allow read / write calls)
3195     if self.type == "mdcontainer":
3196     return False
3197     # BIOS RAID sets should show as present even when teared down
3198     elif self.type == "mdbiosraidarray":
3199     return True
3200     else:
3201     return self.partedDevice is not None
3202    
3203     @property
3204     def model(self):
3205     return self.description
3206    
3207     @property
3208     def partitionable(self):
3209     return self.type == "mdbiosraidarray"
3210    
3211     @property
3212     def isDisk(self):
3213     return self.type == "mdbiosraidarray"
3214    
3215     def dracutSetupArgs(self):
3216     return set(["rd_MD_UUID=%s" % self.uuid])
3217    
3218    
3219     class DMRaidArrayDevice(DMDevice):
3220     """ A dmraid (device-mapper RAID) device """
3221     _type = "dm-raid array"
3222     _packages = ["dmraid"]
3223     _partitionable = True
3224     _isDisk = True
3225    
3226     def __init__(self, name, raidSet=None, format=None,
3227     size=None, parents=None, sysfsPath=''):
3228     """ Create a DMRaidArrayDevice instance.
3229    
3230     Arguments:
3231    
3232     name -- the dmraid name also the device node's basename
3233    
3234     Keyword Arguments:
3235    
3236     raidSet -- the RaidSet object from block
3237     parents -- a list of the member devices
3238     sysfsPath -- sysfs device path
3239     size -- the device's size
3240     format -- a DeviceFormat instance
3241     """
3242     if isinstance(parents, list):
3243     for parent in parents:
3244     if not parent.format or parent.format.type != "dmraidmember":
3245     raise ValueError("parent devices must contain dmraidmember format")
3246     DMDevice.__init__(self, name, format=format, size=size,
3247     parents=parents, sysfsPath=sysfsPath, exists=True)
3248    
3249     self.formatClass = get_device_format_class("dmraidmember")
3250     if not self.formatClass:
3251     raise StorageError("cannot find class for 'dmraidmember'")
3252    
3253     self._raidSet = raidSet
3254    
3255     @property
3256     def raidSet(self):
3257     return self._raidSet
3258    
3259     def _addDevice(self, device):
3260     """ Add a new member device to the array.
3261    
3262     XXX This is for use when probing devices, not for modification
3263     of arrays.
3264     """
3265     log_method_call(self, self.name, device=device.name, status=self.status)
3266    
3267     if not self.exists:
3268     raise DeviceError("device has not been created", self.name)
3269    
3270     if not isinstance(device.format, self.formatClass):
3271     raise ValueError("invalid device format for dmraid member")
3272    
3273     if device in self.members:
3274     raise ValueError("device is already a member of this array")
3275    
3276     # we added it, so now set up the relations
3277     self.devices.append(device)
3278     device.addChild()
3279    
3280     @property
3281     def members(self):
3282     return self.parents
3283    
3284     @property
3285     def devices(self):
3286     """ Return a list of this array's member device instances. """
3287     return self.parents
3288    
3289     def deactivate(self):
3290     """ Deactivate the raid set. """
3291     log_method_call(self, self.name, status=self.status)
3292     # This call already checks if the set is not active.
3293     self._raidSet.deactivate()
3294    
3295     def activate(self):
3296     """ Activate the raid set. """
3297     log_method_call(self, self.name, status=self.status)
3298     # This call already checks if the set is active.
3299     self._raidSet.activate(mknod=True)
3300     udev_settle()
3301    
3302     def setup(self, intf=None, orig=False):
3303     """ Open, or set up, a device. """
3304     log_method_call(self, self.name, orig=orig, status=self.status)
3305     StorageDevice.setup(self, intf=intf, orig=orig)
3306     self.activate()
3307    
3308     def teardown(self, recursive=None):
3309     """ Close, or tear down, a device. """
3310     log_method_call(self, self.name, status=self.status)
3311     if not self.exists and not recursive:
3312     raise DeviceError("device has not been created", self.name)
3313    
3314     log.debug("not tearing down dmraid device %s" % self.name)
3315    
3316     @property
3317     def description(self):
3318     return "BIOS RAID set (%s)" % self._raidSet.rs.set_type
3319    
3320     @property
3321     def model(self):
3322     return self.description
3323    
3324     def dracutSetupArgs(self):
3325     return set(["rd_DM_UUID=%s" % self.name])
3326    
3327     class MultipathDevice(DMDevice):
3328     """ A multipath device """
3329     _type = "dm-multipath"
3330     _packages = ["device-mapper-multipath"]
3331     _services = ["multipathd"]
3332     _partitionable = True
3333     _isDisk = True
3334    
3335     def __init__(self, name, info, format=None, size=None,
3336     parents=None, sysfsPath=''):
3337     """ Create a MultipathDevice instance.
3338    
3339     Arguments:
3340    
3341     name -- the device name (generally a device node's basename)
3342     info -- the udev info for this device
3343    
3344     Keyword Arguments:
3345    
3346     sysfsPath -- sysfs device path
3347     size -- the device's size
3348     format -- a DeviceFormat instance
3349     parents -- a list of the backing devices (Device instances)
3350     """
3351    
3352     self._info = info
3353     self.setupIdentity()
3354     DMDevice.__init__(self, name, format=format, size=size,
3355     parents=parents, sysfsPath=sysfsPath,
3356     exists=True)
3357    
3358     self.config = {
3359     'wwid' : self.identity,
3360     'mode' : '0600',
3361     'uid' : '0',
3362     'gid' : '0',
3363     }
3364    
3365     def setupIdentity(self):
3366     """ Adds identifying remarks to MultipathDevice object.
3367    
3368     May be overridden by a sub-class for e.g. RDAC handling.
3369     """
3370     self._identity = self._info.get("ID_SERIAL_RAW", self._info.get("ID_SERIAL_SHORT"))
3371    
3372     @property
3373     def identity(self):
3374     """ Get identity set with setupIdentityFromInfo()
3375    
3376     May be overridden by a sub-class for e.g. RDAC handling.
3377     """
3378     if not hasattr(self, "_identity"):
3379     raise RuntimeError, "setupIdentityFromInfo() has not been called."
3380     return self._identity
3381    
3382     @property
3383     def wwid(self):
3384     identity = self.identity
3385     ret = []
3386     while identity:
3387     ret.append(identity[:2])
3388     identity = identity[2:]
3389     return ":".join(ret)
3390    
3391     @property
3392     def model(self):
3393     if not self.parents:
3394     return ""
3395     return self.parents[0].model
3396    
3397     @property
3398     def vendor(self):
3399     if not self.parents:
3400     return ""
3401     return self.parents[0].vendor
3402    
3403     @property
3404     def description(self):
3405     return "WWID %s" % (self.wwid,)
3406    
3407     def addParent(self, parent):
3408     """ Add a parent device to the mpath. """
3409     log_method_call(self, self.name, status=self.status)
3410     if self.status:
3411     self.teardown()
3412     self.parents.append(parent)
3413     self.setup()
3414     else:
3415     self.parents.append(parent)
3416    
3417     def setupPartitions(self):
3418     log_method_call(self, name=self.name, kids=self.kids)
3419     rc = iutil.execWithRedirect("kpartx",
3420     ["-a", "-p", "p", "/dev/mapper/%s" % self.name],
3421     stdout = "/dev/tty5",
3422     stderr = "/dev/tty5")
3423     if rc:
3424     raise MPathError("multipath partition activation failed for '%s'" %
3425     self.name)
3426     udev_settle()
3427    
3428     def teardown(self, recursive=None):
3429     """ Tear down the mpath device. """
3430     log_method_call(self, self.name, status=self.status)
3431    
3432     if not self.exists and not recursive:
3433     raise DeviceError("device has not been created", self.name)
3434    
3435     if self.status:
3436     # in case format is not a disklabel but a filesystem
3437     if self.originalFormat.exists:
3438     self.originalFormat.teardown()
3439     if self.format.exists:
3440     self.format.teardown()
3441     udev_settle()
3442    
3443     if recursive:
3444     self.teardownParents(recursive=recursive)
3445    
3446     def deactivate(self):
3447     """
3448     This is never called, included just for documentation.
3449    
3450     If we called this during teardown(), we wouldn't be able to get parted
3451     object because /dev/mapper/mpathX wouldn't exist.
3452     """
3453     if self.exists and os.path.exists(self.path):
3454     #self.teardownPartitions()
3455     #rc = iutil.execWithRedirect("multipath",
3456     # ['-f', self.name],
3457     # stdout = "/dev/tty5",
3458     # stderr = "/dev/tty5")
3459     #if rc:
3460     # raise MPathError("multipath deactivation failed for '%s'" %
3461     # self.name)
3462     bdev = block.getDevice(self.name)
3463     devmap = block.getMap(major=bdev[0], minor=bdev[1])
3464     if devmap.open_count:
3465     return
3466     try:
3467     block.removeDeviceMap(devmap)
3468     except Exception as e:
3469     raise MPathError("failed to tear down multipath device %s: %s"
3470     % (self.name, e))
3471    
3472     def setup(self, intf=None, orig=False):
3473     """ Open, or set up, a device. """
3474     log_method_call(self, self.name, orig=orig, status=self.status)
3475    
3476     if self.status:
3477     return
3478    
3479     StorageDevice.setup(self, intf=intf, orig=orig)
3480     udev_settle()
3481     rc = iutil.execWithRedirect("multipath",
3482     [self.name],
3483     stdout = "/dev/tty5",
3484     stderr = "/dev/tty5")
3485     if rc:
3486     raise MPathError("multipath activation failed for '%s'" %
3487     self.name, hardware_fault=True)
3488     udev_settle()
3489     self.setupPartitions()
3490     udev_settle()
3491    
3492     class NoDevice(StorageDevice):
3493     """ A nodev device for nodev filesystems like tmpfs. """
3494     _type = "nodev"
3495    
3496     def __init__(self, format=None):
3497     """ Create a NoDevice instance.
3498    
3499     Arguments:
3500    
3501     Keyword Arguments:
3502    
3503     format -- a DeviceFormat instance
3504     """
3505     if format:
3506     name = format.type
3507     else:
3508     name = "none"
3509    
3510     StorageDevice.__init__(self, name, format=format)
3511    
3512     @property
3513     def path(self):
3514     """ Device node representing this device. """
3515     return self.name
3516    
3517     def probe(self):
3518     """ Probe for any missing information about this device. """
3519     log_method_call(self, self.name, status=self.status)
3520    
3521     def setup(self, intf=None, orig=False):
3522     """ Open, or set up, a device. """
3523     log_method_call(self, self.name, orig=orig, status=self.status)
3524    
3525     def teardown(self, recursive=False):
3526     """ Close, or tear down, a device. """
3527     log_method_call(self, self.name, status=self.status)
3528    
3529     def create(self, intf=None):
3530     """ Create the device. """
3531     log_method_call(self, self.name, status=self.status)
3532     self.setupParents()
3533    
3534     def destroy(self):
3535     """ Destroy the device. """
3536     log_method_call(self, self.name, status=self.status)
3537    
3538    
3539     class FileDevice(StorageDevice):
3540     """ A file on a filesystem.
3541    
3542     This exists because of swap files.
3543     """
3544     _type = "file"
3545     _devDir = ""
3546    
3547     def __init__(self, path, format=None, size=None,
3548     exists=None, parents=None):
3549     """ Create a FileDevice instance.
3550    
3551     Arguments:
3552    
3553     path -- full path to the file
3554    
3555     Keyword Arguments:
3556    
3557     format -- a DeviceFormat instance
3558     size -- the file size (units TBD)
3559     parents -- a list of required devices (Device instances)
3560     exists -- indicates whether this is an existing device
3561     """
3562     StorageDevice.__init__(self, path, format=format, size=size,
3563     exists=exists, parents=parents)
3564    
3565     def probe(self):
3566     """ Probe for any missing information about this device. """
3567     pass
3568    
3569     @property
3570     def fstabSpec(self):
3571     return self.name
3572    
3573     @property
3574     def path(self):
3575     path = self.name
3576     root = ""
3577     try:
3578     status = self.parents[0].format.status
3579     except (AttributeError, IndexError):
3580     status = False
3581    
3582     if status:
3583     # this is the actual active mountpoint
3584     root = self.parents[0].format._mountpoint
3585     # trim the mountpoint down to the chroot since we already have
3586     # the otherwise fully-qualified path
3587     mountpoint = self.parents[0].format.mountpoint
3588     if mountpoint.endswith("/"):
3589     mountpoint = mountpoint[:-1]
3590     if mountpoint:
3591     root = root[:-len(mountpoint)]
3592    
3593     return os.path.normpath("%s/%s" % (root, path))
3594    
3595     def setup(self, intf=None, orig=False):
3596     StorageDevice.setup(self, orig=orig)
3597     if self.format and self.format.exists and not self.format.status:
3598     self.format.device = self.path
3599    
3600     for parent in self.parents:
3601     if orig:
3602     parent.originalFormat.setup()
3603     else:
3604     parent.format.setup()
3605    
3606     def teardown(self, recursive=None):
3607     StorageDevice.teardown(self)
3608     if self.format and self.format.exists and not self.format.status:
3609     self.format.device = self.path
3610    
3611     def create(self, intf=None):
3612     """ Create the device. """
3613     log_method_call(self, self.name, status=self.status)
3614     if self.exists:
3615     raise DeviceError("device already exists", self.name)
3616    
3617     w = None
3618     if intf:
3619     w = intf.waitWindow(_("Creating"),
3620     _("Creating file %s") % (self.path,))
3621    
3622     try:
3623     # this only checks that parents exist
3624     self.createParents()
3625     self.setupParents()
3626    
3627     fd = os.open(self.path, os.O_RDWR)
3628     buf = '\0' * 1024 * 1024 * self.size
3629     os.write(fd, buf)
3630     except (OSError, TypeError) as e:
3631     log.error("error writing out %s: %s" % (self.path, e))
3632     raise DeviceError(e, self.name)
3633     else:
3634     self.exists = True
3635     finally:
3636     os.close(fd)
3637     if w:
3638     w.pop()
3639    
3640     def destroy(self):
3641     """ Destroy the device. """
3642     log_method_call(self, self.name, status=self.status)
3643     if not self.exists:
3644     raise DeviceError("device has not been created", self.name)
3645    
3646     os.unlink(self.path)
3647     self.exists = False
3648    
3649    
3650     class DirectoryDevice(FileDevice):
3651     """ A directory on a filesystem.
3652    
3653     This exists because of bind mounts.
3654     """
3655     _type = "directory"
3656    
3657     def create(self):
3658     """ Create the device. """
3659     log_method_call(self, self.name, status=self.status)
3660     if self.exists:
3661     raise DeviceError("device already exists", self.name)
3662    
3663     self.createParents()
3664     self.setupParents()
3665     try:
3666     iutil.mkdirChain(self.path)
3667     except Exception, e:
3668     raise DeviceError(e, self.name)
3669    
3670     self.exists = True
3671    
3672     def destroy(self):
3673     """ Destroy the device. """
3674     log_method_call(self, self.name, status=self.status)
3675     if not self.exists:
3676     raise DeviceError("device has not been created", self.name)
3677    
3678     os.unlink(self.path)
3679     self.exists = False
3680    
3681    
3682     class iScsiDiskDevice(DiskDevice, NetworkStorageDevice):
3683     """ An iSCSI disk. """
3684     _type = "iscsi"
3685     _packages = ["iscsi-initiator-utils", "dracut-network"]
3686    
3687     def __init__(self, device, **kwargs):
3688     self.node = kwargs.pop("node")
3689     self.ibft = kwargs.pop("ibft")
3690     self.nic = kwargs.pop("nic")
3691     self.initiator = kwargs.pop("initiator")
3692    
3693     if self.node is None:
3694     # qla4xxx partial offload
3695     name = kwargs.pop("fw_name")
3696     address = kwargs.pop("fw_address")
3697     port = kwargs.pop("fw_port")
3698     DiskDevice.__init__(self, device, **kwargs)
3699     NetworkStorageDevice.__init__(self,
3700     host_address=address,
3701     nic=self.nic)
3702     log.debug("created new iscsi disk %s %s:%s using fw initiator %s"
3703     % (name, address, port, self.initiator))
3704     else:
3705     DiskDevice.__init__(self, device, **kwargs)
3706     NetworkStorageDevice.__init__(self, host_address=self.node.address,
3707     nic=self.nic)
3708     log.debug("created new iscsi disk %s %s:%d via %s:%s" % (self.node.name,
3709     self.node.address,
3710     self.node.port,
3711     self.node.iface,
3712     self.nic))
3713    
3714     def dracutSetupArgs(self):
3715     if self.ibft:
3716     return set(["iscsi_firmware"])
3717    
3718     # qla4xxx partial offload
3719     if self.node is None:
3720     return set()
3721    
3722     address = self.node.address
3723     # surround ipv6 addresses with []
3724     if ":" in address:
3725     address = "[%s]" % address
3726    
3727     netroot="netroot=iscsi:"
3728     auth = self.node.getAuth()
3729     if auth:
3730     netroot += "%s:%s" % (auth.username, auth.password)
3731     if len(auth.reverse_username) or len(auth.reverse_password):
3732     netroot += ":%s:%s" % (auth.reverse_username,
3733     auth.reverse_password)
3734    
3735     iface_spec = ""
3736     if self.nic != "default":
3737     iface_spec = ":%s:%s" % (self.node.iface, self.nic)
3738     netroot += "@%s::%d%s::%s" % (address,
3739     self.node.port,
3740     iface_spec,
3741     self.node.name)
3742    
3743     initiator = "iscsi_initiator=%s" % self.initiator
3744    
3745     return set([netroot, initiator])
3746    
3747     class FcoeDiskDevice(DiskDevice, NetworkStorageDevice):
3748     """ An FCoE disk. """
3749     _type = "fcoe"
3750     _packages = ["fcoe-utils", "dracut-network"]
3751    
3752     def __init__(self, device, **kwargs):
3753     self.nic = kwargs.pop("nic")
3754     self.identifier = kwargs.pop("identifier")
3755     DiskDevice.__init__(self, device, **kwargs)
3756     NetworkStorageDevice.__init__(self, nic=self.nic)
3757     log.debug("created new fcoe disk %s (%s) @ %s" %
3758     (device, self.identifier, self.nic))
3759    
3760     def dracutSetupArgs(self):
3761     dcb = True
3762    
3763     from .fcoe import fcoe
3764     for nic, dcb, auto_vlan in fcoe().nics:
3765     if nic == self.nic:
3766     break
3767    
3768     if dcb:
3769     dcbOpt = "dcb"
3770     else:
3771     dcbOpt = "nodcb"
3772    
3773     if nic in fcoe().ksnics:
3774     return set(["fcoe=%s:%s" % (nic, dcbOpt)])
3775     else:
3776     return set(["fcoe=edd:%s" % dcbOpt])
3777    
3778    
3779     class OpticalDevice(StorageDevice):
3780     """ An optical drive, eg: cdrom, dvd+r, &c.
3781    
3782     XXX Is this useful?
3783     """
3784     _type = "cdrom"
3785    
3786     def __init__(self, name, major=None, minor=None, exists=None,
3787     format=None, parents=None, sysfsPath='', vendor="",
3788     model=""):
3789     StorageDevice.__init__(self, name, format=format,
3790     major=major, minor=minor, exists=True,
3791     parents=parents, sysfsPath=sysfsPath,
3792     vendor=vendor, model=model)
3793    
3794     @property
3795     def mediaPresent(self):
3796     """ Return a boolean indicating whether or not the device contains
3797     media.
3798     """
3799     log_method_call(self, self.name, status=self.status)
3800     if not self.exists:
3801     raise DeviceError("device has not been created", self.name)
3802    
3803     try:
3804     fd = os.open(self.path, os.O_RDONLY)
3805     except OSError as e:
3806     # errno 123 = No medium found
3807     if e.errno == 123:
3808     return False
3809     else:
3810     return True
3811     else:
3812     os.close(fd)
3813     return True
3814    
3815     def eject(self):
3816     """ Eject the drawer. """
3817     import _isys
3818    
3819     log_method_call(self, self.name, status=self.status)
3820     if not self.exists:
3821     raise DeviceError("device has not been created", self.name)
3822    
3823     #try to umount and close device before ejecting
3824     self.teardown()
3825    
3826     if flags.noeject:
3827     log.info("noeject in effect, not ejecting cdrom")
3828     return
3829    
3830     # Make a best effort attempt to do the eject. If it fails, it's not
3831     # critical.
3832     fd = os.open(self.path, os.O_RDONLY | os.O_NONBLOCK)
3833    
3834     try:
3835     _isys.ejectcdrom(fd)
3836     except SystemError as e:
3837     log.warning("error ejecting cdrom %s: %s" % (self.name, e))
3838    
3839     os.close(fd)
3840    
3841    
3842     class ZFCPDiskDevice(DiskDevice):
3843     """ A mainframe ZFCP disk. """
3844     _type = "zfcp"
3845    
3846     def __init__(self, device, **kwargs):
3847     self.hba_id = kwargs.pop("hba_id")
3848     self.wwpn = kwargs.pop("wwpn")
3849     self.fcp_lun = kwargs.pop("fcp_lun")
3850     DiskDevice.__init__(self, device, **kwargs)
3851    
3852     def __str__(self):
3853     s = DiskDevice.__str__(self)
3854     s += (" hba_id = %(hba_id)s wwpn = %(wwpn)s fcp_lun = %(fcp_lun)s" %
3855     {"hba_id": self.hba_id,
3856     "wwpn": self.wwpn,
3857     "fcp_lun": self.fcp_lun})
3858     return s
3859    
3860     @property
3861     def description(self):
3862     return "FCP device %(device)s with WWPN %(wwpn)s and LUN %(lun)s" \
3863     % {'device': self.hba_id,
3864     'wwpn': self.wwpn,
3865     'lun': self.fcp_lun}
3866    
3867     def dracutSetupArgs(self):
3868     return set(["rd_ZFCP=%s,%s,%s" % (self.hba_id, self.wwpn, self.fcp_lun,)])
3869    
3870    
3871     class DASDDevice(DiskDevice):
3872     """ A mainframe DASD. """
3873     _type = "dasd"
3874    
3875     def __init__(self, device, **kwargs):
3876     self.busid = kwargs.pop('busid')
3877     self.opts = kwargs.pop('opts')
3878     self.dasd = kwargs.pop('dasd')
3879     DiskDevice.__init__(self, device, **kwargs)
3880    
3881     if self.dasd:
3882     self.dasd.addDASD(self)
3883    
3884     @property
3885     def description(self):
3886     return "DASD device %s" % self.busid
3887    
3888     def getOpts(self):
3889     return map(lambda (k, v): "%s=%s" % (k, v,), self.opts.items())
3890    
3891     def dracutSetupArgs(self):
3892     conf = "/etc/dasd.conf"
3893     opts = {}
3894    
3895     if os.path.isfile(conf):
3896     f = open(conf)
3897     lines = filter(lambda y: not y.startswith('#') and y != '',
3898     map(lambda x: x.strip(), f.readlines()))
3899     f.close()
3900    
3901     for line in lines:
3902     parts = line.split()
3903     if parts != []:
3904     opts[parts[0]] = parts
3905    
3906     if self.busid in opts.keys():
3907     return set(["rd_DASD=%s" % ",".join(opts[self.busid])])
3908     else:
3909     return set(["rd_DASD=%s" % ",".join([self.busid] + self.getOpts())])
3910    
3911     class NFSDevice(StorageDevice, NetworkStorageDevice):
3912     """ An NFS device """
3913     _type = "nfs"
3914     _packages = ["dracut-network"]
3915    
3916     def __init__(self, device, format=None, parents=None):
3917     # we could make host/ip, path, &c but will anything use it?
3918     StorageDevice.__init__(self, device, format=format, parents=parents)
3919     NetworkStorageDevice.__init__(self, device.split(":")[0])
3920    
3921     @property
3922     def path(self):
3923     """ Device node representing this device. """
3924     return self.name
3925    
3926     def setup(self, intf=None, orig=False):
3927     """ Open, or set up, a device. """
3928     log_method_call(self, self.name, orig=orig, status=self.status)
3929    
3930     def teardown(self, recursive=None):
3931     """ Close, or tear down, a device. """
3932     log_method_call(self, self.name, status=self.status)
3933    
3934     def create(self, intf=None):
3935     """ Create the device. """
3936     log_method_call(self, self.name, status=self.status)
3937     self.createParents()
3938     self.setupParents()
3939    
3940     def destroy(self):
3941     """ Destroy the device. """
3942     log_method_call(self, self.name, status=self.status)

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