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

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

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


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

1 # __init__.py
2 # Entry point 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 time
25 import stat
26 import errno
27 import sys
28 import statvfs
29
30 import nss.nss
31 import parted
32
33 import isys
34 import iutil
35 from constants import *
36 from pykickstart.constants import *
37 from flags import flags
38
39 import storage_log
40 from errors import *
41 from devices import *
42 from devicetree import DeviceTree
43 from deviceaction import *
44 from formats import getFormat
45 from formats import get_device_format_class
46 from formats import get_default_filesystem_type
47 from devicelibs.lvm import safeLvmName
48 from devicelibs.dm import name_from_dm_node
49 from devicelibs.crypto import generateBackupPassphrase
50 from devicelibs.mpath import MultipathConfigWriter
51 from devicelibs.edd import get_edd_dict
52 from udev import *
53 import iscsi
54 import fcoe
55 import zfcp
56 import dasd
57
58 import shelve
59 import contextlib
60
61 import gettext
62 _ = lambda x: gettext.ldgettext("anaconda", x)
63
64 import logging
65 log = logging.getLogger("storage")
66
67 def storageInitialize(anaconda, examine_all=True):
68 """ Initialize the storage system
69
70 Setting examine_all to False will use clearPartType to
71 determine which disks will be used. This should be set to False
72 for kickstart and True for everything else.
73 """
74 storage = anaconda.id.storage
75 storage.shutdown()
76
77 if anaconda.dir == DISPATCH_BACK:
78 return
79
80 # touch /dev/.in_sysinit so that /lib/udev/rules.d/65-md-incremental.rules
81 # does not mess with any mdraid sets
82 open("/dev/.in_sysinit", "w")
83
84 # XXX I don't understand why I have to do this, but this is needed to
85 # populate the udev db
86 udev_trigger(subsystem="block", action="change")
87
88
89 anaconda.intf.resetInitializeDiskQuestion()
90 anaconda.intf.resetReinitInconsistentLVMQuestion()
91
92 # Set up the protected partitions list now.
93 if anaconda.protected:
94 storage.protectedDevSpecs.extend(anaconda.protected)
95 storage.reset(examine_all=examine_all)
96
97 if not flags.livecdInstall and not storage.protectedDevices:
98 if anaconda.id.getUpgrade():
99 return
100 else:
101 anaconda.intf.messageWindow(_("Unknown Device"),
102 _("The installation source given by device %s "
103 "could not be found. Please check your "
104 "parameters and try again.") % anaconda.protected,
105 type="custom", custom_buttons = [_("_Exit installer")])
106 sys.exit(1)
107 else:
108 storage.reset(examine_all=examine_all)
109
110 if not storage.disks:
111 custom_buttons=[_("_Try again"), _("_Exit installer")]
112 if anaconda.dispatch and anaconda.dispatch.canGoBack():
113 custom_buttons = [_("_Back"), _("_Exit installer")]
114 rc = anaconda.intf.messageWindow(_("No disks found"),
115 _("No usable disks have been found."),
116 type="custom",
117 custom_buttons=custom_buttons, default=0)
118 if rc == 0:
119 if anaconda.dispatch and anaconda.dispatch.canGoBack():
120 return DISPATCH_BACK
121 else:
122 return storageInitialize(anaconda)
123 sys.exit(1)
124
125 # dispatch.py helper function
126 def storageComplete(anaconda):
127 if anaconda.dir == DISPATCH_BACK:
128 rc = anaconda.intf.messageWindow(_("Installation cannot continue."),
129 _("The storage configuration you have "
130 "chosen has already been activated. You "
131 "can no longer return to the disk editing "
132 "screen. Would you like to continue with "
133 "the installation process?"),
134 type = "yesno")
135 if rc == 0:
136 sys.exit(0)
137 return DISPATCH_FORWARD
138
139 devs = anaconda.id.storage.devicetree.getDevicesByType("luks/dm-crypt")
140 existing_luks = False
141 new_luks = False
142 for dev in devs:
143 if dev.exists:
144 existing_luks = True
145 else:
146 new_luks = True
147
148 if (anaconda.id.storage.encryptedAutoPart or new_luks) and \
149 not anaconda.id.storage.encryptionPassphrase:
150 while True:
151 (passphrase, retrofit) = anaconda.intf.getLuksPassphrase(preexist=existing_luks)
152 if passphrase:
153 anaconda.id.storage.encryptionPassphrase = passphrase
154 anaconda.id.storage.encryptionRetrofit = retrofit
155 break
156 else:
157 rc = anaconda.intf.messageWindow(_("Encrypt device?"),
158 _("You specified block device encryption "
159 "should be enabled, but you have not "
160 "supplied a passphrase. If you do not "
161 "go back and provide a passphrase, "
162 "block device encryption will be "
163 "disabled."),
164 type="custom",
165 custom_buttons=[_("Back"), _("Continue")],
166 default=0)
167 if rc == 1:
168 log.info("user elected to not encrypt any devices.")
169 undoEncryption(anaconda.id.storage)
170 anaconda.id.storage.encryptedAutoPart = False
171 break
172
173 if anaconda.id.storage.encryptionPassphrase:
174 for dev in anaconda.id.storage.devices:
175 if dev.format.type == "luks" and not dev.format.exists and \
176 not dev.format.hasKey:
177 dev.format.passphrase = anaconda.id.storage.encryptionPassphrase
178
179 map(lambda d: anaconda.id.storage.services.update(d.services),
180 anaconda.id.storage.fsset.devices)
181
182 if anaconda.isKickstart:
183 return
184
185 # Warn the user if they are trying to boot a GPT disk on a non-EFI system
186 # This may or may not work -- we have no way to tell, so just warn them
187 bootdisk = anaconda.id.bootloader.drivelist[0]
188 bootdisk = anaconda.id.storage.devicetree.getDeviceByName(bootdisk)
189
190 if not iutil.isEfi() and bootdisk and bootdisk.format \
191 and bootdisk.format.type == 'disklabel' \
192 and bootdisk.format.labelType == 'gpt':
193 warning = _("\n\nWARNING:\n"
194 "You are using a GPT bootdisk on a non-EFI "
195 "system. This may not work, depending on your BIOS's "
196 "support for booting from GPT disks.")
197 log.warning("Using a GPT bootdisk on non-EFI system")
198 else:
199 warning = ""
200
201 # Prevent users from installing on s390x with (a) no /boot volume, (b) the
202 # root volume on LVM, and (c) the root volume not restricted to a single
203 # PV
204 # NOTE: There is not really a way for users to create a / volume
205 # restricted to a single PV. The backend support is there, but there are
206 # no UI hook-ups to drive that functionality, but I do not personally
207 # care. --dcantrell
208 if iutil.isS390() and \
209 not anaconda.id.storage.mountpoints.has_key('/boot') and \
210 anaconda.id.storage.mountpoints['/'].type == 'lvmlv' and \
211 not anaconda.id.storage.mountpoints['/'].singlePV:
212 rc = anaconda.intf.messageWindow(_("Missing /boot Volume"),
213 _("This platform requires /boot on "
214 "a dedicated partition or logical "
215 "volume. If you do not want a "
216 "/boot volume, you must place / "
217 "on a dedicated non-LVM "
218 "partition."),
219 type="custom", custom_icon="error",
220 custom_buttons=[_("Go _back"),
221 _("_Exit installer")],
222 default=0)
223 if rc == 0:
224 return DISPATCH_BACK
225 sys.exit(1)
226
227 rc = anaconda.intf.messageWindow(_("Writing storage configuration to disk"),
228 _("This option performs a new install of "
229 "SME Server. All hard drives, including "
230 "removable media, will be re-partitioned and formatted. Proceed? "
231 "%s") % (warning),
232 type = "custom", custom_icon="warning",
233 custom_buttons=[_("Go _back"),
234 _("_Write changes to disk")],
235 default = 0)
236
237 # Make sure that all is down, even the disks that we setup after popluate.
238 anaconda.id.storage.devicetree.teardownAll()
239
240 if rc == 0:
241 return DISPATCH_BACK
242
243 def writeEscrowPackets(anaconda):
244 escrowDevices = filter(lambda d: d.format.type == "luks" and \
245 d.format.escrow_cert,
246 anaconda.id.storage.devices)
247
248 if not escrowDevices:
249 return
250
251 log.debug("escrow: writeEscrowPackets start")
252
253 wait_win = anaconda.intf.waitWindow(_("Running..."),
254 _("Storing encryption keys"))
255
256 nss.nss.nss_init_nodb() # Does nothing if NSS is already initialized
257
258 backupPassphrase = generateBackupPassphrase()
259 try:
260 for device in escrowDevices:
261 log.debug("escrow: device %s: %s" %
262 (repr(device.path), repr(device.format.type)))
263 device.format.escrow(anaconda.rootPath + "/root",
264 backupPassphrase)
265
266 wait_win.pop()
267 except (IOError, RuntimeError) as e:
268 wait_win.pop()
269 anaconda.intf.messageWindow(_("Error"),
270 _("Error storing an encryption key: "
271 "%s\n") % str(e), type="custom",
272 custom_icon="error",
273 custom_buttons=[_("_Exit installer")])
274 sys.exit(1)
275
276 log.debug("escrow: writeEscrowPackets done")
277
278
279 def undoEncryption(storage):
280 for device in storage.devicetree.getDevicesByType("luks/dm-crypt"):
281 if device.exists:
282 continue
283
284 slave = device.slave
285 format = device.format
286
287 # set any devices that depended on the luks device to now depend on
288 # the former slave device
289 for child in storage.devicetree.getChildren(device):
290 child.parents.remove(device)
291 device.removeChild()
292 child.parents.append(slave)
293
294 storage.devicetree.registerAction(ActionDestroyFormat(device))
295 storage.devicetree.registerAction(ActionDestroyDevice(device))
296 storage.devicetree.registerAction(ActionDestroyFormat(slave))
297 storage.devicetree.registerAction(ActionCreateFormat(slave, format))
298
299 class Storage(object):
300 def __init__(self, anaconda):
301 self.anaconda = anaconda
302
303 # storage configuration variables
304 self.ignoreDiskInteractive = False
305 self.ignoredDisks = []
306 self.exclusiveDisks = []
307 self.doAutoPart = False
308 self.clearPartType = None
309 self.clearPartDisks = []
310 self.clearPartChoice = None
311 self.encryptedAutoPart = False
312 self.encryptionPassphrase = None
313 self.encryptionCipher = None
314 self.autoPartEscrowCert = None
315 self.autoPartAddBackupPassphrase = False
316 self.encryptionRetrofit = False
317 self.reinitializeDisks = False
318 self.zeroMbr = None
319 self.protectedDevSpecs = []
320 self.autoPartitionRequests = []
321 self.eddDict = {}
322 self.mpathFriendlyNames = True
323
324 self.__luksDevs = {}
325
326 self.iscsi = iscsi.iscsi()
327 self.fcoe = fcoe.fcoe()
328 self.zfcp = zfcp.ZFCP()
329 self.dasd = dasd.DASD()
330
331 self._nextID = 0
332 self.defaultFSType = get_default_filesystem_type()
333 self.defaultBootFSType = get_default_filesystem_type(boot=True)
334 self._dumpFile = "/tmp/storage.state"
335
336 # these will both be empty until our reset method gets called
337 self.devicetree = DeviceTree(intf=self.anaconda.intf,
338 ignored=self.ignoredDisks,
339 exclusive=self.exclusiveDisks,
340 type=self.clearPartType,
341 clear=self.clearPartDisks,
342 reinitializeDisks=self.reinitializeDisks,
343 protected=self.protectedDevSpecs,
344 zeroMbr=self.zeroMbr,
345 passphrase=self.encryptionPassphrase,
346 luksDict=self.__luksDevs,
347 iscsi=self.iscsi,
348 dasd=self.dasd,
349 mpathFriendlyNames=self.mpathFriendlyNames)
350 self.fsset = FSSet(self.devicetree, self.anaconda.rootPath)
351 self.services = set()
352
353 def doIt(self):
354 self.devicetree.processActions()
355 self.doEncryptionPassphraseRetrofits()
356
357 # now set the boot partition's flag
358 try:
359 boot = self.anaconda.platform.bootDevice()
360 if boot.type == "mdarray":
361 bootDevs = boot.parents
362 else:
363 bootDevs = [boot]
364 except DeviceError:
365 bootDevs = []
366 else:
367 for dev in bootDevs:
368 if hasattr(dev, "bootable"):
369 # Dos labels can only have one partition marked as active
370 # and unmarking ie the windows partition is not a good idea
371 skip = False
372 if dev.disk.format.partedDisk.type == "msdos":
373 for p in dev.disk.format.partedDisk.partitions:
374 if p.type == parted.PARTITION_NORMAL and \
375 p.getFlag(parted.PARTITION_BOOT):
376 skip = True
377 break
378 if skip:
379 log.info("not setting boot flag on %s as there is"
380 "another active partition" % dev.name)
381 continue
382 log.info("setting boot flag on %s" % dev.name)
383 dev.bootable = True
384 dev.disk.setup()
385 dev.disk.format.commitToDisk()
386
387 self.dumpState("final")
388
389 @property
390 def nextID(self):
391 id = self._nextID
392 self._nextID += 1
393 return id
394
395 def shutdown(self):
396 try:
397 self.devicetree.teardownAll()
398 except Exception as e:
399 log.error("failure tearing down device tree: %s" % e)
400
401 # TODO: iscsi.shutdown()
402
403 def reset(self, examine_all=False):
404 """ Reset storage configuration to reflect actual system state.
405
406 Setting examine_all will cause it to examine all devices regardless
407 of the clearPartType setting which could cause it to skip some
408 devices. This is useful when looking for existing partitions to be
409 upgraded.
410
411 This should rescan from scratch but not clobber user-obtained
412 information like passphrases, iscsi config, &c
413
414 """
415 # save passphrases for luks devices so we don't have to reprompt
416 self.encryptionPassphrase = None
417 for device in self.devices:
418 if device.format.type == "luks" and device.format.exists:
419 self.__luksDevs[device.format.uuid] = device.format._LUKS__passphrase
420
421 prog = self.anaconda.intf.progressWindow(_("Examining Devices"),
422 _("Examining storage devices"),
423 100, 0.03, pulse=True)
424 self.iscsi.startup(self.anaconda.intf)
425 self.fcoe.startup(self.anaconda.intf)
426 self.zfcp.startup(self.anaconda.intf)
427 self.dasd.startup(self.anaconda.intf, self.exclusiveDisks, self.zeroMbr)
428 if examine_all:
429 clearPartType = CLEARPART_TYPE_NONE
430 else:
431 clearPartType = self.clearPartType
432
433 if self.dasd:
434 # Reset the internal dasd list (823534)
435 self.dasd.clear_device_list()
436
437 self.devicetree = DeviceTree(intf=self.anaconda.intf,
438 ignored=self.ignoredDisks,
439 exclusive=self.exclusiveDisks,
440 type=clearPartType,
441 clear=self.clearPartDisks,
442 reinitializeDisks=self.reinitializeDisks,
443 protected=self.protectedDevSpecs,
444 zeroMbr=self.zeroMbr,
445 passphrase=self.encryptionPassphrase,
446 luksDict=self.__luksDevs,
447 iscsi=self.iscsi,
448 dasd=self.dasd,
449 mpathFriendlyNames=self.mpathFriendlyNames)
450 self.devicetree.populate(prog)
451 self.fsset = FSSet(self.devicetree, self.anaconda.rootPath)
452 self.eddDict = get_edd_dict(self.partitioned)
453 self.anaconda.id.rootParts = None
454 self.anaconda.id.upgradeRoot = None
455 self.dumpState("initial")
456 prog.pop()
457
458 @property
459 def devices(self):
460 """ A list of all the devices in the device tree. """
461 devices = self.devicetree.devices
462 devices.sort(key=lambda d: d.name)
463 return devices
464
465 @property
466 def disks(self):
467 """ A list of the disks in the device tree.
468
469 Ignored disks are not included, as are disks with no media present.
470
471 This is based on the current state of the device tree and
472 does not necessarily reflect the actual on-disk state of the
473 system's disks.
474 """
475 disks = []
476 for device in self.devicetree.devices:
477 if device.isDisk:
478 if not device.mediaPresent:
479 log.info("Skipping disk: %s: No media present" % device.name)
480 continue
481 disks.append(device)
482 disks.sort(key=lambda d: d.name, cmp=self.compareDisks)
483 return disks
484
485 @property
486 def partitioned(self):
487 """ A list of the partitioned devices in the device tree.
488
489 Ignored devices are not included, nor disks with no media present.
490
491 Devices of types for which partitioning is not supported are also
492 not included.
493
494 This is based on the current state of the device tree and
495 does not necessarily reflect the actual on-disk state of the
496 system's disks.
497 """
498 partitioned = []
499 for device in self.devicetree.devices:
500 if not device.partitioned:
501 continue
502
503 if not device.mediaPresent:
504 log.info("Skipping device: %s: No media present" % device.name)
505 continue
506
507 partitioned.append(device)
508
509 partitioned.sort(key=lambda d: d.name)
510 return partitioned
511
512 @property
513 def partitions(self):
514 """ A list of the partitions in the device tree.
515
516 This is based on the current state of the device tree and
517 does not necessarily reflect the actual on-disk state of the
518 system's disks.
519 """
520 partitions = self.devicetree.getDevicesByInstance(PartitionDevice)
521 partitions.sort(key=lambda d: d.name)
522 return partitions
523
524 @property
525 def vgs(self):
526 """ A list of the LVM Volume Groups in the device tree.
527
528 This is based on the current state of the device tree and
529 does not necessarily reflect the actual on-disk state of the
530 system's disks.
531 """
532 vgs = self.devicetree.getDevicesByType("lvmvg")
533 vgs.sort(key=lambda d: d.name)
534 return vgs
535
536 @property
537 def lvs(self):
538 """ A list of the LVM Logical Volumes in the device tree.
539
540 This is based on the current state of the device tree and
541 does not necessarily reflect the actual on-disk state of the
542 system's disks.
543 """
544 lvs = self.devicetree.getDevicesByType("lvmlv")
545 lvs.sort(key=lambda d: d.name)
546 return lvs
547
548 @property
549 def pvs(self):
550 """ A list of the LVM Physical Volumes in the device tree.
551
552 This is based on the current state of the device tree and
553 does not necessarily reflect the actual on-disk state of the
554 system's disks.
555 """
556 devices = self.devicetree.devices
557 pvs = [d for d in devices if d.format.type == "lvmpv"]
558 pvs.sort(key=lambda d: d.name)
559 return pvs
560
561 def unusedPVs(self, vg=None):
562 unused = []
563 for pv in self.pvs:
564 used = False
565 for _vg in self.vgs:
566 if _vg.dependsOn(pv) and _vg != vg:
567 used = True
568 break
569 elif _vg == vg:
570 break
571 if not used:
572 unused.append(pv)
573 return unused
574
575 @property
576 def mdarrays(self):
577 """ A list of the MD arrays in the device tree.
578
579 This is based on the current state of the device tree and
580 does not necessarily reflect the actual on-disk state of the
581 system's disks.
582 """
583 arrays = self.devicetree.getDevicesByType("mdarray")
584 arrays.sort(key=lambda d: d.name)
585 return arrays
586
587 @property
588 def mdcontainers(self):
589 """ A list of the MD containers in the device tree. """
590 arrays = self.devicetree.getDevicesByType("mdcontainer")
591 arrays.sort(key=lambda d: d.name)
592 return arrays
593
594 @property
595 def mdmembers(self):
596 """ A list of the MD member devices in the device tree.
597
598 This is based on the current state of the device tree and
599 does not necessarily reflect the actual on-disk state of the
600 system's disks.
601 """
602 devices = self.devicetree.devices
603 members = [d for d in devices if d.format.type == "mdmember"]
604 members.sort(key=lambda d: d.name)
605 return members
606
607 def unusedMDMembers(self, array=None):
608 unused = []
609 for member in self.mdmembers:
610 used = False
611 for _array in self.mdarrays + self.mdcontainers:
612 if _array.dependsOn(member) and _array != array:
613 used = True
614 break
615 elif _array == array:
616 break
617 if not used:
618 unused.append(member)
619 return unused
620
621 @property
622 def unusedMDMinors(self):
623 """ Return a list of unused minors for use in RAID. """
624 raidMinors = range(0,32)
625 for array in self.mdarrays + self.mdcontainers:
626 if array.minor is not None and array.minor in raidMinors:
627 raidMinors.remove(array.minor)
628 return raidMinors
629
630 @property
631 def swaps(self):
632 """ A list of the swap devices in the device tree.
633
634 This is based on the current state of the device tree and
635 does not necessarily reflect the actual on-disk state of the
636 system's disks.
637 """
638 devices = self.devicetree.devices
639 swaps = [d for d in devices if d.format.type == "swap"]
640 swaps.sort(key=lambda d: d.name)
641 return swaps
642
643 @property
644 def protectedDevices(self):
645 devices = self.devicetree.devices
646 protected = [d for d in devices if d.protected]
647 protected.sort(key=lambda d: d.name)
648 return protected
649
650 def exceptionDisks(self):
651 """ Return a list of removable devices to save exceptions to.
652
653 FIXME: This raises the problem that the device tree can be
654 in a state that does not reflect that actual current
655 state of the system at any given point.
656
657 We need a way to provide direct scanning of disks,
658 partitions, and filesystems without relying on the
659 larger objects' correctness.
660
661 Also, we need to find devices that have just been made
662 available for the purpose of storing the exception
663 report.
664 """
665 # When a usb is connected from before the start of the installation,
666 # it is not correctly detected.
667 udev_trigger(subsystem="block", action="change")
668 self.reset()
669
670 dests = []
671
672 for disk in self.disks:
673 if not disk.removable and \
674 disk.format is not None and \
675 disk.format.mountable:
676 dests.append([disk.path, disk.name])
677
678 for part in self.partitions:
679 if not part.disk.removable:
680 continue
681
682 elif part.partedPartition.active and \
683 not part.partedPartition.getFlag(parted.PARTITION_RAID) and \
684 not part.partedPartition.getFlag(parted.PARTITION_LVM) and \
685 part.format is not None and part.format.mountable:
686 dests.append([part.path, part.name])
687
688 return dests
689
690 def deviceImmutable(self, device, ignoreProtected=False):
691 """ Return any reason the device cannot be modified/removed.
692
693 Return False if the device can be removed.
694
695 Devices that cannot be removed include:
696
697 - protected partitions
698 - devices that are part of an md array or lvm vg
699 - extended partition containing logical partitions that
700 meet any of the above criteria
701
702 """
703 if not isinstance(device, Device):
704 raise ValueError("arg1 (%s) must be a Device instance" % device)
705
706 if not ignoreProtected and device.protected:
707 return _("This partition is holding the data for the hard "
708 "drive install.")
709 elif isinstance(device, PartitionDevice) and device.isProtected:
710 # LDL formatted DASDs always have one partition, you'd have to
711 # reformat the DASD in CDL mode to get rid of it
712 return _("You cannot delete a partition of a LDL formatted "
713 "DASD.")
714 elif device.format.type == "mdmember":
715 for array in self.mdarrays + self.mdcontainers:
716 if array.dependsOn(device):
717 if array.minor is not None:
718 return _("This device is part of the RAID "
719 "device %s.") % (array.path,)
720 else:
721 return _("This device is part of a RAID device.")
722 elif device.format.type == "lvmpv":
723 for vg in self.vgs:
724 if vg.dependsOn(device):
725 if vg.name is not None:
726 return _("This device is part of the LVM "
727 "volume group '%s'.") % (vg.name,)
728 else:
729 return _("This device is part of a LVM volume "
730 "group.")
731 elif device.format.type == "luks":
732 try:
733 luksdev = self.devicetree.getChildren(device)[0]
734 except IndexError:
735 pass
736 else:
737 return self.deviceImmutable(luksdev)
738 elif isinstance(device, PartitionDevice) and device.isExtended:
739 reasons = {}
740 for dep in self.deviceDeps(device):
741 reason = self.deviceImmutable(dep)
742 if reason:
743 reasons[dep.path] = reason
744 if reasons:
745 msg = _("This device is an extended partition which "
746 "contains logical partitions that cannot be "
747 "deleted:\n\n")
748 for dev in reasons:
749 msg += "%s: %s" % (dev, reasons[dev])
750 return msg
751
752 if device.immutable:
753 return device.immutable
754
755 return False
756
757 def deviceDeps(self, device):
758 return self.devicetree.getDependentDevices(device)
759
760 def newPartition(self, *args, **kwargs):
761 """ Return a new PartitionDevice instance for configuring. """
762 if kwargs.has_key("fmt_type"):
763 kwargs["format"] = getFormat(kwargs.pop("fmt_type"),
764 mountpoint=kwargs.pop("mountpoint",
765 None),
766 **kwargs.pop("fmt_args", {}))
767
768 if kwargs.has_key("disks"):
769 parents = kwargs.pop("disks")
770 if isinstance(parents, Device):
771 kwargs["parents"] = [parents]
772 else:
773 kwargs["parents"] = parents
774
775 if kwargs.has_key("name"):
776 name = kwargs.pop("name")
777 else:
778 name = "req%d" % self.nextID
779
780 return PartitionDevice(name, *args, **kwargs)
781
782 def newMDArray(self, *args, **kwargs):
783 """ Return a new MDRaidArrayDevice instance for configuring. """
784 if kwargs.has_key("fmt_type"):
785 kwargs["format"] = getFormat(kwargs.pop("fmt_type"),
786 mountpoint=kwargs.pop("mountpoint",
787 None))
788
789 if kwargs.has_key("minor"):
790 kwargs["minor"] = int(kwargs["minor"])
791 else:
792 kwargs["minor"] = self.unusedMDMinors[0]
793
794 if kwargs.has_key("name"):
795 name = kwargs.pop("name")
796 else:
797 name = "md%d" % kwargs["minor"]
798
799 return MDRaidArrayDevice(name, *args, **kwargs)
800
801 def newVG(self, *args, **kwargs):
802 """ Return a new LVMVolumeGroupDevice instance. """
803 pvs = kwargs.pop("pvs", [])
804 for pv in pvs:
805 if pv not in self.devices:
806 raise ValueError("pv is not in the device tree")
807
808 if kwargs.has_key("name"):
809 name = kwargs.pop("name")
810 safe_name = safeLvmName(name)
811 if safe_name != name:
812 log.warning("using '%s' instead of specified name '%s'"
813 % (safe_name, name))
814 name = safe_name
815 else:
816 name = self.createSuggestedVGName(self.anaconda.id.network)
817
818 if name in [d.name for d in self.devices]:
819 raise ValueError("name already in use")
820
821 return LVMVolumeGroupDevice(name, pvs, *args, **kwargs)
822
823 def newLV(self, *args, **kwargs):
824 """ Return a new LVMLogicalVolumeDevice instance. """
825 if kwargs.has_key("vg"):
826 vg = kwargs.pop("vg")
827
828 mountpoint = kwargs.pop("mountpoint", None)
829 if kwargs.has_key("fmt_type"):
830 kwargs["format"] = getFormat(kwargs.pop("fmt_type"),
831 mountpoint=mountpoint)
832
833 if kwargs.has_key("name"):
834 name = kwargs.pop("name")
835 # make sure the specified name is sensible
836 safe_name = safeLvmName(name)
837 if safe_name != name:
838 log.warning("using '%s' instead of specified name '%s'"
839 % (safe_name, name))
840 name = safe_name
841 else:
842 if kwargs.get("format") and kwargs["format"].type == "swap":
843 swap = True
844 else:
845 swap = False
846 name = self.createSuggestedLVName(vg,
847 swap=swap,
848 mountpoint=mountpoint)
849
850 if name in [d.name for d in self.devices]:
851 raise ValueError("name already in use")
852
853 return LVMLogicalVolumeDevice(name, vg, *args, **kwargs)
854
855 def createDevice(self, device):
856 """ Schedule creation of a device.
857
858 TODO: We could do some things here like assign the next
859 available raid minor if one isn't already set.
860 """
861 self.devicetree.registerAction(ActionCreateDevice(device))
862 if device.format.type:
863 self.devicetree.registerAction(ActionCreateFormat(device))
864
865 def destroyDevice(self, device):
866 """ Schedule destruction of a device. """
867 if device.format.exists and device.format.type:
868 # schedule destruction of any formatting while we're at it
869 self.devicetree.registerAction(ActionDestroyFormat(device))
870
871 action = ActionDestroyDevice(device)
872 self.devicetree.registerAction(action)
873
874 def formatDevice(self, device, format):
875 """ Schedule formatting of a device. """
876 self.devicetree.registerAction(ActionDestroyFormat(device))
877 self.devicetree.registerAction(ActionCreateFormat(device, format))
878
879 def formatByDefault(self, device):
880 """Return whether the device should be reformatted by default."""
881 formatlist = ['/boot', '/var', '/tmp', '/usr']
882 exceptlist = ['/home', '/usr/local', '/opt', '/var/www']
883
884 if not device.format.linuxNative:
885 return False
886
887 if device.format.mountable:
888 if not device.format.mountpoint:
889 return False
890
891 if device.format.mountpoint == "/" or \
892 device.format.mountpoint in formatlist:
893 return True
894
895 for p in formatlist:
896 if device.format.mountpoint.startswith(p):
897 for q in exceptlist:
898 if device.format.mountpoint.startswith(q):
899 return False
900 return True
901 elif device.format.type == "swap":
902 return True
903
904 # be safe for anything else and default to off
905 return False
906
907 def extendedPartitionsSupported(self):
908 """ Return whether any disks support extended partitions."""
909 for disk in self.partitioned:
910 if disk.format.partedDisk.supportsFeature(parted.DISK_TYPE_EXTENDED):
911 return True
912 return False
913
914 def createSuggestedVGName(self, network):
915 """ Return a reasonable, unused VG name. """
916 # try to create a volume group name incorporating the hostname
917 hn = network.hostname
918 vgnames = [vg.name for vg in self.vgs]
919 if hn is not None and hn != '':
920 if hn == 'localhost' or hn == 'localhost.localdomain':
921 vgtemplate = "main"
922 elif hn.find('.') != -1:
923 template = "vg_%s" % (hn.split('.')[0].lower(),)
924 vgtemplate = safeLvmName(template)
925 else:
926 template = "vg_%s" % (hn.lower(),)
927 vgtemplate = safeLvmName(template)
928 else:
929 vgtemplate = "main"
930
931 if vgtemplate not in vgnames and \
932 vgtemplate not in lvm.lvm_vg_blacklist:
933 return vgtemplate
934 else:
935 i = 0
936 while 1:
937 tmpname = "%s%02d" % (vgtemplate, i,)
938 if not tmpname in vgnames and \
939 tmpname not in lvm.lvm_vg_blacklist:
940 break
941
942 i += 1
943 if i > 99:
944 tmpname = ""
945
946 return tmpname
947
948 def createSuggestedLVName(self, vg, swap=None, mountpoint=None):
949 """ Return a suitable, unused name for a new logical volume. """
950 # FIXME: this is not at all guaranteed to work
951 if mountpoint:
952 # try to incorporate the mountpoint into the name
953 if mountpoint == '/':
954 lvtemplate = 'root'
955 else:
956 if mountpoint.startswith("/"):
957 template = "%s" % mountpoint[1:]
958 else:
959 template = "%s" % (mountpoint,)
960
961 lvtemplate = safeLvmName(template)
962 else:
963 if swap:
964 if len([s for s in self.swaps if s in vg.lvs]):
965 idx = len([s for s in self.swaps if s in vg.lvs])
966 while True:
967 lvtemplate = "swap%02d" % idx
968 if lvtemplate in [lv.lvname for lv in vg.lvs]:
969 idx += 1
970 else:
971 break
972 else:
973 lvtemplate = "swap"
974 else:
975 idx = len(vg.lvs)
976 while True:
977 lvtemplate = "LogVol%02d" % idx
978 if lvtemplate in [l.lvname for l in vg.lvs]:
979 idx += 1
980 else:
981 break
982
983 return lvtemplate
984
985 def doEncryptionPassphraseRetrofits(self):
986 """ Add the global passphrase to all preexisting LUKS devices.
987
988 This establishes a common passphrase for all encrypted devices
989 in the system so that users only have to enter one passphrase
990 during system boot.
991 """
992 if not self.encryptionRetrofit:
993 return
994
995 for device in self.devices:
996 if device.format.type == "luks" and \
997 device.format._LUKS__passphrase != self.encryptionPassphrase:
998 log.info("adding new passphrase to preexisting encrypted "
999 "device %s" % device.path)
1000 try:
1001 device.format.addPassphrase(self.encryptionPassphrase)
1002 except CryptoError:
1003 log.error("failed to add new passphrase to existing "
1004 "device %s" % device.path)
1005
1006 def sanityCheck(self):
1007 """ Run a series of tests to verify the storage configuration.
1008
1009 This function is called at the end of partitioning so that
1010 we can make sure you don't have anything silly (like no /,
1011 a really small /, etc). Returns (errors, warnings) where
1012 each is a list of strings.
1013 """
1014 checkSizes = [('/usr', 250), ('/tmp', 50), ('/var', 384),
1015 ('/home', 100), ('/boot', 75)]
1016 warnings = []
1017 errors = []
1018
1019 mustbeonlinuxfs = ['/', '/var', '/tmp', '/usr', '/home', '/usr/share', '/usr/lib']
1020 mustbeonroot = ['/bin','/dev','/sbin','/etc','/lib','/root', '/mnt', 'lost+found', '/proc']
1021
1022 filesystems = self.mountpoints
1023 root = self.fsset.rootDevice
1024 swaps = self.fsset.swapDevices
1025 try:
1026 boot = self.anaconda.platform.bootDevice()
1027 except DeviceError:
1028 boot = None
1029
1030 if not root:
1031 errors.append(_("You have not defined a root partition (/), "
1032 "which is required for installation of %s "
1033 "to continue.") % (productName,))
1034
1035 if root and root.size < 250:
1036 warnings.append(_("Your root partition is less than 250 "
1037 "megabytes which is usually too small to "
1038 "install %s.") % (productName,))
1039
1040 if (root and
1041 root.size < self.anaconda.backend.getMinimumSizeMB("/")):
1042 errors.append(_("Your / partition is less than %(min)s "
1043 "MB which is lower than recommended "
1044 "for a normal %(productName)s install.")
1045 % {'min': self.anaconda.backend.getMinimumSizeMB("/"),
1046 'productName': productName})
1047
1048 if root and root.format.type == "xfs":
1049 errors.append(_("Placing the root partition on an XFS "
1050 "filesystem is not supported in %s.") %
1051 productName)
1052
1053 # livecds have to have the rootfs type match up
1054 if (root and
1055 self.anaconda.backend.rootFsType and
1056 root.format.type != self.anaconda.backend.rootFsType):
1057 errors.append(_("Your / partition does not match the "
1058 "the live image you are installing from. "
1059 "It must be formatted as %s.")
1060 % (self.anaconda.backend.rootFsType,))
1061
1062 for (mount, size) in checkSizes:
1063 if mount in filesystems and filesystems[mount].size < size:
1064 warnings.append(_("Your %(mount)s partition is less than "
1065 "%(size)s megabytes which is lower than "
1066 "recommended for a normal %(productName)s "
1067 "install.")
1068 % {'mount': mount, 'size': size,
1069 'productName': productName})
1070
1071 for (mount, device) in filesystems.items():
1072 problem = filesystems[mount].checkSize()
1073 if problem:
1074 errors.append(_("Your %s partition is too %s for %s formatting "
1075 "(allowable size is %d MB to %d MB)")
1076 % (mount, problem, device.format.name,
1077 device.minSize, device.maxSize))
1078
1079 usb_disks = []
1080 firewire_disks = []
1081 for disk in self.disks:
1082 if isys.driveUsesModule(disk.name, ["usb-storage", "ub"]):
1083 usb_disks.append(disk)
1084 elif isys.driveUsesModule(disk.name, ["sbp2", "firewire-sbp2"]):
1085 firewire_disks.append(disk)
1086
1087 uses_usb = False
1088 uses_firewire = False
1089 for device in filesystems.values():
1090 for disk in usb_disks:
1091 if device.dependsOn(disk):
1092 uses_usb = True
1093 break
1094
1095 for disk in firewire_disks:
1096 if device.dependsOn(disk):
1097 uses_firewire = True
1098 break
1099
1100 if uses_usb:
1101 warnings.append(_("Installing on a USB device. This may "
1102 "or may not produce a working system."))
1103 if uses_firewire:
1104 warnings.append(_("Installing on a FireWire device. This may "
1105 "or may not produce a working system."))
1106
1107 errors.extend(self.anaconda.platform.checkBootRequest(boot))
1108
1109 if not swaps:
1110 if iutil.memInstalled() < isys.EARLY_SWAP_RAM:
1111 errors.append(_("You have not specified a swap partition. "
1112 "Due to the amount of memory present, a "
1113 "swap partition is required to complete "
1114 "installation."))
1115 else:
1116 warnings.append(_("You have not specified a swap partition. "
1117 "Although not strictly required in all cases, "
1118 "it will significantly improve performance "
1119 "for most installations."))
1120 no_uuid = [s for s in swaps if s.format.exists and not s.format.uuid]
1121 if no_uuid:
1122 warnings.append(_("At least one of your swap devices does not have "
1123 "a UUID, which is common in swap space created "
1124 "using older versions of mkswap. These devices "
1125 "will be referred to by device path in "
1126 "/etc/fstab, which is not ideal since device "
1127 "paths can change under a variety of "
1128 "circumstances. "))
1129
1130 for (mountpoint, dev) in filesystems.items():
1131 if mountpoint in mustbeonroot:
1132 errors.append(_("This mount point is invalid. The %s directory must "
1133 "be on the / file system.") % mountpoint)
1134
1135 if mountpoint in mustbeonlinuxfs and (not dev.format.mountable or not dev.format.linuxNative):
1136 errors.append(_("The mount point %s must be on a linux file system.") % mountpoint)
1137
1138 return (errors, warnings)
1139
1140 def isProtected(self, device):
1141 """ Return True is the device is protected. """
1142 return device.protected
1143
1144 def checkNoDisks(self):
1145 """Check that there are valid disk devices."""
1146 if not self.disks:
1147 self.anaconda.intf.messageWindow(_("No Drives Found"),
1148 _("An error has occurred - no valid devices were "
1149 "found on which to create new file systems. "
1150 "Please check your hardware for the cause "
1151 "of this problem."))
1152 return True
1153 return False
1154
1155 def dumpState(self, suffix):
1156 """ Dump the current device list to the storage shelf. """
1157 key = "devices.%d.%s" % (time.time(), suffix)
1158 with contextlib.closing(shelve.open(self._dumpFile)) as shelf:
1159 shelf[key] = [d.dict for d in self.devices]
1160
1161 def write(self, instPath):
1162 self.fsset.write(instPath)
1163 self.iscsi.write(instPath, self.anaconda)
1164 self.fcoe.write(instPath, self.anaconda)
1165 self.zfcp.write(instPath)
1166 self.dasd.write(instPath)
1167
1168 def writeKS(self, f):
1169 def useExisting(lst):
1170 foundCreateDevice = False
1171 foundCreateFormat = False
1172
1173 for l in lst:
1174 if isinstance(l, ActionCreateDevice):
1175 foundCreateDevice = True
1176 elif isinstance(l, ActionCreateFormat):
1177 foundCreateFormat = True
1178
1179 return (foundCreateFormat and not foundCreateDevice)
1180
1181 log.warning("Storage.writeKS not completely implemented")
1182 f.write("# The following is the partition information you requested\n")
1183 f.write("# Note that any partitions you deleted are not expressed\n")
1184 f.write("# here so unless you clear all partitions first, this is\n")
1185 f.write("# not guaranteed to work\n")
1186
1187 # clearpart
1188 if self.clearPartType is None or self.clearPartType == CLEARPART_TYPE_NONE:
1189 args = ["--none"]
1190 elif self.clearPartType == CLEARPART_TYPE_LINUX:
1191 args = ["--linux"]
1192 else:
1193 args = ["--all"]
1194
1195 if self.clearPartDisks:
1196 args += ["--drives=%s" % ",".join(self.clearPartDisks)]
1197 if self.reinitializeDisks:
1198 args += ["--initlabel"]
1199
1200 f.write("#clearpart %s\n" % " ".join(args))
1201
1202 # ignoredisks
1203 if self.ignoreDiskInteractive:
1204 f.write("#ignoredisk --interactive\n")
1205 elif self.ignoredDisks:
1206 f.write("#ignoredisk --drives=%s\n" % ",".join(self.ignoredDisks))
1207 elif self.exclusiveDisks:
1208 f.write("#ignoredisk --only-use=%s\n" % ",".join(self.exclusiveDisks))
1209
1210 # the various partitioning commands
1211 dict = {}
1212 actions = filter(lambda x: x.device.format.type != "luks",
1213 self.devicetree.findActions(type="create"))
1214
1215 for action in actions:
1216 if dict.has_key(action.device.path):
1217 dict[action.device.path].append(action)
1218 else:
1219 dict[action.device.path] = [action]
1220
1221 for device in [d for d in self.devices if d.format.type != "luks"]:
1222 # If there's no action for the given device, it must be one
1223 # we are reusing.
1224 if not dict.has_key(device.path):
1225 noformat = True
1226 preexisting = True
1227 else:
1228 noformat = False
1229 preexisting = useExisting(dict[device.path])
1230
1231 device.writeKS(f, preexisting=preexisting, noformat=noformat)
1232 f.write("\n")
1233
1234 self.iscsi.writeKS(f)
1235 self.fcoe.writeKS(f)
1236 self.zfcp.writeKS(f)
1237 f.write("\n")
1238
1239 def turnOnSwap(self, upgrading=None):
1240 self.fsset.turnOnSwap(self.anaconda, upgrading=upgrading)
1241
1242 def mountFilesystems(self, raiseErrors=None, readOnly=None, skipRoot=False):
1243 self.fsset.mountFilesystems(self.anaconda, raiseErrors=raiseErrors,
1244 readOnly=readOnly, skipRoot=skipRoot)
1245
1246 def umountFilesystems(self, ignoreErrors=True, swapoff=True):
1247 self.fsset.umountFilesystems(ignoreErrors=ignoreErrors, swapoff=swapoff)
1248
1249 def parseFSTab(self):
1250 self.fsset.parseFSTab()
1251
1252 def mkDevRoot(self):
1253 self.fsset.mkDevRoot()
1254
1255 def createSwapFile(self, device, size):
1256 self.fsset.createSwapFile(device, size)
1257
1258 @property
1259 def fsFreeSpace(self):
1260 return self.fsset.fsFreeSpace()
1261
1262 @property
1263 def mtab(self):
1264 return self.fsset.mtab()
1265
1266 @property
1267 def mountpoints(self):
1268 return self.fsset.mountpoints
1269
1270 @property
1271 def migratableDevices(self):
1272 return self.fsset.migratableDevices
1273
1274 @property
1275 def rootDevice(self):
1276 return self.fsset.rootDevice
1277
1278 def compareDisks(self, first, second):
1279 if self.eddDict.has_key(first) and self.eddDict.has_key(second):
1280 one = self.eddDict[first]
1281 two = self.eddDict[second]
1282 if (one < two):
1283 return -1
1284 elif (one > two):
1285 return 1
1286
1287 # if one is in the BIOS and the other not prefer the one in the BIOS
1288 if self.eddDict.has_key(first):
1289 return -1
1290 if self.eddDict.has_key(second):
1291 return 1
1292
1293 if first.startswith("hd"):
1294 type1 = 0
1295 elif first.startswith("sd"):
1296 type1 = 1
1297 elif (first.startswith("vd") or first.startswith("xvd")):
1298 type1 = -1
1299 else:
1300 type1 = 2
1301
1302 if second.startswith("hd"):
1303 type2 = 0
1304 elif second.startswith("sd"):
1305 type2 = 1
1306 elif (second.startswith("vd") or second.startswith("xvd")):
1307 type2 = -1
1308 else:
1309 type2 = 2
1310
1311 if (type1 < type2):
1312 return -1
1313 elif (type1 > type2):
1314 return 1
1315 else:
1316 len1 = len(first)
1317 len2 = len(second)
1318
1319 if (len1 < len2):
1320 return -1
1321 elif (len1 > len2):
1322 return 1
1323 else:
1324 if (first < second):
1325 return -1
1326 elif (first > second):
1327 return 1
1328
1329 return 0
1330
1331 def getReleaseString(mountpoint):
1332 relName = None
1333 relVer = None
1334
1335 filename = "%s/etc/redhat-release" % mountpoint
1336 if os.access(filename, os.R_OK):
1337 with open(filename) as f:
1338 try:
1339 relstr = f.readline().strip()
1340 except (IOError, AttributeError):
1341 relstr = ""
1342
1343 # get the release name and version
1344 # assumes that form is something
1345 # like "Red Hat Linux release 6.2 (Zoot)"
1346 (product, sep, version) = relstr.partition(" release ")
1347 if sep:
1348 relName = product
1349 relVer = version.split()[0]
1350
1351 return (relName, relVer)
1352
1353 def findExistingRootDevices(anaconda, upgradeany=False):
1354 """ Return a list of all root filesystems in the device tree. """
1355 rootDevs = []
1356
1357 if not os.path.exists(anaconda.rootPath):
1358 iutil.mkdirChain(anaconda.rootPath)
1359
1360 roots = []
1361 for device in anaconda.id.storage.devicetree.leaves:
1362 if not device.format.linuxNative or not device.format.mountable:
1363 continue
1364
1365 if device.protected:
1366 # can't upgrade the part holding hd: media so why look at it?
1367 continue
1368
1369 try:
1370 device.setup()
1371 except Exception as e:
1372 log.warning("setup of %s failed: %s" % (device.name, e))
1373 continue
1374
1375 try:
1376 device.format.mount(options="ro", mountpoint=anaconda.rootPath)
1377 except Exception as e:
1378 log.warning("mount of %s as %s failed: %s" % (device.name,
1379 device.format.type,
1380 e))
1381 device.teardown()
1382 continue
1383
1384 if os.access(anaconda.rootPath + "/etc/fstab", os.R_OK):
1385 (product, version) = getReleaseString(anaconda.rootPath)
1386 if upgradeany or \
1387 anaconda.id.instClass.productUpgradable(product, version):
1388 rootDevs.append((device, "%s %s" % (product, version)))
1389 else:
1390 log.info("product %s version %s found on %s is not upgradable"
1391 % (product, version, device.name))
1392
1393 # this handles unmounting the filesystem
1394 device.teardown(recursive=True)
1395
1396 return rootDevs
1397
1398 def mountExistingSystem(anaconda, rootEnt,
1399 allowDirty=None, warnDirty=None,
1400 readOnly=None):
1401 """ Mount filesystems specified in rootDevice's /etc/fstab file. """
1402 rootDevice = rootEnt[0]
1403 rootPath = anaconda.rootPath
1404 fsset = anaconda.id.storage.fsset
1405 if readOnly:
1406 readOnly = "ro"
1407 else:
1408 readOnly = ""
1409
1410 if rootDevice.protected and os.path.ismount("/mnt/isodir"):
1411 isys.mount("/mnt/isodir",
1412 rootPath,
1413 fstype=rootDevice.format.type,
1414 bindMount=True)
1415 else:
1416 rootDevice.setup()
1417 rootDevice.format.mount(chroot=rootPath,
1418 mountpoint="/",
1419 options="ro")
1420
1421 fsset.parseFSTab()
1422
1423 # check for dirty filesystems
1424 dirtyDevs = []
1425 for device in fsset.mountpoints.values():
1426 if not hasattr(device.format, "isDirty"):
1427 continue
1428
1429 try:
1430 device.setup()
1431 except DeviceError as e:
1432 # we'll catch this in the main loop
1433 continue
1434
1435 if device.format.isDirty:
1436 log.info("%s contains a dirty %s filesystem" % (device.path,
1437 device.format.type))
1438 dirtyDevs.append(device.path)
1439
1440 messageWindow = anaconda.intf.messageWindow
1441 if not allowDirty and dirtyDevs:
1442 messageWindow(_("Dirty File Systems"),
1443 _("The following file systems for your Linux system "
1444 "were not unmounted cleanly. Please boot your "
1445 "Linux installation, let the file systems be "
1446 "checked and shut down cleanly to upgrade.\n"
1447 "%s") % "\n".join(dirtyDevs))
1448 anaconda.id.storage.devicetree.teardownAll()
1449 sys.exit(0)
1450 elif warnDirty and dirtyDevs:
1451 rc = messageWindow(_("Dirty File Systems"),
1452 _("The following file systems for your Linux "
1453 "system were not unmounted cleanly. Would "
1454 "you like to mount them anyway?\n"
1455 "%s") % "\n".join(dirtyDevs),
1456 type = "yesno")
1457 if rc == 0:
1458 return -1
1459
1460 log.info("All is well mounting the rest of referenced filesystems")
1461 if not readOnly:
1462 log.info("Remounting /mnt/sysimage read-write")
1463 rootDevice.format.remount(options="rw")
1464 fsset.mountFilesystems(anaconda, readOnly=readOnly, skipRoot=True)
1465
1466
1467 class BlkidTab(object):
1468 """ Dictionary-like interface to blkid.tab with device path keys """
1469 def __init__(self, chroot=""):
1470 self.chroot = chroot
1471 self.devices = {}
1472
1473 def parse(self):
1474 path = "%s/etc/blkid/blkid.tab" % self.chroot
1475 log.debug("parsing %s" % path)
1476 with open(path) as f:
1477 for line in f.readlines():
1478 # this is pretty ugly, but an XML parser is more work than
1479 # is justifiable for this purpose
1480 if not line.startswith("<device "):
1481 continue
1482
1483 line = line[len("<device "):-len("</device>\n")]
1484 (data, sep, device) = line.partition(">")
1485 if not device:
1486 continue
1487
1488 self.devices[device] = {}
1489 for pair in data.split():
1490 try:
1491 (key, value) = pair.split("=")
1492 except ValueError:
1493 continue
1494
1495 self.devices[device][key] = value[1:-1] # strip off quotes
1496
1497 def __getitem__(self, key):
1498 return self.devices[key]
1499
1500 def get(self, key, default=None):
1501 return self.devices.get(key, default)
1502
1503
1504 class CryptTab(object):
1505 """ Dictionary-like interface to crypttab entries with map name keys """
1506 def __init__(self, devicetree, blkidTab=None, chroot=""):
1507 self.devicetree = devicetree
1508 self.blkidTab = blkidTab
1509 self.chroot = chroot
1510 self.mappings = {}
1511
1512 def parse(self, chroot=""):
1513 """ Parse /etc/crypttab from an existing installation. """
1514 if not chroot or not os.path.isdir(chroot):
1515 chroot = ""
1516
1517 path = "%s/etc/crypttab" % chroot
1518 log.debug("parsing %s" % path)
1519 with open(path) as f:
1520 if not self.blkidTab:
1521 try:
1522 self.blkidTab = BlkidTab(chroot=chroot)
1523 self.blkidTab.parse()
1524 except Exception:
1525 self.blkidTab = None
1526
1527 for line in f.readlines():
1528 (line, pound, comment) = line.partition("#")
1529 fields = line.split()
1530 if not 2 <= len(fields) <= 4:
1531 continue
1532 elif len(fields) == 2:
1533 fields.extend(['none', ''])
1534 elif len(fields) == 3:
1535 fields.append('')
1536
1537 (name, devspec, keyfile, options) = fields
1538
1539 # resolve devspec to a device in the tree
1540 device = self.devicetree.resolveDevice(devspec,
1541 blkidTab=self.blkidTab)
1542 if device:
1543 self.mappings[name] = {"device": device,
1544 "keyfile": keyfile,
1545 "options": options}
1546
1547 def populate(self):
1548 """ Populate the instance based on the device tree's contents. """
1549 for device in self.devicetree.devices:
1550 # XXX should we put them all in there or just the ones that
1551 # are part of a device containing swap or a filesystem?
1552 #
1553 # Put them all in here -- we can filter from FSSet
1554 if device.format.type != "luks":
1555 continue
1556
1557 key_file = device.format.keyFile
1558 if not key_file:
1559 key_file = "none"
1560
1561 options = device.format.options
1562 if not options:
1563 options = ""
1564
1565 self.mappings[device.format.mapName] = {"device": device,
1566 "keyfile": key_file,
1567 "options": options}
1568
1569 def crypttab(self):
1570 """ Write out /etc/crypttab """
1571 crypttab = ""
1572 for name in self.mappings:
1573 entry = self[name]
1574 crypttab += "%s UUID=%s %s %s\n" % (name,
1575 entry['device'].format.uuid,
1576 entry['keyfile'],
1577 entry['options'])
1578 return crypttab
1579
1580 def __getitem__(self, key):
1581 return self.mappings[key]
1582
1583 def get(self, key, default=None):
1584 return self.mappings.get(key, default)
1585
1586 def get_containing_device(path, devicetree):
1587 """ Return the device that a path resides on. """
1588 if not os.path.exists(path):
1589 return None
1590
1591 st = os.stat(path)
1592 major = os.major(st.st_dev)
1593 minor = os.minor(st.st_dev)
1594 link = "/sys/dev/block/%s:%s" % (major, minor)
1595 if not os.path.exists(link):
1596 return None
1597
1598 try:
1599 device_name = os.path.basename(os.readlink(link))
1600 except Exception:
1601 return None
1602
1603 if device_name.startswith("dm-"):
1604 # have I told you lately that I love you, device-mapper?
1605 device_name = name_from_dm_node(device_name)
1606
1607 return devicetree.getDeviceByName(device_name)
1608
1609
1610 class FSSet(object):
1611 """ A class to represent a set of filesystems. """
1612 def __init__(self, devicetree, rootpath):
1613 self.devicetree = devicetree
1614 self.rootpath = rootpath
1615 self.cryptTab = None
1616 self.blkidTab = None
1617 self.origFStab = None
1618 self.active = False
1619 self._dev = None
1620 self._devpts = None
1621 self._sysfs = None
1622 self._proc = None
1623 self._devshm = None
1624 self.preserveLines = [] # lines we just ignore and preserve
1625
1626 @property
1627 def sysfs(self):
1628 if not self._sysfs:
1629 self._sysfs = NoDevice(format=getFormat("sysfs",
1630 device="sys",
1631 mountpoint="/sys"))
1632 return self._sysfs
1633
1634 @property
1635 def dev(self):
1636 if not self._dev:
1637 self._dev = DirectoryDevice("/dev", format=getFormat("bind",
1638 device="/dev",
1639 mountpoint="/dev",
1640 exists=True),
1641 exists=True)
1642
1643 return self._dev
1644
1645 @property
1646 def devpts(self):
1647 if not self._devpts:
1648 self._devpts = NoDevice(format=getFormat("devpts",
1649 device="devpts",
1650 mountpoint="/dev/pts"))
1651 return self._devpts
1652
1653 @property
1654 def proc(self):
1655 if not self._proc:
1656 self._proc = NoDevice(format=getFormat("proc",
1657 device="proc",
1658 mountpoint="/proc"))
1659 return self._proc
1660
1661 @property
1662 def devshm(self):
1663 if not self._devshm:
1664 self._devshm = NoDevice(format=getFormat("tmpfs",
1665 device="tmpfs",
1666 mountpoint="/dev/shm"))
1667 return self._devshm
1668
1669 @property
1670 def devices(self):
1671 return sorted(self.devicetree.devices, key=lambda d: d.path)
1672
1673 @property
1674 def mountpoints(self):
1675 filesystems = {}
1676 for device in self.devices:
1677 if device.format.mountable and device.format.mountpoint:
1678 filesystems[device.format.mountpoint] = device
1679 return filesystems
1680
1681 def _parseOneLine(self, (devspec, mountpoint, fstype, options, dump, passno)):
1682 # find device in the tree
1683 device = self.devicetree.resolveDevice(devspec,
1684 cryptTab=self.cryptTab,
1685 blkidTab=self.blkidTab)
1686 if device:
1687 # fall through to the bottom of this block
1688 pass
1689 elif devspec.startswith("/dev/loop"):
1690 # FIXME: create devices.LoopDevice
1691 log.warning("completely ignoring your loop mount")
1692 elif ":" in devspec and fstype.startswith("nfs"):
1693 # NFS -- preserve but otherwise ignore
1694 device = NFSDevice(devspec,
1695 format=getFormat(fstype,
1696 device=devspec))
1697 elif devspec.startswith("/") and fstype == "swap":
1698 # swap file
1699 device = FileDevice(devspec,
1700 parents=get_containing_device(devspec, self.devicetree),
1701 format=getFormat(fstype,
1702 device=devspec,
1703 exists=True),
1704 exists=True)
1705 elif fstype == "bind" or "bind" in options:
1706 # bind mount... set fstype so later comparison won't
1707 # turn up false positives
1708 fstype = "bind"
1709
1710 # This is probably not going to do anything useful, so we'll
1711 # make sure to try again from FSSet.mountFilesystems. The bind
1712 # mount targets should be accessible by the time we try to do
1713 # the bind mount from there.
1714 parents = get_containing_device(devspec, self.devicetree)
1715 device = DirectoryDevice(devspec, parents=parents, exists=True)
1716 device.format = getFormat("bind",
1717 device=device.path,
1718 exists=True)
1719 elif mountpoint in ("/proc", "/sys", "/dev/shm", "/dev/pts"):
1720 # drop these now -- we'll recreate later
1721 return None
1722 else:
1723 # nodev filesystem -- preserve or drop completely?
1724 format = getFormat(fstype)
1725 if devspec == "none" or \
1726 isinstance(format, get_device_format_class("nodev")):
1727 device = NoDevice(format=format)
1728 else:
1729 device = StorageDevice(devspec, format=format)
1730
1731 if device is None:
1732 log.error("failed to resolve %s (%s) from fstab" % (devspec,
1733 fstype))
1734 raise UnrecognizedFSTabEntryError()
1735
1736 if device.format.type is None:
1737 log.info("Unrecognized filesystem type for %s (%s)"
1738 % (device.name, fstype))
1739 raise UnrecognizedFSTabEntryError()
1740
1741 # make sure, if we're using a device from the tree, that
1742 # the device's format we found matches what's in the fstab
1743 fmt = getFormat(fstype, device=device.path)
1744 ftype = getattr(fmt, "mountType", fmt.type)
1745 dtype = getattr(device.format, "mountType", device.format.type)
1746 if ftype != dtype:
1747 raise StorageError("scanned format (%s) differs from fstab "
1748 "format (%s)" % (dtype, ftype))
1749 del ftype
1750 del dtype
1751
1752 if device.format.mountable:
1753 device.format.mountpoint = mountpoint
1754 device.format.mountopts = options
1755
1756 # is this useful?
1757 try:
1758 device.format.options = options
1759 except AttributeError:
1760 pass
1761
1762 return device
1763
1764 def parseFSTab(self, chroot=None):
1765 """ parse /etc/fstab
1766
1767 preconditions:
1768 all storage devices have been scanned, including filesystems
1769 postconditions:
1770
1771 FIXME: control which exceptions we raise
1772
1773 XXX do we care about bind mounts?
1774 how about nodev mounts?
1775 loop mounts?
1776 """
1777 if not chroot or not os.path.isdir(chroot):
1778 chroot = self.rootpath
1779
1780 path = "%s/etc/fstab" % chroot
1781 if not os.access(path, os.R_OK):
1782 # XXX should we raise an exception instead?
1783 log.info("cannot open %s for read" % path)
1784 return
1785
1786 blkidTab = BlkidTab(chroot=chroot)
1787 try:
1788 blkidTab.parse()
1789 log.debug("blkid.tab devs: %s" % blkidTab.devices.keys())
1790 except Exception as e:
1791 log.info("error parsing blkid.tab: %s" % e)
1792 blkidTab = None
1793
1794 cryptTab = CryptTab(self.devicetree, blkidTab=blkidTab, chroot=chroot)
1795 try:
1796 cryptTab.parse(chroot=chroot)
1797 log.debug("crypttab maps: %s" % cryptTab.mappings.keys())
1798 except Exception as e:
1799 log.info("error parsing crypttab: %s" % e)
1800 cryptTab = None
1801
1802 self.blkidTab = blkidTab
1803 self.cryptTab = cryptTab
1804
1805 with open(path) as f:
1806 log.debug("parsing %s" % path)
1807
1808 lines = f.readlines()
1809
1810 # save the original file
1811 self.origFStab = ''.join(lines)
1812
1813 for line in lines:
1814 # strip off comments
1815 (line, pound, comment) = line.partition("#")
1816 fields = line.split()
1817
1818 if not 4 <= len(fields) <= 6:
1819 continue
1820 elif len(fields) == 4:
1821 fields.extend([0, 0])
1822 elif len(fields) == 5:
1823 fields.append(0)
1824
1825 (devspec, mountpoint, fstype, options, dump, passno) = fields
1826
1827 try:
1828 device = self._parseOneLine((devspec, mountpoint, fstype, options, dump, passno))
1829 except UnrecognizedFSTabEntryError:
1830 # just write the line back out as-is after upgrade
1831 self.preserveLines.append(line)
1832 continue
1833 except Exception as e:
1834 raise Exception("fstab entry %s is malformed: %s" % (devspec, e))
1835
1836 if not device:
1837 continue
1838
1839 if device not in self.devicetree.devices:
1840 try:
1841 self.devicetree._addDevice(device)
1842 except ValueError:
1843 # just write duplicates back out post-install
1844 self.preserveLines.append(line)
1845
1846 def fsFreeSpace(self, chroot=None):
1847 if not chroot:
1848 chroot = self.rootpath
1849
1850 space = []
1851 for device in self.devices:
1852 if not device.format.mountable or \
1853 not device.format.mountpoint or \
1854 not device.format.status:
1855 continue
1856
1857 path = "%s/%s" % (chroot, device.format.mountpoint)
1858
1859 ST_RDONLY = 1 # this should be in python's posix module
1860 if not os.path.exists(path) or os.statvfs(path)[statvfs.F_FLAG] & ST_RDONLY:
1861 continue
1862
1863 try:
1864 space.append((device.format.mountpoint,
1865 isys.pathSpaceAvailable(path)))
1866 except SystemError:
1867 log.error("failed to calculate free space for %s" % (device.format.mountpoint,))
1868
1869 space.sort(key=lambda s: s[1])
1870 return space
1871
1872 def mtab(self):
1873 format = "%s %s %s %s 0 0\n"
1874 mtab = ""
1875 devices = self.mountpoints.values() + self.swapDevices
1876 devices.extend([self.devshm, self.devpts, self.sysfs, self.proc])
1877 devices.sort(key=lambda d: getattr(d.format, "mountpoint", None))
1878 for device in devices:
1879 if not device.format.status:
1880 continue
1881 if not device.format.mountable:
1882 continue
1883 if device.format.mountpoint:
1884 options = device.format.mountopts
1885 if options:
1886 options = options.replace("defaults,", "")
1887 options = options.replace("defaults", "")
1888
1889 if options:
1890 options = "rw," + options
1891 else:
1892 options = "rw"
1893 mtab = mtab + format % (device.path,
1894 device.format.mountpoint,
1895 device.format.type,
1896 options)
1897 return mtab
1898
1899 def turnOnSwap(self, anaconda, upgrading=None):
1900 def swapErrorDialog(msg, device):
1901 if not anaconda.intf:
1902 sys.exit(0)
1903
1904 buttons = [_("Skip"), _("Format"), _("_Exit")]
1905 ret = anaconda.intf.messageWindow(_("Error"), msg, type="custom",
1906 custom_buttons=buttons,
1907 custom_icon="warning")
1908
1909 if ret == 0:
1910 self.devicetree._removeDevice(device)
1911 return False
1912 elif ret == 1:
1913 device.format.create(force=True)
1914 return True
1915 else:
1916 sys.exit(0)
1917
1918 for device in self.swapDevices:
1919 if isinstance(device, FileDevice):
1920 # set up FileDevices' parents now that they are accessible
1921 targetDir = "%s/%s" % (anaconda.rootPath, device.path)
1922 parent = get_containing_device(targetDir, self.devicetree)
1923 if not parent:
1924 log.error("cannot determine which device contains "
1925 "directory %s" % device.path)
1926 device.parents = []
1927 self.devicetree._removeDevice(device)
1928 continue
1929 else:
1930 device.parents = [parent]
1931
1932 while True:
1933 try:
1934 device.setup()
1935 device.format.setup()
1936 except OldSwapError:
1937 msg = _("The swap device:\n\n %s\n\n"
1938 "is an old-style Linux swap partition. If "
1939 "you want to use this device for swap space, "
1940 "you must reformat as a new-style Linux swap "
1941 "partition.") \
1942 % device.path
1943
1944 if swapErrorDialog(msg, device):
1945 continue
1946 except SuspendError:
1947 if upgrading:
1948 msg = _("The swap device:\n\n %s\n\n"
1949 "in your /etc/fstab file is currently in "
1950 "use as a software suspend device, "
1951 "which means your system is hibernating. "
1952 "To perform an upgrade, please shut down "
1953 "your system rather than hibernating it.") \
1954 % device.path
1955 else:
1956 msg = _("The swap device:\n\n %s\n\n"
1957 "in your /etc/fstab file is currently in "
1958 "use as a software suspend device, "
1959 "which means your system is hibernating. "
1960 "If you are performing a new install, "
1961 "make sure the installer is set "
1962 "to format all swap devices.") \
1963 % device.path
1964
1965 if swapErrorDialog(msg, device):
1966 continue
1967 except UnknownSwapError:
1968 msg = _("The swap device:\n\n %s\n\n"
1969 "does not contain a supported swap volume. In "
1970 "order to continue installation, you will need "
1971 "to format the device or skip it.") \
1972 % device.path
1973
1974 if swapErrorDialog(msg, device):
1975 continue
1976 except DeviceError as (msg, name):
1977 if anaconda.intf:
1978 if upgrading:
1979 err = _("Error enabling swap device %(name)s: "
1980 "%(msg)s\n\n"
1981 "The /etc/fstab on your upgrade partition "
1982 "does not reference a valid swap "
1983 "device.\n\nPress OK to exit the "
1984 "installer") % {'name': name, 'msg': msg}
1985 else:
1986 err = _("Error enabling swap device %(name)s: "
1987 "%(msg)s\n\n"
1988 "This most likely means this swap "
1989 "device has not been initialized.\n\n"
1990 "Press OK to exit the installer.") % \
1991 {'name': name, 'msg': msg}
1992 anaconda.intf.messageWindow(_("Error"), err)
1993 sys.exit(0)
1994
1995 break
1996
1997 def mountFilesystems(self, anaconda, raiseErrors=None, readOnly=None,
1998 skipRoot=False):
1999 intf = anaconda.intf
2000 devices = self.mountpoints.values() + self.swapDevices
2001 devices.extend([self.dev, self.devshm, self.devpts, self.sysfs, self.proc])
2002 devices.sort(key=lambda d: getattr(d.format, "mountpoint", None))
2003
2004 for device in devices:
2005 if not device.format.mountable or not device.format.mountpoint:
2006 continue
2007
2008 if skipRoot and device.format.mountpoint == "/":
2009 continue
2010
2011 options = device.format.options
2012 if "noauto" in options.split(","):
2013 continue
2014
2015 if device.format.type == "bind" and device != self.dev:
2016 # set up the DirectoryDevice's parents now that they are
2017 # accessible
2018 #
2019 # -- bind formats' device and mountpoint are always both
2020 # under the chroot. no exceptions. none, damn it.
2021 targetDir = "%s/%s" % (anaconda.rootPath, device.path)
2022 parent = get_containing_device(targetDir, self.devicetree)
2023 if not parent:
2024 log.error("cannot determine which device contains "
2025 "directory %s" % device.path)
2026 device.parents = []
2027 self.devicetree._removeDevice(device)
2028 continue
2029 else:
2030 device.parents = [parent]
2031
2032 try:
2033 device.setup()
2034 except Exception as msg:
2035 # FIXME: need an error popup
2036 continue
2037
2038 if readOnly:
2039 options = "%s,%s" % (options, readOnly)
2040
2041 try:
2042 device.format.setup(options=options,
2043 chroot=anaconda.rootPath)
2044 except OSError as e:
2045 log.error("OSError: (%d) %s" % (e.errno, e.strerror))
2046
2047 if intf:
2048 if e.errno == errno.EEXIST:
2049 intf.messageWindow(_("Invalid mount point"),
2050 _("An error occurred when trying "
2051 "to create %s. Some element of "
2052 "this path is not a directory. "
2053 "This is a fatal error and the "
2054 "install cannot continue.\n\n"
2055 "Press <Enter> to exit the "
2056 "installer.")
2057 % (device.format.mountpoint,))
2058 else:
2059 na = {'mountpoint': device.format.mountpoint,
2060 'msg': e.strerror}
2061 intf.messageWindow(_("Invalid mount point"),
2062 _("An error occurred when trying "
2063 "to create %(mountpoint)s: "
2064 "%(msg)s. This is "
2065 "a fatal error and the install "
2066 "cannot continue.\n\n"
2067 "Press <Enter> to exit the "
2068 "installer.") % na)
2069 sys.exit(0)
2070 except SystemError as (num, msg):
2071 log.error("SystemError: (%d) %s" % (num, msg) )
2072
2073 if raiseErrors:
2074 raise
2075 if intf and not device.format.linuxNative:
2076 na = {'path': device.path,
2077 'mountpoint': device.format.mountpoint}
2078 ret = intf.messageWindow(_("Unable to mount filesystem"),
2079 _("An error occurred mounting "
2080 "device %(path)s as "
2081 "%(mountpoint)s. You may "
2082 "continue installation, but "
2083 "there may be problems.") % na,
2084 type="custom",
2085 custom_icon="warning",
2086 custom_buttons=[_("_Exit installer"),
2087 _("_Continue")])
2088
2089 if ret == 0:
2090 sys.exit(0)
2091 else:
2092 continue
2093
2094 sys.exit(0)
2095 except FSError as msg:
2096 log.error("FSError: %s" % msg)
2097
2098 if intf:
2099 na = {'path': device.path,
2100 'mountpoint': device.format.mountpoint,
2101 'msg': msg}
2102 intf.messageWindow(_("Unable to mount filesystem"),
2103 _("An error occurred mounting "
2104 "device %(path)s as %(mountpoint)s: "
2105 "%(msg)s. This is "
2106 "a fatal error and the install "
2107 "cannot continue.\n\n"
2108 "Press <Enter> to exit the "
2109 "installer.") % na)
2110 sys.exit(0)
2111
2112 self.active = True
2113
2114 def umountFilesystems(self, ignoreErrors=True, swapoff=True):
2115 devices = self.mountpoints.values() + self.swapDevices
2116 devices.extend([self.dev, self.devshm, self.devpts, self.sysfs, self.proc])
2117 devices.sort(key=lambda d: getattr(d.format, "mountpoint", None))
2118 devices.reverse()
2119 for device in devices:
2120 if not device.format.mountable and \
2121 (device.format.type != "swap" or swapoff):
2122 continue
2123
2124 device.format.teardown()
2125 device.teardown()
2126
2127 self.active = False
2128
2129 def createSwapFile(self, device, size, rootPath=None):
2130 """ Create and activate a swap file under rootPath. """
2131 if not rootPath:
2132 rootPath = self.rootpath
2133
2134 filename = "/SWAP"
2135 count = 0
2136 basedir = os.path.normpath("%s/%s" % (rootPath,
2137 device.format.mountpoint))
2138 while os.path.exists("%s/%s" % (basedir, filename)) or \
2139 self.devicetree.getDeviceByName(filename):
2140 file = os.path.normpath("%s/%s" % (basedir, filename))
2141 count += 1
2142 filename = "/SWAP-%d" % count
2143
2144 dev = FileDevice(filename,
2145 size=size,
2146 parents=[device],
2147 format=getFormat("swap", device=filename))
2148 dev.create()
2149 dev.setup()
2150 dev.format.create()
2151 dev.format.setup()
2152 # nasty, nasty
2153 self.devicetree._addDevice(dev)
2154
2155 def mkDevRoot(self, instPath=None):
2156 if not instPath:
2157 instPath = self.rootpath
2158
2159 root = self.rootDevice
2160 dev = "%s/%s" % (instPath, root.path)
2161 if not os.path.exists("%s/dev/root" %(instPath,)) and os.path.exists(dev):
2162 rdev = os.stat(dev).st_rdev
2163 os.mknod("%s/dev/root" % (instPath,), stat.S_IFBLK | 0600, rdev)
2164
2165 @property
2166 def swapDevices(self):
2167 swaps = []
2168 for device in self.devices:
2169 if device.format.type == "swap":
2170 swaps.append(device)
2171 return swaps
2172
2173 @property
2174 def rootDevice(self):
2175 for path in ["/", self.rootpath]:
2176 for device in self.devices:
2177 try:
2178 mountpoint = device.format.mountpoint
2179 except AttributeError:
2180 mountpoint = None
2181
2182 if mountpoint == path:
2183 return device
2184
2185 @property
2186 def migratableDevices(self):
2187 """ List of devices whose filesystems can be migrated. """
2188 migratable = []
2189 for device in self.devices:
2190 if device.format.migratable and device.format.exists:
2191 migratable.append(device)
2192
2193 return migratable
2194
2195 def write(self, instPath=None):
2196 """ write out all config files based on the set of filesystems """
2197 if not instPath:
2198 instPath = self.rootpath
2199
2200 # /etc/fstab
2201 fstab_path = os.path.normpath("%s/etc/fstab" % instPath)
2202 fstab = self.fstab()
2203 open(fstab_path, "w").write(fstab)
2204
2205 # /etc/crypttab
2206 crypttab_path = os.path.normpath("%s/etc/crypttab" % instPath)
2207 crypttab = self.crypttab()
2208 open(crypttab_path, "w").write(crypttab)
2209
2210 # /etc/mdadm.conf
2211 mdadm_path = os.path.normpath("%s/etc/mdadm.conf" % instPath)
2212 mdadm_conf = self.mdadmConf()
2213 if mdadm_conf:
2214 open(mdadm_path, "w").write(mdadm_conf)
2215
2216 # /etc/multipath.conf
2217 multipath_conf = self.multipathConf()
2218 if multipath_conf:
2219 multipath_path = os.path.normpath("%s/etc/multipath.conf" %
2220 instPath)
2221 conf_contents = multipath_conf.write(self.devicetree.mpathFriendlyNames)
2222 f = open(multipath_path, "w")
2223 f.write(conf_contents)
2224 f.close()
2225 else:
2226 log.info("not writing out mpath configuration")
2227 iutil.copy_to_sysimage("/etc/multipath/wwids", root_path=instPath)
2228 if self.devicetree.mpathFriendlyNames:
2229 iutil.copy_to_sysimage("/etc/multipath/bindings", root_path=instPath)
2230
2231 def crypttab(self):
2232 # if we are upgrading, do we want to update crypttab?
2233 # gut reaction says no, but plymouth needs the names to be very
2234 # specific for passphrase prompting
2235 if not self.cryptTab:
2236 self.cryptTab = CryptTab(self.devicetree)
2237 self.cryptTab.populate()
2238
2239 devices = self.mountpoints.values() + self.swapDevices
2240
2241 # prune crypttab -- only mappings required by one or more entries
2242 for name in self.cryptTab.mappings.keys():
2243 keep = False
2244 mapInfo = self.cryptTab[name]
2245 cryptoDev = mapInfo['device']
2246 for device in devices:
2247 if device == cryptoDev or device.dependsOn(cryptoDev):
2248 keep = True
2249 break
2250
2251 if not keep:
2252 del self.cryptTab.mappings[name]
2253
2254 return self.cryptTab.crypttab()
2255
2256 def mdadmConf(self):
2257 """ Return the contents of mdadm.conf. """
2258 arrays = self.devicetree.getDevicesByType("mdarray")
2259 arrays.extend(self.devicetree.getDevicesByType("mdbiosraidarray"))
2260 arrays.extend(self.devicetree.getDevicesByType("mdcontainer"))
2261 # Sort it, this not only looks nicer, but this will also put
2262 # containers (which get md0, md1, etc.) before their members
2263 # (which get md127, md126, etc.). and lame as it is mdadm will not
2264 # assemble the whole stack in one go unless listed in the proper order
2265 # in mdadm.conf
2266 arrays.sort(key=lambda d: d.path)
2267 if not arrays:
2268 return ""
2269
2270 conf = "# mdadm.conf written out by anaconda\n"
2271 conf += "MAILADDR root\n"
2272 conf += "AUTO +imsm +1.x -all\n"
2273 devices = self.mountpoints.values() + self.swapDevices
2274 for array in arrays:
2275 for device in devices:
2276 if device == array or device.dependsOn(array):
2277 conf += array.mdadmConfEntry
2278 break
2279
2280 return conf
2281
2282 def multipathConf(self):
2283 """ Return the contents of multipath.conf. """
2284 mpaths = self.devicetree.getDevicesByType("dm-multipath")
2285 if not mpaths:
2286 return None
2287 mpaths.sort(key=lambda d: d.name)
2288 config = MultipathConfigWriter()
2289 whitelist = []
2290 for mpath in mpaths:
2291 config.addMultipathDevice(mpath)
2292 whitelist.append(mpath.name)
2293 whitelist.extend([d.name for d in mpath.parents])
2294
2295 # blacklist everything we're not using and let the
2296 # sysadmin sort it out.
2297 for d in self.devicetree.devices:
2298 if not d.name in whitelist:
2299 config.addBlacklistDevice(d)
2300
2301 return config
2302
2303 def fstab (self):
2304 format = "%-23s %-23s %-7s %-15s %d %d\n"
2305 fstab = """
2306 #
2307 # /etc/fstab
2308 # Created by anaconda on %s
2309 #
2310 # Accessible filesystems, by reference, are maintained under '/dev/disk'
2311 # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
2312 #
2313 """ % time.asctime()
2314
2315 devices = sorted(self.mountpoints.values(),
2316 key=lambda d: d.format.mountpoint)
2317 devices += self.swapDevices
2318 devices.extend([self.devshm, self.devpts, self.sysfs, self.proc])
2319 netdevs = self.devicetree.getDevicesByInstance(NetworkStorageDevice)
2320 for device in devices:
2321 # why the hell do we put swap in the fstab, anyway?
2322 if not device.format.mountable and device.format.type != "swap":
2323 continue
2324
2325 # Don't write out lines for optical devices, either.
2326 if isinstance(device, OpticalDevice):
2327 continue
2328
2329 fstype = getattr(device.format, "mountType", device.format.type)
2330 if fstype == "swap":
2331 mountpoint = "swap"
2332 options = device.format.options
2333 else:
2334 mountpoint = device.format.mountpoint
2335 options = device.format.options
2336 if not mountpoint:
2337 log.warning("%s filesystem on %s has no mountpoint" % \
2338 (fstype,
2339 device.path))
2340 continue
2341
2342 options = options or "defaults"
2343 for netdev in netdevs:
2344 if device.dependsOn(netdev):
2345 if mountpoint == "/":
2346 options = options + ",_rnetdev"
2347 else:
2348 options = options + ",_netdev"
2349 break
2350 devspec = device.fstabSpec
2351 dump = device.format.dump
2352 if device.format.check and mountpoint == "/":
2353 passno = 1
2354 elif device.format.check:
2355 passno = 2
2356 else:
2357 passno = 0
2358 fstab = fstab + device.fstabComment
2359 fstab = fstab + format % (devspec, mountpoint, fstype,
2360 options, dump, passno)
2361
2362 # now, write out any lines we were unable to process because of
2363 # unrecognized filesystems or unresolveable device specifications
2364 for line in self.preserveLines:
2365 fstab += line
2366
2367 return fstab

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