Merge ~chad.smith/cloud-init:ubuntu/devel into cloud-init:ubuntu/devel

Proposed by Chad Smith on 2018-03-27
Status: Merged
Merged at revision: 10ab9a7c231b6f7d01da3a3a4c08cf4db01f5a87
Proposed branch: ~chad.smith/cloud-init:ubuntu/devel
Merge into: cloud-init:ubuntu/devel
Diff against target: 574 lines (+317/-15)
16 files modified
ChangeLog (+58/-0)
cloudinit/config/cc_puppet.py (+3/-0)
cloudinit/config/cc_resizefs.py (+22/-0)
cloudinit/settings.py (+1/-0)
cloudinit/sources/DataSourceHetzner.py (+6/-0)
cloudinit/util.py (+35/-9)
cloudinit/version.py (+1/-1)
debian/changelog (+11/-2)
packages/debian/control.in (+2/-1)
tests/data/mount_parse_ext.txt (+19/-0)
tests/data/mount_parse_zfs.txt (+21/-0)
tests/data/zpool_status_simple.txt (+10/-0)
tests/unittests/test_datasource/test_common.py (+2/-0)
tests/unittests/test_datasource/test_hetzner.py (+19/-1)
tests/unittests/test_handler/test_handler_resizefs.py (+57/-1)
tests/unittests/test_util.py (+50/-0)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve on 2018-03-27
Scott Moser 2018-03-27 Pending
Review via email: mp+342239@code.launchpad.net

Commit message

Sync upstream 18.2 release to Bionic for publish.

To post a comment you must log in.

