diff --git a/pyanaconda/ui/gui/spokes/custom.py b/pyanaconda/ui/gui/spokes/custom.py index 3e19707..e4ab027 100644 --- a/pyanaconda/ui/gui/spokes/custom.py +++ b/pyanaconda/ui/gui/spokes/custom.py @@ -822,22 +822,19 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): def _replace_device(self, *args, **kwargs): """ Create a replacement device and update the device selector. """ selector = kwargs.pop("selector", None) - self.__storage.factoryDevice(*args, **kwargs) + new_device = self.__storage.factoryDevice(*args, **kwargs) self._devices = self.__storage.devices if selector: - # newest device should be the one with the highest id - max_id = max([d.id for d in self._devices]) - # update the selector with the new device and its size - selectorFromDevice(self.__storage.devicetree.getDeviceByID(max_id), + selectorFromDevice(new_device, selector=selector) - def _update_device_in_selectors(self, old_device): + def _update_device_in_selectors(self, old_device, new_device): for s in self._accordion.allSelectors: if s._device == old_device: - s._device = device + s._device = new_device def _save_right_side(self, selector): """ Save settings from RHS and apply changes to the device. @@ -861,7 +858,14 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): log.info("ui: saving changes to device %s" % device.name) - # TODO: member type + # + # FIXME: collect all data items, old and new, and log them all using the + # same format + # + + # TODO: member type (as a device type?) + + # NAME old_name = getattr(use_dev, "lvname", use_dev.name) name = old_name changed_name = False @@ -876,36 +880,50 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): log.debug("old_name: %s" % old_name) log.debug("new_name: %s" % name) + # SIZE + old_size = device.size size = self.builder.get_object("sizeSpinner").get_value() + changed_size = (int(size) != int(old_size)) log.debug("new size: %s" % size) - log.debug("old size: %s" % device.size) + log.debug("old size: %s" % old_size) + # DEVICE TYPE device_type = self._get_current_device_type() + old_device_type = devicefactory.get_device_type(device) + changed_device_type = (old_device_type != device_type) log.debug("new device type: %s" % device_type) + # REFORMAT reformat = self._reformatCheckbox.get_active() log.debug("reformat: %s" % reformat) + # FS TYPE + old_fs_type = device.format.type fs_type_combo = self.builder.get_object("fileSystemTypeCombo") fs_type_index = fs_type_combo.get_active() fs_type = fs_type_combo.get_model()[fs_type_index][0] + fs_type_short = getFormat(fs_type).type + changed_fs_type = (old_fs_type != fs_type_short) log.debug("new fs type: %s" % fs_type) - prev_encrypted = device.encrypted - log.debug("old encryption setting: %s" % prev_encrypted) + # ENCRYPTION + old_encrypted = isinstance(device, LUKSDevice) + log.debug("old encryption setting: %s" % old_encrypted) encryption_checkbox = self.builder.get_object("encryptCheckbox") encrypted = encryption_checkbox.get_active() - - changed_encryption = (prev_encrypted != encrypted) + changed_encryption = (old_encrypted != encrypted) log.debug("new encryption setting: %s" % encrypted) + # FS LABEL label_entry = self.builder.get_object("labelEntry") label = "" if label_entry.get_sensitive(): label = label_entry.get_text() old_label = getattr(device.format, "label", "") or "" + changed_label = (label != old_label) + # MOUNTPOINT mountpoint = None # None means format type is not mountable mountPointEntry = self.builder.get_object("mountPointEntry") if mountPointEntry.get_sensitive(): @@ -928,9 +946,15 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): self._populate_right_side(selector) return - raid_level = self._get_raid_level() + changed_mountpoint = (old_mountpoint != mountpoint) - fs_type_short = getFormat(fs_type).type + # RAID LEVEL + raid_level = self._get_raid_level() + old_raid_level = devicefactory.get_raid_level(device) + changed_raid_level = (old_device_type == device_type and + device_type in (DEVICE_TYPE_MD, + DEVICE_TYPE_BTRFS) and + old_raid_level != raid_level) ## ## VALIDATION @@ -965,13 +989,6 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): self._populate_right_side(selector) return - # XXX Now we have to do something squirrely with the encryption setting. - # We've done the checks for things that cannot be on an encrypted - # device. From here on out, it means "encrypt new devices we create", - # which we don't want to do in the event that encryption is already - # present below the surface of an existing device stack. - encrypted = encrypted and encryption_checkbox.get_sensitive() - with ui_storage_logger(): # create a new factory using the appropriate size and type factory = devicefactory.get_device_factory(self.__storage, @@ -979,74 +996,74 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): disks=device.disks, encrypted=encrypted, raid_level=raid_level) - - # for member type, we'll have to adjust the member set. - # XXX not going to worry about this for now - - ## - ## DEVICE TYPE (early return) - ## - current_device_type = devicefactory.get_device_type(device) - old_raid_level = devicefactory.get_raid_level(device) - changed_device_type = (current_device_type != device_type) - changed_raid_level = (current_device_type == device_type and - device_type in (DEVICE_TYPE_MD, - DEVICE_TYPE_BTRFS) and - old_raid_level != raid_level) - old_disk_set = device.disks - if hasattr(device, "req_disks") and not device.exists: - old_disk_set = device.req_disks - - changed_disk_set = (set(old_disk_set) != set(self._device_disks)) - + + # CONTAINER changed_container = False old_container_name = None + container_name = self._device_container_name container = factory.get_container() - if not changed_device_type and device_type == DEVICE_TYPE_LVM: + old_container_encrypted = False + if not changed_device_type: old_container = factory.get_container(device=use_dev) - container = factory.get_container(name=self._device_container_name) - if self._device_container_name != old_container.name: + container = factory.get_container(name=container_name) + if old_container and container_name != old_container.name: old_container_name = old_container.name + old_container_encrypted = old_container.encrypted changed_container = True - if changed_device_type or changed_raid_level or changed_container: - if changed_device_type: - log.info("changing device type from %s to %s" - % (current_device_type, device_type)) + # eventually this will be controlled by the "edit container" dialog + container_encrypted = getattr(container, "encrypted", False) + changed_container_encrypted = (container_encrypted != old_container_encrypted) - if changed_raid_level: - log.info("changing raid level from %s to %s" - % (old_raid_level, raid_level)) - - if changed_disk_set: - log.info("changing disk set from %s to %s" - % ([d.name for d in device.disks], - [d.name for d in self._device_disks])) + # DISK SET + old_disks = device.disks + if hasattr(device, "req_disks") and not device.exists: + old_disks = device.req_disks + + disks = self._device_disks[:] + if container and changed_device_type: + log.debug("overriding disk set with container's") + disks = container.disks[:] + log.debug("disks: %s" % [d.name for d in disks]) + changed_disk_set = (set(old_disks) != set(disks)) + + changed = (changed_name or changed_size or changed_device_type or + changed_label or changed_mountpoint or changed_disk_set or + changed_encryption or raid_level or changed_container or + changed_container_encrypted) + + if not use_dev.exists: + if not changed: + log.debug("nothing changed for new device") + return - if changed_container: - log.info("changing container from %s to %s" - % (old_container.name, self._device_container_name)) + # + # Handle change of device type and change of container + # + if changed_device_type or changed_container: + # remove the current device + self.clear_errors() + root = self._current_selector._root + self._destroy_device(device) + if device in self._devices: + # the removal failed. don't continue. + log.error("device removal failed") + return - # remove the current device - self.clear_errors() - root = self._current_selector._root - self._destroy_device(device) - if device in self._devices: - # the removal failed. don't continue. - return + _device = None + _old_device = None + else: + _device = device with ui_storage_logger(): - disks = self._device_disks[:] - if container and changed_device_type: - log.debug("overriding disk set with container's") - disks = container.disks[:] - log.debug("disks: %s" % [d.name for d in disks]) try: self._replace_device(device_type, size, fstype=fs_type, disks=disks, mountpoint=mountpoint, label=label, raid_level=raid_level, encrypted=encrypted, name=name, - container_name=self._device_container_name, + container_name=container_name, + container_encrypted=container_encrypted, + device=_device, selector=selector) except ErrorRecoveryFailure as e: self._error = e @@ -1059,129 +1076,97 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): self.set_warning(_(device_configuration_error_msg)) self.window.show_all() - # in this case we have removed the old device so we now have - # to re-create it - try: - self._replace_device(current_device_type, device.size, - disks=old_disk_set, - fstype=device.format.type, - mountpoint=old_mountpoint, - label=old_label, - raid_level=old_raid_level, - encrypted=prev_encrypted, - name=old_name, - container_name=old_container_name, - selector=selector) - except StorageError as e: - # failed to recover. - self.clear_errors() - self._error = e - self.set_warning(_(unrecoverable_error_msg)) - self.window.show_all() - self._reset_storage() + if _device is None: + # in this case we have removed the old device so we now have + # to re-create it + try: + self._replace_device(old_device_type, device.size, + disks=old_disks, + fstype=old_fstype, + mountpoint=old_mountpoint, + label=old_label, + raid_level=old_raid_level, + encrypted=old_encrypted, + name=old_name, + container_name=old_container_name, + container_encrypted=old_container_encrypted, + selector=selector) + except StorageError as e: + # failed to recover. + self.clear_errors() + self._error = e + self.set_warning(_(unrecoverable_error_msg)) + self.window.show_all() + self._reset_storage() else: - # you can't change the type of an existing device, so we - # don't need to concern ourselves with adding a new - # selector to the new page - selectorFromDevice(device, selector=selector) + # remove selector from old page? + # add selector to new page? + # update existing selector(s)? + selectorFromDevice(selector._device, selector=selector) self._devices = self.__storage.devices + # update size props of all btrfs devices' selectors self._update_btrfs_selectors() self._updateSpaceDisplay() + self._populate_right_side(selector) + log.debug("leaving save_right_side") return - ## - ## SIZE - ## - # new size means resize for existing devices and adjust for new ones - changed_size = (int(size) != int(device.size)) - if changed_size or changed_disk_set or \ - (changed_encryption and factory.encrypt_members and - not device.exists): - self.clear_errors() - old_size = device.size - if changed_size and device.exists and device.resizable: - with ui_storage_logger(): - try: - self.__storage.resizeDevice(device, size) - except StorageError as e: - log.error("failed to schedule device resize: %s" % e) - device.size = old_size - self._error = e - self.set_warning(_("Device resize request failed. " - "Click for details.")) - self.window.show_all() - else: - log.debug("%r" % device) - log.debug("new size: %s" % device.size) - log.debug("target size: %s" % device.targetSize) - elif not device.exists: - if changed_disk_set: - log.info("changing disk set from %s to %s" - % ([d.name for d in device.disks], - [d.name for d in self._device_disks])) + # + # Handle changes to preexisting devices + # + + # Handle deactivation of the reformat checkbutton after having committed + # a reformat. + if not reformat and (not use_dev.format.exists or + not device.format.exists): + # figure out the existing device and reset it + if not use_dev.format.exists: + original_device = use_dev + else: + original_device = device - with ui_storage_logger(): - try: - self.__storage.factoryDevice(device_type, size, - device=device, - disks=self._device_disks[:], - encrypted=encrypted, - container_name=self._device_container_name, - raid_level=raid_level) - except ErrorRecoveryFailure as e: - self._error = e - self.set_warning(_(unrecoverable_error_msg)) - self.window.show_all() - self._reset_storage() - except StorageError as e: - log.error("factoryDevice failed: %s" % e) - self._error = e - self.set_warning(_(device_configuration_error_msg)) - self.window.show_all() + with ui_storage_logger(): + self.__storage.resetDevice(original_device) - self._devices = self.__storage.devices + if changed_size and device.resizable: + with ui_storage_logger(): + try: + self.__storage.resizeDevice(device, size) + except StorageError as e: + log.error("failed to schedule device resize: %s" % e) + device.size = old_size + self._error = e + self.set_warning(_("Device resize request failed. " + "Click for details.")) + self.window.show_all() + else: + log.debug("%r" % device) + log.debug("new size: %s" % device.size) + log.debug("target size: %s" % device.targetSize) - log.debug("updating selector size to '%s'" - % str(Size(spec="%f MB" % device.size)).upper()) - # update the selector's size property - selectorFromDevice(device, selector=selector) + # update the selector's size property + selectorFromDevice(device, selector=selector) # update size props of all btrfs devices' selectors self._update_btrfs_selectors() - self._updateSpaceDisplay() - self._populate_right_side(selector) - - - ## - ## NAME - ## - if changed_name: - use_dev._name = name - new_name = use_dev.name - if new_name in self.__storage.names: - use_dev._name = old_name - self.set_info(_("Specified name %s already in use.") % new_name) - else: - if old_name == selector.props.name: - selector.props.name = new_name - - if reformat: - ## - ## ENCRYPTION - ## - # for existing devices, we always encrypt the leaf - old_fs_type = device.format.type - old_device = device - - # if the encryption is on member devices it was handled above - if changed_encryption and (device.exists or factory.encrypt_leaves): - if prev_encrypted and not encrypted: + # it's possible that reformat is active but fstype is unchanged, in + # which case we're not going to schedule another reformat unless + # encryption got toggled + do_reformat = (reformat and (encryption_changed or + fs_type_changed or + device.format.exists)) + if do_reformat: + # + # ENCRYPTION + # + if changed_encryption: + if not encrypted: log.info("removing encryption from %s" % device.name) with ui_storage_logger(): self.__storage.destroyDevice(device) @@ -1189,8 +1174,8 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): old_device = device device = device.slave selector._device = device - self._update_device_in_selectors(old_device) - elif encrypted and not prev_encrypted: + self._update_device_in_selectors(old_device, device) + elif encrypted: log.info("applying encryption to %s" % device.name) with ui_storage_logger(): old_device = device @@ -1202,76 +1187,80 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): self._devices.append(luks_dev) device = luks_dev selector._device = device - self._update_device_in_selectors(old_device) + self._update_device_in_selectors(old_device, device) - ## - ## FORMATTING - ## - encryption_changed = (device != old_device) - if encryption_changed: self._devices = self.__storage.devices - fs_type_changed = (fs_type_short != old_fs_type) - fs_exists = old_device.format.exists - if encryption_changed or fs_type_changed or fs_exists: - log.info("scheduling reformat of %s as %s" % (device.name, - fs_type_short)) - self.clear_errors() - with ui_storage_logger(): - old_format = device.format - new_format = getFormat(fs_type, - mountpoint=mountpoint, label=label, - device=device.path) - try: - self.__storage.formatDevice(device, new_format) - except StorageError as e: - log.error("failed to register device format action: %s" % e) - device.format = old_format - self._error = e - self.set_warning(_("Device reformat request failed. " - "Click for details.")) - self.window.show_all() + # + # FORMATTING + # + log.info("scheduling reformat of %s as %s" % (device.name, + fs_type_short)) + self.clear_errors() + with ui_storage_logger(): + old_format = device.format + new_format = getFormat(fs_type, + mountpoint=mountpoint, label=label, + device=device.path) + try: + self.__storage.formatDevice(device, new_format) + except StorageError as e: + log.error("failed to register device format action: %s" % e) + device.format = old_format + self._error = e + self.set_warning(_("Device reformat request failed. " + "Click for details.")) + self.window.show_all() + else: + # first, remove this selector from any old install page(s) + new_selector = None + for (page, _selector) in self._accordion.allMembers: + if _selector._device in (device, old_device): + if page.pageTitle == self.translated_new_install_name: + new_selector = _selector + continue + + page.removeSelector(_selector) + if not page.members: + log.debug("removing empty page %s" % page.pageTitle) + self._accordion.removePage(page.pageTitle) + + # either update the existing selector or add a new one + if new_selector: + selectorFromDevice(device, selector=new_selector) else: - # first, remove this selector from any old install page(s) - new_selector = None - for (page, _selector) in self._accordion.allMembers: - if _selector._device in (device, old_device): - if page.pageTitle == self.translated_new_install_name: - new_selector = _selector - continue - - page.removeSelector(_selector) - if not page.members: - log.debug("removing empty page %s" % page.pageTitle) - self._accordion.removePage(page.pageTitle) - - # either update the existing selector or add a new one - if new_selector: - selectorFromDevice(device, selector=new_selector) - else: - self.add_new_selector(device) - - self._populate_right_side(selector) - return + self.add_new_selector(device) + + if not do_reformat: + # Set various attributes that do not require actions. + if old_label != label and hasattr(device.format, "label") and \ + not device.format.exists: + log.debug("updating label to %s" % label) + device.format.label = label + + if mountpoint and old_mountpoint != mountpoint: + log.debug("updating mountpoint to %s" % mountpoint) + device.format.mountpoint = mountpoint + if old_mountpoint: + selectorFromDevice(device, selector=selector) + else: + # add an entry to the new page but do not remove any entries + # from other pages since we haven't altered the filesystem + self.add_new_selector(device) - ## - ## FORMATTING ATTRIBUTES - ## - # Set various attributes that do not require actions. - if old_label != label and hasattr(device.format, "label") and \ - not device.format.exists: - log.debug("updating label to %s" % label) - device.format.label = label - - if mountpoint and old_mountpoint != mountpoint: - log.debug("updating mountpoint to %s" % mountpoint) - device.format.mountpoint = mountpoint - if old_mountpoint: - selectorFromDevice(device, selector=selector) + # + # NAME + # + if changed_name: + use_dev._name = name + new_name = use_dev.name + if new_name in self.__storage.names: + use_dev._name = old_name + self.set_info(_("Specified name %s already in use.") % new_name) else: - # add an entry to the new page but do not remove any entries - # from other pages since we haven't altered the filesystem - self.add_new_selector(device) + selectorFromDevice(device, selector=selector) + + self._populate_right_side(selector) def _raid_level_visible(self, model, itr, user_data): device_type = self._get_current_device_type() @@ -1333,7 +1322,8 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): raid_level=raid_level) try: - base_size = factory.device_size + # FIXME: We shouldn't be calling private methods like this. + base_size = factory._get_total_space() except MDRaidError as e: log.error("failed to populate UI raid options: %s" % e) self._error = e @@ -1349,7 +1339,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): min_disks = mdraid.get_raid_min_members(md_level) if min_disks <= len(factory.disks): factory.raid_level = md_level - delta = factory.device_size - base_size + delta = factory._get_total_space() - base_size def _get_current_device_type(self): typeCombo = self.builder.get_object("deviceTypeCombo") @@ -1417,6 +1407,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): mountPointEntry.set_text(getattr(device.format, "mountpoint", "") or "") mountPointEntry.set_sensitive(hasattr(device.format, "mountpoint")) + # FIXME: Make sure you cannot set a label for specific btrfs subvols labelEntry.set_text(getattr(device.format, "label", "") or "") # We could label existing formats that have a labelFsProg if we added an # ActionLabelFormat class. @@ -1452,7 +1443,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): use_dev.exists and not use_dev.type.startswith("btrfs")) - encryptCheckbox.set_active(device.encrypted) + encryptCheckbox.set_active(isinstance(device, LUKSDevice)) encryptCheckbox.set_sensitive(self._reformatCheckbox.get_active()) ancestors = use_dev.ancestors ancestors.remove(use_dev) @@ -1655,7 +1646,9 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): size = dialog.size fstype = self.storage.getFSType(mountpoint) - encrypted = self.data.autopart.encrypted + encrypted = (self.data.autopart.encrypted and + self.data.autopart.type == AUTOPART_TYPE_PLAIN) + encrypt_container = self.data.autopart.encrypted # we're doing nothing here to ensure that bootable requests end up on # the boot disk, but the weight from platform should take care of this @@ -1685,12 +1678,9 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): factory = devicefactory.get_device_factory(self.__storage, device_type, size) container = factory.get_container() - if container: # don't override user-initiated changes to a defined container - if factory.encrypt_members: - encrypted = container.encrypted - + encrypt_container = container.encrypted disks = container.disks try: @@ -1699,6 +1689,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): fstype=fstype, mountpoint=mountpoint, encrypted=encrypted, + container_encrypted=encrypt_container, disks=disks) except ErrorRecoveryFailure as e: log.error("error recovery failure") @@ -1709,7 +1700,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): except StorageError as e: log.error("factoryDevice failed: %s" % e) log.debug("trying to find an existing container to use") - container = factory.get_container(existing=True) + container = factory.get_container(allow_existing=True) log.debug("found container %s" % container) if container: try: @@ -1796,9 +1787,9 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): factory = devicefactory.get_device_factory(self.__storage, device_type, 0, disks=container.disks, - encrypted=container.encrypted, + container_encrypted=container.encrypted, raid_level=raid_level) - parents = factory.set_container_members(container) + factory.configure() # if this device has parents with no other children, remove them too for parent in device.parents: