[PATCH 3/4] Add ability to manipulate container sizes directly.

David Lehman dlehman at redhat.com
Thu May 2 18:00:39 UTC 2013


There are three policies to choose from:

 - "Automatic" (the current/f18 behavior), maintains the container
   size so that it is always just large enough to hold the devices it
   contains.
 - "As large as possible" grows the container as large as is possible
   regardless of the sizes of the devices it contains.
 - "Fixed" sets the size of the container as close as possible to the
   specified value. Specification of the fixed size is the same as for
   the other size entries in the custom spoke.
---
 anaconda.spec.in                      |   2 +-
 pyanaconda/ui/gui/spokes/custom.glade |  55 ++++++++++++++
 pyanaconda/ui/gui/spokes/custom.py    | 138 +++++++++++++++++++++++++++++-----
 3 files changed, 174 insertions(+), 21 deletions(-)

diff --git a/anaconda.spec.in b/anaconda.spec.in
index 84161d9..1c1438f 100644
--- a/anaconda.spec.in
+++ b/anaconda.spec.in
@@ -76,7 +76,7 @@ BuildRequires: s390utils-devel
 %endif
 
 Requires: anaconda-widgets = %{version}-%{release}
-Requires: python-blivet >= 0.10
+Requires: python-blivet >= 0.12
 Requires: gnome-icon-theme-symbolic
 Requires: python-meh >= %{mehver}
 Requires: libreport-anaconda >= 2.0.21-1
diff --git a/pyanaconda/ui/gui/spokes/custom.glade b/pyanaconda/ui/gui/spokes/custom.glade
index c969a86..7db9bf3 100644
--- a/pyanaconda/ui/gui/spokes/custom.glade
+++ b/pyanaconda/ui/gui/spokes/custom.glade
@@ -636,6 +636,61 @@ use.  Try something else?</property>
                 <property name="position">5</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkBox" id="reservedSpaceBox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="containerSizeLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Size policy: </property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkComboBoxText" id="containerSizeCombo">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="entry_text_column">0</property>
+                    <property name="id_column">1</property>
+                    <items>
+                      <item translatable="yes">Automatic</item>
+                      <item translatable="yes">As large as possible</item>
+                      <item translatable="yes">Fixed</item>
+                    </items>
+                    <signal name="changed" handler="on_size_changed" swapped="no"/>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="containerSizeEntry">
+                    <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">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">6</property>
+              </packing>
+            </child>
           </object>
           <packing>
             <property name="expand">False</property>
diff --git a/pyanaconda/ui/gui/spokes/custom.py b/pyanaconda/ui/gui/spokes/custom.py
index 27fa286..159f7bb 100644
--- a/pyanaconda/ui/gui/spokes/custom.py
+++ b/pyanaconda/ui/gui/spokes/custom.py
@@ -49,6 +49,8 @@ from blivet.devicefactory import DEVICE_TYPE_PARTITION
 from blivet.devicefactory import DEVICE_TYPE_MD
 from blivet.devicefactory import DEVICE_TYPE_DISK
 from blivet.devicefactory import get_raid_level
+from blivet.devicefactory import SIZE_POLICY_AUTO
+from blivet.devicefactory import SIZE_POLICY_MAX
 from blivet import findExistingInstallations
 from blivet.partitioning import doPartitioning
 from blivet.partitioning import doAutoPartition
@@ -352,7 +354,8 @@ class ContainerDialog(GUIObject):
     builderObjects = ["container_dialog", "disk_store", "container_disk_view",
                       "containerRaidStoreFiltered", "containerRaidLevelLabel",
                       "containerRaidLevelCombo", "raidLevelStore",
-                      "containerEncryptedCheckbox"]
+                      "containerSizeCombo", "containerSizeEntry",
+                      "containerSizeLabel", "containerEncryptedCheckbox"]
     mainWidgetName = "container_dialog"
     uiFile = "spokes/custom.glade"
 
@@ -369,6 +372,9 @@ class ContainerDialog(GUIObject):
         self.encrypted = kwargs.pop("encrypted", False)
         self.exists = kwargs.pop("exists", False)
 
+        self.size_policy = kwargs.pop("size_policy", SIZE_POLICY_AUTO)
+        self.size = kwargs.pop("size", 0)
+
         self._error = None
         GUIObject.__init__(self, *args, **kwargs)
 
@@ -386,7 +392,6 @@ class ContainerDialog(GUIObject):
         # populate the dialog widgets
         name_entry = self.builder.get_object("container_name_entry")
         name_entry.set_text(self.name)
-        fancy_set_sensitive(name_entry, not self.exists)
 
         self._store = self.builder.get_object("disk_store")
         # populate the store
