I've been looking some at what we need to have to implement resizing of partitions and filesystems within anaconda. At this point, the code is starting to work, although there are still some large-ish holes. Also, there are some functionality pieces that are entirely missing. So to start with the caveats: 1) The UI is just enough to make it possible to test the code and not the UI that we're going to actually want to use. 2) Only implemented for ext[23]. Relatively easy to add additional implementations for other filesystems, though 3) Only implemented for partitions. Not RAID. Not LVM. I'm not sure if we can do RAID. LVM LVs are pretty easy. LVM PVs look like they have a lot more caveats based on looking at the pvresize man page 4) Real minimum and maximum calculations still need to be written. But stubs are there 5) Doesn't really have any error handling to speak of All that said, I have done installs to resized partitions and filesystems which is pretty freaking cool :-) I'm going to keep working on filling in the holes but anyone else that wants to take a look, feel free. I just wanted to get this out there for any comments, thoughts, etc that people have Jeremy
diff --git a/pydisk.c b/pydisk.c index 51c2e27..20bb714 100644 --- a/pydisk.c +++ b/pydisk.c @@ -589,6 +589,28 @@ py_ped_partition_set_system (PyPedPartition *p, PyObject * args) } static PyObject * +py_ped_partition_set_geometry (PyPedPartition *p, PyObject * args) +{ + PyPedConstraint *cs = NULL; + PedSector start, end; + + if (!PyArg_ParseTuple(args, "O!LL", &PyPedConstraintType, &cs, + &start, &end)) + return NULL; + + py_ped_exception_string_clear (); + + if (!ped_disk_set_partition_geom (p->disk->disk, p->part, cs->constraint, + start, end)) { + py_ped_set_error_from_ped_exception (); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * py_ped_partition_set_name (PyPedPartition *p, PyObject * args) { char *name; @@ -685,6 +707,8 @@ static struct PyMethodDef PyPedPartitionMethods[] = { METH_VARARGS, NULL }, { "set_name", (PyCFunction) py_ped_partition_set_name, METH_VARARGS, NULL }, + { "set_geometry", (PyCFunction) py_ped_partition_set_geometry, + METH_VARARGS, NULL }, { "get_name", (PyCFunction) py_ped_partition_get_name, METH_VARARGS, NULL }, { "is_busy", (PyCFunction) py_ped_partition_is_busy, diff --git a/pygeometry.c b/pygeometry.c index fdd7614..8522161 100644 --- a/pygeometry.c +++ b/pygeometry.c @@ -24,6 +24,7 @@ #include "partedmodule.h" #include "pygeometry.h" #include "pyfilesystem.h" +#include "pyconstraint.h" /* geometry implementation */ @@ -157,6 +158,24 @@ py_ped_geometry_duplicate (PyPedGeometry * self, PyObject * args) return (PyObject *) py_ped_geometry_obj_new (geom, self->dev, 0); } +static PyObject * +py_ped_constraint_exact (PyPedGeometry * self, PyObject * args) +{ + PedConstraint *constraint; + PyPedConstraint *pyconstraint; + + py_ped_exception_string_clear (); + constraint = ped_constraint_exact (self->geom); + if (constraint == NULL) { + py_ped_set_error_from_ped_exception (); + return NULL; + } + + pyconstraint = py_ped_constraint_obj_new (constraint, self->dev, 0); + + return (PyObject *) pyconstraint; +} + static struct PyMethodDef PyPedGeometryMethods[] = { { "file_system_open", (PyCFunction) py_ped_file_system_open, METH_VARARGS, NULL }, @@ -172,7 +191,8 @@ static struct PyMethodDef PyPedGeometryMethods[] = { (PyCFunction) py_ped_geometry_set_end, METH_VARARGS, NULL }, { "duplicate", (PyCFunction) py_ped_geometry_duplicate, METH_VARARGS, NULL }, - + { "constraint_exact", + (PyCFunction) py_ped_constraint_exact, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } };
diff --git a/autopart.py b/autopart.py index 8886241..f660114 100644 --- a/autopart.py +++ b/autopart.py @@ -865,6 +865,23 @@ def setPreexistParts(diskset, requests): part = disk.next_partition() while part: if part.geom.start == request.start and part.geom.end == request.end: + # if the partition is being resized, we do that now + if request.targetSize is not None: + startSec = part.geom.start + endSec = part.geom.start + long(((request.targetSize * 1024L * 1024L) / disk.dev.sector_size)) - 1 + + # FIXME: could do with some error handling + g = part.geom.duplicate() + g.set_end(endSec) + + constraint = g.constraint_exact() + part.set_geometry(constraint, startSec, endSec) + + if startSec != part.geom.start: + isys.vtActivate(1) + import pdb; pdb.set_trace() + raise RuntimeError, "start of partition moved" + request.device = partedUtils.get_partition_name(part) if request.fstype: if request.fstype.getName() != request.origfstype.getName(): diff --git a/dispatch.py b/dispatch.py index 5775108..8d36483 100644 --- a/dispatch.py +++ b/dispatch.py @@ -78,8 +78,10 @@ installSteps = [ ("upgradeswapsuggestion", upgradeSwapSuggestion, ), ("addswap", ), ("partitiondone", partitioningComplete, ), + ("enablefilesystems", turnOnFilesystems, ), ("upgrademigfind", upgradeMigrateFind, ), ("upgrademigratefs", ), + ("migratefilesystems", doMigrateFilesystems, ), ("upgbootloader", ), ("bootloadersetup", bootloaderSetupChoices, ), ("bootloader", ), @@ -96,8 +98,6 @@ installSteps = [ ("confirminstall", ), ("confirmupgrade", ), ("install", ), - ("enablefilesystems", turnOnFilesystems, ), - ("migratefilesystems", doMigrateFilesystems, ), ("setuptime", setupTimezone, ), ("preinstallconfig", doPreInstall, ), ("installpackages", doInstall, ), diff --git a/fsset.py b/fsset.py index 61ba6a0..84b8440 100644 --- a/fsset.py +++ b/fsset.py @@ -177,10 +177,18 @@ class FileSystemType: self.extraFormatArgs = [] self.maxLabelChars = 16 self.packages = [] + self.resizable = False self.supportsFsProfiles = False self.fsProfileSpecifier = None self.fsprofile = None + def isResizable(self): + return self.resizable + def resize(self, entry, size, chroot='/'): + pass + def getMinimumSize(self): + pass + def isKernelFS(self): """Returns True if this is an in-kernel pseudo-filesystem.""" return False @@ -505,6 +513,26 @@ class extFileSystem(FileSystemType): self.packages = [ "e2fsprogs" ] self.supportsFsProfiles = True self.fsProfileSpecifier = "-T" + self.resizable = True + + def resize(self, entry, size, chroot='/'): + devicePath = entry.device.setupDevice(chroot) + log.info("checking %s prior to resize" %(devicePath,)) + rc = iutil.execWithRedirect("e2fsck", ["-f", devicePath], + stdout="/dev/tty5", stderr="/dev/tty5", + searchPath = 1) + log.info("resizing %s" %(devicePath,)) + rc = iutil.execWithRedirect("resize2fs", [devicePath, "%sM" %(size,)], + stdout="/dev/tty5", stderr="/dev/tty5", + searchPath = 1) + if rc: + raise RuntimeError, "resize failed" + + def getMinimumSize(self): + """Return the minimum filesystem size in megabytes""" + # FIXME: there isn't a good way to get this information with + # an ext filesystem. + return 100 def labelDevice(self, entry, chroot): devicePath = entry.device.setupDevice(chroot) @@ -1376,6 +1404,23 @@ MAILADDR root if bootPart: del bootPart + def resizeFilesystems (self, shrinkgrow): + for entry in self.entries: + if not entry.fsystem or not entry.fsystem.isResizable(): + continue + if entry.resizetarget is None: + continue + if entry.resizedirection < 0 and shrinkgrow != -1: + continue + if entry.resizedirection > 0 and shrinkgrow != 1: + continue + entry.fsystem.resize(entry, entry.resizetarget) + + def shrinkFilesystems (self): + self.resizeFilesystems(-1) + def growFilesystems (self): + self.resizeFilesystems(1) + def formatSwap (self, chroot, forceFormat=False): formatted = [] notformatted = [] @@ -1826,6 +1871,8 @@ class FileSystemSetEntry: self.fsystem = fsystem self.origfsystem = origfsystem self.migrate = migrate + self.resizetarget = None + self.resizedirection = 0 self.options = options self.mountcount = 0 self.label = None @@ -1907,6 +1954,18 @@ class FileSystemSetEntry: def getMigrate (self): return self.migrate + def setResizeTarget (self, targetsize, size): + if not self.fsystem.isResizable() and targetsize is not None: + raise ValueError, "Can't set a resize target for a non-resizable filesystem" + self.resizetarget = targetsize + # FIXME: this is pretty hacky. we should determine shrink/grow + # more automatically + # a grow or shrink by looking at the sizes + self.resizedirection = targetsize - size + + def getResizeTarget (self): + return self.targetsize + def isMounted (self): return self.mountcount > 0 diff --git a/iw/partition_dialog_gui.py b/iw/partition_dialog_gui.py index 042be17..2644cfd 100644 --- a/iw/partition_dialog_gui.py +++ b/iw/partition_dialog_gui.py @@ -214,6 +214,17 @@ class PartitionEditor: else: request.migrate = 0 + if self.fsoptionsDict.has_key("resizecb"): + resizecb = self.fsoptionsDict["resizecb"] + else: + resizecb = None + + if resizecb and resizecb.get_active(): + request.targetSize = self.fsoptionsDict["resizesb"].get_value_as_int() + else: + request.targetSize = None + print "the target size for %s is %s" %(request.mountpoint, request.targetSize) + # set back if we are not formatting or migrating origfstype = self.origrequest.origfstype if not request.format and not request.migrate: diff --git a/iw/partition_ui_helpers_gui.py b/iw/partition_ui_helpers_gui.py index be66780..09a3a96 100644 --- a/iw/partition_ui_helpers_gui.py +++ b/iw/partition_ui_helpers_gui.py @@ -206,6 +206,9 @@ def mountptchangeCB(widget, fstypecombo): if rhpl.getArch() == "ia64" and widget.get_children()[0].get_text() == "/boot/efi": fstypecombo.set_active_text("vfat") +def resizeOptionCB(widget, resizesb): + resizesb.set_sensitive(widget.get_active()) + def formatOptionCB(widget, data): (combowidget, mntptcombo, ofstype) = data combowidget.set_sensitive(widget.get_active()) @@ -305,11 +308,50 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, migraterb = None migfstypeCombo = None + if origrequest.origfstype.isResizable(): + maintable.attach(gtk.HSeparator(), 0, 2, row, row + 1) + row = row + 1 + + label = gtk.Label(_("How large would you like this partition to be?")) + label.set_line_wrap(1) + label.set_alignment(0.0, 0.0) + + maintable.attach(label, 0, 2, row, row + 1) + row = row + 1 + + resizecb = gtk.CheckButton(label=_("_Resize partition:")) + resizecb.set_active(0) + if origrequest.targetSize is not None: + resizecb.set_active(1) + + maintable.attach(resizecb, 0, 1, row, row + 1) + + origsize = origrequest.size + if origrequest.targetSize is not None: + val = origrequest.targetSize + else: + val = origsize + adj = gtk.Adjustment(value = val, + lower = origrequest.origfstype.getMinimumSize(), + upper = origrequest.getMaximumSize(), + step_incr = 1) + resizesb = gtk.SpinButton(adj, digits = 0) + resizesb.set_property('numeric', True) + maintable.attach(resizesb, 1, 2, row, row + 1) + + resizecb.connect('toggled', resizeOptionCB, resizesb) + resizeOptionCB(resizecb, resizesb) + + row = row + 1 + else: + resizecb = resizesb = None + + row = row + 1 rc = {} for var in ['noformatrb', 'formatrb', 'fstypeCombo', - 'migraterb', 'migfstypeCombo']: + 'migraterb', 'migfstypeCombo', 'resizecb', 'resizesb']: if eval("%s" % (var,)) is not None: rc[var] = eval("%s" % (var,)) diff --git a/packages.py b/packages.py index aeefde2..f9b8169 100644 --- a/packages.py +++ b/packages.py @@ -148,8 +148,10 @@ def turnOnFilesystems(anaconda): searchPath = 1) anaconda.id.partitions.doMetaDeletes(anaconda.id.diskset) anaconda.id.fsset.setActive(anaconda.id.diskset) + anaconda.id.fsset.shrinkFilesystems() if not anaconda.id.fsset.isActive(): anaconda.id.diskset.savePartitions () + anaconda.id.fsset.growFilesystems() 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 3fbb812..7437cdf 100644 --- a/partRequests.py +++ b/partRequests.py @@ -152,6 +152,9 @@ class RequestSpec: self.dev = None """A Device() as defined in fsset.py to correspond to this request.""" + self.targetSize = None + """Size to resize to""" + def __str__(self): if self.fstype: fsname = self.fstype.getName() @@ -209,6 +212,9 @@ class RequestSpec: if self.fslabel: entry.setLabel(self.fslabel) + if self.targetSize and self.fstype.isResizable(): + entry.setResizeTarget(self.targetSize, self.size) + return entry def setProtected(self, val): @@ -558,6 +564,10 @@ class PreexistingPartitionSpec(PartitionSpec): mountpoint = mountpoint, preexist = 1) self.type = REQUEST_PREEXIST + def getMaximumSize(self): + # FIXME: obviously this needs to be figured out for real + return MAX_PART_SIZE + class RaidRequestSpec(RequestSpec): """Request to represent RAID devices."""
_______________________________________________ Anaconda-devel-list mailing list Anaconda-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/anaconda-devel-list