Skip to content

Commit

Permalink
fix: Stop attempting to resize ZFS in cc_growpart on Linux (#5370)
Browse files Browse the repository at this point in the history
In e520c94, growing ZFS was added to cc_growpart to support BSD
systems. However, this left Linux in a state where it will fail to
resize and log a warning anytime it encounters ZFS. This commit
ensures that the cc_growpart resize is only attempted on BSD (or any
other system that uses `growfs`). This leaves cc_resizefs to grow the
filesystem on Linux.

This is admittedly a hack solution and long term, we should ensure
that ZFS gets grown in one module, and find a better long-term
abstraction for volume managers.
  • Loading branch information
TheRealFalcon authored Jun 5, 2024
1 parent fbec4d0 commit 325abf1
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 41 deletions.
71 changes: 38 additions & 33 deletions cloudinit/config/cc_growpart.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,36 +118,6 @@ class RESIZE:
LOG = logging.getLogger(__name__)


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(devices=devices):
resize_class = cur
break

if not resize_class:
raise ValueError("No resizers available")

else:
mmap = {}
for k, v in RESIZERS:
mmap[k] = v

if mode not in mmap:
raise TypeError("unknown resize mode %s" % mode)

mclass = mmap[mode](distro)
if mclass.available(devices=devices):
resize_class = mclass

if not resize_class:
raise ValueError("mode %s not available" % mode)

return resize_class


class ResizeFailedException(Exception):
pass

Expand Down Expand Up @@ -280,6 +250,36 @@ def resize(self, diskdev, partnum, partdev, fs):
return (before, get_size(partdev, fs))


def resizer_factory(mode: str, distro: Distro, devices: list) -> Resizer:
resize_class = None
if mode == "auto":
for _name, resizer in RESIZERS:
cur = resizer(distro)
if cur.available(devices=devices):
resize_class = cur
break

if not resize_class:
raise ValueError("No resizers available")

else:
mmap = {}
for k, v in RESIZERS:
mmap[k] = v

if mode not in mmap:
raise TypeError("unknown resize mode %s" % mode)

mclass = mmap[mode](distro)
if mclass.available(devices=devices):
resize_class = mclass

if not resize_class:
raise ValueError("mode %s not available" % mode)

return resize_class


def get_size(filename, fs) -> Optional[int]:
fd = None
try:
Expand Down Expand Up @@ -473,7 +473,7 @@ def _call_resizer(resizer, devent, disk, ptnum, blockdev, fs):
return info


def resize_devices(resizer, devices, distro: Distro):
def resize_devices(resizer: Resizer, devices, distro: Distro):
# returns a tuple of tuples containing (entry-in-devices, action, message)
devices = copy.copy(devices)
info = []
Expand All @@ -496,9 +496,14 @@ def resize_devices(resizer, devices, distro: Distro):
continue

LOG.debug("growpart found fs=%s", fs)
if fs == "zfs":
# TODO: This seems to be the wrong place for this. On Linux, we the
# `os.stat(blockdev)` call below will fail on a ZFS filesystem.
# We then delay resizing the FS until calling cc_resizefs. Yet
# the code here is to accommodate the FreeBSD `growfs` service.
# Ideally we would grow the FS for both OSes in the same module.
if fs == "zfs" and isinstance(resizer, ResizeGrowFS):
info += _call_resizer(resizer, devent, disk, ptnum, blockdev, fs)
return info
continue

try:
statret = os.stat(blockdev)
Expand Down
22 changes: 14 additions & 8 deletions tests/unittests/config/test_cc_growpart.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,21 +440,27 @@ def common_mocks(self, mocker):
def test_zroot(self, dev, expected, common_mocks):
resize_calls = []

class myresizer:
class MyResizer(cc_growpart.ResizeGrowFS):
def resize(self, diskdev, partnum, partdev, fs):
resize_calls.append((diskdev, partnum, partdev, fs))
if partdev == "zroot/ROOT/changed":
return (1024, 2048)
return (1024, 1024) # old size, new size

def find(name, res):
for f in res:
if f[0] == name:
return f
return None
def get_status_from_device(device_name, resize_results):
for result in resize_results:
if result[0] == device_name:
return result[1]
raise ValueError(
f"Device {device_name} not found in {resize_results}"
)

resized = cc_growpart.resize_devices(myresizer(), [dev], self.distro)
assert expected == find(dev, resized)[1]
resized = cc_growpart.resize_devices(
resizer=MyResizer(distro=self.distro),
devices=[dev],
distro=self.distro,
)
assert expected == get_status_from_device(dev, resized)


class TestGetSize:
Expand Down

0 comments on commit 325abf1

Please sign in to comment.