@@ -410,12 +415,9 @@ class ContainerDialog(GUIObject):
 
             itr = model.iter_next(itr)
 
-        treeview.set_sensitive(not self.exists)
-
         # XXX how will this be related to the device encryption setting?
         encryptCheckbutton = self.builder.get_object("containerEncryptedCheckbox")
         encryptCheckbutton.set_active(self.encrypted)
-        encryptCheckbutton.set_sensitive(not self.exists)
 
         # set up the raid level combo
         # XXX how will this be related to the device raid level setting?
@@ -424,6 +426,26 @@ class ContainerDialog(GUIObject):
         raidStoreFilter.refilter()
         self._populate_raid()
 
+        size_policy_combo = self.builder.get_object("containerSizeCombo")
+        size_entry = self.builder.get_object("containerSizeEntry")
+        size_label = self.builder.get_object("containerSizeLabel")
+        size = Size(spec="%d mb" % self.size)
+        size_entry.set_text(size.humanReadable(max_places=None))
+        if self.size_policy == SIZE_POLICY_AUTO:
+            size_policy_combo.set_active(0)
+        elif self.size_policy == SIZE_POLICY_MAX:
+            size_policy_combo.set_active(1)
+        else:
+            size_policy_combo.set_active(2)
+
+        if self.exists:
+            fancy_set_sensitive(name_entry, False)
+            treeview.set_sensitive(False)
+            fancy_set_sensitive(encryptCheckbutton, False)
+            size_policy_combo.set_sensitive(False)
+            size_entry.set_sensitive(False)
+            size_label.set_sensitive(False)
+
     def on_cancel_clicked(self, button):
         self.window.destroy()
 
@@ -459,6 +481,22 @@ class ContainerDialog(GUIObject):
                 self.window.show_all()
                 return
 
+        size_combo = self.builder.get_object("containerSizeCombo")
+        size_entry = self.builder.get_object("containerSizeEntry")
+        idx = size_combo.get_active()
+        if idx == 0:
+            size = SIZE_POLICY_AUTO
+        elif idx == 1:
+            size = SIZE_POLICY_MAX
+        elif idx == 2:
+            size = size_from_entry(size_entry)
+            if size:
+                size = int(size.convertTo(spec="MB"))
+            elif size is None:
+                size = SIZE_POLICY_MAX
+
+        # now save the changes
+
         self.selected = []
         for path in paths:
             itr = model.get_iter(path)
@@ -468,6 +506,7 @@ class ContainerDialog(GUIObject):
         self.name = name
         self.raid_level = raid_level
         self.encrypted = self.builder.get_object("containerEncryptedCheckbox").get_active()
+        self.size_policy = size
 
         self.builder.get_object("containerErrorLabel").set_text("")
         self.window.destroy()
@@ -479,6 +518,16 @@ class ContainerDialog(GUIObject):
             if not self._error:
                 return rc
 
+    def on_size_changed(self, combo):
+        size_entry = self.builder.get_object("containerSizeEntry")
+        active_index = combo.get_active()
+        if active_index == 0:
+            size_entry.set_sensitive(False)
+        elif active_index == 1:
+            size_entry.set_sensitive(False)
+        else:
+            size_entry.set_sensitive(True)
+
     def _raid_level_visible(self, model, itr, user_data):
         # This is weird because for lvm's container-wide raid we use md.
         if self.device_type == DEVICE_TYPE_LVM:
@@ -563,6 +612,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         self._device_disks = []
         self._device_container_name = None
         self._device_container_raid_level = None
