Merge ~raharper/curtin:fix/support-i386 into curtin:master

Proposed by Ryan Harper
Status: Merged
Approved by: Ryan Harper
Approved revision: 92f2032d3a6fed961a51aaa7ff7f7eb3e7c65fea
Merge reported by: Ryan Harper
Merged at revision: 5313b9e2df45309195ea30cdaba836ae1e9cf66e
Proposed branch: ~raharper/curtin:fix/support-i386
Merge into: curtin:master
Diff against target: 599 lines (+270/-57)
10 files modified
curtin/block/clear_holders.py (+5/-2)
curtin/block/zfs.py (+25/-0)
curtin/commands/block_meta.py (+3/-0)
tests/unittests/test_block_zfs.py (+96/-0)
tests/unittests/test_clear_holders.py (+15/-13)
tests/vmtests/__init__.py (+34/-5)
tests/vmtests/helpers.py (+49/-32)
tests/vmtests/test_basic.py (+5/-0)
tests/vmtests/test_zfsroot.py (+36/-1)
tools/vmtest-sync-images (+2/-4)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Scott Moser (community) Approve
Review via email: mp+345300@code.launchpad.net

Commit message

zfs: implement a supported check to handle i386

The zfs kernel module is not available for i386 so curtin cannot
expect that missing that module is fatal. Concentrate the various
checks for when we load the zfs kernel module into the zfs python
module. Add vmtest to exercise the i386 path.

LP: #1768709

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
~raharper/curtin:fix/support-i386 updated
99e6520... by Ryan Harper

Fix tox

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
~raharper/curtin:fix/support-i386 updated
21715ca... by Ryan Harper

Fix skip_if_expected_failure implementation

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Scott Moser (smoser) wrote :

So essentially, you have to decorate all tests that would be run
by an expected failure run with the 'skip_if_flag'.

it seems heavy to decorate in a superclass of all tests
for this small usecase.

I like the other changes, and I'm not strictly opposed to this one, but it just seems heavy handed.

Revision history for this message
Ryan Harper (raharper) wrote :

I think doing it in the base class protects against when we add new base-class tests that would fail, we'd need to add a skip in the zfsroot class which could have copied all of the base class tests and decorated them.

I'm open to alternatives that are lighter if you have them; and we may want to create new expected_failure classes to test error paths, in which case we'd want something like this anyhow.

Revision history for this message
Ryan Harper (raharper) :
~raharper/curtin:fix/support-i386 updated
9c3eab6... by Ryan Harper

Fix missing space in RuntimeError exception.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Scott Moser (smoser) wrote :

I wont block you on this, but subsequently I'll look at a lighter maintenance-wise version of expectedFailure.

Revision history for this message
Scott Moser (smoser) :
review: Approve
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ryan Harper (raharper) wrote :

Started vmtest run on jenkins here (full run since we touch BaseVMClass ).

https://jenkins.ubuntu.com/server/view/curtin/job/curtin-vmtest-devel-debug/61/console

Revision history for this message
Ryan Harper (raharper) wrote :

Bah, the i386 vmtests failed since we don't have the i386 images synced yet. So instead I'll do a full run on diglett and post results here.

Revision history for this message
Ryan Harper (raharper) wrote :

Looks like I need to add some code to detect the arch values in the test-cases much like we do for distro/release so that 'make sync-images' pulls in the i386 variants.

Fixing that now.

~raharper/curtin:fix/support-i386 updated
c55b507... by Ryan Harper

vmtest-sync-images: add find_arches() to sync all test images

Introduction of i386 tests means that when generating the mirroring
filter vmtest needs to find all values of the 'arch' attribute.
Refactor generating the list of testcases into common find_testcases()

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ryan Harper (raharper) wrote :

Diglett vmtest run of this branch is successful after sync'ing i386 images.

----------------------------------------------------------------------
Ran 3098 tests in 15824.715s

OK (SKIP=249)
Tue, 15 May 2018 18:46:25 -0500: vmtest end [0] in 15826s

~raharper/curtin:fix/support-i386 updated
92f2032... by Ryan Harper

Add unittests for zfs.zfs_supported and update mock for clear-holders use of zfs_supported

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ryan Harper (raharper) wrote :

