/[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.5 - (hide annotations) (download) (as text)
Sun Dec 22 04:27:51 2013 UTC (10 years, 7 months ago) by wellsi
Branch: MAIN
CVS Tags: HEAD
Changes since 1.4: +0 -0 lines
Content type: text/x-python
FILE REMOVED
anaconda updates now handled via patches

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

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