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

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

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


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

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

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