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

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

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


Revision 1.3 - (show annotations) (download) (as text)
Sun Dec 22 04:27:51 2013 UTC (10 years, 10 months ago) by wellsi
Branch: MAIN
CVS Tags: HEAD
Changes since 1.2: +0 -0 lines
Content type: text/x-python
FILE REMOVED
anaconda updates now handled via patches

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 # 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 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