Somehow the unittests had started to fail when I went to rebase this on master. I rebased and then applied a refactor to the unittests. I'm re-running vmtest on diglett.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ryan Harper (raharper) wrote :

Clean vmtest run after rebase and unittest fixes:

% rm -rf output/; CURTIN_VMTEST_PARALLEL=4 CURTIN_VMTEST_BOOT_TIMEOUT=300 CURTIN_VMTEST_KEEP_DATA_FAIL=all ./tools/jenkins-runner -vv --nologcapture tests/vmtests/test_zfsroot.py
CURTIN_VMTEST_BOOT_TIMEOUT=300
CURTIN_VMTEST_IMAGE_SYNC=0
CURTIN_VMTEST_ISCSI_PORTAL=10.245.168.20:1202
CURTIN_VMTEST_KEEP_DATA_FAIL=all
CURTIN_VMTEST_KEEP_DATA_PASS=logs,collect
CURTIN_VMTEST_LOG=/home/rharper/work/git/curtin/output/debug.log
CURTIN_VMTEST_PARALLEL=4
CURTIN_VMTEST_REUSE_TOPDIR=0
CURTIN_VMTEST_TAR_DISKS=0
CURTIN_VMTEST_TOPDIR=/home/rharper/work/git/curtin/output
TGT_IPC_SOCKET=/home/rharper/work/git/curtin/output/tgt.d/socket
TGT_LOG_D=/home/rharper/work/git/curtin/output/tgt.d
TGT_PID=14721
TGT_PORTAL=10.245.168.20:1202
http_proxy=http://squid.internal:3128/
https_proxy=http://squid.internal:3128/
no_proxy=
Quering synced ephemeral images/kernels in /srv/images
======================================================================================
 Release Codename ImageDate Arch/SubArch Path
--------------------------------------------------------------------------------------
   12.04 precise 20170424 amd64/hwe-t precise/amd64/20170424/root-image.gz
   12.04 precise 20170424.1 amd64/hwe-p precise/amd64/20170424/squashfs
   14.04 trusty 20180502 amd64/hwe-t trusty/amd64/20180502/squashfs
   14.04 trusty 20180502 amd64/hwe-x trusty/amd64/20180502/squashfs
   14.04 trusty 20180502 i386/hwe-t trusty/i386/20180502/squashfs
   14.04 trusty 20180502 i386/hwe-x trusty/i386/20180502/squashfs
   16.04 xenial 20180511 amd64/ga-16.04 xenial/amd64/20180511/squashfs
   16.04 xenial 20180511 amd64/hwe-16.04 xenial/amd64/20180511/squashfs
   16.04 xenial 20180511 amd64/hwe-16.04-edge xenial/amd64/20180511/squashfs
   16.04 xenial 20180511 i386/ga-16.04 xenial/i386/20180511/squashfs
   16.04 xenial 20180511 i386/hwe-16.04 xenial/i386/20180511/squashfs
   16.04 xenial 20180511 i386/hwe-16.04-edge xenial/i386/20180511/squashfs
   17.04 zesty 20171219 amd64/ga-17.04 zesty/amd64/20171219/squashfs
   17.10 artful 20180516 amd64/ga-17.10 artful/amd64/20180516/squashfs
   18.04 bionic 20180509 amd64/ga-18.04 bionic/amd64/20180509/squashfs
   18.04 bionic 20180509 i386/ga-18.04 bionic/i386/20180509/squashfs
======================================================================================

...

----------------------------------------------------------------------
Ran 3109 tests in 15897.269s

OK (SKIP=249)
Thu, 17 May 2018 18:07:47 -0500: vmtest end [0] in 15899s

Revision history for this message
Ryan Harper (raharper) wrote :

An upstream commit landed for this bug.

