diff options
author | Ryan Harper <ryan.harper@canonical.com> | 2018-04-02 13:33:39 -0600 |
---|---|---|
committer | Chad Smith <chad.smith@canonical.com> | 2018-04-02 13:33:39 -0600 |
commit | c436e173c4162c940523a2978799193672ff9cd3 (patch) | |
tree | 55c043231e24fa05389a6099f3801f578a30ec6e | |
parent | a510726d622b9d1768b5417d09277d5cc786e952 (diff) | |
download | vyos-cloud-init-c436e173c4162c940523a2978799193672ff9cd3.tar.gz vyos-cloud-init-c436e173c4162c940523a2978799193672ff9cd3.zip |
cc_resizefs, util: handle no /dev/zfs
The zfs/zpool commands will hang for 10 seconds if /dev/zfs is not
present (bug 1760173). This is a common occurence for containers
using zfs as rootfs. Additionally handle missing zpool command or
other errors that may occur while executing the zpool command.
-rw-r--r-- | cloudinit/config/cc_resizefs.py | 2 | ||||
-rw-r--r-- | cloudinit/util.py | 10 | ||||
-rw-r--r-- | tests/unittests/test_util.py | 29 |
3 files changed, 38 insertions, 3 deletions
diff --git a/cloudinit/config/cc_resizefs.py b/cloudinit/config/cc_resizefs.py index c8e1752f..013e69b5 100644 --- a/cloudinit/config/cc_resizefs.py +++ b/cloudinit/config/cc_resizefs.py @@ -251,6 +251,8 @@ def handle(name, cfg, _cloud, log, args): if fs_type == 'zfs': zpool = devpth.split('/')[0] devpth = util.get_device_info_from_zpool(zpool) + if not devpth: + return # could not find device from zpool resize_what = zpool info = "dev=%s mnt_point=%s path=%s" % (devpth, mount_point, resize_what) diff --git a/cloudinit/util.py b/cloudinit/util.py index 0ab2c484..acdc0d85 100644 --- a/cloudinit/util.py +++ b/cloudinit/util.py @@ -2249,7 +2249,15 @@ def get_mount_info_freebsd(path): def get_device_info_from_zpool(zpool): - (zpoolstatus, err) = subp(['zpool', 'status', zpool]) + # zpool has 10 second timeout waiting for /dev/zfs LP: #1760173 + if not os.path.exists('/dev/zfs'): + LOG.debug('Cannot get zpool info, no /dev/zfs') + return None + try: + (zpoolstatus, err) = subp(['zpool', 'status', zpool]) + except ProcessExecutionError as err: + LOG.warning("Unable to get zpool status of %s: %s", zpool, err) + return None if len(err): return None r = r'.*(ONLINE).*' diff --git a/tests/unittests/test_util.py b/tests/unittests/test_util.py index 8685b8e2..50101906 100644 --- a/tests/unittests/test_util.py +++ b/tests/unittests/test_util.py @@ -366,8 +366,11 @@ class TestMountinfoParsing(helpers.ResourceUsingTestCase): expected = ('none', 'tmpfs', '/run/lock') self.assertEqual(expected, util.parse_mount_info('/run/lock', lines)) + @mock.patch('cloudinit.util.os') @mock.patch('cloudinit.util.subp') - def test_get_device_info_from_zpool(self, zpool_output): + def test_get_device_info_from_zpool(self, zpool_output, m_os): + # mock /dev/zfs exists + m_os.path.exists.return_value = True # mock subp command from util.get_mount_info_fs_on_zpool zpool_output.return_value = ( self.readResource('zpool_status_simple.txt'), '' @@ -376,9 +379,31 @@ class TestMountinfoParsing(helpers.ResourceUsingTestCase): ret = util.get_device_info_from_zpool('vmzroot') self.assertEqual('gpt/system', ret) self.assertIsNotNone(ret) + m_os.path.exists.assert_called_with('/dev/zfs') + @mock.patch('cloudinit.util.os') + def test_get_device_info_from_zpool_no_dev_zfs(self, m_os): + # mock /dev/zfs missing + m_os.path.exists.return_value = False + # save function return values and do asserts + ret = util.get_device_info_from_zpool('vmzroot') + self.assertIsNone(ret) + + @mock.patch('cloudinit.util.os') + @mock.patch('cloudinit.util.subp') + def test_get_device_info_from_zpool_handles_no_zpool(self, m_sub, m_os): + """Handle case where there is no zpool command""" + # mock /dev/zfs exists + m_os.path.exists.return_value = True + m_sub.side_effect = util.ProcessExecutionError("No zpool cmd") + ret = util.get_device_info_from_zpool('vmzroot') + self.assertIsNone(ret) + + @mock.patch('cloudinit.util.os') @mock.patch('cloudinit.util.subp') - def test_get_device_info_from_zpool_on_error(self, zpool_output): + def test_get_device_info_from_zpool_on_error(self, zpool_output, m_os): + # mock /dev/zfs exists + m_os.path.exists.return_value = True # mock subp command from util.get_mount_info_fs_on_zpool zpool_output.return_value = ( self.readResource('zpool_status_simple.txt'), 'error' |