[PATCH] Add installer support for lvm thin provisioning.
David Lehman
dlehman at redhat.com
Tue Jun 25 20:40:10 UTC 2013
This adds a new autopart type as well as the ability to create thinly-
provisioned lvm using the custom spoke.
---
anaconda.spec.in | 4 +--
pyanaconda/installclass.py | 4 +--
pyanaconda/kickstart.py | 36 +++++++++++++++++++----
pyanaconda/ui/gui/spokes/custom.glade | 1 +
pyanaconda/ui/gui/spokes/custom.py | 53 ++++++++++++++++++++++++----------
pyanaconda/ui/gui/spokes/storage.glade | 6 ++--
6 files changed, 77 insertions(+), 27 deletions(-)
diff --git a/anaconda.spec.in b/anaconda.spec.in
index 382737b..52eda35 100644
--- a/anaconda.spec.in
+++ b/anaconda.spec.in
@@ -21,7 +21,7 @@ Source0: %{name}-%{version}.tar.bz2
%define gconfversion 2.28.1
%define intltoolver 0.31.2-3
%define libnlver 1.0
-%define pykickstartver 1.99.32
+%define pykickstartver 1.99.33
%define yumver 3.4.3-91
%define partedver 1.8.1
%define pypartedver 2.5-2
@@ -80,7 +80,7 @@ BuildRequires: s390utils-devel
%endif
Requires: anaconda-widgets = %{version}-%{release}
-Requires: python-blivet >= 0.16
+Requires: python-blivet >= 0.18
Requires: gnome-icon-theme-symbolic
Requires: python-meh >= %{mehver}
Requires: libreport-anaconda >= 2.0.21-1
diff --git a/pyanaconda/installclass.py b/pyanaconda/installclass.py
index 7e04cc4..8f38548 100644
--- a/pyanaconda/installclass.py
+++ b/pyanaconda/installclass.py
@@ -81,10 +81,10 @@ class BaseInstallClass(object):
def setDefaultPartitioning(self, storage):
autorequests = [PartSpec(mountpoint="/", fstype=storage.defaultFSType,
size=1024, maxSize=50*1024, grow=True,
- btr=True, lv=True, encrypted=True),
+ btr=True, lv=True, thin=True, encrypted=True),
PartSpec(mountpoint="/home", fstype=storage.defaultFSType,
size=500, grow=True, requiredSpace=50*1024,
- btr=True, lv=True, encrypted=True)]
+ btr=True, lv=True, thin=True, encrypted=True)]
bootreqs = platform.setDefaultPartitioning()
if bootreqs:
diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py
index fea9697..b88f14e 100644
--- a/pyanaconda/kickstart.py
+++ b/pyanaconda/kickstart.py
@@ -216,7 +216,7 @@ class Authconfig(commands.authconfig.FC3_Authconfig):
except RuntimeError as msg:
log.error("Error running /usr/sbin/authconfig %s: %s", args, msg)
-class AutoPart(commands.autopart.F18_AutoPart):
+class AutoPart(commands.autopart.F19_AutoPart):
def execute(self, storage, ksdata, instClass):
from blivet.partitioning import doAutoPartition
@@ -630,7 +630,7 @@ class Lang(commands.lang.F19_Lang):
def execute(self, *args, **kwargs):
localization.write_language_configuration(self, ROOT_PATH)
-class LogVol(commands.logvol.F18_LogVol):
+class LogVol(commands.logvol.F19_LogVol):
def execute(self, storage, ksdata, instClass):
for l in self.lvList:
l.execute(storage, ksdata, instClass)
@@ -638,7 +638,7 @@ class LogVol(commands.logvol.F18_LogVol):
if self.lvList:
growLVM(storage)
-class LogVolData(commands.logvol.F18_LogVolData):
+class LogVolData(commands.logvol.F19_LogVolData):
def execute(self, storage, ksdata, instClass):
devicetree = storage.devicetree
@@ -659,6 +659,10 @@ class LogVolData(commands.logvol.F18_LogVolData):
else:
type = storage.defaultFSType
+ if self.thin_pool:
+ self.mountpoint = ""
+ type = None
+
# Sanity check mountpoint
if self.mountpoint != "" and self.mountpoint[0] != '/':
raise KickstartValueError, formatErrorMsg(self.lineno, msg="The mount point \"%s\" is not valid." % (self.mountpoint,))
@@ -668,6 +672,12 @@ class LogVolData(commands.logvol.F18_LogVolData):
if not vg:
raise KickstartValueError, formatErrorMsg(self.lineno, msg="No volume group exists with the name \"%s\". Specify volume groups before logical volumes." % self.vgname)
+ pool = None
+ if self.thin_volume:
+ pool = devicetree.getDeviceByName("%s-%s" % (vg.name, self.pool_name))
+ if not pool:
+ raise KickstartValueError, formatErrorMsg(self.lineno, msg="No thin pool exists with the name \"%s\". Specify thin pools before thin volumes." % self.pool_name)
+
# If this specifies an existing request that we should not format,
# quit here after setting up enough information to mount it later.
if not self.format:
@@ -721,7 +731,7 @@ class LogVolData(commands.logvol.F18_LogVolData):
label=self.label,
fsprofile=self.fsprofile,
mountopts=self.fsopts)
- if not format.type:
+ if not format.type and not self.thin_pool:
raise KickstartValueError, formatErrorMsg(self.lineno, msg="The \"%s\" filesystem type is not supported." % type)
# If we were given a pre-existing LV to create a filesystem on, we need
@@ -753,13 +763,27 @@ class LogVolData(commands.logvol.F18_LogVolData):
except KeyError:
pass
+ if self.thin_volume:
+ parents = [pool]
+ else:
+ parents = [vg]
+
+ if self.thin_pool:
+ pool_args = { "metadatasize": self.metadata_size,
+ "chunksize": self.chunk_size / 1024.0 }
+ else:
+ pool_args = {}
+
request = storage.newLV(format=format,
name=self.name,
- parents=[vg],
+ parents=parents,
size=self.size,
+ thin_pool=self.thin_pool,
+ thin_volume=self.thin_volume,
grow=self.grow,
maxsize=self.maxSizeMB,
- percent=self.percent)
+ percent=self.percent,
+ **pool_args)
storage.createDevice(request)
diff --git a/pyanaconda/ui/gui/spokes/custom.glade b/pyanaconda/ui/gui/spokes/custom.glade
index 0212ac4..49b6756 100644
--- a/pyanaconda/ui/gui/spokes/custom.glade
+++ b/pyanaconda/ui/gui/spokes/custom.glade
@@ -1058,6 +1058,7 @@ use. Try something else?</property>
<item translatable="yes">BTRFS</item>
<item translatable="yes">LVM</item>
<item translatable="yes">RAID</item>
+ <item translatable="yes">LVM Thin Provisioning</item>
</items>
<signal name="changed" handler="on_device_type_changed" swapped="no"/>
<signal name="changed" handler="on_value_changed" swapped="no"/>
diff --git a/pyanaconda/ui/gui/spokes/custom.py b/pyanaconda/ui/gui/spokes/custom.py
index da2808c..6563e6a 100644
--- a/pyanaconda/ui/gui/spokes/custom.py
+++ b/pyanaconda/ui/gui/spokes/custom.py
@@ -26,6 +26,7 @@
# - Device descriptions, suggested sizes, etc. should be moved out into a support file.
# - Tabbing behavior in the accordion is weird.
# - Implement striping and mirroring for LVM.
+# - Activating reformat should always enable resize for existing devices.
from contextlib import contextmanager
import re
@@ -48,6 +49,7 @@ from blivet.devicefactory import DEVICE_TYPE_BTRFS
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 DEVICE_TYPE_LVM_THINP
from blivet.devicefactory import get_raid_level
from blivet.devicefactory import SIZE_POLICY_AUTO
from blivet.devicefactory import SIZE_POLICY_MAX
@@ -114,6 +116,7 @@ empty_name_msg = N_("Please enter a valid name.")
invalid_name_msg = N_("That name is invalid. Try something else?")
container_type_names = {DEVICE_TYPE_LVM: lvm_container_name,
+ DEVICE_TYPE_LVM_THINP: lvm_container_name,
DEVICE_TYPE_BTRFS: btrfs_container_name}
MOUNTPOINT_OK = 0
@@ -127,6 +130,7 @@ mountpoint_validation_msgs = {MOUNTPOINT_OK: "",
MOUNTPOINT_EMPTY: empty_mountpoint_msg}
DEVICE_TEXT_LVM = N_("LVM")
+DEVICE_TEXT_LVM_THINP = N_("LVM Thin Provisioning")
DEVICE_TEXT_MD = N_("RAID")
DEVICE_TEXT_PARTITION = N_("Standard Partition")
DEVICE_TEXT_BTRFS = N_("BTRFS")
@@ -135,7 +139,8 @@ DEVICE_TEXT_DISK = N_("Disk")
device_text_map = {DEVICE_TYPE_LVM: DEVICE_TEXT_LVM,
DEVICE_TYPE_MD: DEVICE_TEXT_MD,
DEVICE_TYPE_PARTITION: DEVICE_TEXT_PARTITION,
- DEVICE_TYPE_BTRFS: DEVICE_TEXT_BTRFS}
+ DEVICE_TYPE_BTRFS: DEVICE_TEXT_BTRFS,
+ DEVICE_TYPE_LVM_THINP: DEVICE_TEXT_LVM_THINP}
partition_only_format_types = ["efi", "hfs+", "prepboot", "biosboot",
"appleboot"]
@@ -526,7 +531,7 @@ class ContainerDialog(GUIObject):
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:
+ if self.device_type in (DEVICE_TYPE_LVM, DEVICE_TYPE_LVM_THINP):
return model[itr][4]
elif self.device_type == DEVICE_TYPE_BTRFS:
return model[itr][3]
@@ -551,7 +556,7 @@ class ContainerDialog(GUIObject):
raid_label = self.builder.get_object("containerRaidLevelLabel")
raid_combo = self.builder.get_object("containerRaidLevelCombo")
- if self.device_type not in [DEVICE_TYPE_LVM, DEVICE_TYPE_BTRFS]:
+ if self.device_type not in [DEVICE_TYPE_LVM, DEVICE_TYPE_BTRFS, DEVICE_TYPE_LVM_THINP]:
map(really_hide, [raid_label, raid_combo])
return
@@ -611,6 +616,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
self._device_container_size = SIZE_POLICY_AUTO
self._device_name_dict = {DEVICE_TYPE_LVM: None,
DEVICE_TYPE_MD: None,
+ DEVICE_TYPE_LVM_THINP: None,
DEVICE_TYPE_PARTITION: "",
DEVICE_TYPE_BTRFS: "",
DEVICE_TYPE_DISK: ""}
@@ -1590,6 +1596,8 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
device_type = DEVICE_TYPE_BTRFS
elif device_type_text == _(DEVICE_TEXT_DISK):
device_type = DEVICE_TYPE_DISK
+ elif device_type_text == _(DEVICE_TEXT_LVM_THINP):
+ device_type = DEVICE_TYPE_LVM_THINP
else:
log.error("unknown device type: '%s'" % device_type_text)
@@ -1755,6 +1763,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
btrfs_pos = None
partition_pos = None
lvm_pos = None
+ thinp_pos = None
for idx, itr in enumerate(self._typeCombo.get_model()):
if itr[0] == _(DEVICE_TEXT_BTRFS):
btrfs_pos = idx
@@ -1766,12 +1775,15 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
lvm_pos = idx
elif itr[0] == _(DEVICE_TEXT_DISK):
disk_pos = idx
+ elif itr[0] == _(DEVICE_TEXT_LVM_THINP):
+ thinp_pos = idx
device_type = devicefactory.get_device_type(device)
raid_level = devicefactory.get_raid_level(device)
type_index_map = {DEVICE_TYPE_PARTITION: partition_pos,
DEVICE_TYPE_BTRFS: btrfs_pos,
DEVICE_TYPE_LVM: lvm_pos,
+ DEVICE_TYPE_LVM_THINP: thinp_pos,
DEVICE_TYPE_MD: md_pos,
DEVICE_TYPE_DISK: disk_pos}
@@ -1779,7 +1791,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
if _type == device_type:
self._device_name_dict[_type] = device_name
continue
- elif _type not in (DEVICE_TYPE_LVM, DEVICE_TYPE_MD, DEVICE_TYPE_BTRFS):
+ elif _type not in (DEVICE_TYPE_LVM, DEVICE_TYPE_MD, DEVICE_TYPE_BTRFS, DEVICE_TYPE_LVM_THINP):
continue
swap = (device.format.type == "swap")
@@ -1805,7 +1817,9 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
# FIXME: device raid should be mutually exclusive with container raid
# you can't encrypt a btrfs subvolume -- only the volume/container
- fancy_set_sensitive(self._encryptCheckbox, device_type != DEVICE_TYPE_BTRFS)
+ # XXX CHECKME: encryption of thin logical volumes is not supported at this time
+ if device_type in [DEVICE_TYPE_BTRFS, DEVICE_TYPE_LVM_THINP]:
+ fancy_set_sensitive(self._encryptCheckbox, False)
# The size entry is only sensitive for resizable existing devices and
# new devices that are not btrfs subvolumes.
@@ -1919,6 +1933,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
mountpoint = None
device_type_from_autopart = {AUTOPART_TYPE_LVM: DEVICE_TYPE_LVM,
+ AUTOPART_TYPE_LVM_THINP: DEVICE_TYPE_LVM_THINP,
AUTOPART_TYPE_PLAIN: DEVICE_TYPE_PARTITION,
AUTOPART_TYPE_BTRFS: DEVICE_TYPE_BTRFS}
device_type = device_type_from_autopart[self.data.autopart.type]
@@ -1927,6 +1942,14 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
fstype in partition_only_format_types)):
device_type = DEVICE_TYPE_PARTITION
+ # we shouldn't create swap on a thinly provisioned volume
+ if fstype == "swap" and device_type == DEVICE_TYPE_LVM_THINP:
+ device_type = DEVICE_TYPE_LVM
+
+ # encryption of thinly provisioned volumes isn't supported
+ if encrypted and device_type == DEVICE_TYPE_LVM_THINP:
+ encrypted = False
+
# some devices should never be encrypted
if ((mountpoint and mountpoint.startswith("/boot")) or
fstype in partition_only_format_types):
@@ -2046,7 +2069,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
container = None
if hasattr(device, "vg"):
container = device.vg
- device_type = DEVICE_TYPE_LVM
+ device_type = devicefactory.get_device_type(device)
elif hasattr(device, "volume"):
container = device.volume
device_type = DEVICE_TYPE_BTRFS
@@ -2178,8 +2201,8 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
if device.exists:
return
- if self._get_current_device_type() == DEVICE_TYPE_LVM:
- # LVM disk set management happens through VG edit on RHS
+ if self._get_current_device_type() in (DEVICE_TYPE_LVM, DEVICE_TYPE_LVM_THINP, DEVICE_TYPE_BTRFS):
+ # disk set management happens through container edit on RHS
return
self.clear_errors()
@@ -2428,7 +2451,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
self._applyButton.set_sensitive(False)
self._configButton.set_sensitive(not selector._device.exists and
not selector._device.protected and
- devicefactory.get_device_type(selector._device) != DEVICE_TYPE_LVM)
+ devicefactory.get_device_type(selector._device) in (DEVICE_TYPE_PARTITION, DEVICE_TYPE_MD))
self._removeButton.set_sensitive(not selector._device.protected)
return True
@@ -2567,7 +2590,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
container_combo = self.builder.get_object("containerCombo")
container_label = self.builder.get_object("containerLabel")
container_size_policy = SIZE_POLICY_AUTO
- if device_type in (DEVICE_TYPE_LVM, DEVICE_TYPE_BTRFS):
+ if device_type in (DEVICE_TYPE_LVM, DEVICE_TYPE_BTRFS, DEVICE_TYPE_LVM_THINP):
# set up the vg widgets and then bail out
if devicefactory.get_device_type(device) == device_type:
_device = device
@@ -2586,10 +2609,10 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
container_type_text = container_type_names[device_type]
container_label.set_text(container_type_text.title())
container_combo.remove_all()
- if device_type == DEVICE_TYPE_LVM:
- containers = self.__storage.vgs
- else:
+ if device_type == DEVICE_TYPE_BTRFS:
containers = self.__storage.btrfsVolumes
+ else:
+ containers = self.__storage.vgs
default_seen = False
for c in containers:
@@ -2660,7 +2683,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
# lvm uses the RHS to set disk set. no foolish minds here.
exists = self._current_selector and self._current_selector._device.exists
- self._configButton.set_sensitive(not exists and new_type != DEVICE_TYPE_LVM)
+ self._configButton.set_sensitive(not exists and new_type not in (DEVICE_TYPE_LVM, DEVICE_TYPE_LVM_THINP, DEVICE_TYPE_BTRFS))
# this has to be done before calling populate_raid since it will need
# the raid level combo to contain the relevant raid levels for the new
@@ -2670,7 +2693,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
self._populate_raid(raid_level)
self._populate_container()
- fancy_set_sensitive(self._nameEntry, new_type in (DEVICE_TYPE_BTRFS, DEVICE_TYPE_LVM, DEVICE_TYPE_MD))
+ fancy_set_sensitive(self._nameEntry, new_type in (DEVICE_TYPE_BTRFS, DEVICE_TYPE_LVM, DEVICE_TYPE_MD, DEVICE_TYPE_LVM_THINP))
self._nameEntry.set_text(self._device_name_dict[new_type])
fancy_set_sensitive(self._sizeEntry, new_type != DEVICE_TYPE_BTRFS)
diff --git a/pyanaconda/ui/gui/spokes/storage.glade b/pyanaconda/ui/gui/spokes/storage.glade
index 24c7a4b..cefed27 100644
--- a/pyanaconda/ui/gui/spokes/storage.glade
+++ b/pyanaconda/ui/gui/spokes/storage.glade
@@ -186,11 +186,12 @@
<object class="GtkComboBoxText" id="options1_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="active">2</property>
+ <property name="active">3</property>
<items>
<item translatable="yes">Standard Partition</item>
<item translatable="yes">BTRFS</item>
<item translatable="yes">LVM</item>
+ <item translatable="yes">LVM Thin Provisioning</item>
</items>
<signal name="changed" handler="on_type_changed" swapped="no"/>
</object>
@@ -485,11 +486,12 @@
<object class="GtkComboBoxText" id="options2_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="active">2</property>
+ <property name="active">3</property>
<items>
<item translatable="yes">Standard Partition</item>
<item translatable="yes">BTRFS</item>
<item translatable="yes">LVM</item>
+ <item translatable="yes">LVM Thin Provisioning</item>
</items>
<signal name="changed" handler="on_type_changed" swapped="no"/>
</object>
--
1.8.1.4
More information about the anaconda-patches
mailing list