/[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.2 - (hide annotations) (download) (as text)
Tue Jul 30 21:25:43 2013 UTC (11 years, 3 months ago) by charliebrady
Branch: MAIN
Changes since 1.1: +5 -1 lines
Content type: text/x-python
Support installation with degraded raid1 configuration as default.

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 charliebrady 1.2 # Exception: allow degraded RAID1 arrays to be detected
2055     if (array.level == 1) and (array.memberDevices-1) == len(array.parents):
2056     log.warning("RAID1 array %s is degraded - %d of %d members found." % \
2057     (array.name, len(array.parents), array.memberDevices))
2058     elif array.memberDevices > len(array.parents):
2059 charliebrady 1.1 self._recursiveRemove(array)
2060    
2061     def _recursiveRemove(self, device):
2062     for d in self.getChildren(device):
2063     self._recursiveRemove(d)
2064    
2065     device.teardown()
2066     self._removeDevice(device)
2067    
2068     def _setupLvs(self):
2069     ret = False
2070    
2071     for device in self.getDevicesByType("lvmvg"):
2072     if self.handleVgLvs(device):
2073     ret = True
2074    
2075     return ret
2076    
2077     def populate(self, progressWindow=None):
2078     """ Locate all storage devices. """
2079     log.debug("DeviceTree.populate: ignoredDisks is %s ; exclusiveDisks is %s"
2080     % (self._ignoredDisks, self.exclusiveDisks))
2081    
2082     # mark the tree as unpopulated so exception handlers can tell the
2083     # exception originated while finding storage devices
2084     self.populated = False
2085    
2086     # resolve the protected device specs to device names
2087     for spec in self.protectedDevSpecs:
2088     name = udev_resolve_devspec(spec)
2089     if name:
2090     self.protectedDevNames.append(name)
2091    
2092     # FIXME: the backing dev for the live image can't be used as an
2093     # install target. note that this is a little bit of a hack
2094     # since we're assuming that /dev/live will exist
2095     if os.path.exists("/dev/live") and \
2096     stat.S_ISBLK(os.stat("/dev/live")[stat.ST_MODE]):
2097     livetarget = devicePathToName(os.path.realpath("/dev/live"))
2098     log.info("%s looks to be the live device; marking as protected"
2099     % (livetarget,))
2100     self.protectedDevNames.append(livetarget)
2101    
2102     devicelibs.mpath.writeMultipathConf(writer=self.__multipathConfigWriter,
2103     friendly_names=self.mpathFriendlyNames)
2104    
2105     devices = udev_get_block_devices()
2106     if os.access("/etc/multipath.conf", os.R_OK):
2107     (singles, mpaths, partitions) = devicelibs.mpath.identifyMultipaths(devices)
2108     devices = singles + reduce(list.__add__, mpaths, []) + partitions
2109    
2110     # remember all the devices idenitfyMultipaths() gave us at this point
2111     old_devices = {}
2112     for dev in devices:
2113     old_devices[dev['name']] = dev
2114    
2115     log.info("devices to scan: %s" % [d['name'] for d in devices])
2116     for dev in devices:
2117     self.addUdevDevice(dev)
2118     if progressWindow:
2119     progressWindow.pulse()
2120    
2121     # Having found all the disks, we can now find all the multipaths built
2122     # upon them.
2123     whitelist = []
2124     mpaths = self.__multipaths.values()
2125     mpaths.sort(key=lambda d: d.name)
2126     for mp in mpaths:
2127     log.info("adding mpath device %s" % mp.name)
2128     mp.setup()
2129     mp.updateSysfsPath()
2130     mp_info = udev_get_block_device(mp.sysfsPath)
2131     if mp_info is None or self.isIgnored(mp_info):
2132     mp.teardown()
2133     continue
2134    
2135     whitelist.append(mp.name)
2136     for p in mp.parents:
2137     whitelist.append(p.name)
2138     self.__multipathConfigWriter.addMultipathDevice(mp)
2139     self._addDevice(mp)
2140     self.addUdevDevice(mp_info)
2141     if progressWindow:
2142     progressWindow.pulse()
2143     for d in self.devices:
2144     if not d.name in whitelist:
2145     self.__multipathConfigWriter.addBlacklistDevice(d)
2146    
2147     devicelibs.mpath.writeMultipathConf(writer=self.__multipathConfigWriter,
2148     friendly_names=self.mpathFriendlyNames)
2149    
2150     # Now, loop and scan for devices that have appeared since the two above
2151     # blocks or since previous iterations.
2152     while True:
2153     devices = []
2154     new_devices = udev_get_block_devices()
2155    
2156     for new_device in new_devices:
2157     if not old_devices.has_key(new_device['name']):
2158     old_devices[new_device['name']] = new_device
2159     devices.append(new_device)
2160    
2161     if len(devices) == 0:
2162     # nothing is changing -- time to setup lvm lvs and scan them
2163     # we delay this till all other devices are scanned so that
2164     # 1) the lvm filter for ignored disks is completely setup
2165     # 2) we have checked all devs for duplicate vg names
2166     if self._setupLvs():
2167     continue
2168     # nothing is changing -- we are finished building devices
2169     break
2170    
2171     log.info("devices to scan: %s" % [d['name'] for d in devices])
2172     for dev in devices:
2173     self.addUdevDevice(dev)
2174     if progressWindow:
2175     progressWindow.pulse()
2176    
2177     self.populated = True
2178    
2179     # After having the complete tree we make sure that the system
2180     # inconsistencies are ignored or resolved.
2181     self._handleInconsistencies()
2182    
2183     self.teardownAll()
2184     try:
2185     os.unlink("/etc/mdadm.conf")
2186     except OSError:
2187     log.info("failed to unlink /etc/mdadm.conf")
2188    
2189     def teardownAll(self):
2190     """ Run teardown methods on all devices. """
2191     for device in self.leaves:
2192     try:
2193     device.teardown(recursive=True)
2194     except StorageError as e:
2195     log.info("teardown of %s failed: %s" % (device.name, e))
2196    
2197     def setupAll(self):
2198     """ Run setup methods on all devices. """
2199     for device in self.leaves:
2200     try:
2201     device.setup()
2202     except DeviceError as (msg, name):
2203     log.debug("setup of %s failed: %s" % (device.name, msg))
2204    
2205     def getDeviceBySysfsPath(self, path):
2206     if not path:
2207     return None
2208    
2209     found = None
2210     for device in self._devices:
2211     if device.sysfsPath == path:
2212     found = device
2213     break
2214    
2215     return found
2216    
2217     def getDeviceByUuid(self, uuid):
2218     if not uuid:
2219     return None
2220    
2221     found = None
2222     for device in self._devices:
2223     if device.uuid == uuid:
2224     found = device
2225     break
2226     elif device.format.uuid == uuid:
2227     found = device
2228     break
2229    
2230     return found
2231    
2232     def getDevicesBySerial(self, serial):
2233     devices = []
2234     for device in self._devices:
2235     if not hasattr(device, "serial"):
2236     log.warning("device %s has no serial attr" % device.name)
2237     continue
2238     if device.serial == serial:
2239     devices.append(device)
2240     return devices
2241    
2242     def getDeviceByLabel(self, label):
2243     if not label:
2244     return None
2245    
2246     found = None
2247     for device in self._devices:
2248     _label = getattr(device.format, "label", None)
2249     if not _label:
2250     continue
2251    
2252     if _label == label:
2253     found = device
2254     break
2255    
2256     return found
2257    
2258     def getDeviceByName(self, name):
2259     log.debug("looking for device '%s'..." % name)
2260     if not name:
2261     return None
2262    
2263     found = None
2264     for device in self._devices:
2265     if device.name == name:
2266     found = device
2267     break
2268     elif (device.type == "lvmlv" or device.type == "lvmvg") and \
2269     device.name == name.replace("--","-"):
2270     found = device
2271     break
2272    
2273     log.debug("found %s" % found)
2274     return found
2275    
2276     def getDeviceByPath(self, path):
2277     log.debug("looking for device '%s'..." % path)
2278     if not path:
2279     return None
2280    
2281     found = None
2282     for device in self._devices:
2283     if device.path == path:
2284     found = device
2285     break
2286     elif (device.type == "lvmlv" or device.type == "lvmvg") and \
2287     device.path == path.replace("--","-"):
2288     found = device
2289     break
2290    
2291     log.debug("found %s" % found)
2292     return found
2293    
2294     def getDevicesByType(self, device_type):
2295     # TODO: expand this to catch device format types
2296     return [d for d in self._devices if d.type == device_type]
2297    
2298     def getDevicesByInstance(self, device_class):
2299     return [d for d in self._devices if isinstance(d, device_class)]
2300    
2301     @property
2302     def devices(self):
2303     """ List of device instances """
2304     devices = []
2305     for device in self._devices:
2306     if device.path in [d.path for d in devices] and \
2307     not isinstance(device, NoDevice):
2308     raise DeviceTreeError("duplicate paths in device tree")
2309    
2310     devices.append(device)
2311    
2312     return devices
2313    
2314     @property
2315     def filesystems(self):
2316     """ List of filesystems. """
2317     #""" Dict with mountpoint keys and filesystem values. """
2318     filesystems = []
2319     for dev in self.leaves:
2320     if dev.format and getattr(dev.format, 'mountpoint', None):
2321     filesystems.append(dev.format)
2322    
2323     return filesystems
2324    
2325     @property
2326     def uuids(self):
2327     """ Dict with uuid keys and Device values. """
2328     uuids = {}
2329     for dev in self._devices:
2330     try:
2331     uuid = dev.uuid
2332     except AttributeError:
2333     uuid = None
2334    
2335     if uuid:
2336     uuids[uuid] = dev
2337    
2338     try:
2339     uuid = dev.format.uuid
2340     except AttributeError:
2341     uuid = None
2342    
2343     if uuid:
2344     uuids[uuid] = dev
2345    
2346     return uuids
2347    
2348     @property
2349     def labels(self):
2350     """ Dict with label keys and Device values.
2351    
2352     FIXME: duplicate labels are a possibility
2353     """
2354     labels = {}
2355     for dev in self._devices:
2356     if dev.format and getattr(dev.format, "label", None):
2357     labels[dev.format.label] = dev
2358    
2359     return labels
2360    
2361     @property
2362     def leaves(self):
2363     """ List of all devices upon which no other devices exist. """
2364     leaves = [d for d in self._devices if d.isleaf]
2365     return leaves
2366    
2367     def getChildren(self, device):
2368     """ Return a list of a device's children. """
2369     return [c for c in self._devices if device in c.parents]
2370    
2371     def resolveDevice(self, devspec, blkidTab=None, cryptTab=None):
2372     # find device in the tree
2373     device = None
2374     if devspec.startswith("UUID="):
2375     # device-by-uuid
2376     uuid = devspec.partition("=")[2]
2377     device = self.uuids.get(uuid)
2378     if device is None:
2379     log.error("failed to resolve device %s" % devspec)
2380     elif devspec.startswith("LABEL="):
2381     # device-by-label
2382     label = devspec.partition("=")[2]
2383     device = self.labels.get(label)
2384     if device is None:
2385     log.error("failed to resolve device %s" % devspec)
2386     elif devspec.startswith("/dev/"):
2387     if devspec.startswith("/dev/disk/"):
2388     devspec = os.path.realpath(devspec)
2389    
2390     if devspec.startswith("/dev/dm-"):
2391     dm_name = devicelibs.dm.name_from_dm_node(devspec[5:])
2392     if dm_name:
2393     devspec = "/dev/mapper/" + dm_name
2394    
2395     # device path
2396     device = self.getDeviceByPath(devspec)
2397     if device is None:
2398     if blkidTab:
2399     # try to use the blkid.tab to correlate the device
2400     # path with a UUID
2401     blkidTabEnt = blkidTab.get(devspec)
2402     if blkidTabEnt:
2403     log.debug("found blkid.tab entry for '%s'" % devspec)
2404     uuid = blkidTabEnt.get("UUID")
2405     if uuid:
2406     device = self.getDeviceByUuid(uuid)
2407     if device:
2408     devstr = device.name
2409     else:
2410     devstr = "None"
2411     log.debug("found device '%s' in tree" % devstr)
2412     if device and device.format and \
2413     device.format.type == "luks":
2414     map_name = device.format.mapName
2415     log.debug("luks device; map name is '%s'" % map_name)
2416     mapped_dev = self.getDeviceByName(map_name)
2417     if mapped_dev:
2418     device = mapped_dev
2419    
2420     if device is None and cryptTab and \
2421     devspec.startswith("/dev/mapper/"):
2422     # try to use a dm-crypt mapping name to
2423     # obtain the underlying device, possibly
2424     # using blkid.tab
2425     cryptTabEnt = cryptTab.get(devspec.split("/")[-1])
2426     if cryptTabEnt:
2427     luks_dev = cryptTabEnt['device']
2428     try:
2429     device = self.getChildren(luks_dev)[0]
2430     except IndexError as e:
2431     pass
2432     elif device is None:
2433     # dear lvm: can we please have a few more device nodes
2434     # for each logical volume?
2435     # three just doesn't seem like enough.
2436     name = devspec[5:] # strip off leading "/dev/"
2437     (vg_name, slash, lv_name) = name.partition("/")
2438     if lv_name and not "/" in lv_name:
2439     # looks like we may have one
2440     lv = "%s-%s" % (vg_name, lv_name)
2441     device = self.getDeviceByName(lv)
2442    
2443     if device:
2444     log.debug("resolved '%s' to '%s' (%s)" % (devspec, device.name, device.type))
2445     else:
2446     log.debug("failed to resolve '%s'" % devspec)
2447     return device

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