All storage related. Lots to do with uninitialized disk handling.
--- pyanaconda/storage/__init__.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py index 00c8486..60acb4d 100644 --- a/pyanaconda/storage/__init__.py +++ b/pyanaconda/storage/__init__.py @@ -753,7 +753,7 @@ class Storage(object): clearPartType = self.config.clearPartType
free = {} - for disk in [d for d in disks if d.partitioned]: + for disk in disks: should_clear = shouldClear(disk, clearPartType, [disk.name]) if should_clear: free[disk.name] = (Size(spec="%f mb" % disk.size), 0)
--- pyanaconda/storage/__init__.py | 9 ++++++--- pyanaconda/storage/partitioning.py | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py index 60acb4d..5919de6 100644 --- a/pyanaconda/storage/__init__.py +++ b/pyanaconda/storage/__init__.py @@ -509,9 +509,7 @@ class Storage(object): if self.bootloader: # clear out bootloader attributes that refer to devices that are # no longer in the tree - boot_disks = [d for d in self.disks if d.partitioned] - boot_disks.sort(cmp=self.compareDisks, key=lambda d: d.name) - self.bootloader.set_disk_list(boot_disks) + self.updateBootLoaderDiskList() self.bootloader.stage1_disk = None self.bootloader.stage1_device = None self.bootloader.stage2_device = None @@ -1526,6 +1524,11 @@ class Storage(object): self._bootloader = self.platform.bootloaderClass(self.platform) return self._bootloader
+ def updateBootLoaderDiskList(self): + boot_disks = [d for d in self.disks if d.partitioned] + boot_disks.sort(cmp=self.compareDisks, key=lambda d: d.name) + self.bootloader.set_disk_list(boot_disks) + def setUpBootLoader(self): """ Propagate ksdata into BootLoader. """ if not self.bootloader or not self.data: diff --git a/pyanaconda/storage/partitioning.py b/pyanaconda/storage/partitioning.py index e057999..d764134 100644 --- a/pyanaconda/storage/partitioning.py +++ b/pyanaconda/storage/partitioning.py @@ -524,6 +524,9 @@ def clearPartitions(storage): storage.devicetree.registerAction(destroy_action) storage.devicetree.registerAction(create_action)
+ if storage.bootloader: + storage.updateBootLoaderDiskList() + def removeEmptyExtendedPartitions(storage): for disk in storage.partitioned: log.debug("checking whether disk %s has an empty extended" % disk.name)
--- pyanaconda/storage/__init__.py | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py index 5919de6..195ebef 100644 --- a/pyanaconda/storage/__init__.py +++ b/pyanaconda/storage/__init__.py @@ -525,6 +525,10 @@ class Storage(object): for device in root.mounts.values() + root.swaps: used_devices.extend(device.ancestors)
+ for new in [d for d in self.devicetree.leaves if not d.exists]: + if new in self.swaps or getattr(new.format, "mountpoint", None): + used_devices.extend(new.ancestors) + used = set(used_devices) _all = set(self.devices) return list(_all.difference(used))
Unset autopart flag after running ksdata.autopart.execute since we are still techically doing custom partitioning.
Don't show devices queued for removal in the custom spoke.
Don't include Roots whose root device has been queued for removal. --- pyanaconda/ui/gui/spokes/custom.py | 60 +++++++++++++++++------------------ 1 files changed, 29 insertions(+), 31 deletions(-)
diff --git a/pyanaconda/ui/gui/spokes/custom.py b/pyanaconda/ui/gui/spokes/custom.py index a54d274..4398025 100644 --- a/pyanaconda/ui/gui/spokes/custom.py +++ b/pyanaconda/ui/gui/spokes/custom.py @@ -35,6 +35,7 @@ P_ = lambda x, y, z: gettext.ldngettext("anaconda", x, y, z) from pyanaconda.product import productName, productVersion from pyanaconda.storage.formats import device_formats from pyanaconda.storage.size import Size +from pyanaconda.storage import Root
from pyanaconda.ui.gui import UIObject from pyanaconda.ui.gui.spokes import NormalSpoke @@ -47,6 +48,8 @@ from gi.repository import Gtk
__all__ = ["CustomPartitioningSpoke"]
+new_install_name = = _("New %s %s Installation") % (productName, productVersion) + class AddDialog(UIObject): builderObjects = ["addDialog"] mainWidgetName = "addDialog" @@ -240,11 +243,15 @@ class CustomPartitioningSpoke(NormalSpoke): # We can only have one page expanded at a time. did_expand = False
+ unused = self._unusedDevices() + new_devices = [d for d in self.storage.devicetree.leaves if d not in unused and not d.exists] + roots = self.storage.roots[:] + # If we've not yet run autopart, add an instance of CreateNewPage. This # ensures it's only added once. - if not self._ran_autopart: + if not self._ran_autopart and not new_devices: page = CreateNewPage(self.on_create_clicked) - page.pageTitle = _("New %s %s Installation") % (productName, productVersion) + page.pageTitle = new_install_name self._accordion.addPage(page, cb=self.on_page_clicked) self._accordion.expandPage(page.pageTitle) did_expand = True @@ -252,18 +259,32 @@ class CustomPartitioningSpoke(NormalSpoke): self._partitionsNotebook.set_current_page(0) label = self.builder.get_object("whenCreateLabel") label.set_text(self._when_create_text % (productName, productVersion)) + elif new_devices: + swaps = [d for d in new_devices if d in self.storage.swaps] + mounts = dict([(d.format.mountpoint, d) for d in new_devices if getattr(d.format, "mountpoint", None)]) + new_root = Root(mounts=mounts, swaps=swaps, name=new_install_name) + roots.insert(0, new_root)
# Add in all the existing (or autopart-created) operating systems. - for root in self.storage.roots: + for root in roots: + if root.device and root.device not in self.storage.devices: + continue + page = Page() page.pageTitle = root.name
- for swap in root.swaps: - selector = page.addDevice("Swap", swap.size, None, self.on_selector_clicked) - selector._device = swap + for device in root.swaps: + if device not in self.storage.devices: + continue + + selector = page.addDevice("Swap", device.size, None, self.on_selector_clicked) + selector._device = device selector._root = root
for (mountpoint, device) in root.mounts.iteritems(): + if device not in self.storage.devices: + continue + selector = page.addDevice(self._mountpointName(mountpoint) or device.format.name, device.size, mountpoint, self.on_selector_clicked) selector._device = device selector._root = root @@ -276,7 +297,6 @@ class CustomPartitioningSpoke(NormalSpoke): self._accordion.expandPage(root.name)
# Anything that doesn't go with an OS we understand? Put it in the Other box. - unused = self._unusedDevices() if unused: page = UnknownPage() page.pageTitle = _("Unknown") @@ -539,29 +559,7 @@ class CustomPartitioningSpoke(NormalSpoke): # FIXME: Handle all the autopart exns here. self.data.autopart.autopart = True self.data.autopart.execute(self.storage, self.data, self.instclass) - - # Create a new Root object for the new installation and put it into - # the storage object. This means any boot-related devices we make for - # this new install (biosboot, etc.) will show up as unused and therefore - # be put into the Unknown page. - mounts = {} - swaps = [] - - # Devices just created by autopartitioning will be listed as unused - # since they are not yet a part of any known Root. - for device in self._unusedDevices(): - if device.exists: - continue - - if device.format.type == "swap": - swaps.append(device) - - if getattr(device.format, "mountpoint", None): - mounts[device.format.mountpoint] = device - - newName = _("New %s %s Installation") % (productName, productVersion) - root = Root(mounts=mounts, swaps=swaps, name=newName) - self.storage.roots.append(root) + self.data.autopart.autopart = False
# Setting this ensures the CreateNewPage instance does not reappear when # refresh is called. @@ -569,7 +567,7 @@ class CustomPartitioningSpoke(NormalSpoke):
# And refresh the spoke to make the new partitions appear. self.refresh() - self._accordion.expandPage(newName) + self._accordion.expandPage(new_install_name)
def on_device_type_changed(self, combo): text = combo.get_active_text()
If autopart is selected, we re-run clearpart and autopart on apply. If not, we do neither. --- pyanaconda/ui/gui/spokes/storage.py | 18 +++++++++++++----- 1 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/pyanaconda/ui/gui/spokes/storage.py b/pyanaconda/ui/gui/spokes/storage.py index aa9c3ae..626634b 100644 --- a/pyanaconda/ui/gui/spokes/storage.py +++ b/pyanaconda/ui/gui/spokes/storage.py @@ -50,6 +50,7 @@ from pyanaconda.ui.gui.utils import enlightbox, gdk_threaded from pyanaconda.storage.size import Size from pyanaconda.storage.partitioning import shouldClear from pyanaconda.product import productName +from pyanaconda.flags import flags
from pykickstart.constants import *
@@ -241,6 +242,11 @@ class StorageSpoke(NormalSpoke): NormalSpoke.__init__(self, *args, **kwargs) self._ready = False self.selected_disks = self.data.clearpart.drives[:] + + if not flags.automatedInstall: + # default to using autopart for interactive installs + self.data.autopart.autopart = True + self.autopart = self.data.autopart.autopart
# FIXME: This needs to be set to a real value via some TBD UI. @@ -260,18 +266,20 @@ class StorageSpoke(NormalSpoke):
self.data.bootloader.location = "mbr"
+ self.data.clearpart.type = self.clearPartType + + if self.autopart: + self.data.clearpart.execute(self.storage, self.data, self.instclass) + # Pick the first disk to be the destination device for the bootloader. # This appears to be the minimum amount of configuration required to # make autopart happy with the bootloader settings. if not self.data.bootloader.bootDrive: self.data.bootloader.bootDrive = self.storage.bootloader.disks[0].name
- self.data.clearpart.type = self.clearPartType - - # FIXME: this will not work when users enter this spoke multiple times - # unless they just keep doing autopart - self.data.clearpart.execute(self.storage, self.data, self.instclass) self.data.bootloader.execute(self.storage, self.data, self.instclass) + + # this won't do anything if autopart is not selected self.data.autopart.execute(self.storage, self.data, self.instclass)
@property
--- pyanaconda/bootloader.py | 25 ++++--------------------- pyanaconda/storage/__init__.py | 7 +++++-- pyanaconda/storage/devices.py | 16 ++++++---------- pyanaconda/ui/gui/spokes/storage.py | 2 +- 4 files changed, 16 insertions(+), 34 deletions(-)
diff --git a/pyanaconda/bootloader.py b/pyanaconda/bootloader.py index 658edba..991c8de 100644 --- a/pyanaconda/bootloader.py +++ b/pyanaconda/bootloader.py @@ -980,8 +980,8 @@ class GRUB(BootLoader):
packages = ["grub"]
- def __init__(self, storage): - super(GRUB, self).__init__(storage) + def __init__(self, platform=None): + super(GRUB, self).__init__(platform=platform) self.encrypted_password = ""
# @@ -1357,8 +1357,8 @@ class GRUB2(GRUB): stage2_raid_levels = [mdraid.RAID0, mdraid.RAID1, mdraid.RAID4, mdraid.RAID5, mdraid.RAID6, mdraid.RAID10]
- def __init__(self, storage): - super(GRUB2, self).__init__(storage) + def __init__(self, platform=None): + super(GRUB2, self).__init__(platform=platform) self.boot_args.add("$([ -x /usr/sbin/rhcrashkernel-param ] && "\ "/usr/sbin/rhcrashkernel-param || :)")
@@ -1708,9 +1708,6 @@ class Yaboot(YabootSILOBase): stage2_device_types = ["partition", "mdarray"] stage2_device_raid_levels = [mdraid.RAID1]
- def __init__(self, storage): - BootLoader.__init__(self, storage) - # # configuration # @@ -2000,20 +1997,6 @@ class ZIPL(BootLoader): buf = iutil.execWithCapture("zipl", [], stderr="/dev/tty5", root=ROOT_PATH) - for line in buf.splitlines(): - if line.startswith("Preparing boot device: "): - # Output here may look like: - # Preparing boot device: dasdb (0200). - # Preparing boot device: dasdl. - # We want to extract the device name and pass that. - name = re.sub(".+?: ", "", line) - name = re.sub("(\s(.+))?.$", "", name) - device = self.storage.devicetree.getDeviceByName(name) - if not device: - raise BootLoaderError("could not find IPL device") - - self.stage1_device = device -
class SILO(YabootSILOBase): name = "SILO" diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py index 195ebef..be795b3 100644 --- a/pyanaconda/storage/__init__.py +++ b/pyanaconda/storage/__init__.py @@ -1021,8 +1021,11 @@ class Storage(object): # set up the volume label, using hostname if necessary if not name: hostname = "" - if hasattr(self.anaconda, "network"): - hostname = self.anaconda.network.hostname + if self.data: + for nd in self.data.network.dataList(): + if nd.hostname: + hostname = nd.hostname + break
name = self.suggestContainerName(prefix="btrfs", hostname=hostname) diff --git a/pyanaconda/storage/devices.py b/pyanaconda/storage/devices.py index 9c69dfb..1e22138 100644 --- a/pyanaconda/storage/devices.py +++ b/pyanaconda/storage/devices.py @@ -3909,9 +3909,6 @@ class BTRFSDevice(StorageDevice): self.sysfsPath = self.parents[0].sysfsPath log.debug("%s sysfsPath set to %s" % (self.name, self.sysfsPath))
- def _statusWindow(self, intf=None, title="", msg=""): - return self._progressWindow(intf=intf, title=title, msg=msg) - def _postCreate(self): super(BTRFSDevice, self)._postCreate() self.format.exists = True @@ -4066,24 +4063,23 @@ class BTRFSVolumeDevice(BTRFSDevice):
return subvols
- def createSubVolumes(self, intf=None): + def createSubVolumes(self): self._do_temp_mount() for name, subvol in self.subvolumes: if subvol.exists: continue - subvolume.create(mountpoint=self._temp_dir_prefix, intf=intf) + subvolume.create(mountpoint=self._temp_dir_prefix) self._undo_temp_mount()
def removeSubVolume(self, name): raise NotImplementedError()
- def _create(self, w): + def _create(self): log_method_call(self, self.name, status=self.status) btrfs.create_volume(devices=[d.path for d in self.parents], label=self.format.label, data=self.dataLevel, - metadata=self.metaDataLevel, - progress=w) + metadata=self.metaDataLevel)
def _destroy(self): log_method_call(self, self.name, status=self.status) @@ -4110,14 +4106,14 @@ class BTRFSSubVolumeDevice(BTRFSDevice): log_method_call(self, name=self.name, orig=orig, kids=self.kids) self.volume.setup(orig=orig)
- def _create(self, w): + def _create(self): log_method_call(self, self.name, status=self.status) self.volume._do_temp_mount() mountpoint = self.volume.format._mountpoint if not mountpoint: raise RuntimeError("btrfs subvol create requires mounted volume")
- btrfs.create_subvolume(mountpoint, self.name, progress=w) + btrfs.create_subvolume(mountpoint, self.name) self.volume._undo_temp_mount()
def _destroy(self): diff --git a/pyanaconda/ui/gui/spokes/storage.py b/pyanaconda/ui/gui/spokes/storage.py index 626634b..4e597a7 100644 --- a/pyanaconda/ui/gui/spokes/storage.py +++ b/pyanaconda/ui/gui/spokes/storage.py @@ -257,7 +257,7 @@ class StorageSpoke(NormalSpoke): self.data.autopart.autopart = self.autopart
# no thanks, lvm - self.data.autopart.lvm = False + self.data.autopart.type = AUTOPART_TYPE_PLAIN
if self.autopart: self.clearPartType = CLEARPART_TYPE_ALL
--- pyanaconda/storage/__init__.py | 17 ++++++++++++++++- pyanaconda/storage/partitioning.py | 22 ---------------------- 2 files changed, 16 insertions(+), 23 deletions(-)
diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py index be795b3..e05732f 100644 --- a/pyanaconda/storage/__init__.py +++ b/pyanaconda/storage/__init__.py @@ -509,7 +509,6 @@ class Storage(object): if self.bootloader: # clear out bootloader attributes that refer to devices that are # no longer in the tree - self.updateBootLoaderDiskList() self.bootloader.stage1_disk = None self.bootloader.stage1_device = None self.bootloader.stage2_device = None @@ -518,6 +517,22 @@ class Storage(object):
self.dumpState("initial")
+ # if zerombr is set, go ahead and slap a disklabel on any disk that + # doesn't have (recognizeable) formatting + if self.config.zeroMbr: + for disk in self.disks: + if disk.format.type is None: + log.info("zerombr: initializing %s" % disk.name) + + labelType = storage.platform.bestDiskLabelType(disk) + newLabel = getFormat("disklabel", device=disk.path, + labelType=labelType) + create_action = ActionCreateFormat(disk, format=newLabel) + storage.devicetree.registerAction(create_action) + + # we may have added candidate boot disks just above by initializing + self.updateBootLoaderDiskList() + @property def unusedDevices(self): used_devices = [] diff --git a/pyanaconda/storage/partitioning.py b/pyanaconda/storage/partitioning.py index d764134..1cb9b94 100644 --- a/pyanaconda/storage/partitioning.py +++ b/pyanaconda/storage/partitioning.py @@ -455,28 +455,6 @@ def clearPartitions(storage): recursiveRemove(storage, part) log.debug("partitions: %s" % [p.getDeviceNodeName() for p in part.partedPartition.disk.partitions])
- # now clear devices that are not partitions and were not implicitly cleared - # when clearing partitions - not_visited = [d for d in storage.devices if not d.partitioned] - while not_visited: - for device in [d for d in not_visited if d.isleaf]: - not_visited.remove(device) - - if not shouldClear(device, storage.config.clearPartType, storage.config.clearPartDisks, storage.config.clearPartDevices): - continue - - recursiveRemove(storage, device) - - # put disklabels on unpartitioned disks - if device.isDisk and not d.partitioned: - labelType = storage.platform.bestDiskLabelType(disk) - newLabel = getFormat("disklabel", device=disk.path, - labelType=labelType) - create_action = ActionCreateFormat(disk, format=newLabel) - storage.devicetree.registerAction(create_action) - - not_visited = [d for d in storage.devices if d in not_visited] - # now remove any empty extended partitions removeEmptyExtendedPartitions(storage)
--- pyanaconda/storage/devicetree.py | 39 -------------------------------------- 1 files changed, 0 insertions(+), 39 deletions(-)
diff --git a/pyanaconda/storage/devicetree.py b/pyanaconda/storage/devicetree.py index 25258d7..0070a87 100644 --- a/pyanaconda/storage/devicetree.py +++ b/pyanaconda/storage/devicetree.py @@ -1101,26 +1101,6 @@ class DeviceTree(object): pass return
- # if the disk contains protected partitions we will not wipe the - # disklabel even if clearpart --initlabel was specified - if not self.clearPartDisks or device.name in self.clearPartDisks: - initlabel = self.reinitializeDisks - sysfs_path = udev_device_get_sysfs_path(info) - for protected in self.protectedDevNames: - # check for protected partition - _p = "/sys/%s/%s" % (sysfs_path, protected) - if os.path.exists(os.path.normpath(_p)): - initlabel = False - break - - # check for protected partition on a device-mapper disk - disk_name = re.sub(r'p\d+$', '', protected) - if disk_name != protected and disk_name == device.name: - initlabel = False - break - else: - initlabel = False - # we're going to pass the "best" disklabel type into the DiskLabel # constructor, but it only has meaning for non-existent disklabels. labelType = self.platform.bestDiskLabelType(device) @@ -1133,25 +1113,6 @@ class DeviceTree(object): except InvalidDiskLabelError: log.info("no usable disklabel on %s" % device.name) return - - if not format.exists: - # if we just initialized a disklabel we should schedule - # actions for destruction of the previous format and creation - # of the new one - self.registerAction(ActionDestroyFormat(device)) - self.registerAction(ActionCreateFormat(device, format)) - - # If this is a mac-formatted disk we just initialized, make - # sure the partition table partition gets added to the device - # tree. - if device.format.partedDisk.type == "mac" and \ - len(device.format.partitions) == 1: - name = device.format.partitions[0].getDeviceNodeName() - if not self.getDeviceByName(name): - partDevice = PartitionDevice(name, exists=True, - parents=[device]) - self._addDevice(partDevice) - else: device.format = format
--- pyanaconda/kickstart.py | 6 +-- pyanaconda/storage/__init__.py | 73 ++++++++++++++++++++++++++++++++--- pyanaconda/storage/devicetree.py | 16 ++----- pyanaconda/storage/partitioning.py | 60 +--------------------------- pyanaconda/ui/gui/spokes/storage.py | 1 - 5 files changed, 75 insertions(+), 81 deletions(-)
diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py index 78a1763..9b7124e 100644 --- a/pyanaconda/kickstart.py +++ b/pyanaconda/kickstart.py @@ -25,7 +25,6 @@ from storage.devicelibs.lvm import getPossiblePhysicalExtents from storage.devicelibs.mpath import MultipathConfigWriter, MultipathTopology from storage.formats import getFormat from storage.partitioning import clearPartitions -from storage.partitioning import shouldClear import storage.iscsi import storage.fcoe import storage.zfcp @@ -937,10 +936,7 @@ class PartitionData(commands.partition.F17_PartData): if not disk.partitionable: raise KickstartValueError, formatErrorMsg(self.lineno, msg="Cannot install to read-only media %s." % n)
- should_clear = shouldClear(disk, - storage.config.clearPartType, - storage.config.clearPartDisks, - storage.config.clearPartDevices) + should_clear = storage.shouldClear(disk) if disk and (disk.partitioned or should_clear): kwargs["disks"] = [disk] break diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py index e05732f..79782a2 100644 --- a/pyanaconda/storage/__init__.py +++ b/pyanaconda/storage/__init__.py @@ -394,7 +394,8 @@ class Storage(object): passphrase=self.encryptionPassphrase, luksDict=self.__luksDevs, iscsi=self.iscsi, - dasd=self.dasd) + dasd=self.dasd, + shouldClear=self.shouldClear) self.fsset = FSSet(self.devicetree) self.roots = {} self.services = set() @@ -501,7 +502,8 @@ class Storage(object): passphrase=self.encryptionPassphrase, luksDict=self.__luksDevs, iscsi=self.iscsi, - dasd=self.dasd) + dasd=self.dasd, + shouldClear=self.shouldClear) self.devicetree.populate(cleanupOnly=cleanupOnly) self.config.clearPartType = clearPartType # set it back self.fsset = FSSet(self.devicetree) @@ -750,6 +752,64 @@ class Storage(object): """ The OS image used by live installs. """ return None
+ def shouldClear(self, device, **kwargs): + clearPartType = kwargs.get("clearPartType", self.config.clearPartType) + clearPartDisks = kwargs.get("clearPartDisks", + self.config.clearPartDisks) + clearPartDevices = kwargs.get("clearPartDevices", + self.config.clearPartDevices) + + if clearPartType in [CLEARPART_TYPE_NONE, None]: + return False + + if isinstance(device, PartitionDevice): + # Never clear the special first partition on a Mac disk label, as + # that holds the partition table itself. + # Something similar for the third partition on a Sun disklabel. + if device.disk.format.labelType == "mac" and \ + device.partedPartition.number == 1: + return False + elif device.disk.format.labelType == "sun" and \ + device.partedPartition.number == 3: + return False + + # If we got a list of disks to clear, make sure this one's on it + if clearPartDisks and device.disk.name not in clearPartDisks: + return False + + # We don't want to fool with extended partitions, freespace, &c + if not device.isPrimary and not device.isLogical: + return False + + if clearPartType == CLEARPART_TYPE_LINUX and \ + not device.format.linuxNative and \ + not device.getFlag(parted.PARTITION_LVM) and \ + not device.getFlag(parted.PARTITION_RAID) and \ + not device.getFlag(parted.PARTITION_SWAP): + return False + elif device.isDisk and not device.partitioned: + # If we got a list of disks to clear, make sure this one's on it + if clearPartDisks and device.name not in clearPartDisks: + return False + + # Never clear disks with hidden formats + if device.format.hidden: + return False + + if clearPartType == CLEARPART_TYPE_LINUX and \ + not device.format.linuxNative: + return False + + # Don't clear devices holding install media. + if device.protected: + return False + + if clearPartType == CLEARPART_TYPE_LIST and \ + device.name not in clearPartDevices: + return False + + return True + def getFreeSpace(self, disks=None, clearPartType=None): """ Return a dict with free space info for each disk.
@@ -761,7 +821,6 @@ class Storage(object): self.disks and a clearPartType value other than self.config.clearPartType. """ - from partitioning import shouldClear from size import Size if disks is None: disks = self.disks @@ -771,7 +830,8 @@ class Storage(object):
free = {} for disk in disks: - should_clear = shouldClear(disk, clearPartType, [disk.name]) + should_clear = self.shouldClear(disk, clearPartType=clearPartType, + clearPartDisks=[disk.name]) if should_clear: free[disk.name] = (Size(spec="%f mb" % disk.size), 0) continue @@ -784,8 +844,9 @@ class Storage(object): # only check actual filesystems since lvm &c require a bunch of # operations to translate free filesystem space into free disk # space - should_clear = shouldClear(partition, clearPartType, - [disk.name]) + should_clear = self.shouldClear(partition, + clearPartType=clearPartType, + clearPartDisks=[disk.name]) if should_clear: disk_free += partition.size elif hasattr(partition.format, "free"): diff --git a/pyanaconda/storage/devicetree.py b/pyanaconda/storage/devicetree.py index 0070a87..5d50986 100644 --- a/pyanaconda/storage/devicetree.py +++ b/pyanaconda/storage/devicetree.py @@ -30,7 +30,6 @@ import pprint from errors import * from devices import * from deviceaction import * -from partitioning import shouldClear from pykickstart.constants import * import formats import devicelibs.mdraid @@ -155,11 +154,11 @@ class DeviceTree(object): """
def __init__(self, conf=None, passphrase=None, luksDict=None, - iscsi=None, dasd=None): - self.reset(conf, passphrase, luksDict, iscsi, dasd) + iscsi=None, dasd=None, shouldClear=None): + self.reset(conf, passphrase, luksDict, iscsi, dasd, shouldClear)
def reset(self, conf=None, passphrase=None, luksDict=None, - iscsi=None, dasd=None): + iscsi=None, dasd=None, shouldClear=None): # internal data members self._devices = [] self._actions = [] @@ -171,11 +170,8 @@ class DeviceTree(object): self.populated = False
self.exclusiveDisks = getattr(conf, "exclusiveDisks", []) - self.clearPartType = getattr(conf, "clearPartType", CLEARPART_TYPE_NONE) - self.clearPartDisks = getattr(conf, "clearPartDisks", []) - self.clearPartDevices = getattr(conf, "clearPartDevices", []) self.zeroMbr = getattr(conf, "zeroMbr", False) - self.reinitializeDisks = getattr(conf, "reinitializeDisks", False) + self.shouldClear = shouldClear or (lambda d: False) self.iscsi = iscsi self.dasd = dasd self.mpathFriendlyNames = getattr(conf, "mpathFriendlyNames", True) @@ -1632,9 +1628,7 @@ class DeviceTree(object): device.format = formats.DeviceFormat() return
- if shouldClear(device, self.clearPartType, - clearPartDisks=self.clearPartDisks, - clearPartDevices=self.clearPartDevices): + if self.shouldClear(device): # if this is a device that will be cleared by clearpart, # don't bother with format-specific processing return diff --git a/pyanaconda/storage/partitioning.py b/pyanaconda/storage/partitioning.py index 1cb9b94..43f77a7 100644 --- a/pyanaconda/storage/partitioning.py +++ b/pyanaconda/storage/partitioning.py @@ -341,63 +341,6 @@ def doAutoPartition(storage, data): storage.reset() raise exn
-def shouldClear(device, clearPartType, clearPartDisks=None, clearPartDevices=None): - if clearPartType in [CLEARPART_TYPE_NONE, None]: - return False - if not clearPartDisks: - clearPartDisks = [] - if not clearPartDevices: - clearPartDevices = [] - - if isinstance(device, PartitionDevice): - # Never clear the special first partition on a Mac disk label, as that - # holds the partition table itself. - # Something similar for the third partition on a Sun disklabel. - if device.disk.format.partedDisk.type == "mac" and \ - device.partedPartition.number == 1: - return False - elif device.disk.format.labelType == "sun" and \ - device.partedPartition.number == 3: - return False - - # If we got a list of disks to clear, make sure this one's on it - if device.disk.name not in clearPartDisks: - return False - - # We don't want to fool with extended partitions, freespace, &c - if device.partType not in [parted.PARTITION_NORMAL, - parted.PARTITION_LOGICAL]: - return False - - if clearPartType == CLEARPART_TYPE_LINUX and \ - not device.format.linuxNative and \ - not device.getFlag(parted.PARTITION_LVM) and \ - not device.getFlag(parted.PARTITION_RAID) and \ - not device.getFlag(parted.PARTITION_SWAP): - return False - elif device.isDisk and not device.partitioned: - # If we got a list of disks to clear, make sure this one's on it - if device.name not in clearPartDisks: - return False - - # Never clear disks with hidden formats - if device.format.hidden: - return False - - if clearPartType == CLEARPART_TYPE_LINUX and \ - not device.format.linuxNative: - return False - - # Don't clear devices holding install media. - if device.protected: - return False - - if clearPartType == CLEARPART_TYPE_LIST and \ - not clearPartDevices or device.name not in clearPartDevices: - return False - - return True - def recursiveRemove(storage, device): log.debug("clearing %s" % device.name)
@@ -449,7 +392,7 @@ def clearPartitions(storage): partitions.sort(key=lambda p: p.partedPartition.number, reverse=True) for part in partitions: log.debug("clearpart: looking at %s" % part.name) - if not shouldClear(part, storage.config.clearPartType, storage.config.clearPartDisks, storage.config.clearPartDevices): + if not storage.shouldClear(part): continue
recursiveRemove(storage, part) @@ -460,6 +403,7 @@ def clearPartitions(storage):
# make sure that the the boot device has the correct disklabel type if # we're going to completely clear it. + # also handles clearpart --initlabel boot_disk = storage.bootDisk for disk in storage.partitioned: if not boot_disk and not storage.config.reinitializeDisks: diff --git a/pyanaconda/ui/gui/spokes/storage.py b/pyanaconda/ui/gui/spokes/storage.py index 4e597a7..055e3b6 100644 --- a/pyanaconda/ui/gui/spokes/storage.py +++ b/pyanaconda/ui/gui/spokes/storage.py @@ -48,7 +48,6 @@ from pyanaconda.ui.gui.categories.storage import StorageCategory from pyanaconda.ui.gui.utils import enlightbox, gdk_threaded
from pyanaconda.storage.size import Size -from pyanaconda.storage.partitioning import shouldClear from pyanaconda.product import productName from pyanaconda.flags import flags
--- pyanaconda/kickstart.py | 3 +- pyanaconda/storage/__init__.py | 117 ++++++++++++++++++++++++++++++++++ pyanaconda/storage/partitioning.py | 123 +----------------------------------- 3 files changed, 119 insertions(+), 124 deletions(-)
diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py index 9b7124e..a3a6c5d 100644 --- a/pyanaconda/kickstart.py +++ b/pyanaconda/kickstart.py @@ -24,7 +24,6 @@ from storage.devices import LUKSDevice from storage.devicelibs.lvm import getPossiblePhysicalExtents from storage.devicelibs.mpath import MultipathConfigWriter, MultipathTopology from storage.formats import getFormat -from storage.partitioning import clearPartitions import storage.iscsi import storage.fcoe import storage.zfcp @@ -415,7 +414,7 @@ class ClearPart(commands.clearpart.F17_ClearPart): if self.initAll: storage.config.reinitializeDisks = self.initAll
- clearPartitions(storage) + storage.clearPartitions()
class Fcoe(commands.fcoe.F13_Fcoe): def parse(self, args): diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py index 79782a2..612ee7d 100644 --- a/pyanaconda/storage/__init__.py +++ b/pyanaconda/storage/__init__.py @@ -810,6 +810,120 @@ class Storage(object):
return True
+ def recursiveRemove(self, device): + log.debug("removing %s" % device.name) + + # XXX is there any argument for not removing incomplete devices? + # -- maybe some RAID devices + devices = self.deviceDeps(device) + while devices: + log.debug("devices to remove: %s" % ([d.name for d in devices],)) + leaves = [d for d in devices if d.isleaf] + log.debug("leaves to remove: %s" % ([d.name for d in leaves],)) + for leaf in leaves: + self.destroyDevice(leaf) + devices.remove(leaf) + + if device.isDisk: + self.destroyFormat(device) + else: + self.destroyDevice(device) + + def clearPartitions(self): + """ Clear partitions and dependent devices from disks. + + Arguments: + + None + + NOTES: + + - Needs some error handling + + """ + if self.config.clearPartType in (None, CLEARPART_TYPE_NONE): + # not much to do + return + + # XXX FIXME: We can still clear partitions. What we can't do is create + # appropriately-typed disklabels. + if not hasattr(self.platform, "diskLabelTypes"): + raise StorageError("can't clear partitions without platform data") + + # we are only interested in partitions that physically exist + partitions = [p for p in self.partitions if p.exists] + # Sort partitions by descending partition number to minimize confusing + # things like multiple "destroy sda5" actions due to parted renumbering + # partitions. This can still happen through the UI but it makes sense to + # avoid it where possible. + partitions.sort(key=lambda p: p.partedPartition.number, reverse=True) + for part in partitions: + log.debug("clearpart: looking at %s" % part.name) + if not self.shouldClear(part): + continue + + self.recursiveRemove(part) + log.debug("partitions: %s" % [p.getDeviceNodeName() for p in part.partedPartition.disk.partitions]) + + # now remove any empty extended partitions + self.removeEmptyExtendedPartitions() + + # make sure that the the boot device has the correct disklabel type if + # we're going to completely clear it. + # also handles clearpart --initlabel + boot_disk = self.bootDisk + for disk in self.partitioned: + if not boot_disk and not self.config.reinitializeDisks: + break + + if not self.config.reinitializeDisks and \ + (boot_disk is not None and disk != boot_disk): + continue + + if not self.shouldClear(disk): + continue + + # don't reinitialize the disklabel if the disk contains install media + # or pvs from inconsistent vgs + if filter(lambda p: p.dependsOn(disk), self.protectedDevices): + continue + + if disk.format.labelType == self.platform.bestDiskLabelType(disk): + continue + + magic_partitions = {"mac": 1, "sun": 3} + if disk.format.labelType in magic_partitions: + number = magic_partitions[disk.format.labelType] + # remove the magic partition + for part in storage.partitions: + if part.disk == disk and part.partedPartition.number == number: + log.debug("removing %s" % part.name) + # We can't schedule the magic partition for removal + # because parted will not allow us to remove it from the + # disk. Still, we need it out of the devicetree. + self.devicetree._removeDevice(part, moddisk=False) + + destroy_action = ActionDestroyFormat(disk) + newLabel = getFormat("disklabel", device=disk.path, + labelType=nativeLabelType) + create_action = ActionCreateFormat(disk, format=newLabel) + self.devicetree.registerAction(destroy_action) + self.devicetree.registerAction(create_action) + + self.updateBootLoaderDiskList() + + def removeEmptyExtendedPartitions(self): + for disk in self.partitioned: + log.debug("checking whether disk %s has an empty extended" % disk.name) + extended = disk.format.extendedPartition + logical_parts = disk.format.logicalPartitions + log.debug("extended is %s ; logicals is %s" % (extended, [p.getDeviceNodeName() for p in logical_parts])) + if extended and not logical_parts: + log.debug("removing empty extended partition from %s" % disk.name) + extended_name = devicePathToName(extended.getDeviceNodeName()) + extended = self.devicetree.getDeviceByName(extended_name) + self.destroyDevice(extended) + def getFreeSpace(self, disks=None, clearPartType=None): """ Return a dict with free space info for each disk.
@@ -1608,6 +1722,9 @@ class Storage(object): return self._bootloader
def updateBootLoaderDiskList(self): + if not self.bootloader: + return + boot_disks = [d for d in self.disks if d.partitioned] boot_disks.sort(cmp=self.compareDisks, key=lambda d: d.name) self.bootloader.set_disk_list(boot_disks) diff --git a/pyanaconda/storage/partitioning.py b/pyanaconda/storage/partitioning.py index 43f77a7..b77f499 100644 --- a/pyanaconda/storage/partitioning.py +++ b/pyanaconda/storage/partitioning.py @@ -296,7 +296,7 @@ def doAutoPartition(storage, data):
if storage.doAutoPart: scheduleShrinkActions(storage) - clearPartitions(storage) + storage.clearPartitions()
disks = _getCandidateDisks(storage) devs = _scheduleImplicitPartitions(storage, disks) @@ -341,127 +341,6 @@ def doAutoPartition(storage, data): storage.reset() raise exn
-def recursiveRemove(storage, device): - log.debug("clearing %s" % device.name) - - # XXX is there any argument for not removing incomplete devices? - # -- maybe some RAID devices - devices = storage.deviceDeps(device) - while devices: - log.debug("devices to remove: %s" % ([d.name for d in devices],)) - leaves = [d for d in devices if d.isleaf] - log.debug("leaves to remove: %s" % ([d.name for d in leaves],)) - for leaf in leaves: - storage.destroyDevice(leaf) - devices.remove(leaf) - - if device.isDisk: - storage.destroyFormat(device) - else: - storage.destroyDevice(device) - -def clearPartitions(storage): - """ Clear partitions and dependent devices from disks. - - Arguments: - - storage -- a storage.Storage instance - - Keyword arguments: - - None - - NOTES: - - - Needs some error handling, especially for the parted bits. - - """ - if storage.config.clearPartType in (None, CLEARPART_TYPE_NONE): - # not much to do - return - - if not hasattr(storage.platform, "diskLabelTypes"): - 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 - # things like multiple "destroy sda5" actions due to parted renumbering - # partitions. This can still happen through the UI but it makes sense to - # avoid it where possible. - partitions.sort(key=lambda p: p.partedPartition.number, reverse=True) - for part in partitions: - log.debug("clearpart: looking at %s" % part.name) - if not storage.shouldClear(part): - continue - - recursiveRemove(storage, part) - log.debug("partitions: %s" % [p.getDeviceNodeName() for p in part.partedPartition.disk.partitions]) - - # now remove any empty extended partitions - removeEmptyExtendedPartitions(storage) - - # make sure that the the boot device has the correct disklabel type if - # we're going to completely clear it. - # also handles clearpart --initlabel - boot_disk = storage.bootDisk - for disk in storage.partitioned: - if not boot_disk and not storage.config.reinitializeDisks: - break - - if not storage.config.reinitializeDisks and \ - (boot_disk is not None and disk != boot_disk): - continue - - if storage.config.clearPartType != CLEARPART_TYPE_ALL or \ - (storage.config.clearPartDisks and - disk.name not in storage.config.clearPartDisks): - continue - - # don't reinitialize the disklabel if the disk contains install media - # or pvs from inconsistent vgs - if filter(lambda p: p.dependsOn(disk), storage.protectedDevices): - continue - - nativeLabelType = storage.platform.bestDiskLabelType(disk) - if disk.format.labelType == nativeLabelType: - continue - - magic_partitions = {"mac": 1, "sun": 3} - if disk.format.labelType in magic_partitions: - number = magic_partitions[disk.format.labelType] - # remove the magic partition - for part in storage.partitions: - if part.disk == disk and part.partedPartition.number == number: - log.debug("clearing %s" % part.name) - # We can't schedule the magic partition for removal - # because parted will not allow us to remove it from the - # disk. Still, we need it out of the devicetree. - storage.devicetree._removeDevice(part, moddisk=False) - - destroy_action = ActionDestroyFormat(disk) - newLabel = getFormat("disklabel", device=disk.path, - labelType=nativeLabelType) - create_action = ActionCreateFormat(disk, format=newLabel) - storage.devicetree.registerAction(destroy_action) - storage.devicetree.registerAction(create_action) - - if storage.bootloader: - storage.updateBootLoaderDiskList() - -def removeEmptyExtendedPartitions(storage): - for disk in storage.partitioned: - log.debug("checking whether disk %s has an empty extended" % disk.name) - extended = disk.format.extendedPartition - logical_parts = disk.format.logicalPartitions - log.debug("extended is %s ; logicals is %s" % (extended, [p.getDeviceNodeName() for p in logical_parts])) - if extended and not logical_parts: - log.debug("removing empty extended partition from %s" % disk.name) - extended_name = devicePathToName(extended.getDeviceNodeName()) - extended = storage.devicetree.getDeviceByName(extended_name) - storage.destroyDevice(extended) - #disk.partedDisk.removePartition(extended.partedPartition) - def partitionCompare(part1, part2): """ More specifically defined partitions come first.
--- pyanaconda/storage/__init__.py | 42 +++++++++++++++++++++++++++------------ 1 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py index 612ee7d..4eeed53 100644 --- a/pyanaconda/storage/__init__.py +++ b/pyanaconda/storage/__init__.py @@ -525,12 +525,7 @@ class Storage(object): for disk in self.disks: if disk.format.type is None: log.info("zerombr: initializing %s" % disk.name) - - labelType = storage.platform.bestDiskLabelType(disk) - newLabel = getFormat("disklabel", device=disk.path, - labelType=labelType) - create_action = ActionCreateFormat(disk, format=newLabel) - storage.devicetree.registerAction(create_action) + self.reinitializeDisk(disk)
# we may have added candidate boot disks just above by initializing self.updateBootLoaderDiskList() @@ -891,6 +886,18 @@ class Storage(object): if disk.format.labelType == self.platform.bestDiskLabelType(disk): continue
+ self.reinitializeDisk(disk) + + self.updateBootLoaderDiskList() + + def reinitializeDisk(self, disk): + """ (Re)initialize a disk by creating a disklabel on it. + + The disk should not contain any partitions except perhaps for a + magic partitions on mac and sun disklabels. + """ + # first, remove magic mac/sun partitions from the parted Disk + if disk.partitioned: magic_partitions = {"mac": 1, "sun": 3} if disk.format.labelType in magic_partitions: number = magic_partitions[disk.format.labelType] @@ -903,14 +910,23 @@ class Storage(object): # disk. Still, we need it out of the devicetree. self.devicetree._removeDevice(part, moddisk=False)
- destroy_action = ActionDestroyFormat(disk) - newLabel = getFormat("disklabel", device=disk.path, - labelType=nativeLabelType) - create_action = ActionCreateFormat(disk, format=newLabel) - self.devicetree.registerAction(destroy_action) - self.devicetree.registerAction(create_action) + if disk.partitioned and disk.format.partitions: + raise ValueError("cannot reinitialize a disk that has partitions")
- self.updateBootLoaderDiskList() + # remove existing formatting from the disk + destroy_action = ActionDestroyFormat(disk) + self.devicetree.registerAction(destroy_action) + + if self.platform: + labelType = self.platform.bestDiskLabelType(disk) + else: + labelType = None + + # create a new disklabel on the disk + newLabel = getFormat("disklabel", device=disk.path, + labelType=labelType) + create_action = ActionCreateFormat(disk, format=newLabel) + self.devicetree.registerAction(create_action)
def removeEmptyExtendedPartitions(self): for disk in self.partitioned:
- Fix a typo - Enable remove button for devices - Fix a tb on removal of unused devices --- pyanaconda/ui/gui/spokes/custom.py | 8 +++++--- 1 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/pyanaconda/ui/gui/spokes/custom.py b/pyanaconda/ui/gui/spokes/custom.py index 4398025..3ed40ec 100644 --- a/pyanaconda/ui/gui/spokes/custom.py +++ b/pyanaconda/ui/gui/spokes/custom.py @@ -48,7 +48,7 @@ from gi.repository import Gtk
__all__ = ["CustomPartitioningSpoke"]
-new_install_name = = _("New %s %s Installation") % (productName, productVersion) +new_install_name = _("New %s %s Installation") % (productName, productVersion)
class AddDialog(UIObject): builderObjects = ["addDialog"] @@ -474,8 +474,9 @@ class CustomPartitioningSpoke(NormalSpoke): self._remove_from_root(root, device) self._destroy_device(device)
- # If the root is now empty, remove it. - if len(root.swaps + root.mounts.values()) == 0: + # If the root is now empty, remove it. Devices from the Unused page + # will have no root. + if root and len(root.swaps + root.mounts.values()) == 0: self.storage.roots.remove(root)
self._update_ui_for_removals() @@ -538,6 +539,7 @@ class CustomPartitioningSpoke(NormalSpoke): self._current_selector = selector
self._configButton.set_sensitive(True) + self._removeButton.set_sensitive(True)
def on_page_clicked(self, page): # This is called when a Page header is clicked upon so we can support
Even if the boot disk has the correct disklabel type, we were asked to reinitialize the disk(s), so do it. --- pyanaconda/storage/__init__.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py index 4eeed53..db566b4 100644 --- a/pyanaconda/storage/__init__.py +++ b/pyanaconda/storage/__init__.py @@ -883,7 +883,8 @@ class Storage(object): if filter(lambda p: p.dependsOn(disk), self.protectedDevices): continue
- if disk.format.labelType == self.platform.bestDiskLabelType(disk): + if not self.config.reinitializeDisks and \ + disk.format.labelType == self.platform.bestDiskLabelType(disk): continue
self.reinitializeDisk(disk)
All storage related. Lots to do with uninitialized disk handling.
Testing with a single uninitialized disk:
* Choose the single disk, autopart, review or don't:
storage/spokes.py:277 - IndexError: list index out of range
* Choose the single disk, click on the link to the disk cart:
lib/cart.py:51 - AttributeError: 'DeviceFormat' object has no attribute 'free'
Testing with a single initialized disk appears okay.
I'm still seeing the previously mentioned behavior where the accordion collapses when you delete a filesystem, but that's probably not caused by these patches.
- Chris
On Fri, 2012-06-29 at 11:30 -0400, Chris Lumens wrote:
All storage related. Lots to do with uninitialized disk handling.
Testing with a single uninitialized disk:
Choose the single disk, autopart, review or don't:
storage/spokes.py:277 - IndexError: list index out of range
I'd forgotten I was enabling zerombr here, but not in these patches.
I think uninitialized disk handling is going to require one of the following approaches:
1) Initialize any uninitialized disk that gets selected 2) Don't let users select uninitialized disks until we have ui to allow users to initialize them
Choose the single disk, click on the link to the disk cart:
lib/cart.py:51 - AttributeError: 'DeviceFormat' object has no attribute 'free'
This is because the shopping cart isn't using Storage.getFreeSpace. This is not caused by these patches. I have a patch in my tree to convert it to use getFreeSpace, which will fix this error.
Testing with a single initialized disk appears okay.
I'm still seeing the previously mentioned behavior where the accordion collapses when you delete a filesystem, but that's probably not caused by these patches.
I don't remember ever seeing any behavior other than what you describe above.
- Chris
anaconda-patches mailing list anaconda-patches@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/anaconda-patches
anaconda-patches@lists.fedorahosted.org