To view that commit see the following URL:
https://git.launchpad.net/curtin/commit/?id=5313b9e2

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/curtin/block/clear_holders.py b/curtin/block/clear_holders.py
2index cbb7074..20c572b 100644
3--- a/curtin/block/clear_holders.py
4+++ b/curtin/block/clear_holders.py
5@@ -610,8 +610,11 @@ def start_clear_holders_deps():
6 util.load_kernel_module('bcache')
7 # the zfs module is needed to find and export devices which may be in-use
8 # and need to be cleared, only on xenial+.
9- if not util.lsb_release()['codename'] in ['precise', 'trusty']:
10- util.load_kernel_module('zfs')
11+ try:
12+ if zfs.zfs_supported():
13+ util.load_kernel_module('zfs')
14+ except RuntimeError as e:
15+ LOG.warning('Failed to load zfs kernel module: %s', e)
16
17
18 # anything that is not identified can assumed to be a 'disk' or similar
19diff --git a/curtin/block/zfs.py b/curtin/block/zfs.py
20index 8e00c7e..cfb07a9 100644
21--- a/curtin/block/zfs.py
22+++ b/curtin/block/zfs.py
23@@ -21,6 +21,9 @@ ZFS_DEFAULT_PROPERTIES = {
24 'normalization': 'formD',
25 }
26
27+ZFS_UNSUPPORTED_ARCHES = ['i386']
28+ZFS_UNSUPPORTED_RELEASES = ['precise', 'trusty']
29+
30
31 def _join_flags(optflag, params):
32 """
33@@ -69,6 +72,28 @@ def _join_pool_volume(poolname, volume):
34 return os.path.normpath("%s/%s" % (poolname, volume))
35
36
37+def zfs_supported():
38+ """ Determine if the runtime system supports zfs.
39+ returns: True if system supports zfs
40+ raises: RuntimeError: if system does not support zfs
41+ """
42+ arch = util.get_platform_arch()
43+ if arch in ZFS_UNSUPPORTED_ARCHES:
44+ raise RuntimeError("zfs is not supported on architecture: %s" % arch)
45+
46+ release = util.lsb_release()['codename']
47+ if release in ZFS_UNSUPPORTED_RELEASES:
48+ raise RuntimeError("zfs is not supported on release: %s" % release)
49+
50+ try:
51+ util.subp(['modinfo', 'zfs'], capture=True)
52+ except util.ProcessExecutionError as err:
53+ if err.stderr.startswith("modinfo: ERROR: Module zfs not found."):
54+ raise RuntimeError("zfs kernel module is not available: %s" % err)
55+
56+ return True
57+
58+
59 def zpool_create(poolname, vdevs, mountpoint=None, altroot=None,
60 pool_properties=None, zfs_properties=None):
61 """
62diff --git a/curtin/commands/block_meta.py b/curtin/commands/block_meta.py
63index ff2ab80..f5b82cf 100644
64--- a/curtin/commands/block_meta.py
65+++ b/curtin/commands/block_meta.py
66@@ -1263,6 +1263,8 @@ def zpool_handler(info, storage_config):
67 """
68 Create a zpool based in storage_configuration
69 """
70+ zfs.zfs_supported()
71+
72 state = util.load_command_environment()
73
74 # extract /dev/disk/by-id paths for each volume used
75@@ -1296,6 +1298,7 @@ def zfs_handler(info, storage_config):
76 """
77 Create a zfs filesystem
78 """
79+ zfs.zfs_supported()
80 state = util.load_command_environment()
81 poolname = get_poolname(info, storage_config)
82 volume = info.get('volume')
83diff --git a/tests/unittests/test_block_zfs.py b/tests/unittests/test_block_zfs.py
84index 883f727..c61a6da 100644
85--- a/tests/unittests/test_block_zfs.py
86+++ b/tests/unittests/test_block_zfs.py
87@@ -1,5 +1,8 @@
88+import mock
89+
90 from curtin.config import merge_config
91 from curtin.block import zfs
92+from curtin.util import ProcessExecutionError
93 from .helpers import CiTestCase
94
95
96@@ -375,4 +378,97 @@ class TestBlockZfsDeviceToPoolname(CiTestCase):
97 self.mock_blkid.assert_called_with(devs=[devname])
98
99
100+class TestBlockZfsZfsSupported(CiTestCase):
101+
102+ def setUp(self):
103+ super(TestBlockZfsZfsSupported, self).setUp()
104+ self.add_patch('curtin.block.zfs.util.subp', 'mock_subp')
105+ self.add_patch('curtin.block.zfs.util.get_platform_arch', 'mock_arch')
106+ self.add_patch('curtin.block.zfs.util.lsb_release', 'mock_release')
107+ self.mock_release.return_value = {'codename': 'xenial'}
108+ self.mock_arch.return_value = 'x86_64'
109+
110+ def test_supported_arch(self):
111+ self.assertTrue(zfs.zfs_supported())
112+
113+ def test_unsupported_arch(self):
114+ self.mock_arch.return_value = 'i386'
115+ with self.assertRaises(RuntimeError):
116+ zfs.zfs_supported()
117+
118+ def test_unsupported_releases(self):
119+ for rel in ['precise', 'trusty']:
120+ self.mock_release.return_value = {'codename': rel}
121+ with self.assertRaises(RuntimeError):
122+ zfs.zfs_supported()
123+
124+ def test_missing_module(self):
125+ missing = 'modinfo: ERROR: Module zfs not found.\n '
126+ self.mock_subp.side_effect = ProcessExecutionError(stdout='',
127+ stderr=missing,
128+ exit_code='1')
129+ with self.assertRaises(RuntimeError):
130+ zfs.zfs_supported()
131+
132+
133+class TestZfsSupported(CiTestCase):
134+
135+ def setUp(self):
136+ super(TestZfsSupported, self).setUp()
137+
138+ @mock.patch('curtin.block.zfs.util')
139+ def test_zfs_supported_returns_true(self, mock_util):
140+ """zfs_supported returns True on supported platforms"""
141+ mock_util.get_platform_arch.return_value = 'amd64'
142+ mock_util.lsb_release.return_value = {'codename': 'bionic'}
143+ mock_util.subp.return_value = ("", "")
144+
145+ self.assertNotIn(mock_util.get_platform_arch.return_value,
146+ zfs.ZFS_UNSUPPORTED_ARCHES)
147+ self.assertNotIn(mock_util.lsb_release.return_value['codename'],
148+ zfs.ZFS_UNSUPPORTED_RELEASES)
149+ self.assertTrue(zfs.zfs_supported())
150+
151+ @mock.patch('curtin.block.zfs.util')
152+ def test_zfs_supported_raises_exception_on_bad_arch(self, mock_util):
153+ """zfs_supported raises RuntimeError on unspported arches"""
154+ mock_util.lsb_release.return_value = {'codename': 'bionic'}
155+ mock_util.subp.return_value = ("", "")
156+ for arch in zfs.ZFS_UNSUPPORTED_ARCHES:
157+ mock_util.get_platform_arch.return_value = arch
158+ with self.assertRaises(RuntimeError):
159+ zfs.zfs_supported()
160+
161+ @mock.patch('curtin.block.zfs.util')
162+ def test_zfs_supported_raises_execption_on_bad_releases(self, mock_util):
163+ """zfs_supported raises RuntimeError on unspported releases"""
164+ mock_util.get_platform_arch.return_value = 'amd64'
165+ mock_util.subp.return_value = ("", "")
166+ for release in zfs.ZFS_UNSUPPORTED_RELEASES:
167+ mock_util.lsb_release.return_value = {'codename': release}
168+ with self.assertRaises(RuntimeError):
169+ zfs.zfs_supported()
170+
171+ @mock.patch('curtin.block.zfs.util.subprocess.Popen')
172+ @mock.patch('curtin.block.zfs.util.lsb_release')
173+ @mock.patch('curtin.block.zfs.util.get_platform_arch')
174+ def test_zfs_supported_raises_exception_on_missing_module(self,
175+ m_arch,
176+ m_release,
177+ m_popen):
178+ """zfs_supported raises RuntimeError on missing zfs module"""
179+
180+ m_arch.return_value = 'amd64'
181+ m_release.return_value = {'codename': 'bionic'}
182+ process_mock = mock.Mock()
183+ attrs = {
184+ 'returncode': 1,
185+ 'communicate.return_value':
186+ ('output', "modinfo: ERROR: Module zfs not found."),
187+ }
188+ process_mock.configure_mock(**attrs)
189+ m_popen.return_value = process_mock
190+ with self.assertRaises(RuntimeError):
191+ zfs.zfs_supported()
192+
193 # vi: ts=4 expandtab syntax=python
194diff --git a/tests/unittests/test_clear_holders.py b/tests/unittests/test_clear_holders.py
195index 9c93b5a..ceb5615 100644
196--- a/tests/unittests/test_clear_holders.py
197+++ b/tests/unittests/test_clear_holders.py
198@@ -723,29 +723,31 @@ class TestClearHolders(CiTestCase):
199 mock_gen_holders_tree.return_value = self.example_holders_trees[1][1]
200 clear_holders.assert_clear(device)
201
202+ @mock.patch('curtin.block.clear_holders.zfs')
203 @mock.patch('curtin.block.clear_holders.mdadm')
204 @mock.patch('curtin.block.clear_holders.util')
205- def test_start_clear_holders_deps(self, mock_util, mock_mdadm):
206- mock_util.lsb_release.return_value = {'codename': 'xenial'}
207+ def test_start_clear_holders_deps(self, mock_util, mock_mdadm, mock_zfs):
208+ mock_zfs.zfs_supported.return_value = True
209 clear_holders.start_clear_holders_deps()
210 mock_mdadm.mdadm_assemble.assert_called_with(
211 scan=True, ignore_errors=True)
212 mock_util.load_kernel_module.assert_has_calls([
213 mock.call('bcache'), mock.call('zfs')])
214
215+ @mock.patch('curtin.block.clear_holders.zfs')
216 @mock.patch('curtin.block.clear_holders.mdadm')
217 @mock.patch('curtin.block.clear_holders.util')
218- def test_start_clear_holders_deps_nozfs(self, mock_util, mock_mdadm):
219- """ test that we skip zfs modprobe on precise, trusty """
220- for codename in ['precise', 'trusty']:
221- mock_util.lsb_release.return_value = {'codename': codename}
222- clear_holders.start_clear_holders_deps()
223- mock_mdadm.mdadm_assemble.assert_called_with(
224- scan=True, ignore_errors=True)
225- mock_util.load_kernel_module.assert_has_calls(
226- [mock.call('bcache')])
227- self.assertNotIn(mock.call('zfs'),
228- mock_util.load_kernel_module.call_args_list)
229+ def test_start_clear_holders_deps_nozfs(self, mock_util, mock_mdadm,
230+ mock_zfs):
231+ """test that we skip zfs modprobe on unsupported platforms"""
232+ mock_zfs.zfs_supported.return_value = False
233+ clear_holders.start_clear_holders_deps()
234+ mock_mdadm.mdadm_assemble.assert_called_with(
235+ scan=True, ignore_errors=True)
236+ mock_util.load_kernel_module.assert_has_calls(
237+ [mock.call('bcache')])
238+ self.assertNotIn(mock.call('zfs'),
239+ mock_util.load_kernel_module.call_args_list)
240
241 @mock.patch('curtin.block.clear_holders.util')
242 def test_shutdown_swap_calls_swapoff(self, mock_util):
243diff --git a/tests/vmtests/__init__.py b/tests/vmtests/__init__.py
244index d02ad33..5c30a83 100644
245--- a/tests/vmtests/__init__.py
246+++ b/tests/vmtests/__init__.py
247@@ -350,8 +350,23 @@ class TempDir(object):
248 stdout=DEVNULL, stderr=subprocess.STDOUT)
249
250
251+def skip_if_flag(flag):
252+ def decorator(func):
253+ """the name test_wrapper below has to start with test, or nose's
254+ filter will not run it."""
255+ def test_wrapper(self, *args, **kwargs):
256+ val = getattr(self, flag, None)
257+ if val:
258+ self.skipTest("skip due to %s=%s" % (flag, val))
259+ else:
260+ return func(self, *args, **kwargs)
261+ return test_wrapper
262+ return decorator
263+
264+
265 class VMBaseClass(TestCase):
266 __test__ = False
267+ expected_failure = False
268 arch_skip = []
269 boot_timeout = BOOT_TIMEOUT
270 collect_scripts = [textwrap.dedent("""
271@@ -959,6 +974,10 @@ class VMBaseClass(TestCase):
272 else:
273 logger.warn("Boot for install did not produce a console log.")
274
275+ if cls.expected_failure:
276+ logger.debug('Expected Failure: skipping boot stage')
277+ return
278+
279 logger.debug('')
280 try:
281 if os.path.exists(cls.install_log):
282@@ -1302,6 +1321,7 @@ class VMBaseClass(TestCase):
283 ret[val[0]] = val[1]
284 return ret
285
286+ @skip_if_flag('expected_failure')
287 def test_fstab(self):
288 if self.fstab_expected is None:
289 return
290@@ -1317,6 +1337,7 @@ class VMBaseClass(TestCase):
291 self.assertEqual(fstab_entry.split(' ')[1],
292 mntpoint)
293
294+ @skip_if_flag('expected_failure')
295 def test_dname(self, disk_to_check=None):
296 if "trusty" in [self.release, self.target_release]:
297 raise SkipTest(
298@@ -1339,6 +1360,7 @@ class VMBaseClass(TestCase):
299 self.assertIn(link, contents)
300 self.assertIn(diskname, contents)
301
302+ @skip_if_flag('expected_failure')
303 def test_reporting_data(self):
304 with open(self.reporting_log, 'r') as fp:
305 data = json.load(fp)
306@@ -1358,6 +1380,7 @@ class VMBaseClass(TestCase):
307 self.assertIn('path', files)
308 self.assertEqual('/tmp/install.log', files.get('path', ''))
309
310+ @skip_if_flag('expected_failure')
311 def test_interfacesd_eth0_removed(self):
312 """ Check that curtin has removed /etc/network/interfaces.d/eth0.cfg
313 by examining the output of a find /etc/network > find_interfaces.d
314@@ -1366,9 +1389,9 @@ class VMBaseClass(TestCase):
315 self.assertNotIn("/etc/network/interfaces.d/eth0.cfg",
316 interfacesd.split("\n"))
317
318+ @skip_if_flag('expected_failure')
319 def test_installed_correct_kernel_package(self):
320 """ Test curtin installs the correct kernel package. """
321-
322 # target_distro is set for non-ubuntu targets
323 if self.target_distro is not None:
324 raise SkipTest("Can't check non-ubuntu kernel packages")
325@@ -1415,6 +1438,7 @@ class VMBaseClass(TestCase):
326 self._debian_packages = pkgs
327 return self._debian_packages
328
329+ @skip_if_flag('expected_failure')
330 def test_swaps_used(self):
331 cfg = yaml.load(self.load_collect_file("root/curtin-install-cfg.yaml"))
332 stgcfg = cfg.get("storage", {}).get("config", [])
333@@ -1553,14 +1577,19 @@ def get_rfc4173(ip, port, target, user=None, pword=None,
334
335
336 def find_error_context(err_match, contents, nrchars=200):
337+ traceback_end = re.compile(r'Error:.*')
338+ end_match = traceback_end.search(contents, err_match.start())
339 context_start = err_match.start() - nrchars
340- context_end = err_match.end() + nrchars
341+ if end_match:
342+ context_end = end_match.end()
343+ else:
344+ context_end = err_match.end() + nrchars
345 # extract contents, split into lines, drop the first and last partials
346 # recombine and return
347 return "\n".join(contents[context_start:context_end].splitlines()[1:-1])
348
349
350-def check_install_log(install_log):
351+def check_install_log(install_log, nrchars=200):
352 # look if install is OK via curtin 'Installation ok"
353 # if we dont find that, scan for known error messages and report
354 # if we don't see any errors, fail with general error
355@@ -1574,7 +1603,7 @@ def check_install_log(install_log):
356 'ImportError: No module named.*',
357 'Unexpected error while running command',
358 'E: Unable to locate package.*',
359- 'Traceback.*most recent call last.*:']))
360+ 'cloud-init.*: Traceback.*']))
361
362 install_is_ok = re.findall(install_pass, install_log)
363 # always scan for errors
364@@ -1583,7 +1612,7 @@ def check_install_log(install_log):
365 errmsg = ('Failed to verify Installation is OK')
366
367 for e in found_errors:
368- errors.append(find_error_context(e, install_log))
369+ errors.append(find_error_context(e, install_log, nrchars=nrchars))
370 errmsg = ('Errors during curtin installer')
371
372 return errmsg, errors
373diff --git a/tests/vmtests/helpers.py b/tests/vmtests/helpers.py
374index 7fc92e1..10e20b3 100644
375--- a/tests/vmtests/helpers.py
376+++ b/tests/vmtests/helpers.py
377@@ -86,18 +86,7 @@ def check_call(cmd, signal=signal.SIGTERM, **kwargs):
378 return Command(cmd, signal).run(**kwargs)
379
380
381-def find_releases_by_distro():
382- """
383- Returns a dictionary of distros and the distro releases that will be tested
384-
385- distros:
386- ubuntu:
387- releases: []
388- krels: []
389- centos:
390- releases: []
391- krels: []
392- """
393+def find_testcases():
394 # Use the TestLoder to load all test cases defined within tests/vmtests/
395 # and figure out what distros and releases they are testing. Any tests
396 # which are disabled will be excluded.
397@@ -108,32 +97,60 @@ def find_releases_by_distro():
398 root_dir = os.path.split(os.path.split(tests_dir)[0])[0]
399 # Find all test modules defined in curtin/tests/vmtests/
400 module_test_suites = loader.discover(tests_dir, top_level_dir=root_dir)
401- # find all distros and releases tested for each distro
402- releases = []
403- krels = []
404- rel_by_dist = {}
405 for mts in module_test_suites:
406 for class_test_suite in mts:
407 for test_case in class_test_suite:
408 # skip disabled tests
409 if not getattr(test_case, '__test__', False):
410 continue
411- for (dist, rel, krel) in (
412- (getattr(test_case, a, None) for a in attrs)
413- for attrs in (('distro', 'release', 'krel'),
414- ('target_distro', 'target_release',
415- 'krel'))):
416-
417- if dist and rel:
418- distro = rel_by_dist.get(dist, {'releases': [],
419- 'krels': []})
420- releases = distro.get('releases')
421- krels = distro.get('krels')
422- if rel not in releases:
423- releases.append(rel)
424- if krel and krel not in krels:
425- krels.append(krel)
426- rel_by_dist.update({dist: distro})
427+ yield test_case
428+
429+
430+def find_arches():
431+ """
432+ Return a list of uniq arch values from test cases
433+ """
434+ arches = []
435+ for test_case in find_testcases():
436+ arch = getattr(test_case, 'arch', None)
437+ if arch and arch not in arches:
438+ arches.append(arch)
439+ return arches
440+
441+
442+def find_releases_by_distro():
443+ """
444+ Returns a dictionary of distros and the distro releases that will be tested
445+
446+ distros:
447+ ubuntu:
448+ releases: []
449+ krels: []
450+ centos:
451+ releases: []
452+ krels: []
453+ """
454+ # find all distros and releases tested for each distro
455+ releases = []
456+ krels = []
457+ rel_by_dist = {}
458+ for test_case in find_testcases():
459+ for (dist, rel, krel) in (
460+ (getattr(test_case, a, None) for a in attrs)
461+ for attrs in (('distro', 'release', 'krel'),
462+ ('target_distro', 'target_release',
463+ 'krel'))):
464+
465+ if dist and rel:
466+ distro = rel_by_dist.get(dist, {'releases': [],
467+ 'krels': []})
468+ releases = distro.get('releases')
469+ krels = distro.get('krels')
470+ if rel not in releases:
471+ releases.append(rel)
472+ if krel and krel not in krels:
473+ krels.append(krel)
474+ rel_by_dist.update({dist: distro})
475
476 return rel_by_dist
477
478diff --git a/tests/vmtests/test_basic.py b/tests/vmtests/test_basic.py
479index ce86406..2e47cb6 100644
480--- a/tests/vmtests/test_basic.py
481+++ b/tests/vmtests/test_basic.py
482@@ -152,6 +152,11 @@ class TrustyHWEXTestBasic(relbase.trusty_hwe_x, TrustyTestBasic):
483 __test__ = True
484
485
486+class XenialGAi386TestBasic(relbase.xenial_ga, TestBasicAbs):
487+ __test__ = True
488+ arch = 'i386'
489+
490+
491 class XenialGATestBasic(relbase.xenial_ga, TestBasicAbs):
492 __test__ = True
493
494diff --git a/tests/vmtests/test_zfsroot.py b/tests/vmtests/test_zfsroot.py
495index 84439d3..1ebc616 100644
496--- a/tests/vmtests/test_zfsroot.py
497+++ b/tests/vmtests/test_zfsroot.py
498@@ -1,4 +1,4 @@
499-from . import VMBaseClass
500+from . import VMBaseClass, check_install_log, skip_if_flag
501 from .releases import base_vm_classes as relbase
502
503 import textwrap
504@@ -33,6 +33,7 @@ class TestZfsRootAbs(VMBaseClass):
505 echo "$v" > apt-proxy
506 """)]
507
508+ @skip_if_flag('expected_failure')
509 def test_output_files_exist(self):
510 self.output_files_exist(
511 ["blkid_output_vda", "blkid_output_vda1", "blkid_output_vda2",
512@@ -40,21 +41,49 @@ class TestZfsRootAbs(VMBaseClass):
513 "proc_partitions",
514 "root/curtin-install.log", "root/curtin-install-cfg.yaml"])
515
516+ @skip_if_flag('expected_failure')
517 def test_ptable(self):
518 blkid_info = self.get_blkid_data("blkid_output_vda")
519 self.assertEquals(blkid_info["PTTYPE"], "gpt")
520
521+ @skip_if_flag('expected_failure')
522 def test_zfs_list(self):
523 """Check rpoot/ROOT/zfsroot is mounted at slash"""
524 self.output_files_exist(['zfs_list'])
525 self.check_file_regex('zfs_list', r"rpool/ROOT/zfsroot.*/\n")
526
527+ @skip_if_flag('expected_failure')
528 def test_proc_cmdline_has_root_zfs(self):
529 """Check /proc/cmdline has root=ZFS=<pool>"""
530 self.output_files_exist(['proc_cmdline'])
531 self.check_file_regex('proc_cmdline', r"root=ZFS=rpool/ROOT/zfsroot")
532
533
534+class UnsupportedZfs(VMBaseClass):
535+ expected_failure = True
536+ collect_scripts = []
537+ interactive = False
538+
539+ def test_install_log_finds_zfs_runtime_error(self):
540+ with open(self.install_log, 'rb') as lfh:
541+ install_log = lfh.read().decode('utf-8', errors='replace')
542+ errmsg, errors = check_install_log(install_log)
543+ found_zfs = False
544+ print("errors: %s" % (len(errors)))
545+ for idx, err in enumerate(errors):
546+ print("%s:\n%s" % (idx, err))
547+ if 'RuntimeError' in err:
548+ found_zfs = True
549+ break
550+ self.assertTrue(found_zfs)
551+
552+
553+class XenialGAi386TestZfsRoot(relbase.xenial_ga, TestZfsRootAbs,
554+ UnsupportedZfs):
555+ __test__ = True
556+ arch = 'i386'
557+
558+
559 class XenialGATestZfsRoot(relbase.xenial_ga, TestZfsRootAbs):
560 __test__ = True
561
562@@ -83,5 +112,11 @@ class XenialGATestZfsRootFsType(relbase.xenial_ga, TestZfsRootFsTypeAbs):
563 __test__ = True
564
565
566+class XenialGAi386TestZfsRootFsType(relbase.xenial_ga, TestZfsRootFsTypeAbs,
567+ UnsupportedZfs):
568+ __test__ = True
569+ arch = 'i386'
570+
571+
572 class BionicTestZfsRootFsType(relbase.bionic, TestZfsRootFsTypeAbs):
573 __test__ = True
574diff --git a/tools/vmtest-sync-images b/tools/vmtest-sync-images
575index 26a1962..3d82b62 100755
576--- a/tools/vmtest-sync-images
577+++ b/tools/vmtest-sync-images
578@@ -17,11 +17,9 @@ sys.path.insert(1, os.path.realpath(os.path.join(
579 from tests.vmtests import (
580 IMAGE_DIR, IMAGE_SRC_URL, sync_images)
581 from tests.vmtests.image_sync import ITEM_NAME_FILTERS
582-from tests.vmtests.helpers import find_releases_by_distro
583+from tests.vmtests.helpers import (find_arches, find_releases_by_distro)
584 from curtin.util import get_platform_arch
585
586-DEFAULT_ARCH = get_platform_arch()
587-
588
589 def _fmt_list_filter(filter_name, matches):
590 return '~'.join((filter_name, '|'.join(matches)))
591@@ -53,7 +51,7 @@ if __name__ == '__main__':
592 os.unlink(fpath)
593
594 arg_releases = [r for r in sys.argv[1:] if r != "--clean"]
595- arch_filters = ['arch={}'.format(DEFAULT_ARCH)]
596+ arch_filters = [_fmt_list_filter('arch', find_arches())]
597 filter_sets = []
598 if len(arg_releases):
599 filter_sets.append([_fmt_list_filter('release', arg_releases),

Subscribers

People subscribed via source and target branches