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

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

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


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

1 charliebrady 1.1 # devicetree.py
2     # Device management 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     import os
24     import stat
25     import block
26     import re
27    
28     from errors import *
29     from devices import *
30     from deviceaction import *
31     from partitioning import shouldClear
32     from pykickstart.constants import *
33     import formats
34     import devicelibs.mdraid
35     import devicelibs.dm
36     import devicelibs.lvm
37     import devicelibs.mpath
38     from udev import *
39     from .storage_log import log_method_call
40     import iutil
41     import platform
42     import parted
43     import _ped
44    
45     import gettext
46     _ = lambda x: gettext.ldgettext("anaconda", x)
47    
48     import logging
49     log = logging.getLogger("storage")
50    
51     def getLUKSPassphrase(intf, device, globalPassphrase):
52     """ Obtain a passphrase for a LUKS encrypted block device.
53    
54     The format's mapping name must already be set and the backing
55     device must already be set up before calling this function.
56    
57     If successful, this function leaves the device mapped.
58    
59     Return value is a two-tuple: (passphrase, isglobal)
60    
61     passphrase is the passphrase string, if obtained
62     isglobal is a boolean indicating whether the passphrase is global
63    
64     Either or both can be None, depending on the outcome.
65     """
66     if device.format.type != "luks":
67     # this function only works on luks devices
68     raise ValueError("not a luks device")
69    
70     if not device.status:
71     # the device should have already been set up
72     raise RuntimeError("device is not set up")
73    
74     if device.format.status:
75     # the device is already mapped
76     raise RuntimeError("device is already mapped")
77    
78     if not device.format.configured and globalPassphrase:
79     # try the given passphrase first
80     device.format.passphrase = globalPassphrase
81    
82     try:
83     device.format.setup()
84     except CryptoError as e:
85     device.format.passphrase = None
86     else:
87     # we've opened the device so we're done.
88     return (globalPassphrase, False)
89    
90     if not intf:
91     return (None, None)
92    
93     buttons = [_("Back"), _("Continue")]
94     passphrase_incorrect = False
95     while True:
96     if passphrase_incorrect:
97     # TODO: add a flag to passphraseEntryWindow to say the last
98     # passphrase was incorrect so try again
99     passphrase_incorrect = False
100     (passphrase, isglobal) = intf.passphraseEntryWindow(device.name)
101     if not passphrase:
102     rc = intf.messageWindow(_("Confirm"),
103     _("Are you sure you want to skip "
104     "entering a passphrase for device "
105     "%s?\n\n"
106     "If you skip this step the "
107     "device's contents will not "
108     "be available during "
109     "installation.") % device.name,
110     type = "custom",
111     default = 0,
112     custom_buttons = buttons)
113     if rc == 0:
114     continue
115     else:
116     passphrase = None
117     isglobal = None
118     log.info("skipping passphrase for %s" % (device.name,))
119     break
120    
121     device.format.passphrase = passphrase
122    
123     try:
124     device.format.setup()
125     except CryptoError as e:
126     device.format.passphrase = None
127     passphrase_incorrect = True
128     else:
129     # we've opened the device so we're done.
130     break
131    
132     return (passphrase, isglobal)
133    
134    
135     class DeviceTree(object):
136     """ A quasi-tree that represents the devices in the system.
137    
138     The tree contains a list of device instances, which does not
139     necessarily reflect the actual state of the system's devices.
140     DeviceActions are used to perform modifications to the tree,
141     except when initially populating the tree.
142    
143     DeviceAction instances are registered, possibly causing the
144     addition or removal of Device instances to/from the tree. The
145     DeviceActions are all reversible up to the time their execute
146     method has been called.
147    
148     Only one action of any given type/object pair should exist for
149     any given device at any given time.
150    
151     DeviceAction instances can only be registered for leaf devices,
152     except for resize actions.
153     """
154    
155     def __init__(self, intf=None, ignored=[], exclusive=[], type=CLEARPART_TYPE_NONE,
156     clear=[], zeroMbr=None, reinitializeDisks=None, protected=[],
157     passphrase=None, luksDict=None, iscsi=None, dasd=None,
158     mpathFriendlyNames=True):
159     # internal data members
160     self._devices = []
161     self._actions = []
162    
163     # indicates whether or not the tree has been fully populated
164     self.populated = False
165    
166     self.intf = intf
167     self.exclusiveDisks = exclusive
168     self.clearPartType = type
169     self.clearPartDisks = clear
170     self.zeroMbr = zeroMbr
171     self.reinitializeDisks = reinitializeDisks
172     self.iscsi = iscsi
173     self.dasd = dasd
174     self.platform = platform.getPlatform(None)
175     self.mpathFriendlyNames = mpathFriendlyNames
176    
177     # protected device specs as provided by the user
178     self.protectedDevSpecs = protected
179    
180     # names of protected devices at the time of tree population
181     self.protectedDevNames = []
182    
183     self.unusedRaidMembers = []
184    
185     self.__multipaths = {}
186     self.__multipathConfigWriter = devicelibs.mpath.MultipathConfigWriter()
187    
188     self.__passphrase = passphrase
189     self.__luksDevs = {}
190     if luksDict and isinstance(luksDict, dict):
191     self.__luksDevs = luksDict
192     self._ignoredDisks = []
193     for disk in ignored:
194     self.addIgnoredDisk(disk)
195     lvm.lvm_cc_resetFilter()
196    
197     def addIgnoredDisk(self, disk):
198     self._ignoredDisks.append(disk)
199     lvm.lvm_cc_addFilterRejectRegexp(disk)
200    
201     def pruneActions(self):
202     """ Prune loops and redundant actions from the queue. """
203     # handle device destroy actions
204     actions = self.findActions(type="destroy", object="device")
205     for a in actions:
206     if a not in self._actions:
207     # we may have removed some of the actions in a previous
208     # iteration of this loop
209     continue
210    
211     log.debug("action '%s' (%s)" % (a, id(a)))
212     destroys = self.findActions(devid=a.device.id,
213     type="destroy",
214     object="device")
215    
216     creates = self.findActions(devid=a.device.id,
217     type="create",
218     object="device")
219     log.debug("found %d create and %d destroy actions for device id %d"
220     % (len(creates), len(destroys), a.device.id))
221    
222     # If the device is not preexisting, we remove all actions up
223     # to and including the last destroy action.
224     # If the device is preexisting, we remove all actions from
225     # after the first destroy action up to and including the last
226     # destroy action.
227     # If the device is preexisting and there is only one device
228     # destroy action we remove all resize and format create/migrate
229     # actions on that device that precede the destroy action.
230     loops = []
231     first_destroy_idx = None
232     first_create_idx = None
233     stop_action = None
234     start = None
235     if len(destroys) > 1:
236     # there are multiple destroy actions for this device
237     loops = destroys
238     first_destroy_idx = self._actions.index(loops[0])
239     start = self._actions.index(a) + 1
240     stop_action = destroys[-1]
241    
242     if creates:
243     first_create_idx = self._actions.index(creates[0])
244     if not loops or first_destroy_idx > first_create_idx:
245     # this device is not preexisting
246     start = first_create_idx
247     stop_action = destroys[-1]
248    
249     dev_actions = self.findActions(devid=a.device.id)
250     if start is None:
251     # only one device destroy, so prune preceding resizes and
252     # format creates and migrates
253     for _a in dev_actions[:]:
254     if _a.isResize() or (_a.isFormat() and not _a.isDestroy()):
255     continue
256    
257     dev_actions.remove(_a)
258    
259     if not dev_actions:
260     # nothing to prune
261     continue
262    
263     start = self._actions.index(dev_actions[0])
264     stop_action = dev_actions[-1]
265    
266     # now we remove all actions on this device between the start
267     # index (into self._actions) and stop_action.
268     for rem in dev_actions:
269     end = self._actions.index(stop_action)
270     if start <= self._actions.index(rem) <= end:
271     log.debug(" removing action '%s' (%s)" % (rem, id(rem)))
272     self._actions.remove(rem)
273    
274     if rem == stop_action:
275     break
276    
277     # device create actions
278     actions = self.findActions(type="create", object="device")
279     for a in actions:
280     if a not in self._actions:
281     # we may have removed some of the actions in a previous
282     # iteration of this loop
283     continue
284    
285     log.debug("action '%s' (%s)" % (a, id(a)))
286     creates = self.findActions(devid=a.device.id,
287     type="create",
288     object="device")
289    
290     destroys = self.findActions(devid=a.device.id,
291     type="destroy",
292     object="device")
293    
294     # If the device is preexisting, we remove everything between
295     # the first destroy and the last create.
296     # If the device is not preexisting, we remove everything up to
297     # the last create.
298     loops = []
299     first_destroy_idx = None
300     first_create_idx = None
301     stop_action = None
302     start = None
303     if len(creates) > 1:
304     # there are multiple create actions for this device
305     loops = creates
306     first_create_idx = self._actions.index(loops[0])
307     start = 0
308     stop_action = creates[-1]
309    
310     if destroys:
311     first_destroy_idx = self._actions.index(destroys[0])
312     if not loops or first_create_idx > first_destroy_idx:
313     # this device is preexisting
314     start = first_destroy_idx + 1
315     stop_action = creates[-1]
316    
317     if start is None:
318     continue
319    
320     # remove all actions on this from after the first destroy up
321     # to the last create
322     dev_actions = self.findActions(devid=a.device.id)
323     for rem in dev_actions:
324     if rem == stop_action:
325     break
326    
327     end = self._actions.index(stop_action)
328     if start <= self._actions.index(rem) < end:
329     log.debug(" removing action '%s' (%s)" % (rem, id(rem)))
330     self._actions.remove(rem)
331    
332     # device resize actions
333     actions = self.findActions(type="resize", object="device")
334     for a in actions:
335     if a not in self._actions:
336     # we may have removed some of the actions in a previous
337     # iteration of this loop
338     continue
339    
340     log.debug("action '%s' (%s)" % (a, id(a)))
341     loops = self.findActions(devid=a.device.id,
342     type="resize",
343     object="device")
344    
345     if len(loops) == 1:
346     continue
347    
348     # remove all but the last resize action on this device
349     for rem in loops[:-1]:
350     log.debug(" removing action '%s' (%s)" % (rem, id(rem)))
351     self._actions.remove(rem)
352    
353     # format destroy
354     # XXX I don't think there's a way for these loops to happen
355     actions = self.findActions(type="destroy", object="format")
356     for a in actions:
357     if a not in self._actions:
358     # we may have removed some of the actions in a previous
359     # iteration of this loop
360     continue
361    
362     log.debug("action '%s' (%s)" % (a, id(a)))
363     destroys = self.findActions(devid=a.device.id,
364     type="destroy",
365     object="format")
366    
367     creates = self.findActions(devid=a.device.id,
368     type="create",
369     object="format")
370    
371     # If the format is not preexisting, we remove all actions up
372     # to and including the last destroy action.
373     # If the format is preexisting, we remove all actions from
374     # after the first destroy action up to and including the last
375     # destroy action.
376     loops = []
377     first_destroy_idx = None
378     first_create_idx = None
379     stop_action = None
380     start = None
381     if len(destroys) > 1:
382     # there are multiple destroy actions for this format
383     loops = destroys
384     first_destroy_idx = self._actions.index(loops[0])
385     start = self._actions.index(a) + 1
386     stop_action = destroys[-1]
387    
388     if creates:
389     first_create_idx = self._actions.index(creates[0])
390     if not loops or first_destroy_idx > first_create_idx:
391     # this format is not preexisting
392     start = first_create_idx
393     stop_action = destroys[-1]
394    
395     if start is None:
396     continue
397    
398     # now we remove all actions on this device's format between
399     # the start index (into self._actions) and stop_action.
400     dev_actions = self.findActions(devid=a.device.id,
401     object="format")
402     for rem in dev_actions:
403     end = self._actions.index(stop_action)
404     if start <= self._actions.index(rem) <= end:
405     log.debug(" removing action '%s' (%s)" % (rem, id(rem)))
406     self._actions.remove(rem)
407    
408     if rem == stop_action:
409     break
410    
411     # format create
412     # XXX I don't think there's a way for these loops to happen
413     actions = self.findActions(type="create", object="format")
414     for a in actions:
415     if a not in self._actions:
416     # we may have removed some of the actions in a previous
417     # iteration of this loop
418     continue
419    
420     log.debug("action '%s' (%s)" % (a, id(a)))
421     creates = self.findActions(devid=a.device.id,
422     type="create",
423     object="format")
424    
425     destroys = self.findActions(devid=a.device.id,
426     type="destroy",
427     object="format")
428    
429     # If the format is preexisting, we remove everything between
430     # the first destroy and the last create.
431     # If the format is not preexisting, we remove everything up to
432     # the last create.
433     loops = []
434     first_destroy_idx = None
435     first_create_idx = None
436     stop_action = None
437     start = None
438     if len(creates) > 1:
439     # there are multiple create actions for this format
440     loops = creates
441     first_create_idx = self._actions.index(loops[0])
442     start = 0
443     stop_action = creates[-1]
444    
445     if destroys:
446     first_destroy_idx = self._actions.index(destroys[0])
447     if not loops or first_create_idx > first_destroy_idx:
448     # this format is preexisting
449     start = first_destroy_idx + 1
450     stop_action = creates[-1]
451    
452     if start is None:
453     continue
454    
455     # remove all actions on this from after the first destroy up
456     # to the last create
457     dev_actions = self.findActions(devid=a.device.id,
458     object="format")
459     for rem in dev_actions:
460     if rem == stop_action:
461     break
462    
463     end = self._actions.index(stop_action)
464     if start <= self._actions.index(rem) < end:
465     log.debug(" removing action '%s' (%s)" % (rem, id(rem)))
466     self._actions.remove(rem)
467    
468     # format resize
469     actions = self.findActions(type="resize", object="format")
470     for a in actions:
471     if a not in self._actions:
472     # we may have removed some of the actions in a previous
473     # iteration of this loop
474     continue
475    
476     log.debug("action '%s' (%s)" % (a, id(a)))
477     loops = self.findActions(devid=a.device.id,
478     type="resize",
479     object="format")
480    
481     if len(loops) == 1:
482     continue
483    
484     # remove all but the last resize action on this format
485     for rem in loops[:-1]:
486     log.debug(" removing action '%s' (%s)" % (rem, id(rem)))
487     self._actions.remove(rem)
488    
489     # format migrate
490     # XXX I don't think there's away for these loops to occur
491     actions = self.findActions(type="migrate", object="format")
492     for a in actions:
493     if a not in self._actions:
494     # we may have removed some of the actions in a previous
495     # iteration of this loop
496     continue
497    
498     log.debug("action '%s' (%s)" % (a, id(a)))
499     loops = self.findActions(devid=a.device.id,
500     type="migrate",
501     object="format")
502    
503     if len(loops) == 1:
504     continue
505    
506     # remove all but the last migrate action on this format
507     for rem in loops[:-1]:
508     log.debug(" removing action '%s' (%s)" % (rem, id(rem)))
509     self._actions.remove(rem)
510    
511     def processActions(self, dryRun=None):
512     """ Execute all registered actions. """
513     # in most cases the actions will already be sorted because of the
514     # rules for registration, but let's not rely on that
515     def cmpActions(a1, a2):
516     ret = 0
517     if a1.isDestroy() and a2.isDestroy():
518     if a1.device.path == a2.device.path:
519     # if it's the same device, destroy the format first
520     if a1.isFormat() and a2.isFormat():
521     ret = 0
522     elif a1.isFormat() and not a2.isFormat():
523     ret = -1
524     elif not a1.isFormat() and a2.isFormat():
525     ret = 1
526     elif a1.device.dependsOn(a2.device):
527     ret = -1
528     elif a2.device.dependsOn(a1.device):
529     ret = 1
530     # generally destroy partitions after lvs, vgs, &c
531     elif isinstance(a1.device, PartitionDevice) and \
532     isinstance(a2.device, PartitionDevice):
533     if a1.device.disk == a2.device.disk:
534     ret = cmp(a2.device.partedPartition.number,
535     a1.device.partedPartition.number)
536     else:
537     ret = cmp(a2.device.name, a1.device.name)
538     elif isinstance(a1.device, PartitionDevice) and \
539     a2.device.partitioned:
540     ret = -1
541     elif isinstance(a2.device, PartitionDevice) and \
542     a1.device.partitioned:
543     ret = 1
544     # remove partitions before unpartitioned non-partition
545     # devices
546     elif isinstance(a1.device, PartitionDevice) and \
547     not isinstance(a2.device, PartitionDevice):
548     ret = 1
549     elif isinstance(a2.device, PartitionDevice) and \
550     not isinstance(a1.device, PartitionDevice):
551     ret = -1
552     else:
553     ret = 0
554     elif a1.isDestroy():
555     ret = -1
556     elif a2.isDestroy():
557     ret = 1
558     elif a1.isResize() and a2.isResize():
559     if a1.device.path == a2.device.path:
560     if a1.obj == a2.obj:
561     ret = 0
562     elif a1.isFormat() and not a2.isFormat():
563     # same path, one device, one format
564     if a1.isGrow():
565     ret = 1
566     else:
567     ret = -1
568     elif not a1.isFormat() and a2.isFormat():
569     # same path, one device, one format
570     if a1.isGrow():
571     ret = -1
572     else:
573     ret = 1
574     else:
575     ret = cmp(a1.device.name, a2.device.name)
576     elif a1.device.dependsOn(a2.device):
577     if a1.isGrow():
578     ret = 1
579     else:
580     ret = -1
581     elif a2.device.dependsOn(a1.device):
582     if a1.isGrow():
583     ret = -1
584     else:
585     ret = 1
586     elif isinstance(a1.device, PartitionDevice) and \
587     isinstance(a2.device, PartitionDevice):
588     ret = cmp(a1.device.name, a2.device.name)
589     else:
590     ret = 0
591     elif a1.isResize():
592     ret = -1
593     elif a2.isResize():
594     ret = 1
595     elif a1.isCreate() and a2.isCreate():
596     if a1.device.path == a2.device.path:
597     if a1.obj == a2.obj:
598     ret = 0
599     if a1.isFormat():
600     ret = 1
601     elif a2.isFormat():
602     ret = -1
603     else:
604     ret = 0
605     elif a1.device.dependsOn(a2.device):
606     ret = 1
607     elif a2.device.dependsOn(a1.device):
608     ret = -1
609     # generally create partitions before other device types
610     elif isinstance(a1.device, PartitionDevice) and \
611     isinstance(a2.device, PartitionDevice):
612     if a1.device.disk == a2.device.disk:
613     ret = cmp(a1.device.partedPartition.number,
614     a2.device.partedPartition.number)
615     else:
616     ret = cmp(a1.device.name, a2.device.name)
617     elif isinstance(a1.device, LVMLogicalVolumeDevice) and \
618     isinstance(a2.device, LVMLogicalVolumeDevice) and \
619     a1.device.vg == a2.device.vg:
620     if a1.device.singlePV and not a2.device.singlePV:
621     ret = -1
622     elif not a1.device.singlePV and a2.device.singlePV:
623     ret = 1
624     elif isinstance(a1.device, PartitionDevice) and \
625     a2.device.partitioned:
626     ret = 1
627     elif isinstance(a2.device, PartitionDevice) and \
628     a1.device.partitioned:
629     ret = -1
630     elif isinstance(a1.device, PartitionDevice) and \
631     not isinstance(a2.device, PartitionDevice):
632     ret = -1
633     elif isinstance(a2.device, PartitionDevice) and \
634     not isinstance(a1.device, PartitionDevice):
635     ret = 1
636     else:
637     ret = 0
638     elif a1.isCreate():
639     ret = -1
640     elif a2.isCreate():
641     ret = 1
642     elif a1.isMigrate() and a2.isMigrate():
643     if a1.device.path == a2.device.path:
644     ret = 0
645     elif a1.device.dependsOn(a2.device):
646     ret = 1
647     elif a2.device.dependsOn(a1.device):
648     ret = -1
649     elif isinstance(a1.device, PartitionDevice) and \
650     isinstance(a2.device, PartitionDevice):
651     if a1.device.disk == a2.device.disk:
652     ret = cmp(a1.device.partedPartition.number,
653     a2.device.partedPartition.number)
654     else:
655     ret = cmp(a1.device.name, a2.device.name)
656     else:
657     ret = 0
658     else:
659     ret = 0
660    
661     log.debug("cmp: %d -- %s | %s" % (ret, a1, a2))
662     return ret
663    
664     log.debug("resetting parted disks...")
665     for device in self.devices:
666     if device.partitioned:
667     device.format.resetPartedDisk()
668     if device.originalFormat.type == "disklabel" and \
669     device.originalFormat != device.format:
670     device.originalFormat.resetPartedDisk()
671    
672     # Call preCommitFixup on all devices
673     mpoints = [getattr(d.format, 'mountpoint', "") for d in self.devices]
674     for device in self.devices:
675     device.preCommitFixup(mountpoints=mpoints)
676    
677     # Also call preCommitFixup on any devices we're going to
678     # destroy (these are already removed from the tree)
679     for action in self._actions:
680     if isinstance(action, ActionDestroyDevice):
681     action.device.preCommitFixup(mountpoints=mpoints)
682    
683     # setup actions to create any extended partitions we added
684     #
685     # XXX At this point there can be duplicate partition paths in the
686     # tree (eg: non-existent sda6 and previous sda6 that will become
687     # sda5 in the course of partitioning), so we access the list
688     # directly here.
689     for device in self._devices:
690     if isinstance(device, PartitionDevice) and \
691     device.isExtended and not device.exists:
692     # don't properly register the action since the device is
693     # already in the tree
694     self._actions.append(ActionCreateDevice(device))
695    
696     for action in self._actions:
697     log.debug("action: %s" % action)
698    
699     log.debug("pruning action queue...")
700     self.pruneActions()
701     for action in self._actions:
702     log.debug("action: %s" % action)
703    
704     log.debug("sorting actions...")
705     self._actions.sort(cmp=cmpActions)
706     for action in self._actions:
707     log.debug("action: %s" % action)
708    
709     for action in self._actions:
710     log.info("executing action: %s" % action)
711     if not dryRun:
712     try:
713     action.execute(intf=self.intf)
714     except DiskLabelCommitError:
715     # it's likely that a previous format destroy action
716     # triggered setup of an lvm or md device.
717     self.teardownAll()
718     action.execute(intf=self.intf)
719    
720     udev_settle()
721     for device in self._devices:
722     # make sure we catch any renumbering parted does
723     if device.exists and isinstance(device, PartitionDevice):
724     device.updateName()
725     device.format.device = device.path
726    
727     def _addDevice(self, newdev):
728     """ Add a device to the tree.
729    
730     Raise ValueError if the device's identifier is already
731     in the list.
732     """
733     if newdev.path in [d.path for d in self._devices] and \
734     not isinstance(newdev, NoDevice):
735     raise ValueError("device is already in tree")
736    
737     # make sure this device's parent devices are in the tree already
738     for parent in newdev.parents:
739     if parent not in self._devices:
740     raise DeviceTreeError("parent device not in tree")
741    
742     self._devices.append(newdev)
743     log.debug("added %s %s (id %d) to device tree" % (newdev.type,
744     newdev.name,
745     newdev.id))
746    
747     def _removeDevice(self, dev, force=None, moddisk=True):
748     """ Remove a device from the tree.
749    
750     Only leaves may be removed.
751     """
752     if dev not in self._devices:
753     raise ValueError("Device '%s' not in tree" % dev.name)
754    
755     if not dev.isleaf and not force:
756     log.debug("%s has %d kids" % (dev.name, dev.kids))
757     raise ValueError("Cannot remove non-leaf device '%s'" % dev.name)
758    
759     # if this is a partition we need to remove it from the parted.Disk
760     if moddisk and isinstance(dev, PartitionDevice) and \
761     dev.disk is not None:
762     # if this partition hasn't been allocated it could not have
763     # a disk attribute
764     if dev.partedPartition.type == parted.PARTITION_EXTENDED and \
765     len(dev.disk.format.logicalPartitions) > 0:
766     raise ValueError("Cannot remove extended partition %s. "
767     "Logical partitions present." % dev.name)
768    
769     dev.disk.format.removePartition(dev.partedPartition)
770    
771     # adjust all other PartitionDevice instances belonging to the
772     # same disk so the device name matches the potentially altered
773     # name of the parted.Partition
774     for device in self._devices:
775     if isinstance(device, PartitionDevice) and \
776     device.disk == dev.disk:
777     device.updateName()
778    
779     self._devices.remove(dev)
780     log.debug("removed %s %s (id %d) from device tree" % (dev.type,
781     dev.name,
782     dev.id))
783    
784     for parent in dev.parents:
785     # Will this cause issues with garbage collection?
786     # Do we care about garbage collection? At all?
787     parent.removeChild()
788    
789     def registerAction(self, action):
790     """ Register an action to be performed at a later time.
791    
792     Modifications to the Device instance are handled before we
793     get here.
794     """
795     if (action.isDestroy() or action.isResize() or \
796     (action.isCreate() and action.isFormat())) and \
797     action.device not in self._devices:
798     raise DeviceTreeError("device is not in the tree")
799     elif (action.isCreate() and action.isDevice()):
800     # this allows multiple create actions w/o destroy in between;
801     # we will clean it up before processing actions
802     #raise DeviceTreeError("device is already in the tree")
803     if action.device in self._devices:
804     self._removeDevice(action.device)
805     for d in self._devices:
806     if d.path == action.device.path:
807     self._removeDevice(d)
808    
809     if action.isCreate() and action.isDevice():
810     self._addDevice(action.device)
811     elif action.isDestroy() and action.isDevice():
812     self._removeDevice(action.device)
813     elif action.isCreate() and action.isFormat():
814     if isinstance(action.device.format, formats.fs.FS) and \
815     action.device.format.mountpoint in self.filesystems:
816     raise DeviceTreeError("mountpoint already in use")
817    
818     log.debug("registered action: %s" % action)
819     self._actions.append(action)
820    
821     def cancelAction(self, action):
822     """ Cancel a registered action.
823    
824     This will unregister the action and do any required
825     modifications to the device list.
826    
827     Actions all operate on a Device, so we can use the devices
828     to determine dependencies.
829     """
830     if action.isCreate() and action.isDevice():
831     # remove the device from the tree
832     self._removeDevice(action.device)
833     elif action.isDestroy() and action.isDevice():
834     # add the device back into the tree
835     self._addDevice(action.device)
836     elif action.isFormat() and \
837     (action.isCreate() or action.isMigrate() or action.isResize()):
838     action.cancel()
839    
840     self._actions.remove(action)
841    
842     def findActions(self, device=None, type=None, object=None, path=None,
843     devid=None):
844     """ Find all actions that match all specified parameters.
845    
846     Keyword arguments:
847    
848     device -- device to match (Device, or None to match any)
849     type -- action type to match (string, or None to match any)
850     object -- operand type to match (string, or None to match any)
851     path -- device path to match (string, or None to match any)
852    
853     """
854     if device is None and type is None and object is None and \
855     path is None and devid is None:
856     return self._actions[:]
857    
858     # convert the string arguments to the types used in actions
859     _type = action_type_from_string(type)
860     _object = action_object_from_string(object)
861    
862     actions = []
863     for action in self._actions:
864     if device is not None and action.device != device:
865     continue
866    
867     if _type is not None and action.type != _type:
868     continue
869    
870     if _object is not None and action.obj != _object:
871     continue
872    
873     if path is not None and action.device.path != path:
874     continue
875    
876     if devid is not None and action.device.id != devid:
877     continue
878    
879     actions.append(action)
880    
881     return actions
882    
883     def getDependentDevices(self, dep):
884     """ Return a list of devices that depend on dep.
885    
886     The list includes both direct and indirect dependents.
887     """
888     dependents = []
889    
890     # special handling for extended partitions since the logical
891     # partitions and their deps effectively depend on the extended
892     logicals = []
893     if isinstance(dep, PartitionDevice) and dep.partType and \
894     dep.isExtended:
895     # collect all of the logicals on the same disk
896     for part in self.getDevicesByInstance(PartitionDevice):
897     if part.partType and part.isLogical and part.disk == dep.disk:
898     logicals.append(part)
899    
900     for device in self.devices:
901     if device.dependsOn(dep):
902     dependents.append(device)
903     else:
904     for logical in logicals:
905     if device.dependsOn(logical):
906     dependents.append(device)
907     break
908    
909     return dependents
910    
911     def isIgnored(self, info):
912     """ Return True if info is a device we should ignore.
913    
914     Arguments:
915    
916     info -- a dict representing a udev db entry
917    
918     TODO:
919    
920     - filtering of SAN/FC devices
921     - filtering by driver?
922    
923     """
924     sysfs_path = udev_device_get_sysfs_path(info)
925     name = udev_device_get_name(info)
926     if not sysfs_path:
927     return None
928    
929     if name in self._ignoredDisks:
930     log.debug("device '%s' in ignoredDisks" % name)
931     return True
932    
933     # Special handling for mdraid external metadata sets (mdraid BIOSRAID):
934     # 1) The containers are intermediate devices which will never be
935     # in exclusiveDisks
936     # 2) Sets get added to exclusive disks with their dmraid set name by
937     # the filter ui. Note that making the ui use md names instead is not
938     # possible as the md names are simpy md# and we cannot predict the #
939     if udev_device_get_md_level(info) == "container":
940     return False
941    
942     if udev_device_get_md_container(info) and \
943     udev_device_get_md_name(info):
944     md_name = udev_device_get_md_name(info)
945     for i in range(0, len(self.exclusiveDisks)):
946     if re.match("isw_[a-z]*_%s" % md_name, self.exclusiveDisks[i]):
947     self.exclusiveDisks[i] = name
948     return False
949    
950     # We want exclusiveDisks to operate on anything that could be
951     # considered a directly usable disk, ie: fwraid array, mpath, or disk.
952     #
953     # Unfortunately, since so many things are represented as disks by
954     # udev/sysfs, we have to define what is a disk in terms of what is
955     # not a disk.
956     if udev_device_is_disk(info) and \
957     not udev_device_is_dm_partition(info) and \
958     not udev_device_is_dm_lvm(info) and \
959     not udev_device_is_dm_crypt(info) and \
960     not (udev_device_is_md(info) and
961     not udev_device_get_md_container(info)):
962     if self.exclusiveDisks and name not in self.exclusiveDisks:
963     log.debug("device '%s' not in exclusiveDisks" % name)
964     self.addIgnoredDisk(name)
965     return True
966    
967     # Ignore loop and ram devices, we normally already skip these in
968     # udev.py: enumerate_block_devices(), but we can still end up trying
969     # to add them to the tree when they are slaves of other devices, this
970     # happens for example with the livecd
971     if name.startswith("loop") or name.startswith("ram"):
972     return True
973    
974     # Ignore any readonly disks
975     if (udev_device_is_disk(info) and not
976     (udev_device_is_cdrom(info) or
977     udev_device_is_partition(info) or
978     udev_device_is_dm_partition(info) or
979     udev_device_is_dm_lvm(info) or
980     udev_device_is_dm_crypt(info) or
981     (udev_device_is_md(info) and not
982     udev_device_get_md_container(info)))):
983     if iutil.get_sysfs_attr(info["sysfs_path"], 'ro') == '1':
984     log.debug("Ignoring read only device %s" % name)
985     self.addIgnoredDisk(name)
986     return True
987    
988     # FIXME: check for virtual devices whose slaves are on the ignore list
989    
990     def addUdevDMDevice(self, info):
991     name = udev_device_get_name(info)
992     log_method_call(self, name=name)
993     uuid = udev_device_get_uuid(info)
994     sysfs_path = udev_device_get_sysfs_path(info)
995     device = None
996    
997     for dmdev in self.devices:
998     if not isinstance(dmdev, DMDevice):
999     continue
1000    
1001     try:
1002     # there is a device in the tree already with the same
1003     # major/minor as this one but with a different name
1004     # XXX this is kind of racy
1005     if dmdev.getDMNode() == os.path.basename(sysfs_path):
1006     # XXX should we take the name already in use?
1007     device = dmdev
1008     break
1009     except DMError:
1010     # This is a little lame, but the VG device is a DMDevice
1011     # and it won't have a dm node. At any rate, this is not
1012     # important enough to crash the install.
1013     log.debug("failed to find dm node for %s" % dmdev.name)
1014     continue
1015    
1016     if device is None:
1017     # we couldn't find it, so create it
1018     # first, get a list of the slave devs and look them up
1019     slaves = []
1020     dir = os.path.normpath("/sys/%s/slaves" % sysfs_path)
1021     slave_names = os.listdir(dir)
1022     for slave_name in slave_names:
1023     # if it's a dm-X name, resolve it to a map name first
1024     if slave_name.startswith("dm-"):
1025     dev_name = dm.name_from_dm_node(slave_name)
1026     else:
1027     dev_name = slave_name
1028     slave_dev = self.getDeviceByName(dev_name)
1029     if slave_dev:
1030     slaves.append(slave_dev)
1031     else:
1032     # we haven't scanned the slave yet, so do it now
1033     path = os.path.normpath("%s/%s" % (dir, slave_name))
1034     new_info = udev_get_block_device(os.path.realpath(path)[4:])
1035     if new_info:
1036     self.addUdevDevice(new_info)
1037     if self.getDeviceByName(dev_name) is None:
1038     # if the current slave is still not in
1039     # the tree, something has gone wrong
1040     log.error("failure scanning device %s: could not add slave %s" % (name, dev_name))
1041     return
1042    
1043     # try to get the device again now that we've got all the slaves
1044     device = self.getDeviceByName(name)
1045    
1046     if device is None and udev_device_is_dm_partition(info):
1047     diskname = udev_device_get_dm_partition_disk(info)
1048     disk = self.getDeviceByName(diskname)
1049     return self.addUdevPartitionDevice(info, disk=disk)
1050    
1051     # if we get here, we found all of the slave devices and
1052     # something must be wrong -- if all of the slaves are in
1053     # the tree, this device should be as well
1054     if device is None:
1055     log.warning("ignoring dm device %s" % name)
1056    
1057     return device
1058    
1059     def addUdevMDDevice(self, info):
1060     name = udev_device_get_name(info)
1061     log_method_call(self, name=name)
1062     uuid = udev_device_get_uuid(info)
1063     sysfs_path = udev_device_get_sysfs_path(info)
1064     device = None
1065    
1066     slaves = []
1067     dir = os.path.normpath("/sys/%s/slaves" % sysfs_path)
1068     slave_names = os.listdir(dir)
1069     for slave_name in slave_names:
1070     # if it's a dm-X name, resolve it to a map name
1071     if slave_name.startswith("dm-"):
1072     dev_name = dm.name_from_dm_node(slave_name)
1073     else:
1074     dev_name = slave_name
1075     slave_dev = self.getDeviceByName(dev_name)
1076     if slave_dev:
1077     slaves.append(slave_dev)
1078     else:
1079     # we haven't scanned the slave yet, so do it now
1080     path = os.path.normpath("%s/%s" % (dir, slave_name))
1081     new_info = udev_get_block_device(os.path.realpath(path)[4:])
1082     if new_info:
1083     self.addUdevDevice(new_info)
1084     if self.getDeviceByName(dev_name) is None:
1085     # if the current slave is still not in
1086     # the tree, something has gone wrong
1087     log.error("failure scanning device %s: could not add slave %s" % (name, dev_name))
1088     return
1089    
1090     # try to get the device again now that we've got all the slaves
1091     device = self.getDeviceByName(name)
1092    
1093     if device is None:
1094     device = self.getDeviceByUuid(info.get("MD_UUID"))
1095     if device:
1096     raise DeviceTreeError("MD RAID device %s already in "
1097     "devicetree as %s" % (name, device.name))
1098    
1099     # if we get here, we found all of the slave devices and
1100     # something must be wrong -- if all of the slaves we in
1101     # the tree, this device should be as well
1102     if device is None:
1103     log.warning("using MD RAID device for %s" % name)
1104     try:
1105     # level is reported as, eg: "raid1"
1106     md_level = udev_device_get_md_level(info)
1107     md_devices = int(udev_device_get_md_devices(info))
1108     md_uuid = udev_device_get_md_uuid(info)
1109     except (KeyError, IndexError, ValueError) as e:
1110     log.warning("invalid data for %s: %s" % (name, e))
1111     return
1112    
1113     device = MDRaidArrayDevice(name,
1114     level=md_level,
1115     memberDevices=md_devices,
1116     uuid=md_uuid,
1117     exists=True,
1118     parents=slaves)
1119     self._addDevice(device)
1120    
1121     return device
1122    
1123     def addUdevPartitionDevice(self, info, disk=None):
1124     name = udev_device_get_name(info)
1125     log_method_call(self, name=name)
1126     uuid = udev_device_get_uuid(info)
1127     sysfs_path = udev_device_get_sysfs_path(info)
1128     device = None
1129    
1130     if disk is None:
1131     disk_name = os.path.basename(os.path.dirname(sysfs_path))
1132     disk_name = disk_name.replace('!','/')
1133     disk = self.getDeviceByName(disk_name)
1134    
1135     if disk is None:
1136     # create a device instance for the disk
1137     new_info = udev_get_block_device(os.path.dirname(sysfs_path))
1138     if new_info:
1139     self.addUdevDevice(new_info)
1140     disk = self.getDeviceByName(disk_name)
1141    
1142     if disk is None:
1143     # if the current device is still not in
1144     # the tree, something has gone wrong
1145     log.error("failure scanning device %s" % disk_name)
1146     lvm.lvm_cc_addFilterRejectRegexp(name)
1147     return
1148    
1149     # Check that the disk has partitions. If it does not, we must have
1150     # reinitialized the disklabel.
1151     #
1152     # Also ignore partitions on devices we do not support partitioning
1153     # of, like logical volumes.
1154     if not getattr(disk.format, "partitions", None) or \
1155     not disk.partitionable:
1156     # When we got here because the disk does not have a disklabel
1157     # format (ie a biosraid member), or because it is not
1158     # partitionable we want LVM to ignore this partition too
1159     if disk.format.type != "disklabel" or not disk.partitionable:
1160     lvm.lvm_cc_addFilterRejectRegexp(name)
1161     log.debug("ignoring partition %s" % name)
1162     return
1163    
1164     try:
1165     device = PartitionDevice(name, sysfsPath=sysfs_path,
1166     major=udev_device_get_major(info),
1167     minor=udev_device_get_minor(info),
1168     exists=True, parents=[disk])
1169     except DeviceError:
1170     # corner case sometime the kernel accepts a partition table
1171     # which gets rejected by parted, in this case we will
1172     # prompt to re-initialize the disk, so simply skip the
1173     # faulty partitions.
1174     return
1175    
1176     self._addDevice(device)
1177     return device
1178    
1179     def addUdevDiskDevice(self, info):
1180     name = udev_device_get_name(info)
1181     log_method_call(self, name=name)
1182     uuid = udev_device_get_uuid(info)
1183     sysfs_path = udev_device_get_sysfs_path(info)
1184     serial = udev_device_get_serial(info)
1185     bus = udev_device_get_bus(info)
1186    
1187     # udev doesn't always provide a vendor.
1188     vendor = udev_device_get_vendor(info)
1189     if not vendor:
1190     vendor = ""
1191    
1192     device = None
1193    
1194     kwargs = { "serial": serial, "vendor": vendor, "bus": bus }
1195     if udev_device_is_iscsi(info):
1196     diskType = iScsiDiskDevice
1197     initiator = udev_device_get_iscsi_initiator(info)
1198     target = udev_device_get_iscsi_name(info)
1199     address = udev_device_get_iscsi_address(info)
1200     port = udev_device_get_iscsi_port(info)
1201     nic = udev_device_get_iscsi_nic(info)
1202     kwargs["initiator"] = initiator
1203     if initiator == self.iscsi.initiator:
1204     node = self.iscsi.getNode(target, address, port, nic)
1205     kwargs["node"] = node
1206     kwargs["ibft"] = node in self.iscsi.ibftNodes
1207     kwargs["nic"] = self.iscsi.ifaces.get(node.iface, node.iface)
1208     log.debug("%s is an iscsi disk" % name)
1209     else:
1210     # qla4xxx partial offload
1211     kwargs["node"] = None
1212     kwargs["ibft"] = False
1213     kwargs["nic"] = "offload:not_accessible_via_iscsiadm"
1214     kwargs["fw_address"] = address
1215     kwargs["fw_port"] = port
1216     kwargs["fw_name"] = name
1217     elif udev_device_is_fcoe(info):
1218     diskType = FcoeDiskDevice
1219     kwargs["nic"] = udev_device_get_fcoe_nic(info)
1220     kwargs["identifier"] = udev_device_get_fcoe_identifier(info)
1221     log.debug("%s is an fcoe disk" % name)
1222     elif udev_device_get_md_container(info):
1223     diskType = MDRaidArrayDevice
1224     parentName = devicePathToName(udev_device_get_md_container(info))
1225     container = self.getDeviceByName(parentName)
1226     if not container:
1227     container_sysfs = "/class/block/" + parentName
1228     container_info = udev_get_block_device(container_sysfs)
1229     if not container_info:
1230     log.error("failed to find md container %s at %s"
1231     % (parentName, container_sysfs))
1232     return
1233    
1234     self.addUdevDevice(container_info)
1235     container = self.getDeviceByName(parentName)
1236     if not container:
1237     log.error("failed to scan md container %s" % parentName)
1238     return
1239    
1240     kwargs["parents"] = [container]
1241     kwargs["level"] = udev_device_get_md_level(info)
1242     kwargs["memberDevices"] = int(udev_device_get_md_devices(info))
1243     kwargs["uuid"] = udev_device_get_md_uuid(info)
1244     kwargs["exists"] = True
1245     del kwargs["serial"]
1246     del kwargs["vendor"]
1247     del kwargs["bus"]
1248     elif udev_device_is_dasd(info):
1249     diskType = DASDDevice
1250     kwargs["dasd"] = self.dasd
1251     kwargs["busid"] = udev_device_get_dasd_bus_id(info)
1252     kwargs["opts"] = {}
1253    
1254     for attr in ['readonly', 'use_diag', 'erplog', 'failfast']:
1255     kwargs["opts"][attr] = udev_device_get_dasd_flag(info, attr)
1256    
1257     log.debug("%s is a dasd device" % name)
1258     elif udev_device_is_zfcp(info):
1259     diskType = ZFCPDiskDevice
1260    
1261     for attr in ['hba_id', 'wwpn', 'fcp_lun']:
1262     kwargs[attr] = udev_device_get_zfcp_attribute(info, attr=attr)
1263    
1264     log.debug("%s is a zfcp device" % name)
1265     else:
1266     diskType = DiskDevice
1267     log.debug("%s is a disk" % name)
1268    
1269     device = diskType(name,
1270     major=udev_device_get_major(info),
1271     minor=udev_device_get_minor(info),
1272     sysfsPath=sysfs_path, **kwargs)
1273     self._addDevice(device)
1274     return device
1275    
1276     def addUdevOpticalDevice(self, info):
1277     log_method_call(self)
1278     # XXX should this be RemovableDevice instead?
1279     #
1280     # Looks like if it has ID_INSTANCE=0:1 we can ignore it.
1281     device = OpticalDevice(udev_device_get_name(info),
1282     major=udev_device_get_major(info),
1283     minor=udev_device_get_minor(info),
1284     sysfsPath=udev_device_get_sysfs_path(info),
1285     vendor=udev_device_get_vendor(info),
1286     model=udev_device_get_model(info))
1287     self._addDevice(device)
1288     return device
1289    
1290     def addUdevDevice(self, info):
1291     name = udev_device_get_name(info)
1292     log_method_call(self, name=name, info=info)
1293     uuid = udev_device_get_uuid(info)
1294     sysfs_path = udev_device_get_sysfs_path(info)
1295    
1296     if self.isIgnored(info):
1297     log.debug("ignoring %s (%s)" % (name, sysfs_path))
1298     if name not in self._ignoredDisks:
1299     self.addIgnoredDisk(name)
1300    
1301     if udev_device_is_multipath_member(info):
1302     # last time we are seeing this mpath member is now, so make sure
1303     # LVM ignores its partitions too else a duplicate VG name could
1304     # harm us later during partition creation:
1305     if udev_device_is_dm(info):
1306     path = "/dev/mapper/%s" % name
1307     else:
1308     path = "/dev/%s" % name
1309     log.debug("adding partitions on %s to the lvm ignore list" % path)
1310     partitions_paths = []
1311     try:
1312     partitions_paths = [p.path
1313     for p in parted.Disk(device=parted.Device(path=path)).partitions]
1314     except (_ped.IOException, _ped.DeviceException, _ped.DiskLabelException) as e:
1315     log.error("Parted error scanning partitions on %s:" % path)
1316     log.error(str(e))
1317     # slice off the "/dev/" part, lvm filter cares only about the rest
1318     partitions_paths = [p[5:] for p in partitions_paths]
1319     map(lvm.lvm_cc_addFilterRejectRegexp, partitions_paths)
1320     return
1321    
1322     log.debug("scanning %s (%s)..." % (name, sysfs_path))
1323     device = self.getDeviceByName(name)
1324    
1325     #
1326     # The first step is to either look up or create the device
1327     #
1328     if udev_device_is_multipath_member(info) and device is None:
1329     device = self.addUdevDiskDevice(info)
1330     elif udev_device_is_dm(info) and udev_device_is_dm_mpath(info) and device is None:
1331     log.debug("%s is a multipath device" % name)
1332     device = self.addUdevDMDevice(info)
1333     elif udev_device_is_dm(info):
1334     log.debug("%s is a device-mapper device" % name)
1335     # try to look up the device
1336     if device is None and uuid:
1337     # try to find the device by uuid
1338     device = self.getDeviceByUuid(uuid)
1339    
1340     if device is None:
1341     device = self.addUdevDMDevice(info)
1342     elif udev_device_is_md(info):
1343     log.debug("%s is an md device" % name)
1344     if device is None and uuid:
1345     # try to find the device by uuid
1346     device = self.getDeviceByUuid(uuid)
1347    
1348     if device is None:
1349     device = self.addUdevMDDevice(info)
1350     elif udev_device_is_cdrom(info):
1351     log.debug("%s is a cdrom" % name)
1352     if device is None:
1353     device = self.addUdevOpticalDevice(info)
1354     elif udev_device_is_biosraid(info) and udev_device_is_disk(info):
1355     log.debug("%s is part of a biosraid" % name)
1356     if device is None:
1357     device = DiskDevice(name,
1358     major=udev_device_get_major(info),
1359     minor=udev_device_get_minor(info),
1360     sysfsPath=sysfs_path, exists=True)
1361     self._addDevice(device)
1362     elif udev_device_is_disk(info):
1363     if device is None:
1364     device = self.addUdevDiskDevice(info)
1365     elif udev_device_is_partition(info):
1366     log.debug("%s is a partition" % name)
1367     if device is None:
1368     device = self.addUdevPartitionDevice(info)
1369     else:
1370     log.error("Unknown block device type for: %s" % name)
1371     return
1372    
1373     # If this device is protected, mark it as such now. Once the tree
1374     # has been populated, devices' protected attribute is how we will
1375     # identify protected devices.
1376     if device and device.name in self.protectedDevNames:
1377     device.protected = True
1378    
1379     # Don't try to do format handling on drives without media or
1380     # if we didn't end up with a device somehow.
1381     if not device or not device.mediaPresent:
1382     return
1383    
1384     # now handle the device's formatting
1385     self.handleUdevDeviceFormat(info, device)
1386     log.debug("got device: %s" % device)
1387     if device.format.type:
1388     log.debug("got format: %s" % device.format)
1389     device.originalFormat = device.format
1390    
1391     def handleUdevDiskLabelFormat(self, info, device):
1392     log_method_call(self, device=device.name)
1393     # if there is preexisting formatting on the device use it
1394     if getFormat(udev_device_get_format(info)).type is not None:
1395     log.debug("device %s does not contain a disklabel" % device.name)
1396     return
1397    
1398     if device.partitioned:
1399     # this device is already set up
1400     log.debug("disklabel format on %s already set up" % device.name)
1401     return
1402    
1403     try:
1404     device.setup()
1405     except Exception as e:
1406     log.debug("setup of %s failed: %s" % (device.name, e))
1407     log.warning("aborting disklabel handler for %s" % device.name)
1408     return
1409    
1410     # special handling for unsupported partitioned devices
1411     if not device.partitionable:
1412     try:
1413     format = getFormat("disklabel",
1414     device=device.path,
1415     exists=True)
1416     except InvalidDiskLabelError:
1417     pass
1418     else:
1419     if format.partitions:
1420     # parted's checks for disklabel presence are less than
1421     # rigorous, so we will assume that detected disklabels
1422     # with no partitions are spurious
1423     device.format = format
1424     return
1425    
1426     if self.zeroMbr:
1427     initcb = lambda: True
1428     else:
1429     description = device.description or device.model
1430     try:
1431     bypath = os.path.basename(deviceNameToDiskByPath(device.name))
1432     details = "\n\nDevice details:\n%s" % (bypath,)
1433     except DeviceNotFoundError:
1434     # some devices don't have a /dev/disk/by-path/ #!@#@!@#
1435     bypath = device.name
1436     details = ""
1437    
1438     initcb = lambda: self.intf.questionInitializeDisk(bypath,
1439     description,
1440     device.size,
1441     details)
1442    
1443     labelType = self.platform.bestDiskLabelType(device)
1444    
1445     try:
1446     format = getFormat("disklabel",
1447     device=device.path,
1448     labelType=labelType,
1449     exists=True)
1450     except InvalidDiskLabelError:
1451     # if we have a cb function use it. else we ignore the device.
1452     if initcb is not None and initcb():
1453     format = getFormat("disklabel",
1454     device=device.path,
1455     labelType=labelType,
1456     exists=False)
1457     else:
1458     self._removeDevice(device)
1459     self.addIgnoredDisk(device.name)
1460     return
1461    
1462     if not format.exists:
1463     # if we just initialized a disklabel we should schedule
1464     # actions for destruction of the previous format and creation
1465     # of the new one
1466     self.registerAction(ActionDestroyFormat(device))
1467     self.registerAction(ActionCreateFormat(device, format))
1468    
1469     # If this is a mac-formatted disk we just initialized, make
1470     # sure the partition table partition gets added to the device
1471     # tree.
1472     if device.format.partedDisk.type == "mac" and \
1473     len(device.format.partitions) == 1:
1474     name = device.format.partitions[0].getDeviceNodeName()
1475     if not self.getDeviceByName(name):
1476     partDevice = PartitionDevice(name, exists=True,
1477     parents=[device])
1478     self._addDevice(partDevice)
1479    
1480     else:
1481     device.format = format
1482    
1483     def handleUdevLUKSFormat(self, info, device):
1484     log_method_call(self, name=device.name, type=device.format.type)
1485     if not device.format.uuid:
1486     log.info("luks device %s has no uuid" % device.path)
1487     return
1488    
1489     # look up or create the mapped device
1490     if not self.getDeviceByName(device.format.mapName):
1491     passphrase = self.__luksDevs.get(device.format.uuid)
1492     if passphrase:
1493     device.format.passphrase = passphrase
1494     else:
1495     (passphrase, isglobal) = getLUKSPassphrase(self.intf,
1496     device,
1497     self.__passphrase)
1498     if isglobal and device.format.status:
1499     self.__passphrase = passphrase
1500    
1501     luks_device = LUKSDevice(device.format.mapName,
1502     parents=[device],
1503     exists=True)
1504     try:
1505     luks_device.setup()
1506     except (LUKSError, CryptoError, DeviceError) as e:
1507     log.info("setup of %s failed: %s" % (device.format.mapName,
1508     e))
1509     device.removeChild()
1510     else:
1511     self._addDevice(luks_device)
1512     else:
1513     log.warning("luks device %s already in the tree"
1514     % device.format.mapName)
1515    
1516     def handleVgLvs(self, vg_device):
1517     ret = False
1518     vg_name = vg_device.name
1519     lv_names = vg_device.lv_names
1520     lv_uuids = vg_device.lv_uuids
1521     lv_sizes = vg_device.lv_sizes
1522     lv_attr = vg_device.lv_attr
1523    
1524     if not vg_device.complete:
1525     log.warning("Skipping LVs for incomplete VG %s" % vg_name)
1526     return False
1527    
1528     if not lv_names:
1529     log.debug("no LVs listed for VG %s" % vg_name)
1530     return False
1531    
1532     def lv_attr_cmp(a, b):
1533     """ Sort so that mirror images come first and snapshots last. """
1534     mirror_chars = "Iil"
1535     snapshot_chars = "Ss"
1536     if a[0] in mirror_chars and b[0] not in mirror_chars:
1537     return -1
1538     elif a[0] not in mirror_chars and b[0] in mirror_chars:
1539     return 1
1540     elif a[0] not in snapshot_chars and b[0] in snapshot_chars:
1541     return -1
1542     elif a[0] in snapshot_chars and b[0] not in snapshot_chars:
1543     return 1
1544     else:
1545     return 0
1546    
1547     # make a list of indices with mirror volumes up front and snapshots at
1548     # the end
1549     indices = range(len(lv_names))
1550     indices.sort(key=lambda i: lv_attr[i], cmp=lv_attr_cmp)
1551     mirrors = {}
1552     for index in indices:
1553     lv_name = lv_names[index]
1554     name = "%s-%s" % (vg_name, lv_name)
1555     if lv_attr[index][0] in 'Ss':
1556     log.debug("found lvm snapshot volume '%s'" % name)
1557     origin_name = devicelibs.lvm.lvorigin(vg_name, lv_name)
1558     if not origin_name:
1559     log.error("lvm snapshot '%s-%s' has unknown origin"
1560     % (vg_name, lv_name))
1561     continue
1562    
1563     origin = self.getDeviceByName("%s-%s" % (vg_name,
1564     origin_name))
1565     if not origin:
1566     if origin_name.endswith("_vorigin]"):
1567     log.info("snapshot volume '%s' has vorigin" % name)
1568     vg_device.voriginSnapshots[lv_name] = lv_sizes[index]
1569     else:
1570     log.warning("snapshot lv '%s' origin lv '%s-%s' "
1571     "not found" % (name,
1572     vg_name, origin_name))
1573     continue
1574    
1575     log.debug("adding %dMB to %s snapshot total"
1576     % (lv_sizes[index], origin.name))
1577     origin.snapshotSpace += lv_sizes[index]
1578     continue
1579     elif lv_attr[index][0] == 'v':
1580     # skip vorigins
1581     continue
1582     elif lv_attr[index][0] in 'Ii':
1583     # mirror image
1584     lv_name = re.sub(r'_mimage.+', '', lv_name[1:-1])
1585     name = "%s-%s" % (vg_name, lv_name)
1586     if name not in mirrors:
1587     mirrors[name] = {"stripes": 0, "log": 0}
1588    
1589     mirrors[name]["stripes"] += 1
1590     elif lv_attr[index][0] == 'l':
1591     # log volume
1592     lv_name = re.sub(r'_mlog.*', '', lv_name[1:-1])
1593     name = "%s-%s" % (vg_name, lv_name)
1594     if name not in mirrors:
1595     mirrors[name] = {"stripes": 0, "log": 0}
1596    
1597     mirrors[name]["log"] = lv_sizes[index]
1598    
1599     lv_dev = self.getDeviceByName(name)
1600     if lv_dev is None:
1601     lv_uuid = lv_uuids[index]
1602     lv_size = lv_sizes[index]
1603     lv_device = LVMLogicalVolumeDevice(lv_name,
1604     vg_device,
1605     uuid=lv_uuid,
1606     size=lv_size,
1607     exists=True)
1608     self._addDevice(lv_device)
1609    
1610     try:
1611     lv_device.setup()
1612     ret = True
1613     except DeviceError as (msg, name):
1614     log.info("setup of %s failed: %s"
1615     % (lv_device.name, msg))
1616    
1617     for name, mirror in mirrors.items():
1618     lv_dev = self.getDeviceByName(name)
1619     lv_dev.stripes = mirror["stripes"]
1620     lv_dev.logSize = mirror["log"]
1621     log.debug("set %s stripes to %d, log size to %dMB, total size %dMB"
1622     % (lv_dev.name, lv_dev.stripes, lv_dev.logSize,
1623     lv_dev.vgSpaceUsed))
1624    
1625     return ret
1626    
1627     def handleUdevLVMPVFormat(self, info, device):
1628     log_method_call(self, name=device.name, type=device.format.type)
1629     # lookup/create the VG and LVs
1630     try:
1631     vg_name = udev_device_get_vg_name(info)
1632     except KeyError:
1633     # no vg name means no vg -- we're done with this pv
1634     return
1635    
1636     vg_device = self.getDeviceByName(vg_name)
1637     if vg_device:
1638     vg_device._addDevice(device)
1639     else:
1640     try:
1641     vg_uuid = udev_device_get_vg_uuid(info)
1642     vg_size = udev_device_get_vg_size(info)
1643     vg_free = udev_device_get_vg_free(info)
1644     pe_size = udev_device_get_vg_extent_size(info)
1645     pe_count = udev_device_get_vg_extent_count(info)
1646     pe_free = udev_device_get_vg_free_extents(info)
1647     pv_count = udev_device_get_vg_pv_count(info)
1648     except (KeyError, ValueError) as e:
1649     log.warning("invalid data for %s: %s" % (device.name, e))
1650     return
1651    
1652     vg_device = LVMVolumeGroupDevice(vg_name,
1653     device,
1654     uuid=vg_uuid,
1655     size=vg_size,
1656     free=vg_free,
1657     peSize=pe_size,
1658     peCount=pe_count,
1659     peFree=pe_free,
1660     pvCount=pv_count,
1661     exists=True)
1662     self._addDevice(vg_device)
1663    
1664     # Now we add any lv info found in this pv to the vg_device, we
1665     # do this for all pvs as pvs only contain lv info for lvs which they
1666     # contain themselves
1667     try:
1668     lv_names = udev_device_get_lv_names(info)
1669     lv_uuids = udev_device_get_lv_uuids(info)
1670     lv_sizes = udev_device_get_lv_sizes(info)
1671     lv_attr = udev_device_get_lv_attr(info)
1672     except KeyError as e:
1673     log.warning("invalid data for %s: %s" % (device.name, e))
1674     return
1675    
1676     for i in range(len(lv_names)):
1677     # Skip empty and already added lvs
1678     if not lv_names[i] or lv_names[i] in vg_device.lv_names:
1679     continue
1680    
1681     vg_device.lv_names.append(lv_names[i])
1682     vg_device.lv_uuids.append(lv_uuids[i])
1683     vg_device.lv_sizes.append(lv_sizes[i])
1684     vg_device.lv_attr.append(lv_attr[i])
1685    
1686     def handleUdevMDMemberFormat(self, info, device):
1687     log_method_call(self, name=device.name, type=device.format.type)
1688     # either look up or create the array device
1689     name = udev_device_get_name(info)
1690     sysfs_path = udev_device_get_sysfs_path(info)
1691    
1692     md_array = self.getDeviceByUuid(device.format.mdUuid)
1693     if device.format.mdUuid and md_array:
1694     md_array._addDevice(device)
1695     else:
1696     # create the array with just this one member
1697     try:
1698     # level is reported as, eg: "raid1"
1699     md_level = udev_device_get_md_level(info)
1700     md_devices = int(udev_device_get_md_devices(info))
1701     md_uuid = udev_device_get_md_uuid(info)
1702     except (KeyError, ValueError) as e:
1703     log.warning("invalid data for %s: %s" % (name, e))
1704     return
1705    
1706     # try to name the array based on the preferred minor
1707     md_info = devicelibs.mdraid.mdexamine(device.path)
1708     md_path = md_info.get("device", "")
1709     md_metadata = md_info.get("metadata")
1710     md_name = devicePathToName(md_info.get("device", ""))
1711     if md_name:
1712     try:
1713     # md_name can be either md# or md/#
1714     if md_name.startswith("md/"):
1715     minor = int(md_name[3:]) # strip off leading "md/"
1716     md_name = "md%d" % minor # use a regular md# name
1717     else:
1718     minor = int(md_name[2:]) # strip off leading "md"
1719     except (IndexError, ValueError):
1720     minor = None
1721     md_name = None
1722     else:
1723     array = self.getDeviceByName(md_name)
1724     if array and array.uuid != md_uuid:
1725     md_name = None
1726    
1727     if not md_name:
1728     # if we don't have a name yet, find the first unused minor
1729     minor = 0
1730     while True:
1731     if self.getDeviceByName("md%d" % minor):
1732     minor += 1
1733     else:
1734     break
1735    
1736     md_name = "md%d" % minor
1737    
1738     log.debug("using name %s for md array containing member %s"
1739     % (md_name, device.name))
1740     md_array = MDRaidArrayDevice(md_name,
1741     level=md_level,
1742     minor=minor,
1743     memberDevices=md_devices,
1744     uuid=md_uuid,
1745     metadataVersion=md_metadata,
1746     sysfsPath=sysfs_path,
1747     exists=True)
1748     md_array._addDevice(device)
1749     self._addDevice(md_array)
1750    
1751     def handleMultipathMemberFormat(self, info, device):
1752     log_method_call(self, name=device.name, type=device.format.type)
1753    
1754     name = udev_device_get_multipath_name(info)
1755     if self.__multipaths.has_key(name):
1756     mp = self.__multipaths[name]
1757     mp.addParent(device)
1758     else:
1759     mp = MultipathDevice(name, info, parents=[device])
1760     self.__multipaths[name] = mp
1761    
1762     def handleUdevDMRaidMemberFormat(self, info, device):
1763     log_method_call(self, name=device.name, type=device.format.type)
1764     name = udev_device_get_name(info)
1765     sysfs_path = udev_device_get_sysfs_path(info)
1766     uuid = udev_device_get_uuid(info)
1767     major = udev_device_get_major(info)
1768     minor = udev_device_get_minor(info)
1769    
1770     def _all_ignored(rss):
1771     retval = True
1772     for rs in rss:
1773     if rs.name not in self._ignoredDisks:
1774     retval = False
1775     break
1776     return retval
1777    
1778     # Have we already created the DMRaidArrayDevice?
1779     rss = block.getRaidSetFromRelatedMem(uuid=uuid, name=name,
1780     major=major, minor=minor)
1781     if len(rss) == 0:
1782     # we ignore the device in the hope that all the devices
1783     # from this set will be ignored.
1784     self.unusedRaidMembers.append(device.name)
1785     self.addIgnoredDisk(device.name)
1786     return
1787    
1788     # We ignore the device if all the rss are in self._ignoredDisks
1789     if _all_ignored(rss):
1790     self.addIgnoredDisk(device.name)
1791     return
1792    
1793     for rs in rss:
1794     dm_array = self.getDeviceByName(rs.name)
1795     if dm_array is not None:
1796     # We add the new device.
1797     dm_array._addDevice(device)
1798     else:
1799     # Activate the Raid set.
1800     rs.activate(mknod=True)
1801     dm_array = DMRaidArrayDevice(rs.name,
1802     raidSet=rs,
1803     parents=[device])
1804    
1805     self._addDevice(dm_array)
1806    
1807     # Wait for udev to scan the just created nodes, to avoid a race
1808     # with the udev_get_block_device() call below.
1809     udev_settle()
1810    
1811     # Get the DMRaidArrayDevice a DiskLabel format *now*, in case
1812     # its partitions get scanned before it does.
1813     dm_array.updateSysfsPath()
1814     dm_array_info = udev_get_block_device(dm_array.sysfsPath)
1815     self.handleUdevDiskLabelFormat(dm_array_info, dm_array)
1816    
1817     # Use the rs's object on the device.
1818     # pyblock can return the memebers of a set and the
1819     # device has the attribute to hold it. But ATM we
1820     # are not really using it. Commenting this out until
1821     # we really need it.
1822     #device.format.raidmem = block.getMemFromRaidSet(dm_array,
1823     # major=major, minor=minor, uuid=uuid, name=name)
1824    
1825     def handleUdevDeviceFormat(self, info, device):
1826     log_method_call(self, name=getattr(device, "name", None))
1827     name = udev_device_get_name(info)
1828     sysfs_path = udev_device_get_sysfs_path(info)
1829     uuid = udev_device_get_uuid(info)
1830     label = udev_device_get_label(info)
1831     format_type = udev_device_get_format(info)
1832     serial = udev_device_get_serial(info)
1833    
1834     # Now, if the device is a disk, see if there is a usable disklabel.
1835     # If not, see if the user would like to create one.
1836     # XXX ignore disklabels on multipath or biosraid member disks
1837     if not udev_device_is_biosraid(info) and \
1838     not udev_device_is_multipath_member(info):
1839     self.handleUdevDiskLabelFormat(info, device)
1840     if device.partitioned or self.isIgnored(info) or \
1841     (not device.partitionable and
1842     device.format.type == "disklabel"):
1843     # If the device has a disklabel, or the user chose not to
1844     # create one, we are finished with this device. Otherwise
1845     # it must have some non-disklabel formatting, in which case
1846     # we fall through to handle that.
1847     return
1848    
1849     format = None
1850     if (not device) or (not format_type) or device.format.type:
1851     # this device has no formatting or it has already been set up
1852     # FIXME: this probably needs something special for disklabels
1853     log.debug("no type or existing type for %s, bailing" % (name,))
1854     return
1855    
1856     # set up the common arguments for the format constructor
1857     args = [format_type]
1858     kwargs = {"uuid": uuid,
1859     "label": label,
1860     "device": device.path,
1861     "serial": serial,
1862     "exists": True}
1863    
1864     # set up type-specific arguments for the format constructor
1865     if format_type == "multipath_member":
1866     kwargs["multipath_members"] = self.getDevicesBySerial(serial)
1867     elif format_type == "crypto_LUKS":
1868     # luks/dmcrypt
1869     kwargs["name"] = "luks-%s" % uuid
1870     elif format_type in formats.mdraid.MDRaidMember._udevTypes:
1871     # mdraid
1872     try:
1873     kwargs["mdUuid"] = udev_device_get_md_uuid(info)
1874     except KeyError:
1875     log.debug("mdraid member %s has no md uuid" % name)
1876     kwargs["biosraid"] = udev_device_is_biosraid(info)
1877     elif format_type == "LVM2_member":
1878     # lvm
1879     try:
1880     kwargs["vgName"] = udev_device_get_vg_name(info)
1881     except KeyError as e:
1882     log.debug("PV %s has no vg_name" % name)
1883     try:
1884     kwargs["vgUuid"] = udev_device_get_vg_uuid(info)
1885     except KeyError:
1886     log.debug("PV %s has no vg_uuid" % name)
1887     try:
1888     kwargs["peStart"] = udev_device_get_pv_pe_start(info)
1889     except KeyError:
1890     log.debug("PV %s has no pe_start" % name)
1891     elif format_type == "vfat":
1892     # efi magic
1893     if isinstance(device, PartitionDevice) and device.bootable:
1894     efi = formats.getFormat("efi")
1895     if efi.minSize <= device.size <= efi.maxSize:
1896     args[0] = "efi"
1897     elif format_type == "hfs":
1898     # apple bootstrap magic
1899     if isinstance(device, PartitionDevice) and device.bootable:
1900     apple = formats.getFormat("appleboot")
1901     if apple.minSize <= device.size <= apple.maxSize:
1902     args[0] = "appleboot"
1903    
1904     try:
1905     log.debug("type detected on '%s' is '%s'" % (name, format_type,))
1906     device.format = formats.getFormat(*args, **kwargs)
1907     except FSError:
1908     log.debug("type '%s' on '%s' invalid, assuming no format" %
1909     (format_type, name,))
1910     device.format = formats.DeviceFormat()
1911     return
1912    
1913     if shouldClear(device, self.clearPartType,
1914     clearPartDisks=self.clearPartDisks):
1915     # if this is a device that will be cleared by clearpart,
1916     # don't bother with format-specific processing
1917     return
1918    
1919     #
1920     # now do any special handling required for the device's format
1921     #
1922     if device.format.type == "luks":
1923     self.handleUdevLUKSFormat(info, device)
1924     elif device.format.type == "mdmember":
1925     self.handleUdevMDMemberFormat(info, device)
1926     elif device.format.type == "dmraidmember":
1927     self.handleUdevDMRaidMemberFormat(info, device)
1928     elif device.format.type == "lvmpv":
1929     self.handleUdevLVMPVFormat(info, device)
1930     elif device.format.type == "multipath_member":
1931     self.handleMultipathMemberFormat(info, device)
1932    
1933     def updateDeviceFormat(self, device):
1934     log.debug("updating format of device: %s" % device)
1935     iutil.notify_kernel("/sys%s" % device.sysfsPath)
1936     udev_settle()
1937     info = udev_get_device(device.sysfsPath)
1938     self.handleUdevDeviceFormat(info, device)
1939     if device.format.type:
1940     log.debug("got format: %s" % device.format)
1941    
1942     def _handleInconsistencies(self):
1943     def reinitializeVG(vg):
1944     # First we remove VG data
1945     try:
1946     vg.destroy()
1947     except DeviceError:
1948     # the pvremoves will finish the job.
1949     log.debug("There was an error destroying the VG %s." % vg.name)
1950    
1951     # remove VG device from list.
1952     self._removeDevice(vg)
1953    
1954     for parent in vg.parents:
1955     parent.format.destroy()
1956    
1957     # Give the vg the a default format
1958     kwargs = {"device": parent.path,
1959     "exists": parent.exists}
1960     parent.format = formats.getFormat(*[""], **kwargs)
1961    
1962     def leafInconsistencies(device):
1963     if device.type == "lvmvg":
1964     if device.complete:
1965     return
1966    
1967     paths = []
1968     for parent in device.parents:
1969     paths.append(parent.path)
1970    
1971     # if zeroMbr is true don't ask.
1972     if (self.zeroMbr or
1973     self.intf.questionReinitInconsistentLVM(pv_names=paths,
1974     vg_name=device.name)):
1975     reinitializeVG(device)
1976     else:
1977     # The user chose not to reinitialize.
1978     # hopefully this will ignore the vg components too.
1979     self._removeDevice(device)
1980     lvm.lvm_cc_addFilterRejectRegexp(device.name)
1981     lvm.blacklistVG(device.name)
1982     for parent in device.parents:
1983     if parent.type == "partition":
1984     parent.immutable = \
1985     _("This partition is part of an inconsistent LVM Volume Group.")
1986     else:
1987     self._removeDevice(parent, moddisk=False)
1988     self.addIgnoredDisk(parent.name)
1989     lvm.lvm_cc_addFilterRejectRegexp(parent.name)
1990    
1991     elif device.type == "lvmlv":
1992     # we might have already fixed this.
1993     if device not in self._devices or \
1994     device.name in self._ignoredDisks:
1995     return
1996     if device.complete:
1997     return
1998    
1999     paths = []
2000     for parent in device.vg.parents:
2001     paths.append(parent.path)
2002    
2003     if (self.zeroMbr or
2004     self.intf.questionReinitInconsistentLVM(pv_names=paths,
2005     lv_name=device.name)):
2006    
2007     # destroy all lvs.
2008     for lv in device.vg.lvs:
2009     try:
2010     # reinitializeVG should clean up if necessary
2011     lv.destroy()
2012     except StorageError as e:
2013     log.info("error removing lv %s from "
2014     "inconsistent/incomplete vg %s"
2015     % (lv.lvname, device.vg.name))
2016     device.vg._removeLogVol(lv)
2017     self._removeDevice(lv)
2018    
2019     reinitializeVG(device.vg)
2020     else:
2021     # ignore all the lvs.
2022     for lv in device.vg.lvs:
2023     self._removeDevice(lv)
2024     lvm.lvm_cc_addFilterRejectRegexp(lv.name)
2025     # ignore the vg
2026     self._removeDevice(device.vg)
2027     lvm.lvm_cc_addFilterRejectRegexp(device.vg.name)
2028     lvm.blacklistVG(device.vg.name)
2029     # ignore all the pvs
2030     for parent in device.vg.parents:
2031     if parent.type == "partition":
2032     parent.immutable = \
2033     _("This partition is part of an inconsistent LVM Volume Group.")
2034     else:
2035     self._removeDevice(parent, moddisk=False)
2036     self.addIgnoredDisk(parent.name)
2037     lvm.lvm_cc_addFilterRejectRegexp(parent.name)
2038    
2039     # Address the inconsistencies present in the tree leaves.
2040     for leaf in self.leaves:
2041     leafInconsistencies(leaf)
2042    
2043     # Check for unused BIOS raid members, unused dmraid members are added
2044     # to self.unusedRaidMembers as they are processed, extend this list
2045     # with unused mdraid BIOS raid members
2046     for c in self.getDevicesByType("mdcontainer"):
2047     if c.kids == 0:
2048     self.unusedRaidMembers.extend(map(lambda m: m.name, c.devices))
2049    
2050     self.intf.unusedRaidMembersWarning(self.unusedRaidMembers)
2051    
2052     # remove md array devices for which we did not find all members
2053     for array in self.getDevicesByType("mdarray"):
2054     if array.memberDevices > len(array.parents):
2055     self._recursiveRemove(array)
2056    
2057     def _recursiveRemove(self, device):
2058     for d in self.getChildren(device):
2059     self._recursiveRemove(d)
2060    
2061     device.teardown()
2062     self._removeDevice(device)
2063    
2064     def _setupLvs(self):
2065     ret = False
2066    
2067     for device in self.getDevicesByType("lvmvg"):
2068     if self.handleVgLvs(device):
2069     ret = True
2070    
2071     return ret
2072    
2073     def populate(self, progressWindow=None):
2074     """ Locate all storage devices. """
2075     log.debug("DeviceTree.populate: ignoredDisks is %s ; exclusiveDisks is %s"
2076     % (self._ignoredDisks, self.exclusiveDisks))
2077    
2078     # mark the tree as unpopulated so exception handlers can tell the
2079     # exception originated while finding storage devices
2080     self.populated = False
2081    
2082     # resolve the protected device specs to device names
2083     for spec in self.protectedDevSpecs:
2084     name = udev_resolve_devspec(spec)
2085     if name:
2086     self.protectedDevNames.append(name)
2087    
2088     # FIXME: the backing dev for the live image can't be used as an
2089     # install target. note that this is a little bit of a hack
2090     # since we're assuming that /dev/live will exist
2091     if os.path.exists("/dev/live") and \
2092     stat.S_ISBLK(os.stat("/dev/live")[stat.ST_MODE]):
2093     livetarget = devicePathToName(os.path.realpath("/dev/live"))
2094     log.info("%s looks to be the live device; marking as protected"
2095     % (livetarget,))
2096     self.protectedDevNames.append(livetarget)
2097    
2098     devicelibs.mpath.writeMultipathConf(writer=self.__multipathConfigWriter,
2099     friendly_names=self.mpathFriendlyNames)
2100    
2101     devices = udev_get_block_devices()
2102     if os.access("/etc/multipath.conf", os.R_OK):
2103     (singles, mpaths, partitions) = devicelibs.mpath.identifyMultipaths(devices)
2104     devices = singles + reduce(list.__add__, mpaths, []) + partitions
2105    
2106     # remember all the devices idenitfyMultipaths() gave us at this point
2107     old_devices = {}
2108     for dev in devices:
2109     old_devices[dev['name']] = dev
2110    
2111     log.info("devices to scan: %s" % [d['name'] for d in devices])
2112     for dev in devices:
2113     self.addUdevDevice(dev)
2114     if progressWindow:
2115     progressWindow.pulse()
2116    
2117     # Having found all the disks, we can now find all the multipaths built
2118     # upon them.
2119     whitelist = []
2120     mpaths = self.__multipaths.values()
2121     mpaths.sort(key=lambda d: d.name)
2122     for mp in mpaths:
2123     log.info("adding mpath device %s" % mp.name)
2124     mp.setup()
2125     mp.updateSysfsPath()
2126     mp_info = udev_get_block_device(mp.sysfsPath)
2127     if mp_info is None or self.isIgnored(mp_info):
2128     mp.teardown()
2129     continue
2130    
2131     whitelist.append(mp.name)
2132     for p in mp.parents:
2133     whitelist.append(p.name)
2134     self.__multipathConfigWriter.addMultipathDevice(mp)
2135     self._addDevice(mp)
2136     self.addUdevDevice(mp_info)
2137     if progressWindow:
2138     progressWindow.pulse()
2139     for d in self.devices:
2140     if not d.name in whitelist:
2141     self.__multipathConfigWriter.addBlacklistDevice(d)
2142    
2143     devicelibs.mpath.writeMultipathConf(writer=self.__multipathConfigWriter,
2144     friendly_names=self.mpathFriendlyNames)
2145    
2146     # Now, loop and scan for devices that have appeared since the two above
2147     # blocks or since previous iterations.
2148     while True:
2149     devices = []
2150     new_devices = udev_get_block_devices()
2151    
2152     for new_device in new_devices:
2153     if not old_devices.has_key(new_device['name']):
2154     old_devices[new_device['name']] = new_device
2155     devices.append(new_device)
2156    
2157     if len(devices) == 0:
2158     # nothing is changing -- time to setup lvm lvs and scan them
2159     # we delay this till all other devices are scanned so that
2160     # 1) the lvm filter for ignored disks is completely setup
2161     # 2) we have checked all devs for duplicate vg names
2162     if self._setupLvs():
2163     continue
2164     # nothing is changing -- we are finished building devices
2165     break
2166    
2167     log.info("devices to scan: %s" % [d['name'] for d in devices])
2168     for dev in devices:
2169     self.addUdevDevice(dev)
2170     if progressWindow:
2171     progressWindow.pulse()
2172    
2173     self.populated = True
2174    
2175     # After having the complete tree we make sure that the system
2176     # inconsistencies are ignored or resolved.
2177     self._handleInconsistencies()
2178    
2179     self.teardownAll()
2180     try:
2181     os.unlink("/etc/mdadm.conf")
2182     except OSError:
2183     log.info("failed to unlink /etc/mdadm.conf")
2184    
2185     def teardownAll(self):
2186     """ Run teardown methods on all devices. """
2187     for device in self.leaves:
2188     try:
2189     device.teardown(recursive=True)
2190     except StorageError as e:
2191     log.info("teardown of %s failed: %s" % (device.name, e))
2192    
2193     def setupAll(self):
2194     """ Run setup methods on all devices. """
2195     for device in self.leaves:
2196     try:
2197     device.setup()
2198     except DeviceError as (msg, name):
2199     log.debug("setup of %s failed: %s" % (device.name, msg))
2200    
2201     def getDeviceBySysfsPath(self, path):
2202     if not path:
2203     return None
2204    
2205     found = None
2206     for device in self._devices:
2207     if device.sysfsPath == path:
2208     found = device
2209     break
2210    
2211     return found
2212    
2213     def getDeviceByUuid(self, uuid):
2214     if not uuid:
2215     return None
2216    
2217     found = None
2218     for device in self._devices:
2219     if device.uuid == uuid:
2220     found = device
2221     break
2222     elif device.format.uuid == uuid:
2223     found = device
2224     break
2225    
2226     return found
2227    
2228     def getDevicesBySerial(self, serial):
2229     devices = []
2230     for device in self._devices:
2231     if not hasattr(device, "serial"):
2232     log.warning("device %s has no serial attr" % device.name)
2233     continue
2234     if device.serial == serial:
2235     devices.append(device)
2236     return devices
2237    
2238     def getDeviceByLabel(self, label):
2239     if not label:
2240     return None
2241    
2242     found = None
2243     for device in self._devices:
2244     _label = getattr(device.format, "label", None)
2245     if not _label:
2246     continue
2247    
2248     if _label == label:
2249     found = device
2250     break
2251    
2252     return found
2253    
2254     def getDeviceByName(self, name):
2255     log.debug("looking for device '%s'..." % name)
2256     if not name:
2257     return None
2258    
2259     found = None
2260     for device in self._devices:
2261     if device.name == name:
2262     found = device
2263     break
2264     elif (device.type == "lvmlv" or device.type == "lvmvg") and \
2265     device.name == name.replace("--","-"):
2266     found = device
2267     break
2268    
2269     log.debug("found %s" % found)
2270     return found
2271    
2272     def getDeviceByPath(self, path):
2273     log.debug("looking for device '%s'..." % path)
2274     if not path:
2275     return None
2276    
2277     found = None
2278     for device in self._devices:
2279     if device.path == path:
2280     found = device
2281     break
2282     elif (device.type == "lvmlv" or device.type == "lvmvg") and \
2283     device.path == path.replace("--","-"):
2284     found = device
2285     break
2286    
2287     log.debug("found %s" % found)
2288     return found
2289    
2290     def getDevicesByType(self, device_type):
2291     # TODO: expand this to catch device format types
2292     return [d for d in self._devices if d.type == device_type]
2293    
2294     def getDevicesByInstance(self, device_class):
2295     return [d for d in self._devices if isinstance(d, device_class)]
2296    
2297     @property
2298     def devices(self):
2299     """ List of device instances """
2300     devices = []
2301     for device in self._devices:
2302     if device.path in [d.path for d in devices] and \
2303     not isinstance(device, NoDevice):
2304     raise DeviceTreeError("duplicate paths in device tree")
2305    
2306     devices.append(device)
2307    
2308     return devices
2309    
2310     @property
2311     def filesystems(self):
2312     """ List of filesystems. """
2313     #""" Dict with mountpoint keys and filesystem values. """
2314     filesystems = []
2315     for dev in self.leaves:
2316     if dev.format and getattr(dev.format, 'mountpoint', None):
2317     filesystems.append(dev.format)
2318    
2319     return filesystems
2320    
2321     @property
2322     def uuids(self):
2323     """ Dict with uuid keys and Device values. """
2324     uuids = {}
2325     for dev in self._devices:
2326     try:
2327     uuid = dev.uuid
2328     except AttributeError:
2329     uuid = None
2330    
2331     if uuid:
2332     uuids[uuid] = dev
2333    
2334     try:
2335     uuid = dev.format.uuid
2336     except AttributeError:
2337     uuid = None
2338    
2339     if uuid:
2340     uuids[uuid] = dev
2341    
2342     return uuids
2343    
2344     @property
2345     def labels(self):
2346     """ Dict with label keys and Device values.
2347    
2348     FIXME: duplicate labels are a possibility
2349     """
2350     labels = {}
2351     for dev in self._devices:
2352     if dev.format and getattr(dev.format, "label", None):
2353     labels[dev.format.label] = dev
2354    
2355     return labels
2356    
2357     @property
2358     def leaves(self):
2359     """ List of all devices upon which no other devices exist. """
2360     leaves = [d for d in self._devices if d.isleaf]
2361     return leaves
2362    
2363     def getChildren(self, device):
2364     """ Return a list of a device's children. """
2365     return [c for c in self._devices if device in c.parents]
2366    
2367     def resolveDevice(self, devspec, blkidTab=None, cryptTab=None):
2368     # find device in the tree
2369     device = None
2370     if devspec.startswith("UUID="):
2371     # device-by-uuid
2372     uuid = devspec.partition("=")[2]
2373     device = self.uuids.get(uuid)
2374     if device is None:
2375     log.error("failed to resolve device %s" % devspec)
2376     elif devspec.startswith("LABEL="):
2377     # device-by-label
2378     label = devspec.partition("=")[2]
2379     device = self.labels.get(label)
2380     if device is None:
2381     log.error("failed to resolve device %s" % devspec)
2382     elif devspec.startswith("/dev/"):
2383     if devspec.startswith("/dev/disk/"):
2384     devspec = os.path.realpath(devspec)
2385    
2386     if devspec.startswith("/dev/dm-"):
2387     dm_name = devicelibs.dm.name_from_dm_node(devspec[5:])
2388     if dm_name:
2389     devspec = "/dev/mapper/" + dm_name
2390    
2391     # device path
2392     device = self.getDeviceByPath(devspec)
2393     if device is None:
2394     if blkidTab:
2395     # try to use the blkid.tab to correlate the device
2396     # path with a UUID
2397     blkidTabEnt = blkidTab.get(devspec)
2398     if blkidTabEnt:
2399     log.debug("found blkid.tab entry for '%s'" % devspec)
2400     uuid = blkidTabEnt.get("UUID")
2401     if uuid:
2402     device = self.getDeviceByUuid(uuid)
2403     if device:
2404     devstr = device.name
2405     else:
2406     devstr = "None"
2407     log.debug("found device '%s' in tree" % devstr)
2408     if device and device.format and \
2409     device.format.type == "luks":
2410     map_name = device.format.mapName
2411     log.debug("luks device; map name is '%s'" % map_name)
2412     mapped_dev = self.getDeviceByName(map_name)
2413     if mapped_dev:
2414     device = mapped_dev
2415    
2416     if device is None and cryptTab and \
2417     devspec.startswith("/dev/mapper/"):
2418     # try to use a dm-crypt mapping name to
2419     # obtain the underlying device, possibly
2420     # using blkid.tab
2421     cryptTabEnt = cryptTab.get(devspec.split("/")[-1])
2422     if cryptTabEnt:
2423     luks_dev = cryptTabEnt['device']
2424     try:
2425     device = self.getChildren(luks_dev)[0]
2426     except IndexError as e:
2427     pass
2428     elif device is None:
2429     # dear lvm: can we please have a few more device nodes
2430     # for each logical volume?
2431     # three just doesn't seem like enough.
2432     name = devspec[5:] # strip off leading "/dev/"
2433     (vg_name, slash, lv_name) = name.partition("/")
2434     if lv_name and not "/" in lv_name:
2435     # looks like we may have one
2436     lv = "%s-%s" % (vg_name, lv_name)
2437     device = self.getDeviceByName(lv)
2438    
2439     if device:
2440     log.debug("resolved '%s' to '%s' (%s)" % (devspec, device.name, device.type))
2441     else:
2442     log.debug("failed to resolve '%s'" % devspec)
2443     return device

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