← Back to team overview

cloud-init-dev team mailing list archive

[Merge] ~d-info-e/cloud-init:resizefs-on-zfs-root into cloud-init:master

 

do3meli has proposed merging ~d-info-e/cloud-init:resizefs-on-zfs-root into cloud-init:master.

Commit message:
resizefs module now able to handle zfs/zpool.

Previously there was no support at all for zfs file system. With this change it is now possible to use the resizefs module to extend a root zfs partition that lies on a zpool.

LP: #1721243

Requested reviews:
  cloud-init commiters (cloud-init-dev)

For more details, see:
https://code.launchpad.net/~d-info-e/cloud-init/+git/cloud-init/+merge/340220

this change adds the possibility to use resizefs module to extend a zfs file system on a corresponding zpool.
-- 
Your team cloud-init commiters is requested to review the proposed merge of ~d-info-e/cloud-init:resizefs-on-zfs-root into cloud-init:master.
diff --git a/cloudinit/config/cc_resizefs.py b/cloudinit/config/cc_resizefs.py
index cec22bb..1244215 100644
--- a/cloudinit/config/cc_resizefs.py
+++ b/cloudinit/config/cc_resizefs.py
@@ -84,6 +84,10 @@ def _resize_ufs(mount_point, devpth):
     return ('growfs', devpth)
 
 
+def _resize_zfs(mount_point, devpth):
+    return ('zpool', 'online', '-e', mount_point, devpth)
+
+
 def _get_dumpfs_output(mount_point):
     dumpfs_res, err = util.subp(['dumpfs', '-m', mount_point])
     return dumpfs_res
@@ -148,6 +152,7 @@ RESIZE_FS_PREFIXES_CMDS = [
     ('ext', _resize_ext),
     ('xfs', _resize_xfs),
     ('ufs', _resize_ufs),
+    ('zfs', _resize_zfs),
 ]
 
 RESIZE_FS_PRECHECK_CMDS = {
@@ -188,6 +193,13 @@ def maybe_get_writable_device_path(devpath, info, log):
         log.debug("Not attempting to resize devpath '%s': %s", devpath, info)
         return None
 
+    # FreeBSD zpool can also just use gpt/<label>
+    # with that in mind we can not do an os.stat on "gpt/whatever"
+    # therefore return the devpath already here.
+    if util.is_FreeBSD() and devpath.startswith('gpt/'):
+        log.debug('We have FreeBSD and gpt labels - just go ahead')
+        return devpath
+
     try:
         statret = os.stat(devpath)
     except OSError as exc:
@@ -231,6 +243,13 @@ def handle(name, cfg, _cloud, log, args):
 
     (devpth, fs_type, mount_point) = result
 
+    # this is a bit confusing but easy to understand:
+    # for "zpool online" cmd we need the zpool name and
+    # not the mount_point. util.get_mount_info_fs_on_zpool
+    # will get us this. so we just replace resize what here.
+    if fs_type == 'zfs':
+        resize_what = mount_point
+
     info = "dev=%s mnt_point=%s path=%s" % (devpth, mount_point, resize_what)
     log.debug("resize_info: %s" % info)
 
diff --git a/cloudinit/util.py b/cloudinit/util.py
index 02dc2ce..b4a1585 100644
--- a/cloudinit/util.py
+++ b/cloudinit/util.py
@@ -2174,7 +2174,7 @@ def get_path_dev_freebsd(path, mnt_list):
     return path_found
 
 
-def get_mount_info_freebsd(path, log=LOG):
+def get_mount_info_freebsd(path):
     (result, err) = subp(['mount', '-p', path], rcs=[0, 1])
     if len(err):
         # find a path if the input is not a mounting point
@@ -2188,23 +2188,42 @@ def get_mount_info_freebsd(path, log=LOG):
     return "/dev/" + label_part, ret[2], ret[1]
 
 
+def get_mount_info_fs_on_zpool(zpool, log=LOG):
+    (zpoolstatus, err) = subp(['zpool', 'status', zpool])
+    if len(err):
+        return None
+    r = r'.*(ONLINE).*'
+    for line in zpoolstatus.split("\n"):
+        if re.search(r, line) and zpool not in line and "state" not in line:
+            disk = line.split()[0]
+            log.debug('found zpool "%s" on disk %s', zpool, disk)
+            # onlinging a zpool on first disk results in resize to max
+            return disk, 'zfs', zpool
+
+
 def parse_mount(path):
-    (mountoutput, _err) = subp("mount")
+    (mountoutput, _err) = subp(['mount'])
     mount_locs = mountoutput.splitlines()
+    regex = r'^(/dev/[\S]+|.*zroot.*\S*?) on (/.*) \((.+), .+, (.+)\)$'
     for line in mount_locs:
-        m = re.search(r'^(/dev/[\S]+) on (/.*) \((.+), .+, (.+)\)$', line)
+        m = re.search(regex, line)
         if not m:
             continue
+        devpth = m.group(1)
+        mount_point = m.group(2)
+        fs_type = m.group(3)
         # check whether the dev refers to a label on FreeBSD
         # for example, if dev is '/dev/label/rootfs', we should
         # continue finding the real device like '/dev/da0'.
-        devm = re.search('^(/dev/.+)p([0-9])$', m.group(1))
-        if (not devm and is_FreeBSD()):
+        devm = re.search('^(/dev/.+)p([0-9])$', devpth)
+        # if we have a zfs FS we need to get the device name
+        # from the corresponding zpool.
+        if fs_type == 'zfs':
+            zpool = devpth.split('/')[0]
+            return get_mount_info_fs_on_zpool(zpool)
+        elif not devm and is_FreeBSD():
             return get_mount_info_freebsd(path)
-        devpth = m.group(1)
-        mount_point = m.group(2)
-        fs_type = m.group(3)
-        if mount_point == path:
+        elif mount_point == path:
             return devpth, fs_type, mount_point
     return None