Add optional/keyword args for specifying an InstallInterface or Platform instance when not supplying an Anaconda instance. This is all for improving the storage module's utility when used outside of anaconda. --- pyanaconda/__init__.py | 2 +- pyanaconda/storage/__init__.py | 150 ++++++++++++++++++++++++------------ pyanaconda/storage/fcoe.py | 2 +- pyanaconda/storage/iscsi.py | 6 +- pyanaconda/storage/partitioning.py | 110 ++++++++++++++------------ 5 files changed, 163 insertions(+), 107 deletions(-) diff --git a/pyanaconda/__init__.py b/pyanaconda/__init__.py index b167bd5..3b58028 100644 --- a/pyanaconda/__init__.py +++ b/pyanaconda/__init__.py @@ -188,7 +188,7 @@ class Anaconda(object): def storage(self): if not self._storage: import storage - self._storage = storage.Storage(self) + self._storage = storage.Storage(anaconda=self) return self._storage diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py index 2cc8519..0309f28 100644 --- a/pyanaconda/storage/__init__.py +++ b/pyanaconda/storage/__init__.py @@ -312,8 +312,25 @@ class StorageDiscoveryConfig(object): class Storage(object): - def __init__(self, anaconda): + def __init__(self, anaconda=None, intf=None, platform=None): + """ Create a Storage instance. + + Keyword Arguments: + + anaconda - an Anaconda instance + intf - an InstallInterface instance + platform - a Platform instance + + All arguments are optional. An Anaconda instance will contain + an InstallInterface and a Platform instance, so it makes sense + to pass in either an Anaconda instance or as many of the other + two as is desired. Explicitly passed intf or platform will take + precedence over those in the Anaconda instance. + + """ self.anaconda = anaconda + self._intf = intf + self._platform = platform self.config = StorageDiscoveryConfig() @@ -342,13 +359,13 @@ class Storage(object): self._dumpFile = "/tmp/storage.state" # these will both be empty until our reset method gets called - self.devicetree = DeviceTree(intf=self.anaconda.intf, + self.devicetree = DeviceTree(intf=self.intf, conf=self.config, passphrase=self.encryptionPassphrase, luksDict=self.__luksDevs, iscsi=self.iscsi, dasd=self.dasd) - self.fsset = FSSet(self.devicetree, self.anaconda.rootPath) + self.fsset = FSSet(self.devicetree, getattr(anaconda, "rootPath", "")) self.services = set() def doIt(self): @@ -357,12 +374,13 @@ class Storage(object): # now set the boot partition's flag try: - boot = self.anaconda.platform.bootDevice() + boot = self.platform.bootDevice() if boot.type == "mdarray": bootDevs = boot.parents else: bootDevs = [boot] - except DeviceError: + except (DeviceError, AttributeError): + # AttributeError means we have no platform instance. it's ok. bootDevs = [] else: for dev in bootDevs: @@ -412,20 +430,21 @@ class Storage(object): if device.format.type == "luks" and device.format.exists: self.__luksDevs[device.format.uuid] = device.format._LUKS__passphrase - w = self.anaconda.intf.waitWindow(_("Examining Devices"), + if self.intf: + w = self.intf.waitWindow(_("Examining Devices"), _("Examining storage devices")) if not flags.imageInstall: - self.iscsi.startup(self.anaconda.intf) - self.fcoe.startup(self.anaconda.intf) - self.zfcp.startup(self.anaconda.intf) - self.dasd.startup(self.anaconda.intf, + self.iscsi.startup(self.intf) + self.fcoe.startup(self.intf) + self.zfcp.startup(self.intf) + self.dasd.startup(self.intf, self.config.exclusiveDisks, self.config.zeroMbr) clearPartType = self.config.clearPartType # save this before overriding it - if self.anaconda.upgrade: + if getattr(self.anaconda, "upgrade", False): self.config.clearPartType = CLEARPART_TYPE_NONE - self.devicetree = DeviceTree(intf=self.anaconda.intf, + self.devicetree = DeviceTree(intf=self.intf, conf=self.config, passphrase=self.encryptionPassphrase, luksDict=self.__luksDevs, @@ -433,12 +452,16 @@ class Storage(object): dasd=self.dasd) self.devicetree.populate() self.config.clearPartType = clearPartType # set it back - self.fsset = FSSet(self.devicetree, self.anaconda.rootPath) + self.fsset = FSSet(self.devicetree, + getattr(self.anaconda, "rootPath", "")) self.eddDict = get_edd_dict(self.partitioned) - self.anaconda.rootParts = None - self.anaconda.upgradeRoot = None + if hasattr(self.anaconda, "rootParts") and \ + hasattr(self.anaconda, "upgradeRoot"): + self.anaconda.rootParts = None + self.anaconda.upgradeRoot = None self.dumpState("initial") - w.pop() + if w: + w.pop() @property def devices(self): @@ -636,10 +659,24 @@ class Storage(object): def liveImage(self): """ The OS image used by live installs. """ _image = None - if flags.livecdInstall: + if flags.livecdInstall and hasattr(self.anaconda, "methodstr"): _image = self.devicetree.getDeviceByPath(self.anaconda.methodstr[9:]) return _image + @property + def intf(self): + _intf = self._intf + if not _intf: + _intf = getattr(self.anaconda, "intf", None) + return _intf + + @property + def platform(self): + _platform = self._platform + if not _platform: + _platform = getattr(self.anaconda, "platform", None) + return _platform + def exceptionDisks(self): """ Return a list of removable devices to save exceptions to. @@ -801,7 +838,10 @@ class Storage(object): if kwargs.has_key("name"): name = kwargs.pop("name") else: - name = self.createSuggestedVGName(self.anaconda.network) + hostname = "" + if hasattr(self.anaconda, "network"): + hostname = self.anaconda.network.hostname + name = self.createSuggestedVGName(hostname=hostname) if name in [d.name for d in self.devices]: raise ValueError("name already in use") @@ -893,19 +933,18 @@ class Storage(object): return True return False - def createSuggestedVGName(self, network): + def createSuggestedVGName(self, hostname=None): """ Return a reasonable, unused VG name. """ # try to create a volume group name incorporating the hostname - hn = network.hostname vgnames = [vg.name for vg in self.vgs] - if hn is not None and hn != '': - if hn == 'localhost' or hn == 'localhost.localdomain': + if hostname is not None and hostname != '': + if hostname == 'localhost' or hostname == 'localhost.localdomain': vgtemplate = "VolGroup" - elif hn.find('.') != -1: - template = "vg_%s" % (hn.split('.')[0].lower(),) + elif hostname.find('.') != -1: + template = "vg_%s" % (hostname.split('.')[0].lower(),) vgtemplate = safeLvmName(template) else: - template = "vg_%s" % (hn.lower(),) + template = "vg_%s" % (hostname.lower(),) vgtemplate = safeLvmName(template) else: vgtemplate = "VolGroup" @@ -1009,8 +1048,9 @@ class Storage(object): root = self.fsset.rootDevice swaps = self.fsset.swapDevices try: - boot = self.anaconda.platform.bootDevice() - except DeviceError: + boot = self.platform.bootDevice() + except (DeviceError, AttributeError): + # AttributeError means we have no anaconda or platform. it's ok. boot = None if not root: @@ -1024,6 +1064,7 @@ class Storage(object): "install %s.") % (productName,)) if (root and + hasattr(self.anaconda, "backend") and root.size < self.anaconda.backend.getMinimumSizeMB("/")): if flags.livecdInstall: live = " Live" @@ -1082,7 +1123,8 @@ class Storage(object): warnings.append(_("Installing on a FireWire device. This may " "or may not produce a working system.")) - errors.extend(self.anaconda.platform.checkBootRequest(boot)) + if self.platform: + errors.extend(self.platform.checkBootRequest(boot)) if not swaps: from pyanaconda.storage.size import Size @@ -1117,8 +1159,8 @@ class Storage(object): def checkNoDisks(self): """Check that there are valid disk devices.""" - if not self.disks: - self.anaconda.intf.messageWindow(_("No Drives Found"), + if not self.disks and self.intf: + self.intf.messageWindow(_("No Drives Found"), _("An error has occurred - no valid devices were " "found on which to create new file systems. " "Please check your hardware for the cause " @@ -1135,8 +1177,8 @@ class Storage(object): def write(self, instPath): self.fsset.write(instPath) self.makeMtab(root=instPath) - self.iscsi.write(instPath, self.anaconda) - self.fcoe.write(instPath, self.anaconda) + self.iscsi.write(instPath, self) + self.fcoe.write(instPath) self.zfcp.write(instPath) self.dasd.write(instPath) @@ -1190,10 +1232,15 @@ class Storage(object): self.zfcp.writeKS(f) def turnOnSwap(self, upgrading=None): - self.fsset.turnOnSwap(self.anaconda, upgrading=upgrading) + self.fsset.turnOnSwap(intf=self.intf, + rootPath=getattr(self.anaconda, "rootPath", ""), + upgrading=upgrading) def mountFilesystems(self, raiseErrors=None, readOnly=None, skipRoot=False): - self.fsset.mountFilesystems(self.anaconda, raiseErrors=raiseErrors, + self.fsset.mountFilesystems(intf=self.intf, + rootPath=getattr(self.anaconda, + "rootPath", ""), + raiseErrors=raiseErrors, readOnly=readOnly, skipRoot=skipRoot) def umountFilesystems(self, ignoreErrors=True, swapoff=True): @@ -1425,7 +1472,8 @@ def mountExistingSystem(anaconda, rootEnt, if rc == 0: return -1 - fsset.mountFilesystems(anaconda, readOnly=readOnly, skipRoot=True) + fsset.mountFilesystems(intf=anaconda.intf, rootPath=anaconda.rootPath, + readOnly=readOnly, skipRoot=True) class BlkidTab(object): @@ -1832,15 +1880,16 @@ class FSSet(object): # just write duplicates back out post-install self.preserveLines.append(line) - def turnOnSwap(self, anaconda, upgrading=None): + def turnOnSwap(self, intf=None, rootPath="", upgrading=None): def swapErrorDialog(msg, device): - if not anaconda.intf: - sys.exit(0) - - buttons = [_("Skip"), _("Format"), _("_Exit")] - ret = anaconda.intf.messageWindow(_("Error"), msg, type="custom", - custom_buttons=buttons, - custom_icon="warning") + if not intf: + # can't show a dialog? ignore this busted device. + ret = 0 + else: + buttons = [_("Skip"), _("Format"), _("_Exit")] + ret = intf.messageWindow(_("Error"), msg, type="custom", + custom_buttons=buttons, + custom_icon="warning") if ret == 0: self.devicetree._removeDevice(device) @@ -1854,7 +1903,7 @@ class FSSet(object): for device in self.swapDevices: if isinstance(device, FileDevice): # set up FileDevices' parents now that they are accessible - targetDir = "%s/%s" % (anaconda.rootPath, device.path) + targetDir = "%s/%s" % (rootPath, device.path) parent = get_containing_device(targetDir, self.devicetree) if not parent: log.error("cannot determine which device contains " @@ -1910,7 +1959,7 @@ class FSSet(object): if swapErrorDialog(msg, device): continue except DeviceError as (msg, name): - if anaconda.intf: + if intf: if upgrading: err = _("Error enabling swap device %(name)s: " "%(msg)s\n\n" @@ -1925,14 +1974,13 @@ class FSSet(object): "device has not been initialized.\n\n" "Press OK to exit the installer.") % \ {'name': name, 'msg': msg} - anaconda.intf.messageWindow(_("Error"), err) + intf.messageWindow(_("Error"), err) sys.exit(0) break - def mountFilesystems(self, anaconda, raiseErrors=None, readOnly=None, - skipRoot=False): - intf = anaconda.intf + def mountFilesystems(self, intf=None, rootPath="", readOnly=None, + skipRoot=False, raiseErrors=None): devices = self.mountpoints.values() + self.swapDevices devices.extend([self.dev, self.devshm, self.devpts, self.sysfs, self.proc, self.selinux, self.usb]) @@ -1955,7 +2003,7 @@ class FSSet(object): # # -- bind formats' device and mountpoint are always both # under the chroot. no exceptions. none, damn it. - targetDir = "%s/%s" % (anaconda.rootPath, device.path) + targetDir = "%s/%s" % (rootPath, device.path) parent = get_containing_device(targetDir, self.devicetree) if not parent: log.error("cannot determine which device contains " @@ -1977,7 +2025,7 @@ class FSSet(object): try: device.format.setup(options=options, - chroot=anaconda.rootPath) + chroot=rootPath) except OSError as e: log.error("OSError: (%d) %s" % (e.errno, e.strerror)) diff --git a/pyanaconda/storage/fcoe.py b/pyanaconda/storage/fcoe.py index 2f64f11..79d267b 100644 --- a/pyanaconda/storage/fcoe.py +++ b/pyanaconda/storage/fcoe.py @@ -136,7 +136,7 @@ class fcoe(object): # fixme plenty (including add ks support for fcoe in general) return - def write(self, instPath, anaconda): + def write(self, instPath): if not self.nics: return diff --git a/pyanaconda/storage/iscsi.py b/pyanaconda/storage/iscsi.py index c79181f..c706c93 100644 --- a/pyanaconda/storage/iscsi.py +++ b/pyanaconda/storage/iscsi.py @@ -291,15 +291,15 @@ class iscsi(object): f.write(" --reverse-password %s" % auth.reverse_password) f.write("\n") - def write(self, instPath, anaconda): + def write(self, instPath, storage): if not self.initiatorSet: return # set iscsi nodes to autostart - root = anaconda.storage.rootDevice + root = storage.rootDevice for node in self.nodes: autostart = True - disks = self.getNodeDisks(node, anaconda.storage) + disks = self.getNodeDisks(node, storage) for disk in disks: # nodes used for root get started by the initrd if root.dependsOn(disk): diff --git a/pyanaconda/storage/partitioning.py b/pyanaconda/storage/partitioning.py index f81b5b4..a0f529a 100644 --- a/pyanaconda/storage/partitioning.py +++ b/pyanaconda/storage/partitioning.py @@ -40,13 +40,13 @@ _ = lambda x: gettext.ldgettext("anaconda", x) import logging log = logging.getLogger("storage") -def _createFreeSpacePartitions(anaconda): +def _createFreeSpacePartitions(storage): # get a list of disks that have at least one free space region of at # least the default size for new partitions disks = [] - for disk in anaconda.storage.partitioned: - if anaconda.storage.config.clearPartDisks and \ - (disk.name not in anaconda.storage.config.clearPartDisks): + for disk in storage.partitioned: + if storage.config.clearPartDisks and \ + (disk.name not in storage.config.clearPartDisks): continue part = disk.format.firstPartition @@ -64,47 +64,47 @@ def _createFreeSpacePartitions(anaconda): # create a separate pv partition for each disk with free space devs = [] for disk in disks: - if anaconda.storage.encryptedAutoPart: + if storage.encryptedAutoPart: fmt_type = "luks" - fmt_args = {"escrow_cert": anaconda.storage.autoPartEscrowCert, - "add_backup_passphrase": anaconda.storage.autoPartAddBackupPassphrase} + fmt_args = {"escrow_cert": storage.autoPartEscrowCert, + "add_backup_passphrase": storage.autoPartAddBackupPassphrase} else: fmt_type = "lvmpv" fmt_args = {} - part = anaconda.storage.newPartition(fmt_type=fmt_type, + part = storage.newPartition(fmt_type=fmt_type, fmt_args=fmt_args, grow=True, disks=[disk]) - anaconda.storage.createDevice(part) + storage.createDevice(part) devs.append(part) return (disks, devs) -def _schedulePartitions(anaconda, disks): +def _schedulePartitions(storage, disks): # # Convert storage.autoPartitionRequests into Device instances and # schedule them for creation # # First pass is for partitions only. We'll do LVs later. # - for request in anaconda.storage.autoPartitionRequests: + for request in storage.autoPartitionRequests: if request.asVol: continue if request.fstype is None: - request.fstype = anaconda.storage.defaultFSType + request.fstype = storage.defaultFSType elif request.fstype in ("prepboot", "efi") and \ - anaconda.platform.bootDevice(): + storage.platform.bootDevice(): # there should never be a need for more than one of these # partitions, so skip them. continue # This is a little unfortunate but let the backend dictate the rootfstype # so that things like live installs can do the right thing - if request.mountpoint == "/" and anaconda.storage.liveImage: - request.fstype = anaconda.storage.liveImage.format.type + if request.mountpoint == "/" and storage.liveImage: + request.fstype = storage.liveImage.format.type - dev = anaconda.storage.newPartition(fmt_type=request.fstype, + dev = storage.newPartition(fmt_type=request.fstype, size=request.size, grow=request.grow, maxsize=request.maxSize, @@ -113,13 +113,13 @@ def _schedulePartitions(anaconda, disks): weight=request.weight) # schedule the device for creation - anaconda.storage.createDevice(dev) + storage.createDevice(dev) # make sure preexisting broken lvm/raid configs get out of the way return -def _scheduleLVs(anaconda, devs): - if anaconda.storage.encryptedAutoPart: +def _scheduleLVs(storage, devs): + if storage.encryptedAutoPart: pvs = [] for dev in devs: pv = LUKSDevice("luks-%s" % dev.name, @@ -127,13 +127,13 @@ def _scheduleLVs(anaconda, devs): size=dev.size, parents=dev) pvs.append(pv) - anaconda.storage.createDevice(pv) + storage.createDevice(pv) else: pvs = devs # create a vg containing all of the autopart pvs - vg = anaconda.storage.newVG(pvs=pvs) - anaconda.storage.createDevice(vg) + vg = storage.newVG(pvs=pvs) + storage.createDevice(vg) initialVGSize = vg.size @@ -142,7 +142,7 @@ def _scheduleLVs(anaconda, devs): # schedule them for creation. # # Second pass, for LVs only. - for request in anaconda.storage.autoPartitionRequests: + for request in storage.autoPartitionRequests: if not request.asVol: continue @@ -150,24 +150,24 @@ def _scheduleLVs(anaconda, devs): continue if request.fstype is None: - request.fstype = anaconda.storage.defaultFSType + request.fstype = storage.defaultFSType # This is a little unfortunate but let the backend dictate the rootfstype # so that things like live installs can do the right thing - if request.mountpoint == "/" and anaconda.storage.liveImage: - request.fstype = anaconda.storage.liveImage.format.type + if request.mountpoint == "/" and storage.liveImage: + request.fstype = storage.liveImage.format.type # FIXME: move this to a function and handle exceptions - dev = anaconda.storage.newLV(vg=vg, - fmt_type=request.fstype, - mountpoint=request.mountpoint, - grow=request.grow, - maxsize=request.maxSize, - size=request.size, - singlePV=request.singlePV) + dev = storage.newLV(vg=vg, + fmt_type=request.fstype, + mountpoint=request.mountpoint, + grow=request.grow, + maxsize=request.maxSize, + size=request.size, + singlePV=request.singlePV) # schedule the device for creation - anaconda.storage.createDevice(dev) + storage.createDevice(dev) def doAutoPartition(anaconda): @@ -193,7 +193,7 @@ def doAutoPartition(anaconda): anaconda.bootloader.updateDriveList(anaconda.bootloader.drivelist) if anaconda.storage.doAutoPart: - (disks, devs) = _createFreeSpacePartitions(anaconda) + (disks, devs) = _createFreeSpacePartitions(anaconda.storage) if disks == []: if anaconda.ksdata: @@ -212,7 +212,7 @@ def doAutoPartition(anaconda): anaconda.storage.reset() return DISPATCH_BACK - _schedulePartitions(anaconda, disks) + _schedulePartitions(anaconda.storage, disks) # sanity check the individual devices log.warning("not sanity checking devices because I don't know how yet") @@ -222,7 +222,7 @@ def doAutoPartition(anaconda): doPartitioning(anaconda.storage) if anaconda.storage.doAutoPart: - _scheduleLVs(anaconda, devs) + _scheduleLVs(anaconda.storage, devs) # grow LVs growLVM(anaconda.storage) @@ -337,7 +337,7 @@ def shouldClear(device, clearPartType, clearPartDisks=None): return True -def clearPartitions(storage): +def clearPartitions(storage, bootloader=None): """ Clear partitions and dependent devices from disks. Arguments: @@ -346,7 +346,7 @@ def clearPartitions(storage): Keyword arguments: - None + bootloader -- a bootloaderInfo instance NOTES: @@ -357,6 +357,9 @@ def clearPartitions(storage): # not much to do return + if not hasattr(storage.platform, "diskLabelType"): + raise StorageError("can't clear partitions without platform data") + # we are only interested in partitions that physically exist partitions = [p for p in storage.partitions if p.exists] # Sort partitions by descending partition number to minimize confusing @@ -407,15 +410,13 @@ def clearPartitions(storage): # now remove any empty extended partitions removeEmptyExtendedPartitions(storage) - _platform = storage.anaconda.platform - # make sure that the the boot device has the correct disklabel type if # we're going to completely clear it. for disk in storage.partitioned: - if not storage.anaconda.bootloader.drivelist: + if not bootloader or not bootloader.drivelist: break - if disk.name != storage.anaconda.bootloader.drivelist[0]: + if disk.name != bootloader.drivelist[0]: continue if storage.config.clearPartType != CLEARPART_TYPE_ALL or \ @@ -431,7 +432,7 @@ def clearPartitions(storage): if filter(lambda p: p.dependsOn(disk), storage.protectedDevices): continue - nativeLabelType = _platform.diskLabelType(disk.partedDevice.type) + nativeLabelType = storage.platform.diskLabelType(disk.partedDevice.type) if disk.format.labelType == nativeLabelType: continue @@ -806,7 +807,7 @@ def getFreeRegions(disks): return free -def doPartitioning(storage): +def doPartitioning(storage, bootloader=None): """ Allocate and grow partitions. When this function returns without error, all PartitionDevice @@ -819,8 +820,14 @@ def doPartitioning(storage): storage - Main anaconda Storage instance + Keyword/Optional Arguments: + + bootloader - bootloaderInfo instance + """ - anaconda = storage.anaconda + if not hasattr(storage.platform, "diskLabelType"): + raise StorageError("can't allocate partitions without platform data") + disks = storage.partitioned if storage.config.exclusiveDisks: disks = [d for d in disks if d.name in storage.config.exclusiveDisks] @@ -845,7 +852,7 @@ def doPartitioning(storage): # FIXME: isn't there a better place for this to happen? try: - bootDev = anaconda.platform.bootDevice() + bootDev = storage.platform.bootDevice() except DeviceError: bootDev = None @@ -854,7 +861,7 @@ def doPartitioning(storage): removeNewPartitions(disks, partitions) free = getFreeRegions(disks) - allocatePartitions(storage, disks, partitions, free) + allocatePartitions(storage, disks, partitions, free, bootloader=bootloader) growPartitions(disks, partitions, free) # The number and thus the name of partitions may have changed now, @@ -908,7 +915,7 @@ def doPartitioning(storage): # moment to simplify things storage.devicetree._addDevice(device) -def allocatePartitions(storage, disks, partitions, freespace): +def allocatePartitions(storage, disks, partitions, freespace, bootloader=None): """ Allocate partitions based on requested features. Non-existing partitions are sorted according to their requested @@ -960,8 +967,9 @@ def allocatePartitions(storage, disks, partitions, freespace): req_disks.sort(key=lambda d: d.name, cmp=storage.compareDisks) boot_index = None for disk in req_disks: - if disk.name in storage.anaconda.bootloader.drivelist and \ - disk.name == storage.anaconda.bootloader.drivelist[0]: + if bootloader and \ + disk.name in bootloader.drivelist and \ + disk.name == bootloader.drivelist[0]: boot_index = req_disks.index(disk) if boot_index is not None and len(req_disks) > 1: -- 1.7.3.4 _______________________________________________ Anaconda-devel-list mailing list Anaconda-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/anaconda-devel-list