summaryrefslogtreecommitdiff
path: root/cloudinit/config/cc_growpart.py
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/config/cc_growpart.py')
-rw-r--r--cloudinit/config/cc_growpart.py60
1 files changed, 51 insertions, 9 deletions
diff --git a/cloudinit/config/cc_growpart.py b/cloudinit/config/cc_growpart.py
index 068e678f..903664e3 100644
--- a/cloudinit/config/cc_growpart.py
+++ b/cloudinit/config/cc_growpart.py
@@ -32,7 +32,9 @@ MODULE_DESCRIPTION = """\
Growpart resizes partitions to fill the available disk space.
This is useful for cloud instances with a larger amount of disk space available
than the pristine image uses, as it allows the instance to automatically make
-use of the extra space.
+use of the extra space. Note that this only works if the partition to be
+resized is the last one on a disk with classic partitioning scheme (MBR, BSD,
+GPT). LVM, Btrfs and ZFS have no such restrictions.
The devices on which to run growpart are specified as a list under the
``devices`` key.
@@ -48,6 +50,14 @@ it is present. However, this file can be ignored for ``cc_growpart`` by setting
``ignore_growroot_disabled`` to ``true``. For more information on
``cloud-initramfs-tools`` see: https://launchpad.net/cloud-initramfs-tools
+On FreeBSD, there is also the ``growfs`` service, which has a lot of overlap
+with ``cc_growpart`` and ``cc_resizefs``, but only works on the root partition.
+In that configuration, we use it, otherwise, we fall back to ``gpart``.
+
+Note however, that ``growfs`` may insert a swap partition, if none is present,
+unless instructed not to via ``growfs_swap_size=0`` in either ``kenv(1)``, or
+``rc.conf(5)``.
+
Growpart is enabled by default on the root partition. The default config for
growpart is::
@@ -108,12 +118,12 @@ class RESIZE:
LOG = logging.getLogger(__name__)
-def resizer_factory(mode: str, distro: Distro):
+def resizer_factory(mode: str, distro: Distro, devices: list):
resize_class = None
if mode == "auto":
for _name, resizer in RESIZERS:
cur = resizer(distro)
- if cur.available():
+ if cur.available(devices=devices):
resize_class = cur
break
@@ -129,7 +139,7 @@ def resizer_factory(mode: str, distro: Distro):
raise TypeError("unknown resize mode %s" % mode)
mclass = mmap[mode](distro)
- if mclass.available():
+ if mclass.available(devices=devices):
resize_class = mclass
if not resize_class:
@@ -147,7 +157,7 @@ class Resizer(ABC):
self._distro = distro
@abstractmethod
- def available(self) -> bool:
+ def available(self, devices: list) -> bool:
...
@abstractmethod
@@ -156,7 +166,7 @@ class Resizer(ABC):
class ResizeGrowPart(Resizer):
- def available(self):
+ def available(self, devices: list):
myenv = os.environ.copy()
myenv["LANG"] = "C"
@@ -206,8 +216,36 @@ class ResizeGrowPart(Resizer):
return (before, get_size(partdev))
+class ResizeGrowFS(Resizer):
+ """
+ Use FreeBSD ``growfs`` service to grow root partition to fill available
+ space, optionally adding a swap partition at the end.
+
+ Note that the service file warns us that it uses ``awk(1)``, and as
+ such requires ``/usr`` to be present. However, cloud-init is installed
+ into ``/usr/local``, so we should be fine.
+
+ We invoke the ``growfs`` with ``service growfs onestart``, so it
+ doesn't need to be enabled in ``rc.conf``.
+ """
+
+ def available(self, devices: list):
+ """growfs only works on the root partition"""
+ return os.path.isfile("/etc/rc.d/growfs") and devices == ["/"]
+
+ def resize(self, diskdev, partnum, partdev):
+ before = get_size(partdev)
+ try:
+ self._distro.manage_service(action="onestart", service="growfs")
+ except subp.ProcessExecutionError as e:
+ util.logexc(LOG, "Failed: service growfs onestart")
+ raise ResizeFailedException(e) from e
+
+ return (before, get_size(partdev))
+
+
class ResizeGpart(Resizer):
- def available(self):
+ def available(self, devices: list):
myenv = os.environ.copy()
myenv["LANG"] = "C"
@@ -604,7 +642,7 @@ def handle(name: str, cfg: Config, cloud: Cloud, args: list) -> None:
return
try:
- resizer = resizer_factory(mode, cloud.distro)
+ resizer = resizer_factory(mode, distro=cloud.distro, devices=devices)
except (ValueError, TypeError) as e:
LOG.debug("growpart unable to find resizer for '%s': %s", mode, e)
if mode != "auto":
@@ -624,4 +662,8 @@ def handle(name: str, cfg: Config, cloud: Cloud, args: list) -> None:
LOG.debug("'%s' %s: %s", entry, action, msg)
-RESIZERS = (("growpart", ResizeGrowPart), ("gpart", ResizeGpart))
+RESIZERS = (
+ ("growpart", ResizeGrowPart),
+ ("growfs", ResizeGrowFS),
+ ("gpart", ResizeGpart),
+)