From 21006925f17815fc79e9a15f80088b1585e6f1a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mina=20Gali=C4=87?= Date: Tue, 16 May 2023 19:37:07 +0100 Subject: FreeBSD: add ResizeGrowFS class to cc_growpart (#2334) this FreeBSD specific resizer resizes the root partition and grows the Filesystem all in one. All we have to do is call ``service growfs onestart`` Document behaviour: especially that growfs will insert a swap partition if none is present, unless instructed otherwise. Sponsored by: The FreeBSD Foundation --- cloudinit/config/cc_growpart.py | 60 ++++++++++++++++++++++++++++++++++------- cloudinit/distros/freebsd.py | 1 + 2 files changed, 52 insertions(+), 9 deletions(-) (limited to 'cloudinit') 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), +) diff --git a/cloudinit/distros/freebsd.py b/cloudinit/distros/freebsd.py index 77a94c61..443d0961 100644 --- a/cloudinit/distros/freebsd.py +++ b/cloudinit/distros/freebsd.py @@ -53,6 +53,7 @@ class Distro(cloudinit.distros.bsd.BSD): "start": [service, "start"], "enable": [service, "enable"], "disable": [service, "disable"], + "onestart": [service, "onestart"], "restart": [service, "restart"], "reload": [service, "restart"], "try-reload": [service, "restart"], -- cgit v1.2.1