+        self._device_container_size = SIZE_POLICY_AUTO
         self._device_name_dict = {DEVICE_TYPE_LVM: None,
                                   DEVICE_TYPE_MD: None,
                                   DEVICE_TYPE_PARTITION: "",
@@ -1109,7 +1159,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
 
         # RAID LEVEL
         raid_level = self._get_raid_level()
-        old_raid_level = devicefactory.get_raid_level(device)
+        old_raid_level = get_raid_level(device)
         changed_raid_level = (old_device_type == device_type and
                               device_type in (DEVICE_TYPE_MD,
                                               DEVICE_TYPE_BTRFS) and
@@ -1166,12 +1216,15 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         old_container_encrypted = False
         old_container_raid_level = None
         old_container = None
+        old_container_size = SIZE_POLICY_AUTO
         if not changed_device_type:
             old_container = factory.get_container(device=use_dev)
             if old_container:
                 old_container_name = old_container.name
                 old_container_encrypted = old_container.encrypted
                 old_container_raid_level = get_raid_level(old_container)
+                old_container_size = getattr(old_container, "size_policy",
+                                                            old_container.size)
 
             container = factory.get_container(name=container_name)
             if old_container and container_name != old_container.name:
@@ -1190,6 +1243,11 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         log.debug("new container raid level: %s" % container_raid_level)
         changed_container_raid_level = (old_container_raid_level != container_raid_level)
 
+        container_size = self._device_container_size
+        log.debug("old container size request: %s" % old_container_size)
+        log.debug("new container size request: %s" % container_size)
+        changed_container_size = (old_container_size != container_size)
+
         # DISK SET
         old_disks = device.disks
         if hasattr(device, "req_disks") and not device.exists:
@@ -1208,8 +1266,9 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         changed = (changed_name or changed_size or changed_device_type or
                    changed_label or changed_mountpoint or changed_disk_set or
                    changed_encryption or changed_raid_level or
+                   changed_fs_type or
                    changed_container or changed_container_encrypted or
-                   changed_container_raid_level)
+                   changed_container_raid_level or changed_container_size)
 
         if not use_dev.exists:
             if not changed:
@@ -1244,6 +1303,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
                                          container_name=container_name,
                                          container_encrypted=container_encrypted,
                                          container_raid_level=container_raid_level,
+                                         container_size=container_size,
                                          device=_device,
                                          selector=selector)
                 except StorageError as e:
@@ -1271,6 +1331,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
                                                  container_name=old_container_name,
                                                  container_encrypted=old_container_encrypted,
                                                  container_raid_level=old_container_raid_level,
+                                                 container_size=old_container_size,
                                                  selector=selector)
                         except StorageError as e:
                             # failed to recover.
@@ -1545,18 +1606,22 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
             self._device_container_name = use_dev.vg.name
             self._device_container_raid_level = get_raid_level(use_dev.vg)
             self._device_container_encrypted = use_dev.vg.encrypted
+            self._device_container_size = use_dev.vg.size_policy
         elif hasattr(use_dev, "volume"):
             self._device_container_name = use_dev.volume.name
             self._device_container_raid_level = get_raid_level(use_dev.volume)
             self._device_container_encrypted = use_dev.volume.encrypted
+            self._device_container_size = use_dev.volume.size_policy
         else:
             self._device_container_name = None
             self._device_container_raid_level = None
             self._device_container_encrypted = False
+            self._device_container_size = SIZE_POLICY_AUTO
 
         log.debug("updated device_container_name to %s" % self._device_container_name)
         log.debug("updated device_container_raid_level to %s" % self._device_container_raid_level)
         log.debug("updated device_container_encrypted to %s" % self._device_container_encrypted)
+        log.debug("updated device_container_size to %s" % self._device_container_size)
 
         selectedDeviceLabel.set_text(selector.props.name)
         selectedDeviceDescLabel.set_text(self._description(selector.props.name))
@@ -1815,7 +1880,6 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         # button or wait until we have a way to control container-level
         # encryption.
         encrypted = self.data.autopart.encrypted
-        encrypt_container = False
 
         # 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
@@ -1847,10 +1911,14 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
             factory = devicefactory.get_device_factory(self.__storage,
                                                      device_type, size)
             container = factory.get_container()
+            kwargs = {}
             if container:
                 # don't override user-initiated changes to a defined container
-                encrypt_container = container.encrypted
                 disks = container.disks
+                kwargs = {"container_encrypted": container.encrypted,
+                          "container_raid_level": get_raid_level(container),
+                          "container_size": getattr(container, "size_policy",
+                                                               container.size)}
 
             try:
                 self.__storage.factoryDevice(device_type,
@@ -1858,14 +1926,20 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
                                          fstype=fstype,
                                          mountpoint=mountpoint,
                                          encrypted=encrypted,
-                                         container_encrypted=encrypt_container,
-                                         disks=disks)
+                                         disks=disks,
+                                         **kwargs)
             except StorageError as e:
                 log.error("factoryDevice failed: %s" % e)
                 log.debug("trying to find an existing container to use")
                 container = factory.get_container(allow_existing=True)
                 log.debug("found container %s" % container)
                 if container:
+                    # don't override user-initiated changes to a defined container
+                    disks = container.disks
+                    kwargs = {"container_encrypted": container.encrypted,
+                              "container_raid_level": get_raid_level(container),
+                              "container_size": getattr(container, "size_policy",
+                                                                   container.size)}
                     try:
                         self.__storage.factoryDevice(device_type,
                                                  size=size,
@@ -1873,7 +1947,8 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
                                                  mountpoint=mountpoint,
                                                  encrypted=encrypted,
                                                  disks=disks,
-                                                 container_name=container.name)
+                                                 container_name=container.name,
+                                                 **kwargs)
                     except StorageError as e2:
                         log.error("factoryDevice failed w/ old container: %s" % e2)
                     else:
