[PATCH 2/4] Add support for changing a new LV's VG.
David Lehman
dlehman at redhat.com
Wed Nov 21 19:44:38 UTC 2012
You can choose from any of the defined VGs or you can create a new VG.
Also provided is some basic editing for new VGs: name and disk set.
---
pyanaconda/storage/__init__.py | 46 +++++-
pyanaconda/ui/gui/spokes/custom.glade | 270 ++++++++++++++++++++++++++++++++-
pyanaconda/ui/gui/spokes/custom.py | 256 ++++++++++++++++++++++++++++++-
3 files changed, 560 insertions(+), 12 deletions(-)
diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py
index 4b3ba0f..07f2cce 100644
--- a/pyanaconda/storage/__init__.py
+++ b/pyanaconda/storage/__init__.py
@@ -1832,6 +1832,10 @@ class Storage(object):
# short-circuit the logic below for partitions
return factory.member_list
+ if container and container.exists:
+ # don't try to modify an existing container
+ return container.parents
+
if factory.container_size_func is None:
return []
@@ -1988,9 +1992,17 @@ class Storage(object):
self, size, [d.name for d in disks], raid_level))
return factory_class(self, size, disks, raid_level, encrypted)
- def getContainer(self, factory, device=None):
+ def getContainer(self, factory, device=None, container_name=None):
+ # XXX would it be useful to implement this as a series of fallbacks
+ # instead of mutually exclusive branches?
container = None
- if device:
+ if container_name:
+ container = self.devicetree.getDeviceByName(container_name)
+ if container and container not in factory.container_list:
+ log.debug("specified container name %s is wrong type (%s)"
+ % (container_name, container.type))
+ container = None
+ elif device:
if hasattr(device, "vg"):
container = device.vg
elif hasattr(device, "volume"):
@@ -2032,6 +2044,9 @@ class Storage(object):
raid_level (btrfs/md/lvm only) RAID level (string)
+ name name for new device
+ container_name name of requested container
+
device an already-defined but non-existent device
to adjust instead of creating a new device
@@ -2059,11 +2074,19 @@ class Storage(object):
disks = kwargs.get("disks")
encrypted = kwargs.get("encrypted", self.data.autopart.encrypted)
+ name = kwargs.get("name")
+ container_name = kwargs.get("container_name")
+
device = kwargs.get("device")
# md, btrfs
raid_level = kwargs.get("raid_level")
+ # we can't do anything with existing devices
+ if device and device.exists:
+ log.info("newDevice refusing to change device %s" % device)
+ return
+
if not fstype:
fstype = self.getFSType(mountpoint=mountpoint)
if fstype == "swap":
@@ -2082,7 +2105,10 @@ class Storage(object):
raise StorageError("no disks specified for new device")
self.size_sets = [] # clear this since there are no growable reqs now
- container = self.getContainer(factory, device=device)
+
+
+ container = self.getContainer(factory, device=device,
+ container_name=container_name)
# TODO: striping, mirroring, &c
# TODO: non-partition members (pv-on-md)
@@ -2129,8 +2155,12 @@ class Storage(object):
# set up container
if not container and factory.new_container_attr:
log.debug("creating new container")
+ if container_name:
+ kwa = {"name": container_name}
+ else:
+ kwa = {}
try:
- container = factory.new_container(parents=parents)
+ container = factory.new_container(parents=parents, **kwa)
except StorageError as e:
log.error("failed to create new device: %s" % e)
# Clean up by destroying the newly created member devices.
@@ -2213,12 +2243,18 @@ class Storage(object):
else:
self.__cleanUpMemberDevices(parents)
+ if name:
+ kwa = {"name": name}
+ else:
+ kwa = {}
+
try:
device = factory.new_device(parents=parents,
size=size,
fmt_type=fstype,
mountpoint=mountpoint,
- fmt_args=fmt_args)
+ fmt_args=fmt_args,
+ **kwa)
except StorageError as e:
log.error("device instance creation failed: %s" % e)
_container_post_error()
diff --git a/pyanaconda/ui/gui/spokes/custom.glade b/pyanaconda/ui/gui/spokes/custom.glade
index 70ec694..f4e27eb 100644
--- a/pyanaconda/ui/gui/spokes/custom.glade
+++ b/pyanaconda/ui/gui/spokes/custom.glade
@@ -1125,7 +1125,7 @@ you'll be able to view their details here.</property>
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">2</property>
+ <property name="top_attach">3</property>
<property name="width">3</property>
<property name="height">1</property>
</packing>
@@ -1147,6 +1147,52 @@ you'll be able to view their details here.</property>
<property name="height">1</property>
</packing>
</child>
+ <child>
+ <object class="GtkLabel" id="volumeGroupLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Volume Group:</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="volumeGroupCombo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="entry_text_column">0</property>
+ <property name="id_column">1</property>
+ <signal name="changed" handler="on_vg_changed" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="modifyVGButton">
+ <property name="label" translatable="yes">Modify ...</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="on_modify_vg_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
</object>
</child>
<child type="label">
@@ -1683,6 +1729,228 @@ you'll be able to view their details here.</property>
</object>
</child>
</object>
+ <object class="GtkDialog" id="vg_dialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="type_hint">dialog</property>
+ <property name="decorated">False</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox5">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area5">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="vg_cancel_button">
+ <property name="label" translatable="yes">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ <signal name="clicked" handler="on_cancel_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="vg_save_button">
+ <property name="label" translatable="yes">_Save</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ <signal name="clicked" handler="on_save_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="configure_vg_title_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">CONFIGURE VOLUME GROUP</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Please create a name for your volume group and select at least one disk below.</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Name:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="vg_name_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport" id="viewport2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTreeView" id="vg_disk_view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">disk_store</property>
+ <property name="search_column">0</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection3">
+ <property name="mode">multiple</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="disk_column1">
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes">Disk</property>
+ <property name="clickable">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="disk_renderer1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="capacity_column1">
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes">Capacity</property>
+ <property name="clickable">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="capacity_renderer1"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="free_column1">
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes">Free</property>
+ <property name="clickable">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="free_renderer1"/>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="id_column1">
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes">ID</property>
+ <property name="clickable">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="id_renderer1"/>
+ <attributes>
+ <attribute name="text">3</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">vg_cancel_button</action-widget>
+ <action-widget response="1">vg_save_button</action-widget>
+ </action-widgets>
+ </object>
<object class="GtkListStore" id="disk_store">
<columns>
<!-- column-name disk_col -->
diff --git a/pyanaconda/ui/gui/spokes/custom.py b/pyanaconda/ui/gui/spokes/custom.py
index fe2d9b0..21501af 100644
--- a/pyanaconda/ui/gui/spokes/custom.py
+++ b/pyanaconda/ui/gui/spokes/custom.py
@@ -21,13 +21,17 @@
#
# TODO:
-# - empty size when adding a mountpoint is no longer using all available space
+# - Add a way for users to specify the names of md, lv, subvol devices.
+# - We should either remove boot disk selection from the cart we show from here
+# or re-partition when it gets changed to make a best-effort at keeping up.
+# - Add new LVs to existing VG if creating a new VG fails outright.
# - Deleting an LV is not reflected in available space in the bottom left.
# - this is only true for preexisting LVs
# - Device descriptions, suggested sizes, etc. should be moved out into a support file.
# - Tabbing behavior in the accordion is weird.
# - Update feature space costs when size spinner changes.
-# - Either disable stripe/mirror for LVM or implement it for device creation.
+# - Implement striping and mirroring for LVM.
+# - Implement container management for btrfs.
# - If you click to add a mountpoint while editing a device the lightbox
# screenshot is taken prior to the ui update so the background shows the old
# size and free space while you're deciding on a size for the new device.
@@ -92,6 +96,7 @@ NOTEBOOK_LUKS_PAGE = 2
NOTEBOOK_UNEDITABLE_PAGE = 3
new_install_name = N_("New %s %s Installation") % (productName, productVersion)
+new_vg_text = N_("Create a new volume group ...")
unrecoverable_error_msg = N_("Storage configuration reset due to unrecoverable "
"error. Click for details.")
device_configuration_error_msg = N_("Device reconfiguration failed. Click for "
@@ -340,6 +345,69 @@ class DisksDialog(GUIObject):
def run(self):
return self.window.run()
+class VolumeGroupDialog(GUIObject):
+ builderObjects = ["vg_dialog", "disk_store", "vg_disk_view"]
+ mainWidgetName = "vg_dialog"
+ uiFile = "spokes/custom.glade"
+
+ def __init__(self, *args, **kwargs):
+ self._disks = kwargs.pop("disks")
+ free = kwargs.pop("free")
+ self.name = kwargs.pop("name") or ""
+ self.selected = kwargs.pop("selected")[:]
+ GUIObject.__init__(self, *args, **kwargs)
+
+ self.builder.get_object("vg_name_entry").set_text(self.name)
+
+ self._store = self.builder.get_object("disk_store")
+ # populate the store
+ for disk in self._disks:
+ self._store.append([disk.description,
+ str(Size(spec="%dMB" % disk.size)),
+ str(free[disk.name][0]),
+ disk.serial,
+ disk.id])
+
+ treeview = self.builder.get_object("vg_disk_view")
+ model = treeview.get_model()
+ itr = model.get_iter_first()
+
+ selected_ids = [d.id for d in self.selected]
+ log.debug("selected: %s" % [d.name for d in self.selected])
+ log.debug("selected: %s" % selected_ids)
+ selection = treeview.get_selection()
+ while itr:
+ disk_id = model.get_value(itr, 4)
+ log.debug("store: %d" % disk_id)
+ if disk_id in selected_ids:
+ selection.select_iter(itr)
+
+ itr = model.iter_next(itr)
+
+ def on_cancel_clicked(self, button):
+ self.window.destroy()
+
+ def _get_disk_by_id(self, disk_id):
+ for disk in self._disks:
+ if disk.id == disk_id:
+ return disk
+
+ def on_save_clicked(self, button):
+ treeview = self.builder.get_object("vg_disk_view")
+ model, paths = treeview.get_selection().get_selected_rows()
+ self.selected = []
+ for path in paths:
+ itr = model.get_iter(path)
+ disk_id = model.get_value(itr, 4)
+ self.selected.append(self._get_disk_by_id(disk_id))
+
+ self.name = self.builder.get_object("vg_name_entry").get_text()
+
+ self.window.destroy()
+
+ def run(self):
+ return self.window.run()
+
class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
builderObjects = ["customStorageWindow", "sizeAdjustment",
"partitionStore",
@@ -360,7 +428,9 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
self._fs_types = [] # list of supported fstypes
self._unused_devices = None # None indicates uninitialized
self._free_space = Size(bytes=0)
+
self._device_disks = []
+ self._device_container_name = None
self._initialized = False
@@ -826,7 +896,6 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
prev_encrypted = device.encrypted
log.debug("old encryption setting: %s" % prev_encrypted)
encryption_checkbox = self.builder.get_object("encryptCheckbox")
- encrypted = None
encrypted = encryption_checkbox.get_active()
changed_encryption = (prev_encrypted != encrypted)
@@ -893,6 +962,13 @@ 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 = self.__storage.getDeviceFactory(device_type, size,
@@ -919,9 +995,22 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
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))
- if changed_device_type or changed_raid_level:
+ changed_container = False
+ old_container_name = None
+ container = self.__storage.getContainer(factory)
+ if not changed_device_type and device_type == DEVICE_TYPE_LVM:
+ old_container = self.__storage.getContainer(factory,
+ device=device)
+ container = self.__storage.getContainer(factory,
+ container_name=self._device_container_name)
+ if self._device_container_name != old_container.name:
+ old_container_name = old_container.name
+ 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))
@@ -935,6 +1024,10 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
% ([d.name for d in device.disks],
[d.name for d in self._device_disks]))
+ if changed_container:
+ log.info("changing container from %s to %s"
+ % (old_container.name, self._device_container_name))
+
# remove the current device
self.clear_errors()
root = self._current_selector._root
@@ -945,7 +1038,6 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
with ui_storage_logger():
disks = self._device_disks[:]
- container = self.__storage.getContainer(factory)
if container and changed_device_type:
log.debug("overriding disk set with container's")
disks = container.disks[:]
@@ -955,6 +1047,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
disks=disks, mountpoint=mountpoint,
label=label, raid_level=raid_level,
encrypted=encrypted,
+ container_name=self._device_container_name,
selector=selector)
except ErrorRecoveryFailure as e:
self._error = e
@@ -979,6 +1072,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
label=old_label,
raid_level=old_raid_level,
encrypted=prev_encrypted,
+ container_name=old_container_name,
selector=selector)
except StorageError as e:
# failed to recover.
@@ -1043,6 +1137,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
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
@@ -1368,6 +1463,13 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
log.debug("updated device_disks to %s" % [d.name for d in self._device_disks])
+ if hasattr(use_dev, "vg"):
+ self._device_container_name = use_dev.vg.name
+ else:
+ self._device_container_name = None
+
+ log.debug("updated device_vg_name to %s" % self._device_container_name)
+
selectedDeviceLabel.set_text(selector.props.name)
selectedDeviceDescLabel.set_text(self._description(selector.props.name))
@@ -1522,9 +1624,10 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
typeCombo.set_active(type_index_map[device_type])
# you can't change the type of an existing device
- typeCombo.set_sensitive(not device.exists)
+ typeCombo.set_sensitive(not use_dev.exists)
self._populate_raid(raid_level, device.size)
+ self._populate_lvm(device=use_dev)
self.builder.get_object("optionsNotebook").set_sensitive(not device.exists)
###
@@ -1807,6 +1910,94 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
self._device_disks = disks
+ def run_vg_editor(self, vg=None, name=None):
+ if vg:
+ vg_name = vg.name
+ elif name:
+ vg_name = name
+
+ dialog = VolumeGroupDialog(self.data,
+ name=vg_name,
+ disks=self._clearpartDevices,
+ free=self._currentFreeInfo,
+ selected=self._device_disks)
+
+ with enlightbox(self.window, dialog.window):
+ rc = dialog.run()
+
+ if rc == 0:
+ return
+
+ disks = dialog.selected
+ name = dialog.name
+ log.debug("new disks for %s: %s" % (name, [d.name for d in disks]))
+ if not disks:
+ self._error = "No disks selected. Not saving changes."
+ self.window.set_info(Gtk.MessageType.INFO,
+ self._error)
+ self.window.show_all()
+ return
+
+ log.debug("new VG name: %s" % name)
+ if name != vg_name and name in self.__storage.names:
+ self._error = _("Volume Group name %s is already in use. Not "
+ "saving changes.")
+ self.window.set_info(Gtk.MessageType.INFO,
+ self._error)
+ self.window.show_all()
+ return
+
+ self._device_disks = disks
+ self._device_container_name = name
+
+ def on_modify_vg_clicked(self, button):
+ vg_name = self.builder.get_object("volumeGroupCombo").get_active_text()
+
+ vg = self.__storage.devicetree.getDeviceByName(vg_name)
+
+ # pass the name along with any found vg since we could be modifying a
+ # vg that hasn't been instantiated yet
+ self.run_vg_editor(vg=vg, name=vg_name)
+
+ log.debug("%s -> %s" % (vg_name, self._device_container_name))
+ if vg_name == self._device_container_name:
+ return
+
+ log.debug("renaming VG %s to %s" % (vg_name, self._device_container_name))
+ if vg:
+ self.__storage.devicetree.names.remove(vg.name)
+ self.__storage.devicetree.names.append(self._device_container_name)
+ vg.name = self._device_container_name
+
+ vg_combo = self.builder.get_object("volumeGroupCombo")
+ for idx, data in enumerate(vg_combo.get_model()):
+ # we're looking for the original vg name
+ if data[0] == vg_name:
+ vg_combo.remove(idx)
+ vg_combo.insert_text(idx, self._device_container_name)
+ vg_combo.set_active(idx)
+ break
+
+ def on_vg_changed(self, combo):
+ vg_name = combo.get_active_text()
+ log.debug("new vg selection: %s" % vg_name)
+ if vg_name == new_vg_text:
+ # run the vg editor dialog with a default name and disk set
+ hostname = self.data.network.hostname
+ name = self.__storage.suggestContainerName(hostname=hostname)
+ self.run_vg_editor(name=name)
+ for idx, data in enumerate(combo.get_model()):
+ if data[0] == new_vg_text:
+ combo.insert_text(idx, self._device_container_name)
+ combo.set_active(idx)
+ break
+ else:
+ self._device_container_name = vg_name
+
+ vg = self.__storage.devicetree.getDeviceByName(self._device_container_name)
+ vg_exists = getattr(vg, "exists", False) # might not be in the tree
+ self.builder.get_object("modifyVGButton").set_sensitive(not vg_exists)
+
def on_selector_clicked(self, selector):
if not self._initialized:
return
@@ -1971,6 +2162,55 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
hasattr(fmt, "label"))
mountPointEntry.set_sensitive(fmt.mountable)
+ def _populate_lvm(self, device=None):
+ #
+ # set up the volume group widgets for lvm or hide them for other types
+ #
+ device_type = self._get_current_device_type()
+ if device is None:
+ if self._current_selector is None:
+ return
+
+ device = self._current_selector._device
+
+ vg_combo = self.builder.get_object("volumeGroupCombo")
+ vg_button = self.builder.get_object("modifyVGButton")
+ vg_label = self.builder.get_object("volumeGroupLabel")
+ if device_type == DEVICE_TYPE_LVM:
+ # set up the vg widgets and then bail out
+ if self._device_container_name:
+ default_vg = self._device_container_name
+ else:
+ with ui_storage_logger():
+ factory = self.__storage.getDeviceFactory(DEVICE_TYPE_LVM,
+ 0)
+ container = self.__storage.getContainer(factory)
+ default_vg = getattr(container, "name", None)
+
+ vg_combo.remove_all()
+ vgs = self.__storage.vgs
+ for vg in vgs:
+ vg_combo.append_text(vg.name)
+ if default_vg and vg.name == default_vg:
+ vg_combo.set_active(vgs.index(vg))
+
+ vg_combo.append_text(new_vg_text)
+ if default_vg is None:
+ vg_combo.set_active(len(vg_combo.get_model()))
+ #vg_combo.set_active(-1)
+
+ vg_combo.show()
+ vg_button.show()
+ vg_label.show()
+
+ # make the combo and button insensitive for existing LVs
+ can_change_vg = (device is not None and not device.exists)
+ vg_combo.set_sensitive(can_change_vg)
+ else:
+ vg_label.hide()
+ vg_combo.hide()
+ vg_button.hide()
+
def on_device_type_changed(self, combo):
if not self._initialized:
return
@@ -2011,8 +2251,12 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
elif new_type == DEVICE_TYPE_MD:
raid_level = "raid0"
+ # lvm uses the RHS to set disk set. no foolish minds here.
+ self._configButton.set_sensitive(new_type != DEVICE_TYPE_LVM)
+
size = self.builder.get_object("sizeSpinner").get_value()
self._populate_raid(raid_level, size)
+ self._populate_lvm()
# begin btrfs magic
fsCombo = self.builder.get_object("fileSystemTypeCombo")
--
1.7.7.6
More information about the anaconda-patches
mailing list