/[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.4 - (hide annotations) (download) (as text)
Tue Dec 3 04:09:59 2013 UTC (10 years, 7 months ago) by wellsi
Branch: MAIN
Changes since 1.3: +7 -7 lines
Content type: text/x-python
Volume group and logical volume names [SME: 7986]

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 wellsi 1.2 _("This option performs a new install of "
229     "SME Server. All hard drives, including "
230 wellsi 1.3 "removable media, will be re-partitioned and formatted. Proceed? "
231 charliebrady 1.1 "%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 wellsi 1.4 vgtemplate = "main"
922 charliebrady 1.1 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 wellsi 1.4 vgtemplate = "main"
930 charliebrady 1.1
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 wellsi 1.4 lvtemplate = 'root'
955 charliebrady 1.1 else:
956     if mountpoint.startswith("/"):
957 wellsi 1.4 template = "%s" % mountpoint[1:]
958 charliebrady 1.1 else:
959 wellsi 1.4 template = "%s" % (mountpoint,)
960 charliebrady 1.1
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 wellsi 1.4 lvtemplate = "swap%02d" % idx
968 charliebrady 1.1 if lvtemplate in [lv.lvname for lv in vg.lvs]:
969     idx += 1
970     else:
971     break
972     else:
973 wellsi 1.4 lvtemplate = "swap"
974 charliebrady 1.1 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