@@ -1941,21 +2016,26 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         if hasattr(device, "vg"):
             container = device.vg
             device_type = DEVICE_TYPE_LVM
-            raid_level = None
         elif hasattr(device, "volume"):
             container = device.volume
             device_type = DEVICE_TYPE_BTRFS
-            raid_level = container.dataLevel
 
+        # adjust container to size of remaining devices, if auto-sized
         if container and not container.exists and \
-           self.__storage.devicetree.getChildren(container):
-            # adjust container to size of remaining devices
+           self.__storage.devicetree.getChildren(container) and \
+           container.size_policy == SIZE_POLICY_AUTO:
+            cont_encrypted = container.encrypted
+            cont_raid = get_raid_level(container)
+            cont_size = container.size_policy
+            cont_name = container.name
             with ui_storage_logger():
                 factory = devicefactory.get_device_factory(self.__storage,
-                                                          device_type, 0,
-                                                          disks=container.disks,
-                                                          container_encrypted=container.encrypted,
-                                                          raid_level=raid_level)
+                                            device_type, 0,
+                                            disks=container.disks,
+                                            container_name=cont_name,
+                                            container_encrypted=cont_encrypted,
+                                            container_raid_level=cont_raid,
+                                            container_size=cont_size)
                 factory.configure()
 
         # if this device has parents with no other children, remove them too
@@ -2096,18 +2176,27 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         self._populate_raid(self._get_raid_level())
 
     def run_container_editor(self, container=None, name=None):
+        size = 0
+        size_policy = self._device_container_size
         if container:
             container_name = container.name
             raid_level = get_raid_level(container)
+            size = container.size
+            size_policy = container.size_policy
         elif name:
             container_name = name
             raid_level = None
+            if name != self._device_container_name:
+                # creating a new container -- switch to the default
+                size_policy = SIZE_POLICY_AUTO
 
         dialog = ContainerDialog(self.data,
                                  device_type=self._get_current_device_type(),
                                  name=container_name,
                                  raid_level=self._device_container_raid_level,
                                  encrypted=self._device_container_encrypted,
+                                 size_policy=size_policy,
+                                 size=size,
                                  disks=self._clearpartDevices,
                                  free=self._currentFreeInfo,
                                  selected=self._device_disks,
@@ -2144,11 +2233,13 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
 
         log.debug("new container raid level: %s" % dialog.raid_level)
         log.debug("new container encrypted: %s" % dialog.encrypted)
+        log.debug("new container size: %s" % dialog.size_policy)
 
         self._device_disks = disks
         self._device_container_name = name
         self._device_container_raid_level = dialog.raid_level
         self._device_container_encrypted = dialog.encrypted
+        self._device_container_size = dialog.size_policy
 
     def on_modify_container_clicked(self, button):
         container_name = self.builder.get_object("containerCombo").get_active_text()
@@ -2215,9 +2306,12 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         if container:
             self._device_container_raid_level = get_raid_level(container)
             self._device_container_encrypted = container.encrypted
+            self._device_container_size = getattr(container, "size_policy",
+                                                             container.size)
         else:
             self._device_container_raid_level = None
             self._device_container_encrypted = self.data.autopart.encrypted
+            self._device_container_size = SIZE_POLICY_AUTO
 
         self.builder.get_object("modifyContainerButton").set_sensitive(not container_exists)
 
@@ -2426,6 +2520,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         container_combo = self.builder.get_object("containerCombo")
         container_button = self.builder.get_object("modifyContainerButton")
         container_label = self.builder.get_object("containerLabel")
+        container_size_policy = SIZE_POLICY_AUTO
         if device_type in (DEVICE_TYPE_LVM, DEVICE_TYPE_BTRFS):
             # set up the vg widgets and then bail out
             if devicefactory.get_device_type(device) == device_type:
@@ -2439,6 +2534,8 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
                                                          0)
                 container = factory.get_container(device=_device)
                 default_container = getattr(container, "name", None)
+                if container:
+                    container_size_policy = container.size_policy
 
             container_type_text = container_type_names[device_type]
             container_label.set_text(container_type_text.title())
@@ -2461,6 +2558,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
 
             log.debug("default container is %s" % default_container)
             self._device_container_name = default_container
+            self._device_container_size = container_size_policy
 
             if not default_seen:
                 container_combo.append_text(default_container)
-- 
1.8.1.4



More information about the anaconda-patches mailing list