To follow up on yesterday's partition resizing patches, I've now got an additional set of patches on top which add support for resizing logical volumes as well. The first is a bit of an infrastructure thing and more in line with the main resizing series and then there's a patch for the backend and one for the UI.
Jeremy
--- iw/lvm_dialog_gui.py | 2 +- iw/partition_dialog_gui.py | 2 +- iw/partition_ui_helpers_gui.py | 11 +++++------ iw/raid_dialog_gui.py | 2 +- partRequests.py | 13 +++++++++++-- 5 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/iw/lvm_dialog_gui.py b/iw/lvm_dialog_gui.py index 560970f..88ead9f 100644 --- a/iw/lvm_dialog_gui.py +++ b/iw/lvm_dialog_gui.py @@ -463,7 +463,7 @@ class VolumeGroupEditor:
self.fsoptionsDict = {} if logrequest.getPreExisting(): - (row, self.fsoptionsDict) = createPreExistFSOptionSection(logrequest, maintable, row, mountCombo, ignorefs = ["software RAID", "physical volume (LVM)", "vfat"]) + (row, self.fsoptionsDict) = createPreExistFSOptionSection(logrequest, maintable, row, mountCombo, self.partitions, ignorefs = ["software RAID", "physical volume (LVM)", "vfat"])
dialog.vbox.pack_start(maintable) dialog.show_all() diff --git a/iw/partition_dialog_gui.py b/iw/partition_dialog_gui.py index fa7a6fd..0dc13ac 100644 --- a/iw/partition_dialog_gui.py +++ b/iw/partition_dialog_gui.py @@ -444,7 +444,7 @@ class PartitionEditor: # aren't protected (we'd still like to be able to mount them, though) self.fsoptionsDict = {} if self.origrequest.type == REQUEST_PREEXIST and self.origrequest.fstype and not self.origrequest.getProtected(): - (row, self.fsoptionsDict) = createPreExistFSOptionSection(self.origrequest, maintable, row, self.mountCombo) + (row, self.fsoptionsDict) = createPreExistFSOptionSection(self.origrequest, maintable, row, self.mountCombo, self.partitions)
# size options if self.origrequest.type == REQUEST_NEW: diff --git a/iw/partition_ui_helpers_gui.py b/iw/partition_ui_helpers_gui.py index e4477d2..4773e8e 100644 --- a/iw/partition_ui_helpers_gui.py +++ b/iw/partition_ui_helpers_gui.py @@ -270,7 +270,7 @@ def noformatCB(widget, data): resizesb - spinbutton with resize target """ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, - ignorefs=[]): + partitions, ignorefs=[]): rc = {} ofstype = origrequest.fstype
@@ -316,9 +316,8 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, migratecb = None migfstypeCombo = None
- # FIXME: we should support resizing LVs too - if origrequest.origfstype.isResizable() and origrequest.type == REQUEST_PREEXIST: - resizecb = gtk.CheckButton(label=_("_Resize partition")) + if origrequest.isResizable(): + resizecb = gtk.CheckButton(label=_("_Resize")) resizecb.set_active(origrequest.targetSize is not None) rc["resizecb"] = resizecb maintable.attach(resizecb, 0, 1, row, row + 1) @@ -328,8 +327,8 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, else: value = origrequest.size
- reqlower = origrequest.getMinimumResizeMB() - requpper = origrequest.getMaximumResizeMB() + reqlower = origrequest.getMinimumResizeMB(partitions) + requpper = origrequest.getMaximumResizeMB(partitions) if not origrequest.format: lower = reqlower else: diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index 22123f8..563cc4a 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -444,7 +444,7 @@ class RaidEditor: maintable.attach(self.lukscb, 0, 2, row, row + 1) row = row + 1 else: - (row, self.fsoptionsDict) = createPreExistFSOptionSection(self.origrequest, maintable, row, self.mountCombo) + (row, self.fsoptionsDict) = createPreExistFSOptionSection(self.origrequest, maintable, row, self.mountCombo, self.partitions)
# put main table into dialog dialog.vbox.pack_start(maintable) diff --git a/partRequests.py b/partRequests.py index c5655e0..84ff18a 100644 --- a/partRequests.py +++ b/partRequests.py @@ -158,6 +158,9 @@ class RequestSpec: self.targetSize = None """Size to resize to"""
+ self.resizable = False + """Is this a request that can be resized?""" + def __str__(self): if self.fstype: fsname = self.fstype.getName() @@ -190,6 +193,11 @@ class RequestSpec: import traceback traceback.print_stack()
+ def isResizable(self): + if self.encryption: # encrypted devices can't be resized currently + return False + return self.resizable and self.fstype.isResizable() + def toEntry(self, partitions): """Turn a request into a fsset entry and return the entry.""" device = self.getDevice(partitions) @@ -581,17 +589,18 @@ class PreexistingPartitionSpec(PartitionSpec): format = format, migrate = migrate, mountpoint = mountpoint, preexist = 1) self.type = REQUEST_PREEXIST + self.resizable = True
self.maxResizeSize = None """Maximum size of this partition request"""
- def getMaximumResizeMB(self): + def getMaximumResizeMB(self, partitions): if self.maxResizeSize is not None: return self.maxResizeSize log.warning("%s doesn't have a max size set" %(self.device,)) return MAX_PART_SIZE
- def getMinimumResizeMB(self): + def getMinimumResizeMB(self, partitions): return self.fstype.getMinimumSize(self.device)
class RaidRequestSpec(RequestSpec):
--- fsset.py | 32 +++++++++++++++++++++++++++----- lvm.py | 24 ++++++++++++++++++++---- lvmErrors.py | 10 ++++++++++ packages.py | 5 +++-- partRequests.py | 23 +++++++++++++++++++++-- partitions.py | 23 ++++++++++++++++++++++- 6 files changed, 103 insertions(+), 14 deletions(-)
diff --git a/fsset.py b/fsset.py index 9a93726..867906c 100644 --- a/fsset.py +++ b/fsset.py @@ -1482,7 +1482,8 @@ MAILADDR root if bootPart: del bootPart
- def resizeFilesystems (self, chroot = '/', shrink = False, grow = False): + def resizeFilesystems (self, diskset, chroot = '/', shrink = False, grow = False): + todo = [] for entry in self.entries: if not entry.fsystem or not entry.fsystem.isResizable(): continue @@ -1492,13 +1493,34 @@ MAILADDR root continue if grow and not (entry.resizeTargetSize > entry.resizeOrigSize): continue + todo.append(entry) + if len(todo) == 0: + return + + # we have to have lvm activated to be able to do resizes of LVs + active = lvm.vgcheckactive() + if not active: + diskset.startMPath() + diskset.startDmRaid() + diskset.startMdRaid() + + lvm.vgscan() + lvm.vgactivate() + + for entry in todo: entry.fsystem.resize(entry, entry.resizeTargetSize, self.progressWindow, chroot)
- def shrinkFilesystems (self, chroot): - self.resizeFilesystems(chroot, shrink = True) - def growFilesystems (self, chroot): - self.resizeFilesystems(chroot, grow = True) + if not active: + lvm.vgdeactivate() + diskset.stopMPath() + diskset.stopDmRaid() + diskset.stopMdRaid() + + def shrinkFilesystems (self, diskset, chroot): + self.resizeFilesystems(diskset, chroot, shrink = True) + def growFilesystems (self, diskset, chroot): + self.resizeFilesystems(diskset, chroot, grow = True)
def formatSwap (self, chroot, forceFormat=False): formatted = [] diff --git a/lvm.py b/lvm.py index e1ead92..4e14ac4 100644 --- a/lvm.py +++ b/lvm.py @@ -200,6 +200,22 @@ def lvremove(lvname, vgname): if rc: raise LVRemoveError(vgname, lvname)
+def lvresize(lvname, vgname, size): + global lvmDevicePresent + if flags.test or lvmDevicePresent == 0: + return + + args = ["lvresize", "-An", "-L", "%dM" %(size,), "-v", + "/dev/%s/%s" %(vgname, lvname,)] + + try: + rc = lvmExec(*args) + except: + rc = 1 + if rc: + raise LVMResizeError(vgname, lvname) + + def vgcreate(vgname, PESize, nodes): """Creates a new volume group."
@@ -364,18 +380,18 @@ def vglist(): vgs = [] args = ["vgdisplay", "-C", "--noheadings", "--units", "b", "--nosuffix", "--separator", ":", "--options", - "vg_name,vg_size,vg_extent_size" + "vg_name,vg_size,vg_extent_size,vg_free" ] for line in lvmCapture(*args): try: - (vg, size, pesize) = line + (vg, size, pesize, free) = line size = long(math.floor(long(size) / (1024 * 1024))) pesize = long(pesize)/1024 + free = math.floor(long(free) / (1024 * 1024)) except: continue log.info("vg %s, size is %s, pesize is %s" %(vg, size, pesize)) - vgs.append( (vg, size, pesize) ) - + vgs.append( (vg, size, pesize, free) ) return vgs
def partialvgs(): diff --git a/lvmErrors.py b/lvmErrors.py index 3371c05..c69e9b5 100644 --- a/lvmErrors.py +++ b/lvmErrors.py @@ -59,6 +59,16 @@ class LVRemoveError(LvmError): return "lvremove of lv "%s" from vg "%s" failed\nLog:\n%s" % ( \ self.lvname, self.vgname, self.log)
+class LVResizeError(LvmError): + def __init__(self, vgname, lvname): + self.vgname = vgname + self.lvname = lvname + self.log = self.getLvmOutput() + + def __str__(self): + return "lvresize of lv "%s" from vg "%s" failed\nLog:\n%s" % ( \ + self.lvname, self.vgname, self.log) + class VGCreateError(LvmError): def __init__(self, vgname, PESize, nodes): self.vgname = vgname diff --git a/packages.py b/packages.py index 99f9a38..42dc12b 100644 --- a/packages.py +++ b/packages.py @@ -148,10 +148,11 @@ def turnOnFilesystems(anaconda): searchPath = 1) anaconda.id.partitions.doMetaDeletes(anaconda.id.diskset) anaconda.id.fsset.setActive(anaconda.id.diskset) - anaconda.id.fsset.shrinkFilesystems(anaconda.rootPath) + anaconda.id.fsset.shrinkFilesystems(anaconda.id.diskset, anaconda.rootPath) if not anaconda.id.fsset.isActive(): anaconda.id.diskset.savePartitions () - anaconda.id.fsset.growFilesystems(anaconda.rootPath) + anaconda.id.partitions.doMetaResizes(anaconda.id.diskset) + anaconda.id.fsset.growFilesystems(anaconda.id.diskset, anaconda.rootPath) if not anaconda.id.fsset.volumesCreated: anaconda.id.fsset.createLogicalVolumes(anaconda.rootPath) anaconda.id.fsset.formatSwap(anaconda.rootPath) diff --git a/partRequests.py b/partRequests.py index 84ff18a..a766404 100644 --- a/partRequests.py +++ b/partRequests.py @@ -795,6 +795,7 @@ class VolumeGroupRequestSpec(RequestSpec): self.physicalVolumes = physvols self.pesize = pesize self.preexist = preexist + self.free = 0
# FIXME: this is a hack so that we can set the vg name automagically # with autopartitioning to not conflict with existing vgs @@ -915,13 +916,15 @@ class LogicalVolumeRequestSpec(RequestSpec): self.grow = grow self.maxSizeMB = maxSizeMB self.startSize = size + + self.minResizeSize = None + self.resizable = True if not percent and not size and not preexist: raise RuntimeError, "Error with Volume Group:Logical Volume %s:%s - Logical Volume must specify either percentage of vgsize or size" % (volgroup, lvname)
if percent and grow: raise RuntimeError, "Error with Volume Group:Logical Volume %s:%s - Logical Volume cannot grow if percentage given" % (volgroup, lvname) -
def __str__(self): if self.fstype: @@ -955,14 +958,19 @@ class LogicalVolumeRequestSpec(RequestSpec): existing = self.preexist) return self.dev
- def getActualSize(self, partitions, diskset): + def getActualSize(self, partitions = None, diskset = None, target = False): """Return the actual size allocated for the request in megabytes.""" if self.percent: + if partitions is None or diskset is None: + raise RuntimeError, "trying to get a percentage lv size on resize path" vgreq = partitions.getRequestByID(self.volumeGroup) vgsize = vgreq.getActualSize(partitions, diskset) lvsize = int(self.percent * 0.01 * vgsize) #lvsize = lvm.clampLVSizeRequest(lvsize, vgreq.pesize) return lvsize + # FIXME: the target bit here is a bit of a hack... + elif self.targetSize is not None and target: + return self.targetSize else: return self.size
@@ -996,3 +1004,14 @@ class LogicalVolumeRequestSpec(RequestSpec): "containing encrypted physical volumes.")
return RequestSpec.sanityCheckRequest(self, partitions, skipMntPtExistCheck) + + def getMaximumResizeMB(self, partitions): + vg = partitions.getRequestByID(self.volumeGroup) + print "max is", self.getActualSize(), vg.free, self.getActualSize() + vg.free + return self.getActualSize() + vg.free + + def getMinimumResizeMB(self, partitions): + if self.minResizeSize is None: + log.warning("don't know the minimum size of %s" %(self.logicalVolumeName,)) + return 1 + return self.minResizeSize diff --git a/partitions.py b/partitions.py index 25871a7..b19b4e3 100644 --- a/partitions.py +++ b/partitions.py @@ -278,7 +278,7 @@ class Partitions: lvm.vgactivate()
pvs = lvm.pvlist() - for (vg, size, pesize) in lvm.vglist(): + for (vg, size, pesize, vgfree) in lvm.vglist(): try: preexist_size = float(size) except: @@ -301,6 +301,7 @@ class Partitions: pesize = pesize, preexist = 1, preexist_size = preexist_size) + spec.free = vgfree vgid = self.addRequest(spec)
for (lvvg, lv, size, lvorigin) in lvm.lvlist(): @@ -332,6 +333,8 @@ class Partitions: format = format, size = lvsize, volgroup = vgid, lvname = lv, mountpoint = mnt, fslabel = fslabel, preexist = 1) + if fsystem.isResizable(): + spec.minResizeSize = fsystem.getMinimumSize("%s/%s" %(vg, lv)) self.addRequest(spec)
for vg in lvm.partialvgs(): @@ -1460,6 +1463,24 @@ class Partitions: lvm.vgdeactivate() diskset.stopMdRaid()
+ def doMetaResizes(self, diskset): + """Does resizing of non-physical volumes.""" + # NOTE: this should be called with volumes active + + # we only support resizing LVM of these types of things currently + for lv in self.getLVMLVRequests(): + if not lv.preexist: + continue + if lv.targetSize is None: + continue + + vg = self.getRequestByID(lv.volumeGroup) + if vg is None: + continue + + lvm.lvresize(lv.logicalVolumeName, vg.volumeGroupName, + lv.targetSize) + def deleteDependentRequests(self, request): """Handle deletion of this request and all requests which depend on it.
NOTE: this has a little bit of a hack in that we abuse lvrequest.getActualSize() somewhat but that's largely due to the fact that lvrequest.getActualSize() was already being abused and updating the size attribute of the request in autopart --- iw/lvm_dialog_gui.py | 29 ++++++++++++++++++++++------- iw/partition_gui.py | 2 +- 2 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/iw/lvm_dialog_gui.py b/iw/lvm_dialog_gui.py index 88ead9f..1d0775b 100644 --- a/iw/lvm_dialog_gui.py +++ b/iw/lvm_dialog_gui.py @@ -103,7 +103,7 @@ class VolumeGroupEditor: used = 0 resize = 0 for lv in self.logvolreqs: - osize = lv.getActualSize(self.partitions, self.diskset) + osize = lv.getActualSize(self.partitions, self.diskset, True) oldused = oldused + osize nsize = lvm.clampLVSizeRequest(osize, newpe, roundup=1) if nsize != osize: @@ -137,7 +137,7 @@ class VolumeGroupEditor: return 0
for lv in self.logvolreqs: - osize = lv.getActualSize(self.partitions, self.diskset) + osize = lv.getActualSize(self.partitions, self.diskset, True) nsize = lvm.clampLVSizeRequest(osize, newpe, roundup=1) lv.setSize(nsize)
@@ -208,7 +208,7 @@ class VolumeGroupEditor: else: maxlv = lvm.getMaxLVSize(curval) for lv in self.logvolreqs: - lvsize = lv.getActualSize(self.partitions, self.diskset) + lvsize = lv.getActualSize(self.partitions, self.diskset, True) if lvsize > maxlv: self.intf.messageWindow(_("Not enough space"), _("The physical extent size " @@ -441,7 +441,7 @@ class VolumeGroupEditor: sizeEntry = gtk.Entry(16) lbl.set_mnemonic_widget(sizeEntry) if logrequest: - sizeEntry.set_text("%d" % (logrequest.getActualSize(self.partitions, self.diskset),)) + sizeEntry.set_text("%d" % (logrequest.getActualSize(self.partitions, self.diskset, True),)) else: lbl = createAlignedLabel(_("Size (MB):")) sizeEntry = gtk.Label(str(logrequest.size)) @@ -478,6 +478,7 @@ class VolumeGroupEditor: fsystem = newfstypeCombo.get_active_value() format = 1 migrate = 0 + targetSize = None else: if self.fsoptionsDict.has_key("formatcb"): format = self.fsoptionsDict["formatcb"].get_active() @@ -493,6 +494,11 @@ class VolumeGroupEditor: else: migrate = 0
+ if self.fsoptionsDict.has_key("resizecb") and self.fsoptionsDict["resizecb"].get_active(): + targetSize = self.fsoptionsDict["resizesb"].get_value_as_int() + else: + targetSize = None + # set back if we are not formatting or migrating origfstype = logrequest.origfstype if not format and not migrate: @@ -614,6 +620,7 @@ class VolumeGroupEditor: request.size = size request.format = format request.migrate = migrate + request.targetSize = targetSize request.grow = 0
# this is needed to clear out any cached info about the device @@ -674,6 +681,14 @@ class VolumeGroupEditor: self.logvolreqs.remove(logrequest) iter = self.getCurrentLogicalVolume() self.logvolstore.remove(iter) + if request.targetSize is not None: + size = request.targetSize + # adjust the free space in the vg + if logrequest.targetSize is not None: + diff = request.targetSize - logrequest.targetSize + else: + diff = request.targetSize - request.size + self.origvgrequest.free -= diff self.logvolreqs.append(request)
@@ -797,7 +812,7 @@ class VolumeGroupEditor: def computeLVSpaceNeeded(self, logreqs): neededSpaceMB = 0 for lv in logreqs: - neededSpaceMB = neededSpaceMB + lv.getActualSize(self.partitions, self.diskset) + neededSpaceMB = neededSpaceMB + lv.getActualSize(self.partitions, self.diskset, True)
return neededSpaceMB
@@ -805,7 +820,7 @@ class VolumeGroupEditor: self.logvolstore.clear() for lv in self.logvolreqs: iter = self.logvolstore.append() - size = lv.getActualSize(self.partitions, self.diskset) + size = lv.getActualSize(self.partitions, self.diskset, True) lvname = lv.logicalVolumeName mntpt = lv.mountpoint if lvname: @@ -1066,7 +1081,7 @@ class VolumeGroupEditor: self.logvolstore.set_value(iter, 1, lvrequest.mountpoint) else: self.logvolstore.set_value(iter, 1, "") - self.logvolstore.set_value(iter, 2, "%d" % (lvrequest.getActualSize(self.partitions, self.diskset))) + self.logvolstore.set_value(iter, 2, "%d" % (lvrequest.getActualSize(self.partitions, self.diskset, True)))
self.logvollist = gtk.TreeView(self.logvolstore) col = gtk.TreeViewColumn(_("Logical Volume Name"), diff --git a/iw/partition_gui.py b/iw/partition_gui.py index 3211a35..894e390 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -737,7 +737,7 @@ class PartitionWindow(InstallWindow): self.tree[iter]['Mount Point'] = lvrequest.mountpoint else: self.tree[iter]['Mount Point'] = "" - self.tree[iter]['Size (MB)'] = "%d" % (lvrequest.getActualSize(self.partitions, self.diskset),) + self.tree[iter]['Size (MB)'] = "%d" % (lvrequest.getActualSize(self.partitions, self.diskset, True),) self.tree[iter]['PyObject'] = str(lvrequest.uniqueID) ptype = lvrequest.fstype.getName()
anaconda-devel@lists.stg.fedoraproject.org