PASSED: Continuous integration, rev:10ab9a7c231b6f7d01da3a3a4c08cf4db01f5a87
https://jenkins.ubuntu.com/server/job/cloud-init-ci/940/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: MAAS Compatability Testing
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/940/rebuild

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/ChangeLog b/ChangeLog
2index be4c357..daa7ccf 100644
3--- a/ChangeLog
4+++ b/ChangeLog
5@@ -1,3 +1,61 @@
6+18.2:
7+ - Hetzner: Exit early if dmi system-manufacturer is not Hetzner.
8+ - Add missing dependency on isc-dhcp-client to trunk ubuntu packaging.
9+ (LP: #1759307)
10+ - FreeBSD: resizefs module now able to handle zfs/zpool.
11+ [Dominic Schlegel] (LP: #1721243)
12+ - cc_puppet: Revert regression of puppet creating ssl and ssl_cert dirs
13+ - Enable IBMCloud datasource in settings.py.
14+ - IBMCloud: Initial IBM Cloud datasource.
15+ - tests: remove jsonschema from xenial tox environment.
16+ - tests: Fix newly added schema unit tests to skip if no jsonschema.
17+ - ec2: Adjust ec2 datasource after exception_cb change.
18+ - Reduce AzurePreprovisioning HTTP timeouts.
19+ [Douglas Jordan] (LP: #1752977)
20+ - Revert the logic of exception_cb in read_url.
21+ [Kurt Garloff] (LP: #1702160, #1298921)
22+ - ubuntu-advantage: Add new config module to support
23+ ubuntu-advantage-tools
24+ - Handle global dns entries in netplan (LP: #1750884)
25+ - Identify OpenTelekomCloud Xen as OpenStack DS.
26+ [Kurt Garloff] (LP: #1756471)
27+ - datasources: fix DataSource subclass get_hostname method signature
28+ (LP: #1757176)
29+ - OpenNebula: Update network to return v2 config rather than ENI.
30+ [Akihiko Ota]
31+ - Add Hetzner Cloud DataSource
32+ - net: recognize iscsi root cases without ip= on kernel command line.
33+ (LP: #1752391)
34+ - tests: fix flakes warning for unused variable
35+ - tests: patch leaked stderr messages from snap unit tests
36+ - cc_snap: Add new module to install and configure snapd and snap
37+ packages.
38+ - tests: Make pylint happy and fix python2.6 uses of assertRaisesRegex.
39+ - netplan: render bridge port-priority values (LP: #1735821)
40+ - util: Fix subp regression. Allow specifying subp command as a string.
41+ (LP: #1755965)
42+ - doc: fix all warnings issued by 'tox -e doc'
43+ - FreeBSD: Set hostname to FQDN. [Dominic Schlegel] (LP: #1753499)
44+ - tests: fix run_tree and bddeb
45+ - tests: Fix some warnings in tests that popped up with newer python.
46+ - set_hostname: When present in metadata, set it before network bringup.
47+ (LP: #1746455)
48+ - tests: Centralize and re-use skipTest based on json schema presense.
49+ - This commit fixes get_hostname on the AzureDataSource.
50+ [Douglas Jordan] (LP: #1754495)
51+ - shellify: raise TypeError on bad input.
52+ - Make salt minion module work on FreeBSD.
53+ [Dominic Schlegel] (LP: #1721503)
54+ - Simplify some comparisions. [Rémy Léone]
55+ - Change some list creation and population to literal. [Rémy Léone]
56+ - GCE: fix reading of user-data that is not base64 encoded. (LP: #1752711)
57+ - doc: fix chef install from apt packages example in RTD.
58+ - Implement puppet 4 support [Romanos Skiadas] (LP: #1446804)
59+ - subp: Fix subp usage with non-ascii characters when no system locale.
60+ (LP: #1751051)
61+ - salt: configure grains in grains file rather than in minion config.
62+ [Daniel Wallace]
63+
64 18.1:
65 - OVF: Fix VMware support for 64-bit platforms. [Sankar Tanguturi]
66 - ds-identify: Fix searching for iso9660 OVF cdroms. (LP: #1749980)
67diff --git a/cloudinit/config/cc_puppet.py b/cloudinit/config/cc_puppet.py
68index 297e072..4190a20 100644
69--- a/cloudinit/config/cc_puppet.py
70+++ b/cloudinit/config/cc_puppet.py
71@@ -140,6 +140,7 @@ def handle(name, cfg, cloud, log, _args):
72 # (TODO(harlowja) is this really needed??)
73 cleaned_lines = [i.lstrip() for i in contents.splitlines()]
74 cleaned_contents = '\n'.join(cleaned_lines)
75+ # Move to puppet_config.read_file when dropping py2.7
76 puppet_config.readfp( # pylint: disable=W1505
77 StringIO(cleaned_contents),
78 filename=p_constants.conf_path)
79@@ -150,6 +151,8 @@ def handle(name, cfg, cloud, log, _args):
80 # Puppet ssl sub-directory isn't created yet
81 # Create it with the proper permissions and ownership
82 util.ensure_dir(p_constants.ssl_dir, 0o771)
83+ util.chownbyname(p_constants.ssl_dir, 'puppet', 'root')
84+ util.ensure_dir(p_constants.ssl_cert_dir)
85
86 util.chownbyname(p_constants.ssl_cert_dir, 'puppet', 'root')
87 util.write_file(p_constants.ssl_cert_path, cfg)
88diff --git a/cloudinit/config/cc_resizefs.py b/cloudinit/config/cc_resizefs.py
89index cec22bb..c8e1752 100644
90--- a/cloudinit/config/cc_resizefs.py
91+++ b/cloudinit/config/cc_resizefs.py
92@@ -84,6 +84,10 @@ def _resize_ufs(mount_point, devpth):
93 return ('growfs', devpth)
94
95
96+def _resize_zfs(mount_point, devpth):
97+ return ('zpool', 'online', '-e', mount_point, devpth)
98+
99+
100 def _get_dumpfs_output(mount_point):
101 dumpfs_res, err = util.subp(['dumpfs', '-m', mount_point])
102 return dumpfs_res
103@@ -148,6 +152,7 @@ RESIZE_FS_PREFIXES_CMDS = [
104 ('ext', _resize_ext),
105 ('xfs', _resize_xfs),
106 ('ufs', _resize_ufs),
107+ ('zfs', _resize_zfs),
108 ]
109
110 RESIZE_FS_PRECHECK_CMDS = {
111@@ -188,6 +193,13 @@ def maybe_get_writable_device_path(devpath, info, log):
112 log.debug("Not attempting to resize devpath '%s': %s", devpath, info)
113 return None
114
115+ # FreeBSD zpool can also just use gpt/<label>
116+ # with that in mind we can not do an os.stat on "gpt/whatever"
117+ # therefore return the devpath already here.
118+ if devpath.startswith('gpt/'):
119+ log.debug('We have a gpt label - just go ahead')
120+ return devpath
121+
122 try:
123 statret = os.stat(devpath)
124 except OSError as exc:
125@@ -231,6 +243,16 @@ def handle(name, cfg, _cloud, log, args):
126
127 (devpth, fs_type, mount_point) = result
128
129+ # if we have a zfs then our device path at this point
130+ # is the zfs label. For example: vmzroot/ROOT/freebsd
131+ # we will have to get the zpool name out of this
132+ # and set the resize_what variable to the zpool
133+ # so the _resize_zfs function gets the right attribute.
134+ if fs_type == 'zfs':
135+ zpool = devpth.split('/')[0]
136+ devpth = util.get_device_info_from_zpool(zpool)
137+ resize_what = zpool
138+
139 info = "dev=%s mnt_point=%s path=%s" % (devpth, mount_point, resize_what)
140 log.debug("resize_info: %s" % info)
141
142diff --git a/cloudinit/settings.py b/cloudinit/settings.py
143index 5fe749d..dde5749 100644
144--- a/cloudinit/settings.py
145+++ b/cloudinit/settings.py
146@@ -37,6 +37,7 @@ CFG_BUILTIN = {
147 'Bigstep',
148 'Scaleway',
149 'Hetzner',
150+ 'IBMCloud',
151 # At the end to act as a 'catch' when none of the above work...
152 'None',
153 ],
154diff --git a/cloudinit/sources/DataSourceHetzner.py b/cloudinit/sources/DataSourceHetzner.py
155index 769fe13..5c75b65 100644
156--- a/cloudinit/sources/DataSourceHetzner.py
157+++ b/cloudinit/sources/DataSourceHetzner.py
158@@ -44,6 +44,8 @@ class DataSourceHetzner(sources.DataSource):
159 self.dsmode = sources.DSMODE_NETWORK
160
161 def get_data(self):
162+ if not on_hetzner():
163+ return False
164 nic = cloudnet.find_fallback_nic()
165 with cloudnet.EphemeralIPv4Network(nic, "169.254.0.1", 16,
166 "169.254.255.255"):
167@@ -87,6 +89,10 @@ class DataSourceHetzner(sources.DataSource):
168 return self._network_config
169
170
171+def on_hetzner():
172+ return util.read_dmi_data('system-manufacturer') == "Hetzner"
173+
174+
175 # Used to match classes to dependencies
176 datasources = [
177 (DataSourceHetzner, (sources.DEP_FILESYSTEM, )),
178diff --git a/cloudinit/util.py b/cloudinit/util.py
179index fb4ee5f..0ab2c48 100644
180--- a/cloudinit/util.py
181+++ b/cloudinit/util.py
182@@ -2234,7 +2234,7 @@ def get_path_dev_freebsd(path, mnt_list):
183 return path_found
184
185
186-def get_mount_info_freebsd(path, log=LOG):
187+def get_mount_info_freebsd(path):
188 (result, err) = subp(['mount', '-p', path], rcs=[0, 1])
189 if len(err):
190 # find a path if the input is not a mounting point
191@@ -2248,23 +2248,49 @@ def get_mount_info_freebsd(path, log=LOG):
192 return "/dev/" + label_part, ret[2], ret[1]
193
194
195+def get_device_info_from_zpool(zpool):
196+ (zpoolstatus, err) = subp(['zpool', 'status', zpool])
197+ if len(err):
198+ return None
199+ r = r'.*(ONLINE).*'
200+ for line in zpoolstatus.split("\n"):
201+ if re.search(r, line) and zpool not in line and "state" not in line:
202+ disk = line.split()[0]
203+ LOG.debug('found zpool "%s" on disk %s', zpool, disk)
204+ return disk
205+
206+
207 def parse_mount(path):
208- (mountoutput, _err) = subp("mount")
209+ (mountoutput, _err) = subp(['mount'])
210 mount_locs = mountoutput.splitlines()
211+ # there are 2 types of mount outputs we have to parse therefore
212+ # the regex is a bit complex. to better understand this regex see:
213+ # https://regex101.com/r/2F6c1k/1
214+ # https://regex101.com/r/T2en7a/1
215+ regex = r'^(/dev/[\S]+|.*zroot\S*?) on (/[\S]*) ' + \
216+ '(?=(?:type)[\s]+([\S]+)|\(([^,]*))'
217 for line in mount_locs:
218- m = re.search(r'^(/dev/[\S]+) on (/.*) \((.+), .+, (.+)\)$', line)
219+ m = re.search(regex, line)
220 if not m:
221 continue
222+ devpth = m.group(1)
223+ mount_point = m.group(2)
224+ # above regex will either fill the fs_type in group(3)
225+ # or group(4) depending on the format we have.
226+ fs_type = m.group(3)
227+ if fs_type is None:
228+ fs_type = m.group(4)
229+ LOG.debug('found line in mount -> devpth: %s, mount_point: %s, '
230+ 'fs_type: %s', devpth, mount_point, fs_type)
231 # check whether the dev refers to a label on FreeBSD
232 # for example, if dev is '/dev/label/rootfs', we should
233 # continue finding the real device like '/dev/da0'.
234- devm = re.search('^(/dev/.+)p([0-9])$', m.group(1))
235- if (not devm and is_FreeBSD()):
236+ # this is only valid for non zfs file systems as a zpool
237+ # can have gpt labels as disk.
238+ devm = re.search('^(/dev/.+)p([0-9])$', devpth)
239+ if not devm and is_FreeBSD() and fs_type != 'zfs':
240 return get_mount_info_freebsd(path)
241- devpth = m.group(1)
242- mount_point = m.group(2)
243- fs_type = m.group(3)
244- if mount_point == path:
245+ elif mount_point == path:
246 return devpth, fs_type, mount_point
247 return None
248
249diff --git a/cloudinit/version.py b/cloudinit/version.py
250index 4a682ad..ccd0f84 100644
251--- a/cloudinit/version.py
252+++ b/cloudinit/version.py
253@@ -4,7 +4,7 @@
254 #
255 # This file is part of cloud-init. See LICENSE file for license information.
256
257-__VERSION__ = "18.1"
258+__VERSION__ = "18.2"
259
260 FEATURES = [
261 # supports network config version 1
262diff --git a/debian/changelog b/debian/changelog
263index 6f022bb..cdfbf6a 100644
264--- a/debian/changelog
265+++ b/debian/changelog
266@@ -1,8 +1,17 @@
267-cloud-init (18.1-35-ge0f644b7-0ubuntu3) UNRELEASED; urgency=medium
268+cloud-init (18.2-0ubuntu1) bionic; urgency=medium
269
270 * debian/control: Add missing dependency on isc-dhcp-client (LP: #1759307).
271+ * New upstream snapshot.
272+ - release 18.2 (LP: #1759318)
273+ - Hetzner: Exit early if dmi system-manufacturer is not Hetzner.
274+ - Add missing dependency on isc-dhcp-client to trunk ubuntu packaging.
275+ (LP: #1759307)
276+ - FreeBSD: resizefs module now able to handle zfs/zpool.
277+ [Dominic Schlegel] (LP: #1721243)
278+ - cc_puppet: Revert regression of puppet creating ssl and ssl_cert dirs
279+ - Enable IBMCloud datasource in settings.py.
280
281- -- Scott Moser <smoser@ubuntu.com> Tue, 27 Mar 2018 12:19:44 -0400
282+ -- Chad Smith <chad.smith@canonical.com> Tue, 27 Mar 2018 14:59:58 -0600
283
284 cloud-init (18.1-35-ge0f644b7-0ubuntu2) bionic; urgency=medium
285
286diff --git a/packages/debian/control.in b/packages/debian/control.in
287index 265b261..46da6df 100644
288--- a/packages/debian/control.in
289+++ b/packages/debian/control.in
290@@ -10,7 +10,8 @@ Standards-Version: 3.9.6
291 Package: cloud-init
292 Architecture: all
293 Depends: ${misc:Depends},
294- ${${python}:Depends}
295+ ${${python}:Depends},
296+ isc-dhcp-client
297 Recommends: eatmydata, sudo, software-properties-common, gdisk
298 XB-Python-Version: ${python:Versions}
299 Description: Init scripts for cloud instances
300diff --git a/tests/data/mount_parse_ext.txt b/tests/data/mount_parse_ext.txt
301new file mode 100644
302index 0000000..da0c870
303--- /dev/null
304+++ b/tests/data/mount_parse_ext.txt
305@@ -0,0 +1,19 @@
306+/dev/mapper/vg00-lv_root on / type ext4 (rw,errors=remount-ro)
307+proc on /proc type proc (rw,noexec,nosuid,nodev)
308+sysfs on /sys type sysfs (rw,noexec,nosuid,nodev)
309+none on /sys/fs/cgroup type tmpfs (rw)
310+none on /sys/fs/fuse/connections type fusectl (rw)
311+none on /sys/kernel/debug type debugfs (rw)
312+none on /sys/kernel/security type securityfs (rw)
313+udev on /dev type devtmpfs (rw,mode=0755)
314+devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=0620)
315+none on /tmp type tmpfs (rw)
316+tmpfs on /run type tmpfs (rw,noexec,nosuid,size=10%,mode=0755)
317+none on /run/lock type tmpfs (rw,noexec,nosuid,nodev,size=5242880)
318+none on /run/shm type tmpfs (rw,nosuid,nodev)
319+none on /run/user type tmpfs (rw,noexec,nosuid,nodev,size=104857600,mode=0755)
320+none on /sys/fs/pstore type pstore (rw)
321+/dev/mapper/vg00-lv_var on /var type ext4 (rw)
322+rpc_pipefs on /run/rpc_pipefs type rpc_pipefs (rw)
323+systemd on /sys/fs/cgroup/systemd type cgroup (rw,noexec,nosuid,nodev,none,name=systemd)
324+10.0.1.1:/backup on /backup type nfs (rw,noexec,nosuid,nodev,bg,nolock,tcp,nfsvers=3,hard,addr=10.0.1.1)
325\ No newline at end of file
326diff --git a/tests/data/mount_parse_zfs.txt b/tests/data/mount_parse_zfs.txt
327new file mode 100644
328index 0000000..08af04f
329--- /dev/null
330+++ b/tests/data/mount_parse_zfs.txt
331@@ -0,0 +1,21 @@
332+vmzroot/ROOT/freebsd on / (zfs, local, nfsv4acls)
333+devfs on /dev (devfs, local, multilabel)
334+fdescfs on /dev/fd (fdescfs)
335+vmzroot/root on /root (zfs, local, nfsv4acls)
336+vmzroot/tmp on /tmp (zfs, local, nosuid, nfsv4acls)
337+vmzroot/ROOT/freebsd/usr on /usr (zfs, local, nfsv4acls)
338+vmzroot/ROOT/freebsd/usr/local on /usr/local (zfs, local, nfsv4acls)
339+vmzroot/ROOT/freebsd/var on /var (zfs, local, nfsv4acls)
340+vmzroot/ROOT/freebsd/var/cache on /var/cache (zfs, local, noexec, nosuid, nfsv4acls)
341+vmzroot/ROOT/freebsd/var/crash on /var/crash (zfs, local, noexec, nosuid, nfsv4acls)
342+vmzroot/var/cron on /var/cron (zfs, local, nosuid, nfsv4acls)
343+vmzroot/ROOT/freebsd/var/db on /var/db (zfs, local, noatime, noexec, nosuid, nfsv4acls)
344+vmzroot/ROOT/freebsd/var/empty on /var/empty (zfs, local, noexec, nosuid, read-only, nfsv4acls)
345+vmzroot/var/log on /var/log (zfs, local, noexec, nosuid, nfsv4acls)
346+vmzroot/var/log/pf on /var/log/pf (zfs, local, noexec, nosuid, nfsv4acls)
347+vmzroot/var/mail on /var/mail (zfs, local, noexec, nosuid, nfsv4acls)
348+vmzroot/ROOT/freebsd/var/run on /var/run (zfs, local, noexec, nosuid, nfsv4acls)
349+vmzroot/var/spool on /var/spool (zfs, local, noexec, nosuid, nfsv4acls)
350+vmzroot/var/tmp on /var/tmp (zfs, local, nosuid, nfsv4acls)
351+10.0.0.1:/vol/test on /mnt/test (nfs, read-only)
352+10.0.0.2:/vol/tes2 on /mnt/test2 (nfs, nosuid)
353\ No newline at end of file
354diff --git a/tests/data/zpool_status_simple.txt b/tests/data/zpool_status_simple.txt
355new file mode 100644
356index 0000000..a2c573a
357--- /dev/null
358+++ b/tests/data/zpool_status_simple.txt
359@@ -0,0 +1,10 @@
360+ pool: vmzroot
361+ state: ONLINE
362+ scan: none requested
363+config:
364+
365+ NAME STATE READ WRITE CKSUM
366+ vmzroot ONLINE 0 0 0
367+ gpt/system ONLINE 0 0 0
368+
369+errors: No known data errors
370\ No newline at end of file
371diff --git a/tests/unittests/test_datasource/test_common.py b/tests/unittests/test_datasource/test_common.py
372index 6d2dc5b..ec33388 100644
373--- a/tests/unittests/test_datasource/test_common.py
374+++ b/tests/unittests/test_datasource/test_common.py
375@@ -15,6 +15,7 @@ from cloudinit.sources import (
376 DataSourceEc2 as Ec2,
377 DataSourceGCE as GCE,
378 DataSourceHetzner as Hetzner,
379+ DataSourceIBMCloud as IBMCloud,
380 DataSourceMAAS as MAAS,
381 DataSourceNoCloud as NoCloud,
382 DataSourceOpenNebula as OpenNebula,
383@@ -33,6 +34,7 @@ DEFAULT_LOCAL = [
384 ConfigDrive.DataSourceConfigDrive,
385 DigitalOcean.DataSourceDigitalOcean,
386 Hetzner.DataSourceHetzner,
387+ IBMCloud.DataSourceIBMCloud,
388 NoCloud.DataSourceNoCloud,
389 OpenNebula.DataSourceOpenNebula,
390 OVF.DataSourceOVF,
391diff --git a/tests/unittests/test_datasource/test_hetzner.py b/tests/unittests/test_datasource/test_hetzner.py
392index f1d1525..a9c1259 100644
393--- a/tests/unittests/test_datasource/test_hetzner.py
394+++ b/tests/unittests/test_datasource/test_hetzner.py
395@@ -73,7 +73,10 @@ class TestDataSourceHetzner(CiTestCase):
396 @mock.patch('cloudinit.net.find_fallback_nic')
397 @mock.patch('cloudinit.sources.helpers.hetzner.read_metadata')
398 @mock.patch('cloudinit.sources.helpers.hetzner.read_userdata')
399- def test_read_data(self, m_usermd, m_readmd, m_fallback_nic, m_net):
400+ @mock.patch('cloudinit.sources.DataSourceHetzner.on_hetzner')
401+ def test_read_data(self, m_on_hetzner, m_usermd, m_readmd, m_fallback_nic,
402+ m_net):
403+ m_on_hetzner.return_value = True
404 m_readmd.return_value = METADATA.copy()
405 m_usermd.return_value = USERDATA
406 m_fallback_nic.return_value = 'eth0'
407@@ -97,3 +100,18 @@ class TestDataSourceHetzner(CiTestCase):
408 self.assertIsInstance(ds.get_public_ssh_keys(), list)
409 self.assertEqual(ds.get_userdata_raw(), USERDATA)
410 self.assertEqual(ds.get_vendordata_raw(), METADATA.get('vendor_data'))
411+
412+ @mock.patch('cloudinit.sources.helpers.hetzner.read_metadata')
413+ @mock.patch('cloudinit.net.find_fallback_nic')
414+ @mock.patch('cloudinit.sources.DataSourceHetzner.on_hetzner')
415+ def test_not_on_hetzner_returns_false(self, m_on_hetzner, m_find_fallback,
416+ m_read_md):
417+ """If helper 'on_hetzner' returns False, return False from get_data."""
418+ m_on_hetzner.return_value = False
419+ ds = self.get_ds()
420+ ret = ds.get_data()
421+
422+ self.assertFalse(ret)
423+ # These are a white box attempt to ensure it did not search.
424+ m_find_fallback.assert_not_called()
425+ m_read_md.assert_not_called()
426diff --git a/tests/unittests/test_handler/test_handler_resizefs.py b/tests/unittests/test_handler/test_handler_resizefs.py
427index c2a7f9f..7a7ba1f 100644
428--- a/tests/unittests/test_handler/test_handler_resizefs.py
429+++ b/tests/unittests/test_handler/test_handler_resizefs.py
430@@ -1,7 +1,8 @@
431 # This file is part of cloud-init. See LICENSE file for license information.
432
433 from cloudinit.config.cc_resizefs import (
434- can_skip_resize, handle, maybe_get_writable_device_path, _resize_btrfs)
435+ can_skip_resize, handle, maybe_get_writable_device_path, _resize_btrfs,
436+ _resize_zfs, _resize_xfs, _resize_ext, _resize_ufs)
437
438 from collections import namedtuple
439 import logging
440@@ -60,6 +61,9 @@ class TestResizefs(CiTestCase):
441 res = can_skip_resize(fs_type, resize_what, devpth)
442 self.assertTrue(res)
443
444+ def test_can_skip_resize_ext(self):
445+ self.assertFalse(can_skip_resize('ext', '/', '/dev/sda1'))
446+
447 def test_handle_noops_on_disabled(self):
448 """The handle function logs when the configuration disables resize."""
449 cfg = {'resize_rootfs': False}
450@@ -122,6 +126,51 @@ class TestResizefs(CiTestCase):
451 logs = self.logs.getvalue()
452 self.assertIn("WARNING: Unable to find device '/dev/root'", logs)
453
454+ def test_resize_zfs_cmd_return(self):
455+ zpool = 'zroot'
456+ devpth = 'gpt/system'
457+ self.assertEqual(('zpool', 'online', '-e', zpool, devpth),
458+ _resize_zfs(zpool, devpth))
459+
460+ def test_resize_xfs_cmd_return(self):
461+ mount_point = '/mnt/test'
462+ devpth = '/dev/sda1'
463+ self.assertEqual(('xfs_growfs', mount_point),
464+ _resize_xfs(mount_point, devpth))
465+
466+ def test_resize_ext_cmd_return(self):
467+ mount_point = '/'
468+ devpth = '/dev/sdb1'
469+ self.assertEqual(('resize2fs', devpth),
470+ _resize_ext(mount_point, devpth))
471+
472+ def test_resize_ufs_cmd_return(self):
473+ mount_point = '/'
474+ devpth = '/dev/sda2'
475+ self.assertEqual(('growfs', devpth),
476+ _resize_ufs(mount_point, devpth))
477+
478+ @mock.patch('cloudinit.util.get_mount_info')
479+ @mock.patch('cloudinit.util.get_device_info_from_zpool')
480+ @mock.patch('cloudinit.util.parse_mount')
481+ def test_handle_zfs_root(self, mount_info, zpool_info, parse_mount):
482+ devpth = 'vmzroot/ROOT/freebsd'
483+ disk = 'gpt/system'
484+ fs_type = 'zfs'
485+ mount_point = '/'
486+
487+ mount_info.return_value = (devpth, fs_type, mount_point)
488+ zpool_info.return_value = disk
489+ parse_mount.return_value = (devpth, fs_type, mount_point)
490+
491+ cfg = {'resize_rootfs': True}
492+
493+ with mock.patch('cloudinit.config.cc_resizefs.do_resize') as dresize:
494+ handle('cc_resizefs', cfg, _cloud=None, log=LOG, args=[])
495+ ret = dresize.call_args[0][0]
496+
497+ self.assertEqual(('zpool', 'online', '-e', 'vmzroot', disk), ret)
498+
499
500 class TestRootDevFromCmdline(CiTestCase):
501
502@@ -305,5 +354,12 @@ class TestMaybeGetDevicePathAsWritableBlock(CiTestCase):
503 ('btrfs', 'filesystem', 'resize', 'max', '/'),
504 _resize_btrfs("/", "/dev/sda1"))
505
506+ @mock.patch('cloudinit.util.is_FreeBSD')
507+ def test_maybe_get_writable_device_path_zfs_freebsd(self, freebsd):
508+ freebsd.return_value = True
509+ info = 'dev=gpt/system mnt_point=/ path=/'
510+ devpth = maybe_get_writable_device_path('gpt/system', info, LOG)
511+ self.assertEqual('gpt/system', devpth)
512+
513
514 # vi: ts=4 expandtab
515diff --git a/tests/unittests/test_util.py b/tests/unittests/test_util.py
516index 67d9607..8685b8e 100644
517--- a/tests/unittests/test_util.py
518+++ b/tests/unittests/test_util.py
519@@ -366,6 +366,56 @@ class TestMountinfoParsing(helpers.ResourceUsingTestCase):
520 expected = ('none', 'tmpfs', '/run/lock')
521 self.assertEqual(expected, util.parse_mount_info('/run/lock', lines))
522
523+ @mock.patch('cloudinit.util.subp')
524+ def test_get_device_info_from_zpool(self, zpool_output):
525+ # mock subp command from util.get_mount_info_fs_on_zpool
526+ zpool_output.return_value = (
527+ self.readResource('zpool_status_simple.txt'), ''
528+ )
529+ # save function return values and do asserts
530+ ret = util.get_device_info_from_zpool('vmzroot')
531+ self.assertEqual('gpt/system', ret)
532+ self.assertIsNotNone(ret)
533+
534+ @mock.patch('cloudinit.util.subp')
535+ def test_get_device_info_from_zpool_on_error(self, zpool_output):
536+ # mock subp command from util.get_mount_info_fs_on_zpool
537+ zpool_output.return_value = (
538+ self.readResource('zpool_status_simple.txt'), 'error'
539+ )
540+ # save function return values and do asserts
541+ ret = util.get_device_info_from_zpool('vmzroot')
542+ self.assertIsNone(ret)
543+
544+ @mock.patch('cloudinit.util.subp')
545+ def test_parse_mount_with_ext(self, mount_out):
546+ mount_out.return_value = (self.readResource('mount_parse_ext.txt'), '')
547+ # this one is valid and exists in mount_parse_ext.txt
548+ ret = util.parse_mount('/var')
549+ self.assertEqual(('/dev/mapper/vg00-lv_var', 'ext4', '/var'), ret)
550+ # another one that is valid and exists
551+ ret = util.parse_mount('/')
552+ self.assertEqual(('/dev/mapper/vg00-lv_root', 'ext4', '/'), ret)
553+ # this one exists in mount_parse_ext.txt
554+ ret = util.parse_mount('/sys/kernel/debug')
555+ self.assertIsNone(ret)
556+ # this one does not even exist in mount_parse_ext.txt
557+ ret = util.parse_mount('/not/existing/mount')
558+ self.assertIsNone(ret)
559+
560+ @mock.patch('cloudinit.util.subp')
561+ def test_parse_mount_with_zfs(self, mount_out):
562+ mount_out.return_value = (self.readResource('mount_parse_zfs.txt'), '')
563+ # this one is valid and exists in mount_parse_zfs.txt
564+ ret = util.parse_mount('/var')
565+ self.assertEqual(('vmzroot/ROOT/freebsd/var', 'zfs', '/var'), ret)
566+ # this one is the root, valid and also exists in mount_parse_zfs.txt
567+ ret = util.parse_mount('/')
568+ self.assertEqual(('vmzroot/ROOT/freebsd', 'zfs', '/'), ret)
569+ # this one does not even exist in mount_parse_ext.txt
570+ ret = util.parse_mount('/not/existing/mount')
571+ self.assertIsNone(ret)
572+
573
574 class TestReadDMIData(helpers.FilesystemMockingTestCase):
575

Subscribers

People subscribed via source and target branches