Merge ~raharper/curtin:fix/support-i386 into curtin:master
- Git
- lp:~raharper/curtin
- fix/support-i386
- Merge into master
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) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Server Team CI bot | continuous-integration | Approve | |
Scott Moser (community) | Approve | ||
Review via email:
|
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
Description of the change

Server Team CI bot (server-team-bot) wrote : | # |
- 99e6520... by Ryan Harper
-
Fix tox

Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:4e2dfdcf986
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
- 21715ca... by Ryan Harper
-
Fix skip_if_
expected_ failure implementation

Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:a18196af13d
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/

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.

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.

Ryan Harper (raharper) : | # |
- 9c3eab6... by Ryan Harper
-
Fix missing space in RuntimeError exception.

Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:c566934eeeb
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/

Scott Moser (smoser) wrote : | # |
I wont block you on this, but subsequently I'll look at a lighter maintenance-wise version of expectedFailure.

Scott Moser (smoser) : | # |

Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:c05b4828745
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/

Ryan Harper (raharper) wrote : | # |
Started vmtest run on jenkins here (full run since we touch BaseVMClass ).
https:/

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.

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.
- 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()

Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:9ddc08422f2
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/

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
- 92f2032... by Ryan Harper
-
Add unittests for zfs.zfs_supported and update mock for clear-holders use of zfs_supported

Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:382556653e5
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/

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.

Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:92f2032d3a6
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/

Ryan Harper (raharper) wrote : | # |
Clean vmtest run after rebase and unittest fixes:
% rm -rf output/; CURTIN_
CURTIN_
CURTIN_
CURTIN_
CURTIN_
CURTIN_
CURTIN_
CURTIN_
CURTIN_
CURTIN_
CURTIN_
TGT_IPC_
TGT_LOG_
TGT_PID=14721
TGT_PORTAL=
http_proxy=http://
https_proxy=http://
no_proxy=
Quering synced ephemeral images/kernels in /srv/images
=======
Release Codename ImageDate Arch/SubArch Path
-------
12.04 precise 20170424 amd64/hwe-t precise/
12.04 precise 20170424.1 amd64/hwe-p precise/
14.04 trusty 20180502 amd64/hwe-t trusty/
14.04 trusty 20180502 amd64/hwe-x trusty/
14.04 trusty 20180502 i386/hwe-t trusty/
14.04 trusty 20180502 i386/hwe-x trusty/
16.04 xenial 20180511 amd64/ga-16.04 xenial/
16.04 xenial 20180511 amd64/hwe-16.04 xenial/
16.04 xenial 20180511 amd64/hwe-
16.04 xenial 20180511 i386/ga-16.04 xenial/
16.04 xenial 20180511 i386/hwe-16.04 xenial/
16.04 xenial 20180511 i386/hwe-16.04-edge xenial/
17.04 zesty 20171219 amd64/ga-17.04 zesty/amd64/
17.10 artful 20180516 amd64/ga-17.10 artful/
18.04 bionic 20180509 amd64/ga-18.04 bionic/
18.04 bionic 20180509 i386/ga-18.04 bionic/
=======
...
-------
Ran 3109 tests in 15897.269s
OK (SKIP=249)
Thu, 17 May 2018 18:07:47 -0500: vmtest end [0] in 15899s

Ryan Harper (raharper) wrote : | # |
An upstream commit landed for this bug.
To view that commit see the following URL:
https:/
Preview Diff
1 | diff --git a/curtin/block/clear_holders.py b/curtin/block/clear_holders.py |
2 | index 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 |
19 | diff --git a/curtin/block/zfs.py b/curtin/block/zfs.py |
20 | index 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 | """ |
62 | diff --git a/curtin/commands/block_meta.py b/curtin/commands/block_meta.py |
63 | index 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') |
83 | diff --git a/tests/unittests/test_block_zfs.py b/tests/unittests/test_block_zfs.py |
84 | index 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 |
194 | diff --git a/tests/unittests/test_clear_holders.py b/tests/unittests/test_clear_holders.py |
195 | index 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): |
243 | diff --git a/tests/vmtests/__init__.py b/tests/vmtests/__init__.py |
244 | index 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 |
373 | diff --git a/tests/vmtests/helpers.py b/tests/vmtests/helpers.py |
374 | index 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 | |
478 | diff --git a/tests/vmtests/test_basic.py b/tests/vmtests/test_basic.py |
479 | index 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 | |
494 | diff --git a/tests/vmtests/test_zfsroot.py b/tests/vmtests/test_zfsroot.py |
495 | index 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 |
574 | diff --git a/tools/vmtest-sync-images b/tools/vmtest-sync-images |
575 | index 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), |
FAILED: Continuous integration, rev:697b47a6b5d b12969527992548 ee2515309da5ed /jenkins. ubuntu. com/server/ job/curtin- ci/920/ /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-amd64/ 920/console /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-arm64/ 920/console /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-ppc64el/ 920/console /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-s390x/ 920/console
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /jenkins. ubuntu. com/server/ job/curtin- ci/920/ rebuild
https:/