Merge ~raharper/curtin:ubuntu/devel/release-18.2 into curtin:ubuntu/devel
- Git
- lp:~raharper/curtin
- ubuntu/devel/release-18.2
- Merge into ubuntu/devel
Status: | Merged | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Merged at revision: | ce1e3c1d2e9cc1927b07a2dad105820ac80c8948 | ||||||||||||||||||||||||
Proposed branch: | ~raharper/curtin:ubuntu/devel/release-18.2 | ||||||||||||||||||||||||
Merge into: | curtin:ubuntu/devel | ||||||||||||||||||||||||
Diff against target: |
3184 lines (+1417/-240) 59 files modified
curtin/__init__.py (+1/-1) curtin/commands/apt_config.py (+22/-6) curtin/commands/block_meta.py (+157/-49) curtin/swap.py (+9/-3) curtin/udev.py (+37/-0) debian/changelog (+27/-0) doc/topics/apt_source.rst (+57/-0) doc/topics/storage.rst (+45/-2) examples/tests/basic.yaml (+9/-0) examples/tests/basic_scsi.yaml (+27/-0) examples/tests/multipath.yaml (+1/-0) examples/tests/nvme.yaml (+2/-2) examples/tests/nvme_bcache.yaml (+1/-1) examples/tests/simple-storage.yaml (+48/-0) helpers/common (+50/-5) tests/unittests/helpers.py (+7/-0) tests/unittests/test_apt_custom_sources_list.py (+61/-29) tests/unittests/test_commands_block_meta.py (+4/-3) tests/unittests/test_commands_extract.py (+2/-1) tests/unittests/test_make_dname.py (+172/-30) tests/unittests/test_swap.py (+42/-28) tests/unittests/test_udev.py (+68/-0) tests/vmtests/__init__.py (+124/-20) tests/vmtests/releases.py (+6/-0) tests/vmtests/test_apt_config_cmd.py (+11/-2) tests/vmtests/test_apt_source.py (+7/-2) tests/vmtests/test_basic.py (+61/-8) tests/vmtests/test_bcache_basic.py (+6/-0) tests/vmtests/test_bcache_bug1718699.py (+4/-0) tests/vmtests/test_fs_battery.py (+5/-0) tests/vmtests/test_iscsi.py (+6/-0) tests/vmtests/test_journald_reporter.py (+4/-0) tests/vmtests/test_lvm.py (+5/-0) tests/vmtests/test_lvm_iscsi.py (+13/-7) tests/vmtests/test_lvm_raid.py (+16/-8) tests/vmtests/test_lvm_root.py (+2/-0) tests/vmtests/test_mdadm_bcache.py (+42/-0) tests/vmtests/test_mdadm_iscsi.py (+13/-6) tests/vmtests/test_multipath.py (+31/-0) tests/vmtests/test_network.py (+6/-0) tests/vmtests/test_network_alias.py (+6/-0) tests/vmtests/test_network_bonding.py (+6/-0) tests/vmtests/test_network_bridging.py (+7/-0) tests/vmtests/test_network_ipv6.py (+8/-0) tests/vmtests/test_network_ipv6_static.py (+4/-0) tests/vmtests/test_network_ipv6_vlan.py (+4/-0) tests/vmtests/test_network_mtu.py (+9/-0) tests/vmtests/test_network_static.py (+4/-0) tests/vmtests/test_network_static_routes.py (+5/-0) tests/vmtests/test_network_vlan.py (+6/-0) tests/vmtests/test_nvme.py (+17/-4) tests/vmtests/test_old_apt_features.py (+7/-2) tests/vmtests/test_pollinate_useragent.py (+6/-0) tests/vmtests/test_raid5_bcache.py (+15/-7) tests/vmtests/test_simple.py (+67/-1) tests/vmtests/test_ubuntu_core.py (+2/-0) tests/vmtests/test_uefi_basic.py (+17/-7) tests/vmtests/test_zfsroot.py (+10/-0) tools/jenkins-runner (+6/-6) |
||||||||||||||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Server Team CI bot | continuous-integration | Approve | |
curtin developers | Pending | ||
Review via email: mp+360217@code.launchpad.net |
Commit message
curtin (18.2-0ubuntu1) disco; urgency=medium
* New upstream release (18.2).
- Release 18.2
- Adjust helpers/common to edit GRUB_CMDLINE_
(LP: #1527664)
- dname: persistent names based on serial or wwn (LP: #1735839)
- Fix bug in is_swap_device if a device was smaller than page_size.
(LP: #1803672)
- vmtest: add disco tests [Joshua Powers]
- unittest: change directory to tmpdir for testing relative files.
- Add clear-holders to meta-simple (LP: #1786736)
- vmtests: check install log for Out of memory kernel messages and fail
- unittest: correctly use tmpdir for my.img [Joshua Powers] (LP: #1803611)
- block_meta: use wipe config when clearing partitions (LP: #1800153)
- tests: fix vmtests for apt perserve_
- apt: Use new format apt config when writing preserve_
(LP: #1735950)
- vmtests: multipath mount /home with nofail and validate in unittest
- vmtests: fix common collect scripts to not exit failure.
- vmtest: handle collect disk unpack failure
- vmtests: dont use multiple subclasses in uefi 4k tests
- vmtests: disable snapd/seeding to avoid boot hang
- jenkins-runner: fix when using --filter only
Description of the change
Server Team CI bot (server-team-bot) wrote : | # |
Preview Diff
1 | diff --git a/curtin/__init__.py b/curtin/__init__.py |
2 | index ee35ca3..a307e84 100644 |
3 | --- a/curtin/__init__.py |
4 | +++ b/curtin/__init__.py |
5 | @@ -30,6 +30,6 @@ FEATURES = [ |
6 | 'HAS_VERSION_MODULE', |
7 | ] |
8 | |
9 | -__version__ = "18.1" |
10 | +__version__ = "18.2" |
11 | |
12 | # vi: ts=4 expandtab syntax=python |
13 | diff --git a/curtin/commands/apt_config.py b/curtin/commands/apt_config.py |
14 | index 9ce25b3..8bd6e79 100644 |
15 | --- a/curtin/commands/apt_config.py |
16 | +++ b/curtin/commands/apt_config.py |
17 | @@ -10,7 +10,6 @@ import glob |
18 | import os |
19 | import re |
20 | import sys |
21 | -import yaml |
22 | |
23 | from curtin.log import LOG |
24 | from curtin import (config, distro, gpg, paths, util) |
25 | @@ -71,6 +70,7 @@ def handle_apt(cfg, target=None): |
26 | if not config.value_as_boolean(cfg.get('preserve_sources_list', |
27 | True)): |
28 | generate_sources_list(cfg, release, mirrors, target) |
29 | + apply_preserve_sources_list(target) |
30 | rename_apt_lists(mirrors, target) |
31 | |
32 | try: |
33 | @@ -318,16 +318,32 @@ def generate_sources_list(cfg, release, mirrors, target=None): |
34 | disabled = disable_suites(cfg.get('disable_suites'), rendered, release) |
35 | util.write_file(paths.target_path(target, aptsrc), disabled, mode=0o644) |
36 | |
37 | + |
38 | +def apply_preserve_sources_list(target): |
39 | # protect the just generated sources.list from cloud-init |
40 | cloudfile = "/etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg" |
41 | - # this has to work with older cloud-init as well, so use old key |
42 | - cloudconf = yaml.dump({'apt_preserve_sources_list': True}, indent=1) |
43 | + |
44 | + target_ver = distro.get_package_version('cloud-init', target=target) |
45 | + if not target_ver: |
46 | + LOG.info("Attempt to read cloud-init version from target returned " |
47 | + "'%s', not writing preserve_sources_list config.", |
48 | + target_ver) |
49 | + return |
50 | + |
51 | + cfg = {'apt': {'preserve_sources_list': True}} |
52 | + if target_ver['major'] < 1: |
53 | + # anything cloud-init 0.X.X will get the old config key. |
54 | + cfg = {'apt_preserve_sources_list': True} |
55 | + |
56 | try: |
57 | util.write_file(paths.target_path(target, cloudfile), |
58 | - cloudconf, mode=0o644) |
59 | + config.dump_config(cfg), mode=0o644) |
60 | + LOG.debug("Set preserve_sources_list to True in %s with: %s", |
61 | + cloudfile, cfg) |
62 | except IOError: |
63 | - LOG.exception("Failed to protect source.list from cloud-init in (%s)", |
64 | - paths.target_path(target, cloudfile)) |
65 | + LOG.exception( |
66 | + "Failed to protect /etc/apt/sources.list from cloud-init in '%s'", |
67 | + cloudfile) |
68 | raise |
69 | |
70 | |
71 | diff --git a/curtin/commands/block_meta.py b/curtin/commands/block_meta.py |
72 | index 197c1fd..8decab5 100644 |
73 | --- a/curtin/commands/block_meta.py |
74 | +++ b/curtin/commands/block_meta.py |
75 | @@ -8,7 +8,8 @@ from curtin.log import LOG, logged_time |
76 | from curtin.reporter import events |
77 | |
78 | from . import populate_one_subcmd |
79 | -from curtin.udev import compose_udev_equality, udevadm_settle, udevadm_trigger |
80 | +from curtin.udev import (compose_udev_equality, udevadm_settle, |
81 | + udevadm_trigger, udevadm_info) |
82 | |
83 | import glob |
84 | import os |
85 | @@ -35,6 +36,8 @@ CMD_ARGUMENTS = ( |
86 | 'metavar': 'DEVICE', 'default': None, }), |
87 | ('--fstype', {'help': 'root partition filesystem type', |
88 | 'choices': ['ext4', 'ext3'], 'default': 'ext4'}), |
89 | + ('--force-mode', {'help': 'force mode, disable mode detection', |
90 | + 'action': 'store_true', 'default': False}), |
91 | (('-t', '--target'), |
92 | {'help': 'chroot to target. default is env[TARGET_MOUNT_POINT]', |
93 | 'action': 'store', 'metavar': 'TARGET', |
94 | @@ -55,11 +58,35 @@ def block_meta(args): |
95 | state = util.load_command_environment() |
96 | cfg = config.load_command_config(args, state) |
97 | dd_images = util.get_dd_images(cfg.get('sources', {})) |
98 | - if ((args.mode == CUSTOM or cfg.get("storage") is not None) and |
99 | - len(dd_images) == 0): |
100 | - meta_custom(args) |
101 | - elif args.mode in (SIMPLE, SIMPLE_BOOT) or len(dd_images) > 0: |
102 | - meta_simple(args) |
103 | + |
104 | + # run clear holders on potential devices |
105 | + devices = args.devices |
106 | + if devices is None: |
107 | + devices = [] |
108 | + if 'storage' in cfg: |
109 | + devices = get_disk_paths_from_storage_config( |
110 | + extract_storage_ordered_dict(cfg)) |
111 | + if len(devices) == 0: |
112 | + devices = cfg.get('block-meta', {}).get('devices', []) |
113 | + LOG.debug('Declared block devices: %s', devices) |
114 | + args.devices = devices |
115 | + |
116 | + meta_clear(devices, state.get('report_stack_prefix', '')) |
117 | + |
118 | + # dd-images requires use of meta_simple |
119 | + if len(dd_images) > 0 and args.force_mode is False: |
120 | + LOG.info('blockmeta: detected dd-images, using mode=simple') |
121 | + return meta_simple(args) |
122 | + |
123 | + if cfg.get("storage") and args.force_mode is False: |
124 | + LOG.info('blockmeta: detected storage config, using mode=custom') |
125 | + return meta_custom(args) |
126 | + |
127 | + LOG.info('blockmeta: mode=%s force=%s', args.mode, args.force_mode) |
128 | + if args.mode == CUSTOM: |
129 | + return meta_custom(args) |
130 | + elif args.mode in (SIMPLE, SIMPLE_BOOT): |
131 | + return meta_simple(args) |
132 | else: |
133 | raise NotImplementedError("mode=%s is not implemented" % args.mode) |
134 | |
135 | @@ -195,12 +222,43 @@ def sanitize_dname(dname): |
136 | return ''.join(c if c in valid else '-' for c in dname) |
137 | |
138 | |
139 | +def make_dname_byid(path, error_msg=None, info=None): |
140 | + """ Returns a list of udev equalities for a given disk path |
141 | + |
142 | + :param path: string of a kernel device path to a block device |
143 | + :param error_msg: more information about path for log/errors |
144 | + :param info: dict of udevadm info key, value pairs of device specified by |
145 | + path. |
146 | + :returns: list of udev equalities (lists) |
147 | + :raises: ValueError if path is not a disk. |
148 | + :raises: RuntimeError if there is no serial or wwn. |
149 | + """ |
150 | + error_msg = str(path) + ("" if not error_msg else " [%s]" % error_msg) |
151 | + if info is None: |
152 | + info = udevadm_info(path=path) |
153 | + devtype = info.get('DEVTYPE') |
154 | + if devtype != "disk": |
155 | + raise ValueError( |
156 | + "Disk tag udev rules are only for disks, %s has devtype=%s" % |
157 | + (error_msg, devtype)) |
158 | + |
159 | + byid_keys = ['ID_SERIAL', 'ID_WWN_WITH_EXTENSION'] |
160 | + present = [k for k in byid_keys if info.get(k)] |
161 | + if not present: |
162 | + raise RuntimeError( |
163 | + "Cannot create disk tag udev rule for %s, " |
164 | + "missing 'serial' or 'wwn' value" % error_msg) |
165 | + |
166 | + return [[compose_udev_equality('ENV{%s}' % k, info[k]) for k in present]] |
167 | + |
168 | + |
169 | def make_dname(volume, storage_config): |
170 | state = util.load_command_environment() |
171 | rules_dir = os.path.join(state['scratch'], "rules.d") |
172 | vol = storage_config.get(volume) |
173 | path = get_path_to_storage_volume(volume, storage_config) |
174 | ptuuid = None |
175 | + byid = None |
176 | dname = vol.get('name') |
177 | if vol.get('type') in ["partition", "disk"]: |
178 | (out, _err) = util.subp(["blkid", "-o", "export", path], capture=True, |
179 | @@ -209,28 +267,41 @@ def make_dname(volume, storage_config): |
180 | if "PTUUID" in line or "PARTUUID" in line: |
181 | ptuuid = line.split('=')[-1] |
182 | break |
183 | + if vol.get('type') == 'disk': |
184 | + byid = make_dname_byid(path, error_msg="id=%s" % vol.get('id')) |
185 | # we may not always be able to find a uniq identifier on devices with names |
186 | - if not ptuuid and vol.get('type') in ["disk", "partition"]: |
187 | + if (not ptuuid and not byid) and vol.get('type') in ["disk", "partition"]: |
188 | LOG.warning("Can't find a uuid for volume: {}. Skipping dname.".format( |
189 | volume)) |
190 | return |
191 | |
192 | - rule = [ |
193 | + matches = [] |
194 | + base_rule = [ |
195 | compose_udev_equality("SUBSYSTEM", "block"), |
196 | compose_udev_equality("ACTION", "add|change"), |
197 | ] |
198 | if vol.get('type') == "disk": |
199 | - rule.append(compose_udev_equality('ENV{DEVTYPE}', "disk")) |
200 | - rule.append(compose_udev_equality('ENV{ID_PART_TABLE_UUID}', ptuuid)) |
201 | + if ptuuid: |
202 | + matches += [[compose_udev_equality('ENV{DEVTYPE}', "disk"), |
203 | + compose_udev_equality('ENV{ID_PART_TABLE_UUID}', |
204 | + ptuuid)]] |
205 | + for rule in byid: |
206 | + matches += [ |
207 | + [compose_udev_equality('ENV{DEVTYPE}', "disk")] + rule] |
208 | elif vol.get('type') == "partition": |
209 | - rule.append(compose_udev_equality('ENV{DEVTYPE}', "partition")) |
210 | - dname = storage_config.get(vol.get('device')).get('name') + \ |
211 | - "-part%s" % determine_partition_number(volume, storage_config) |
212 | - rule.append(compose_udev_equality('ENV{ID_PART_ENTRY_UUID}', ptuuid)) |
213 | + # if partition has its own name, bind that to the existing PTUUID |
214 | + if dname: |
215 | + matches += [[compose_udev_equality('ENV{DEVTYPE}', "partition"), |
216 | + compose_udev_equality('ENV{ID_PART_ENTRY_UUID}', |
217 | + ptuuid)]] |
218 | + else: |
219 | + # disks generate dname-part%n rules automatically |
220 | + LOG.debug('No partition-specific dname') |
221 | + return |
222 | elif vol.get('type') == "raid": |
223 | md_data = mdadm.mdadm_query_detail(path) |
224 | md_uuid = md_data.get('MD_UUID') |
225 | - rule.append(compose_udev_equality("ENV{MD_UUID}", md_uuid)) |
226 | + matches += [[compose_udev_equality("ENV{MD_UUID}", md_uuid)]] |
227 | elif vol.get('type') == "bcache": |
228 | # bind dname to bcache backing device's dev.uuid as the bcache minor |
229 | # device numbers are not stable across reboots. |
230 | @@ -239,12 +310,12 @@ def make_dname(volume, storage_config): |
231 | bcache_super = bcache.superblock_asdict(device=backing_dev) |
232 | if bcache_super and bcache_super['sb.version'].startswith('1'): |
233 | bdev_uuid = bcache_super['dev.uuid'] |
234 | - rule.append(compose_udev_equality("ENV{CACHED_UUID}", bdev_uuid)) |
235 | + matches += [[compose_udev_equality("ENV{CACHED_UUID}", bdev_uuid)]] |
236 | bcache.write_label(sanitize_dname(dname), backing_dev) |
237 | elif vol.get('type') == "lvm_partition": |
238 | volgroup_name = storage_config.get(vol.get('volgroup')).get('name') |
239 | dname = "%s-%s" % (volgroup_name, dname) |
240 | - rule.append(compose_udev_equality("ENV{DM_NAME}", dname)) |
241 | + matches += [[compose_udev_equality("ENV{DM_NAME}", dname)]] |
242 | else: |
243 | raise ValueError('cannot make dname for device with type: {}' |
244 | .format(vol.get('type'))) |
245 | @@ -257,11 +328,25 @@ def make_dname(volume, storage_config): |
246 | LOG.warning( |
247 | "dname modified to remove invalid chars. old: '{}' new: '{}'" |
248 | .format(dname, sanitized)) |
249 | - rule.append("SYMLINK+=\"disk/by-dname/%s\"\n" % sanitized) |
250 | - LOG.debug("Writing dname udev rule '{}'".format(str(rule))) |
251 | + content = ['# Written by curtin'] |
252 | + for match in matches: |
253 | + rule = (base_rule + match + |
254 | + ["SYMLINK+=\"disk/by-dname/%s\"\n" % sanitized]) |
255 | + LOG.debug("Creating dname udev rule '{}'".format(str(rule))) |
256 | + content.append(', '.join(rule)) |
257 | + |
258 | + if vol.get('type') == 'disk': |
259 | + for brule in byid: |
260 | + rule = (base_rule + |
261 | + [compose_udev_equality('ENV{DEVTYPE}', 'partition')] + |
262 | + brule + |
263 | + ['SYMLINK+="disk/by-dname/%s-part%%n"\n' % sanitized]) |
264 | + LOG.debug("Creating dname udev rule '{}'".format(str(rule))) |
265 | + content.append(', '.join(rule)) |
266 | + |
267 | util.ensure_dir(rules_dir) |
268 | rule_file = os.path.join(rules_dir, '{}.rules'.format(sanitized)) |
269 | - util.write_file(rule_file, ', '.join(rule)) |
270 | + util.write_file(rule_file, '\n'.join(content)) |
271 | |
272 | |
273 | def get_poolname(info, storage_config): |
274 | @@ -584,6 +669,9 @@ def partition_handler(info, storage_config): |
275 | block.zero_file_at_offsets(disk, [wipe_offset], exclusive=False) |
276 | |
277 | if disk_ptable == "msdos": |
278 | + if flag and flag == 'prep': |
279 | + raise ValueError('PReP partitions require a GPT partition table') |
280 | + |
281 | if flag in ["extended", "logical", "primary"]: |
282 | partition_type = flag |
283 | else: |
284 | @@ -604,6 +692,16 @@ def partition_handler(info, storage_config): |
285 | else: |
286 | raise ValueError("parent partition has invalid partition table") |
287 | |
288 | + # wipe the created partition if needed, superblocks have already been wiped |
289 | + wipe_mode = info.get('wipe', 'superblock') |
290 | + if wipe_mode != 'superblock': |
291 | + part_path = block.dev_path(block.partition_kname(disk_kname, |
292 | + partnumber)) |
293 | + block.rescan_block_devices([disk]) |
294 | + udevadm_settle(exists=part_path) |
295 | + LOG.debug('Wiping partition %s mode=%s', part_path, wipe_mode) |
296 | + block.wipe_volume(part_path, mode=wipe_mode, exclusive=False) |
297 | + |
298 | # Make the name if needed |
299 | if storage_config.get(device).get('name') and partition_type != 'extended': |
300 | make_dname(info.get('id'), storage_config) |
301 | @@ -1335,6 +1433,19 @@ def extract_storage_ordered_dict(config): |
302 | return OrderedDict((d["id"], d) for (i, d) in enumerate(scfg)) |
303 | |
304 | |
305 | +def get_disk_paths_from_storage_config(storage_config): |
306 | + """Returns a list of disk paths in a storage config filtering out |
307 | + preserved or disks which do not have wipe configuration. |
308 | + |
309 | + :param: storage_config: Ordered dict of storage configation |
310 | + """ |
311 | + return [get_path_to_storage_volume(k, storage_config) |
312 | + for (k, v) in storage_config.items() |
313 | + if v.get('type') == 'disk' and |
314 | + config.value_as_boolean(v.get('wipe')) and |
315 | + not config.value_as_boolean(v.get('preserve'))] |
316 | + |
317 | + |
318 | def zfsroot_update_storage_config(storage_config): |
319 | """Return an OrderedDict that has 'zfsroot' format expanded into |
320 | zpool and zfs commands to enable ZFS on rootfs. |
321 | @@ -1429,6 +1540,24 @@ def zfsroot_update_storage_config(storage_config): |
322 | return ret |
323 | |
324 | |
325 | +def meta_clear(devices, report_prefix=''): |
326 | + """ Run clear_holders on specified list of devices. |
327 | + |
328 | + :param: devices: a list of block devices (/dev/XXX) to be cleared |
329 | + :param: report_prefix: a string to pass to the ReportEventStack |
330 | + """ |
331 | + # shut down any already existing storage layers above any disks used in |
332 | + # config that have 'wipe' set |
333 | + with events.ReportEventStack( |
334 | + name=report_prefix + '/clear-holders', |
335 | + reporting_enabled=True, level='INFO', |
336 | + description="removing previous storage devices"): |
337 | + clear_holders.start_clear_holders_deps() |
338 | + clear_holders.clear_holders(devices) |
339 | + # if anything was not properly shut down, stop installation |
340 | + clear_holders.assert_clear(devices) |
341 | + |
342 | + |
343 | def meta_custom(args): |
344 | """Does custom partitioning based on the layout provided in the config |
345 | file. Section with the name storage contains information on which |
346 | @@ -1460,21 +1589,6 @@ def meta_custom(args): |
347 | # set up reportstack |
348 | stack_prefix = state.get('report_stack_prefix', '') |
349 | |
350 | - # shut down any already existing storage layers above any disks used in |
351 | - # config that have 'wipe' set |
352 | - with events.ReportEventStack( |
353 | - name=stack_prefix, reporting_enabled=True, level='INFO', |
354 | - description="removing previous storage devices"): |
355 | - clear_holders.start_clear_holders_deps() |
356 | - disk_paths = [get_path_to_storage_volume(k, storage_config_dict) |
357 | - for (k, v) in storage_config_dict.items() |
358 | - if v.get('type') == 'disk' and |
359 | - config.value_as_boolean(v.get('wipe')) and |
360 | - not config.value_as_boolean(v.get('preserve'))] |
361 | - clear_holders.clear_holders(disk_paths) |
362 | - # if anything was not properly shut down, stop installation |
363 | - clear_holders.assert_clear(disk_paths) |
364 | - |
365 | for item_id, command in storage_config_dict.items(): |
366 | handler = command_handlers.get(command['type']) |
367 | if not handler: |
368 | @@ -1500,8 +1614,15 @@ def meta_simple(args): |
369 | create a separate /boot partition. |
370 | """ |
371 | state = util.load_command_environment() |
372 | - |
373 | cfg = config.load_command_config(args, state) |
374 | + if args.target is not None: |
375 | + state['target'] = args.target |
376 | + |
377 | + if state['target'] is None: |
378 | + sys.stderr.write("Unable to find target. " |
379 | + "Use --target or set TARGET_MOUNT_POINT\n") |
380 | + sys.exit(2) |
381 | + |
382 | devpath = None |
383 | if cfg.get("storage") is not None: |
384 | for i in cfg["storage"]["config"]: |
385 | @@ -1512,21 +1633,8 @@ def meta_simple(args): |
386 | diskPath = block.lookup_disk(serial) |
387 | if grub is True: |
388 | devpath = diskPath |
389 | - if config.value_as_boolean(i.get('wipe')): |
390 | - block.wipe_volume(diskPath, mode=i.get('wipe')) |
391 | - |
392 | - if args.target is not None: |
393 | - state['target'] = args.target |
394 | - |
395 | - if state['target'] is None: |
396 | - sys.stderr.write("Unable to find target. " |
397 | - "Use --target or set TARGET_MOUNT_POINT\n") |
398 | - sys.exit(2) |
399 | |
400 | devices = args.devices |
401 | - if devices is None: |
402 | - devices = cfg.get('block-meta', {}).get('devices', []) |
403 | - |
404 | bootpt = get_bootpt_cfg( |
405 | cfg.get('block-meta', {}).get('boot-partition', {}), |
406 | enabled=args.mode == SIMPLE_BOOT, fstype=args.boot_fstype, |
407 | diff --git a/curtin/swap.py b/curtin/swap.py |
408 | index 5f77b03..d3f29dc 100644 |
409 | --- a/curtin/swap.py |
410 | +++ b/curtin/swap.py |
411 | @@ -103,9 +103,15 @@ def is_swap_device(path): |
412 | https://github.com/torvalds/linux/blob/master/include/linux/swap.h#L111 |
413 | """ |
414 | LOG.debug('Checking if %s is a swap device', path) |
415 | - swap_magic_offset = resource.getpagesize() - 10 |
416 | - magic = util.load_file(path, read_len=10, offset=swap_magic_offset, |
417 | - decode=False) |
418 | + pagesize = resource.getpagesize() |
419 | + magic_offset = pagesize - 10 |
420 | + size = util.file_size(path) |
421 | + if size < magic_offset: |
422 | + LOG.debug("%s is to small for swap (size=%d < pagesize=%d)", |
423 | + path, size, pagesize) |
424 | + return False |
425 | + magic = util.load_file( |
426 | + path, read_len=10, offset=magic_offset, decode=False) |
427 | LOG.debug('Found swap magic: %s' % magic) |
428 | return magic in [b'SWAPSPACE2', b'SWAP-SPACE'] |
429 | # vi: ts=4 expandtab syntax=python |
430 | diff --git a/curtin/udev.py b/curtin/udev.py |
431 | index 13d9cc5..106a7e7 100644 |
432 | --- a/curtin/udev.py |
433 | +++ b/curtin/udev.py |
434 | @@ -61,4 +61,41 @@ def udevadm_trigger(devices): |
435 | util.subp(['udevadm', 'trigger'] + list(devices)) |
436 | udevadm_settle() |
437 | |
438 | + |
439 | +def udevadm_info(path=None): |
440 | + """ Return a dictionary populated by properties of the device specified |
441 | + in the `path` variable via querying udev 'property' database. |
442 | + |
443 | + :params: path: path to device, either /dev or /sys |
444 | + :returns: dictionary of key=value pairs as exported from the udev database |
445 | + :raises: ValueError path is None, ProcessExecutionError on exec error. |
446 | + """ |
447 | + if not path: |
448 | + raise ValueError('Invalid path: "%s"' % path) |
449 | + |
450 | + info_cmd = ['udevadm', 'info', '--query=property', path] |
451 | + output, _ = util.subp(info_cmd, capture=True) |
452 | + |
453 | + # strip for trailing empty line |
454 | + info = {} |
455 | + for line in output.splitlines(): |
456 | + if not line: |
457 | + continue |
458 | + # maxsplit=2 gives us key and remaininng part of line is value |
459 | + # py2.7 on Trusty doesn't have keyword, pass as argument |
460 | + key, value = line.split('=', 2) |
461 | + if not value: |
462 | + value = None |
463 | + if value: |
464 | + # devlinks is a list of paths separated by space |
465 | + # convert to a list for easy use |
466 | + if key == 'DEVLINKS': |
467 | + info[key] = value.split() |
468 | + else: |
469 | + # preserve spaces in values, to match udev database |
470 | + info[key] = value |
471 | + |
472 | + return info |
473 | + |
474 | + |
475 | # vi: ts=4 expandtab syntax=python |
476 | diff --git a/debian/changelog b/debian/changelog |
477 | index 89bb20f..ef377fe 100644 |
478 | --- a/debian/changelog |
479 | +++ b/debian/changelog |
480 | @@ -1,3 +1,30 @@ |
481 | +curtin (18.2-0ubuntu1) disco; urgency=medium |
482 | + |
483 | + * New upstream release (18.2). |
484 | + - Release 18.2 |
485 | + - Adjust helpers/common to edit GRUB_CMDLINE_LINUX_DEFAULT in place. |
486 | + (LP: #1527664) |
487 | + - dname: persistent names based on serial or wwn (LP: #1735839) |
488 | + - Fix bug in is_swap_device if a device was smaller than page_size. |
489 | + (LP: #1803672) |
490 | + - vmtest: add disco tests [Joshua Powers] |
491 | + - unittest: change directory to tmpdir for testing relative files. |
492 | + - Add clear-holders to meta-simple (LP: #1786736) |
493 | + - vmtests: check install log for Out of memory kernel messages and fail |
494 | + - unittest: correctly use tmpdir for my.img [Joshua Powers] (LP: #1803611) |
495 | + - block_meta: use wipe config when clearing partitions (LP: #1800153) |
496 | + - tests: fix vmtests for apt perserve_source_list changes |
497 | + - apt: Use new format apt config when writing preserve_sources_list. |
498 | + (LP: #1735950) |
499 | + - vmtests: multipath mount /home with nofail and validate in unittest |
500 | + - vmtests: fix common collect scripts to not exit failure. |
501 | + - vmtest: handle collect disk unpack failure |
502 | + - vmtests: dont use multiple subclasses in uefi 4k tests |
503 | + - vmtests: disable snapd/seeding to avoid boot hang |
504 | + - jenkins-runner: fix when using --filter only |
505 | + |
506 | + -- Ryan Harper <ryan.harper@canonical.com> Thu, 06 Dec 2018 12:11:01 -0600 |
507 | + |
508 | curtin (18.1-59-g0f993084-0ubuntu1) cosmic; urgency=medium |
509 | |
510 | * New upstream snapshot. |
511 | diff --git a/doc/topics/apt_source.rst b/doc/topics/apt_source.rst |
512 | index 9795361..f996c53 100644 |
513 | --- a/doc/topics/apt_source.rst |
514 | +++ b/doc/topics/apt_source.rst |
515 | @@ -165,3 +165,60 @@ Dependencies |
516 | ~~~~~~~~~~~~ |
517 | Cloud-init might need to resolve dependencies and install packages in the ephemeral environment to run curtin. |
518 | Therefore it is recommended to not only provide an apt configuration to curtin for the target, but also one to the install environment via cloud-init. |
519 | + |
520 | + |
521 | +apt preserve_sources_list setting |
522 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
523 | +cloud-init and curtin treat the ``preserve_sources_list`` setting slightly differently, and thus this setting deserves its own section. |
524 | + |
525 | +Interpretation / Meaning |
526 | +------------------------ |
527 | +curtin reads ``preserve_sources_list`` to indicate whether or not it should update the target systems' ``/etc/apt/sources.list``. This includes replacing the mirrors used (apt/primary...). |
528 | + |
529 | +cloud-init reads ``preserve_sources_list`` to indicate whether or not it should *render* ``/etc/apt/sources.list`` from its built-in template. |
530 | + |
531 | +defaults |
532 | +-------- |
533 | +Just for reference, the ``preserve_sources_list`` defaults in curtin and cloud-init are: |
534 | + |
535 | + * curtin: **true** |
536 | + By default curtin will not modify ``/etc/apt/sources.list`` in the installed OS. It is assumed that this file is intentionally as it is. |
537 | + * cloud-init: **false** |
538 | + * cloud-init in ephemeral environment: **false** |
539 | + * cloud-init system installed by curtin: **true** |
540 | + (curtin writes this to a file ``/etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg`` in the target). It does this because we have already written the sources.list that is desired in the installer. We do not want cloud-init to overwrite it when it boots. |
541 | + |
542 | +preserve_sources_list in MAAS |
543 | +----------------------------- |
544 | +Curtin and cloud-init use the same ``apt`` configuration language. |
545 | +MAAS provides apt config in three different scenarios. |
546 | + |
547 | + 1. To cloud-init in ephemeral environment (rescue, install or commissioning) |
548 | + Here MAAS **should not send a value**. If it wants to be explicit it should send ``preserve_sources_list: false``. |
549 | + |
550 | + 2. To curtin in curtin config |
551 | + MAAS **should send ``preserve_sources_list: false``**. curtin will correctly read and update mirrors in official Ubuntu images, so setting this to 'false' is correct. In some cases for custom images, the user might want to be able to have their /etc/apt/sources.list left untouched entirely. In such cases they may want to override this value. |
552 | + |
553 | + 3. To cloud-init via curtin config in debconf_selections. |
554 | + MAAS should **not send a value**. Curtin will handle telling cloud-init to not update /etc/apt/sources.list. MAAS does not need to do this. |
555 | + |
556 | + 4. To installed system via vendor-data or user-data. |
557 | + MAAS should **not send a value**. MAAS does not currently send a value. The user could send one in user-data, but then if they did presumably they did that for a reason. |
558 | + |
559 | +Legacy format |
560 | +------------- |
561 | + |
562 | +Versions of cloud-init in 14.04 and older only support: |
563 | + |
564 | +.. code-block:: yaml |
565 | + |
566 | + apt_preserve_sources_list: VALUE |
567 | + |
568 | +Versions of cloud-init present 16.04+ read the "new" style apt configuration, but support the old style configuration also. The new style configuration is: |
569 | + |
570 | +.. code-block:: yaml |
571 | + |
572 | + apt: |
573 | + preserve_sources_list: VALUE |
574 | + |
575 | +**Note**: If versions of cloud-init that support the new style config receive conflicting values in old style and new style, cloud-init will raise exception and exit failure. It simplly doesn't know what behavior is desired. |
576 | diff --git a/doc/topics/storage.rst b/doc/topics/storage.rst |
577 | index b28964b..f9a9fe8 100644 |
578 | --- a/doc/topics/storage.rst |
579 | +++ b/doc/topics/storage.rst |
580 | @@ -175,7 +175,15 @@ not need to be preserved. |
581 | If the ``name`` key is present, curtin will create a udev rule that makes a |
582 | symbolic link to the disk with the given name value. This makes it easy to find |
583 | disks on an installed system. The links are created in |
584 | -``/dev/disk/by-dname/<name>``. |
585 | +``/dev/disk/by-dname/<name>``. The udev rules will utilize two types of disk |
586 | +metadata to construct the link. For disks with ``serial`` and/or ``wwn`` values |
587 | +these will be used to ensure the name persists even if the contents of the disk |
588 | +change. For legacy purposes, curtin also emits a rule utilizing metadata on |
589 | +the disk contents, typically a partition UUID value, this also preserves these |
590 | +links for disks which lack persistent attributes such as a ``serial`` or |
591 | +``wwn``, typically found on virtualized environments where such values are left |
592 | +unset. |
593 | + |
594 | A link to each partition on the disk will also be created at |
595 | ``/dev/disk/by-dname/<name>-part<number>``, so if ``name: maindisk`` is set, |
596 | the disk will be at ``/dev/disk/by-dname/maindisk`` and the first partition on |
597 | @@ -237,6 +245,14 @@ After the partition is added to the disk's partition table, curtin can run a |
598 | wipe command on the partition. The wipe command values are the sames as for |
599 | disks. |
600 | |
601 | +.. note:: |
602 | + |
603 | + Curtin will automatically wipe 1MB at the starting location of the partition |
604 | + prior to creating the partition to ensure that other block layers or devices |
605 | + do not enable themselves and prevent accessing the partition. Wipe |
606 | + and other destructive operations only occur if the ``preserve`` value |
607 | + is not set to ``True``. |
608 | + |
609 | **flag**: *logical, extended, boot, bios_grub, swap, lvm, raid, home, prep* |
610 | |
611 | If the ``flag`` key is present, curtin will set the specified flag on the |
612 | @@ -268,6 +284,17 @@ filesystem or be mounted anywhere on the system. |
613 | If the preserve flag is set to true, curtin will verify that the partition |
614 | exists and will not modify the partition. |
615 | |
616 | +**name**: *<name>* |
617 | + |
618 | +If the ``name`` key is present, curtin will create a udev rule that makes a |
619 | +symbolic link to the partition with the given name value. The links are created |
620 | +in ``/dev/disk/by-dname/<name>``. |
621 | + |
622 | +For partitions, the udev rule created relies upon disk contents, in this case |
623 | +the partition entry UUID. This will remain in effect unless the underlying disk |
624 | +on which the partition resides has the partition table modified or wiped. |
625 | + |
626 | + |
627 | **Config Example**:: |
628 | |
629 | - id: disk0-part1 |
630 | @@ -276,6 +303,7 @@ exists and will not modify the partition. |
631 | size: 8GB |
632 | device: disk0 |
633 | flag: boot |
634 | + name: boot_partition |
635 | |
636 | .. _format: |
637 | |
638 | @@ -496,6 +524,12 @@ scheme for Logical Volumes follows the pattern |
639 | with ``name`` *lv1* on a ``lvm_volgroup`` named *vg1* would have the path |
640 | ``/dev/disk/by-dname/vg1-lv1``. |
641 | |
642 | +.. note:: |
643 | + |
644 | + dname values for contructed devices (such as lvm) only remain persistent |
645 | + as long as the device metadata does not change. If users modify the device |
646 | + such that device metadata is changed then the udev rule may no longer apply. |
647 | + |
648 | **volgroup**: *<volgroup id>* |
649 | |
650 | The ``volgroup`` key specifies the ``id`` of the Volume Group in which to |
651 | @@ -592,7 +626,9 @@ The ``name`` key specifies the name of the md device. |
652 | .. note:: |
653 | |
654 | Curtin creates a udev rule to create a link to the md device in |
655 | - ``/dev/disk/by-dname/<name>`` using the specified name. |
656 | + ``/dev/disk/by-dname/<name>`` using the specified name. The dname |
657 | + symbolic link is only persistent as long as the raid metadata is |
658 | + not modifed or destroyed. |
659 | |
660 | **raidlevel**: *0, 1, 5, 6, 10* |
661 | |
662 | @@ -669,6 +705,13 @@ reads/writes from the cache. None effectively disables bcache. |
663 | If the ``name`` key is present, curtin will create a link to the device at |
664 | ``/dev/disk/by-dname/<name>``. |
665 | |
666 | +.. note:: |
667 | + |
668 | + dname values for contructed devices (such as bcache) only remain persistent |
669 | + as long as the device metadata does not change. If users modify the device |
670 | + such that device metadata is changed then the udev rule may no longer apply. |
671 | + |
672 | + |
673 | **Config Example**:: |
674 | |
675 | - id: bcache0 |
676 | diff --git a/examples/tests/basic.yaml b/examples/tests/basic.yaml |
677 | index 089e776..71730c0 100644 |
678 | --- a/examples/tests/basic.yaml |
679 | +++ b/examples/tests/basic.yaml |
680 | @@ -26,6 +26,7 @@ storage: |
681 | number: 3 |
682 | size: 1GB |
683 | device: sda |
684 | + name: swap |
685 | - id: sda1_root |
686 | type: format |
687 | fstype: ext4 |
688 | @@ -83,6 +84,14 @@ storage: |
689 | device: pnum_disk |
690 | - id: pnum_disk_p2 |
691 | type: partition |
692 | + number: 2 |
693 | + size: 8MB |
694 | + device: pnum_disk |
695 | + flag: prep |
696 | + wipe: zero |
697 | + name: prep |
698 | + - id: pnum_disk_p3 |
699 | + type: partition |
700 | number: 10 |
701 | size: 1GB |
702 | device: pnum_disk |
703 | diff --git a/examples/tests/basic_scsi.yaml b/examples/tests/basic_scsi.yaml |
704 | index 5f60459..aa62137 100644 |
705 | --- a/examples/tests/basic_scsi.yaml |
706 | +++ b/examples/tests/basic_scsi.yaml |
707 | @@ -20,14 +20,25 @@ storage: |
708 | number: 2 |
709 | size: 1GB |
710 | device: sda |
711 | + - id: sda3 |
712 | + type: partition |
713 | + number: 3 |
714 | + size: 1GB |
715 | + device: sda |
716 | + name: swap |
717 | - id: sda1_root |
718 | type: format |
719 | fstype: ext4 |
720 | volume: sda1 |
721 | + label: 'cloudimg-rootfs' |
722 | - id: sda2_home |
723 | type: format |
724 | fstype: ext4 |
725 | volume: sda2 |
726 | + - id: sda3_swap |
727 | + type: format |
728 | + fstype: swap |
729 | + volume: sda3 |
730 | - id: sda1_mount |
731 | type: mount |
732 | path: / |
733 | @@ -41,6 +52,10 @@ storage: |
734 | wwn: '0x080258d13ea95ae5' |
735 | name: sparedisk |
736 | wipe: superblock |
737 | + - id: sparedisk_fat_fmt_id |
738 | + type: format |
739 | + fstype: fat32 |
740 | + volume: sparedisk_id |
741 | - id: btrfs_disk_id |
742 | type: disk |
743 | wwn: '0x22dc58dc023c7008' |
744 | @@ -68,6 +83,18 @@ storage: |
745 | device: pnum_disk |
746 | - id: pnum_disk_p2 |
747 | type: partition |
748 | + number: 2 |
749 | + size: 8MB |
750 | + device: pnum_disk |
751 | + flag: prep |
752 | + wipe: zero |
753 | + name: prep |
754 | + - id: pnum_disk_p3 |
755 | + type: partition |
756 | number: 10 |
757 | size: 1GB |
758 | device: pnum_disk |
759 | + - id: swap_mnt |
760 | + type: mount |
761 | + path: "none" |
762 | + device: sda3_swap |
763 | diff --git a/examples/tests/multipath.yaml b/examples/tests/multipath.yaml |
764 | index d4b928c..8447d55 100644 |
765 | --- a/examples/tests/multipath.yaml |
766 | +++ b/examples/tests/multipath.yaml |
767 | @@ -36,3 +36,4 @@ storage: |
768 | type: mount |
769 | path: /home |
770 | device: sda2_home |
771 | + options: 'defaults,nofail' |
772 | diff --git a/examples/tests/nvme.yaml b/examples/tests/nvme.yaml |
773 | index b2a1276..4cf7735 100644 |
774 | --- a/examples/tests/nvme.yaml |
775 | +++ b/examples/tests/nvme.yaml |
776 | @@ -44,7 +44,7 @@ storage: |
777 | device: main_disk_home |
778 | - id: nvme_disk |
779 | type: disk |
780 | - path: /dev/nvme0n1 |
781 | + serial: nvme-a |
782 | name: nvme_disk |
783 | wipe: superblock |
784 | ptable: gpt |
785 | @@ -63,7 +63,7 @@ storage: |
786 | device: nvme_disk |
787 | - id: nvme_disk2 |
788 | type: disk |
789 | - path: /dev/nvme1n1 |
790 | + serial: nvme-b |
791 | wipe: superblock |
792 | ptable: msdos |
793 | name: second_nvme |
794 | diff --git a/examples/tests/nvme_bcache.yaml b/examples/tests/nvme_bcache.yaml |
795 | index 2ee0ad3..4fefd94 100644 |
796 | --- a/examples/tests/nvme_bcache.yaml |
797 | +++ b/examples/tests/nvme_bcache.yaml |
798 | @@ -19,7 +19,7 @@ storage: |
799 | model: INTEL SSDPEDME400G4 |
800 | name: nvme0n1 |
801 | ptable: gpt |
802 | - path: /dev/nvme0n1 |
803 | + serial: nvme-a |
804 | type: disk |
805 | wipe: superblock |
806 | - device: sda |
807 | diff --git a/examples/tests/simple-storage.yaml b/examples/tests/simple-storage.yaml |
808 | new file mode 100644 |
809 | index 0000000..644c5aa |
810 | --- /dev/null |
811 | +++ b/examples/tests/simple-storage.yaml |
812 | @@ -0,0 +1,48 @@ |
813 | +# MAAS will send storage config to dd and windows to help pick boot device |
814 | +# this test forces curtin down a block-meta simple path along with storage cfg |
815 | +partitioning_commands: |
816 | + builtin: [curtin, block-meta, simple, --force-mode] |
817 | +showtrace: true |
818 | +storage: |
819 | + version: 1 |
820 | + config: |
821 | + - id: sda |
822 | + type: disk |
823 | + wipe: superblock |
824 | + ptable: msdos |
825 | + model: QEMU HARDDISK |
826 | + serial: disk-a |
827 | + grub_device: true |
828 | + - id: sdb |
829 | + type: disk |
830 | + wipe: superblock |
831 | + ptable: msdos |
832 | + model: QEMU HARDDISK |
833 | + serial: disk-b |
834 | + wipe: superblock |
835 | + - id: sdc |
836 | + type: disk |
837 | + wipe: superblock |
838 | + ptable: msdos |
839 | + model: QEMU HARDDISK |
840 | + serial: disk-c |
841 | + wipe: superblock |
842 | +# This partition config is here to "dirty" the disk |
843 | + - id: sda-part1 |
844 | + type: partition |
845 | + device: sda |
846 | + name: sda-part1 |
847 | + number: 1 |
848 | + size: 3G |
849 | + uuid: ecc1ec63-e8d2-4719-8cee-dd7f4e2b390e |
850 | + wipe: superblock |
851 | + - id: sda-part1_format |
852 | + type: format |
853 | + fstype: ext4 |
854 | + label: root |
855 | + uuid: f793b242-e812-44df-91c0-c245a55ffd59 |
856 | + volume: sda-part1 |
857 | + - id: sda-part1_mount |
858 | + type: mount |
859 | + path: / |
860 | + device: sda-part1_format |
861 | diff --git a/helpers/common b/helpers/common |
862 | index f9217b7..34f0870 100644 |
863 | --- a/helpers/common |
864 | +++ b/helpers/common |
865 | @@ -537,7 +537,44 @@ get_carryover_params() { |
866 | done |
867 | echo "${c# }" |
868 | ) |
869 | - _RET="${carry_lead:+${carry_lead} }${carry_extra}" |
870 | + [ -n "${carry_lead}" -a -n "${carry_extra}" ] && |
871 | + carry_lead="${carry_lead} " |
872 | + _RET="${carry_lead}${carry_extra}" |
873 | +} |
874 | + |
875 | +shell_config_update() { |
876 | + # shell_config_update(file, name, value) |
877 | + # update variable 'name' setting value to 'val' in shell syntax 'file'. |
878 | + # if 'name' is not present, then append declaration. |
879 | + local file="$1" name="$2" val="$3" |
880 | + if ! [ -f "$file" ] || ! grep -q "^$name=" "$file"; then |
881 | + debug 2 "appending to $file shell $name=\"$val\"" |
882 | + echo "$name=\"$val\"" >> "$file" |
883 | + return |
884 | + fi |
885 | + local cand="" del="" |
886 | + for cand in "|" "," "/"; do |
887 | + [ "${val#*${del}}" = "${val}" ] && del="$cand" && break |
888 | + done |
889 | + [ -n "$del" ] || { |
890 | + error "Couldn't find a sed delimiter for '$val'"; |
891 | + return 1; |
892 | + } |
893 | + |
894 | + sed -i -e "s${del}^$name=.*${del}$name=\"$val\"${del}" "$file" || |
895 | + { error "Failed editing '$file' to set $name=$val"; return 1; } |
896 | + debug 2 "updated $file to set $name=\"$val\"" |
897 | + return 0 |
898 | +} |
899 | + |
900 | +apply_grub_cmdline_linux_default() { |
901 | + local mp="$1" newargs="$2" edg="${3:-etc/default/grub}" |
902 | + local gcld="GRUB_CMDLINE_LINUX_DEFAULT" |
903 | + debug 1 "setting $gcld to '$newargs' in $edg" |
904 | + shell_config_update "$mp/$edg" "$gcld" "$newargs" || { |
905 | + error "Failed to set '$gcld=$newargs' in $edg" |
906 | + return 1 |
907 | + } |
908 | } |
909 | |
910 | install_grub() { |
911 | @@ -709,6 +746,8 @@ install_grub() { |
912 | esac |
913 | |
914 | local grub_d="etc/default/grub.d" |
915 | + # ubuntu writes to /etc/default/grub.d/50-curtin-settings.cfg |
916 | + # to avoid tripping prompts on upgrade LP: #564853 |
917 | local mygrub_cfg="$grub_d/50-curtin-settings.cfg" |
918 | case $os_family in |
919 | redhat) |
920 | @@ -736,9 +775,9 @@ install_grub() { |
921 | # always append rd.auto=1 for centos |
922 | case $os_family in |
923 | redhat) |
924 | - newargs="$newargs rd.auto=1";; |
925 | + newargs="${newargs:+${newargs} }rd.auto=1";; |
926 | esac |
927 | - debug 1 "carryover command line params: $newargs" |
928 | + debug 1 "carryover command line params '$newargs'" |
929 | |
930 | case $os_family in |
931 | debian) |
932 | @@ -746,9 +785,15 @@ install_grub() { |
933 | { error "Failed to write '$mygrub_cfg'"; return 1; } |
934 | ;; |
935 | esac |
936 | + |
937 | + if [ "${REPLACE_GRUB_LINUX_DEFAULT:-1}" != "0" ]; then |
938 | + apply_grub_cmdline_linux_default "$mp" "$newargs" || { |
939 | + error "Failed to apply grub cmdline." |
940 | + return 1 |
941 | + } |
942 | + fi |
943 | + |
944 | { |
945 | - [ "${REPLACE_GRUB_LINUX_DEFAULT:-1}" = "0" ] || |
946 | - echo "GRUB_CMDLINE_LINUX_DEFAULT=\"$newargs\"" |
947 | echo "# Curtin disable grub os prober that might find other OS installs." |
948 | echo "GRUB_DISABLE_OS_PROBER=true" |
949 | echo "GRUB_TERMINAL=console" |
950 | diff --git a/tests/unittests/helpers.py b/tests/unittests/helpers.py |
951 | index 58e068b..1268880 100644 |
952 | --- a/tests/unittests/helpers.py |
953 | +++ b/tests/unittests/helpers.py |
954 | @@ -5,7 +5,9 @@ import imp |
955 | import importlib |
956 | import mock |
957 | import os |
958 | +import random |
959 | import shutil |
960 | +import string |
961 | import tempfile |
962 | from unittest import TestCase |
963 | |
964 | @@ -67,6 +69,11 @@ class CiTestCase(TestCase): |
965 | return os.path.normpath( |
966 | os.path.abspath(os.path.sep.join((_dir, path)))) |
967 | |
968 | + def random_string(self, length=8): |
969 | + """ return a random lowercase string with default length of 8""" |
970 | + return ''.join( |
971 | + random.choice(string.ascii_lowercase) for _ in range(length)) |
972 | + |
973 | |
974 | def dir2dict(startdir, prefix=None): |
975 | flist = {} |
976 | diff --git a/tests/unittests/test_apt_custom_sources_list.py b/tests/unittests/test_apt_custom_sources_list.py |
977 | index a427ae9..d77c750 100644 |
978 | --- a/tests/unittests/test_apt_custom_sources_list.py |
979 | +++ b/tests/unittests/test_apt_custom_sources_list.py |
980 | @@ -94,36 +94,30 @@ class TestAptSourceConfigSourceList(CiTestCase): |
981 | self.new_root = self.tmp_dir() |
982 | # self.patchUtils(self.new_root) |
983 | |
984 | - @staticmethod |
985 | - def _apt_source_list(cfg, expected): |
986 | + def _apt_source_list(self, cfg, expected): |
987 | "_apt_source_list - Test rendering from template (generic)" |
988 | |
989 | arch = util.get_architecture() |
990 | # would fail inside the unittest context |
991 | - with mock.patch.object(util, 'get_architecture', |
992 | - return_value=arch) as mockga: |
993 | - with mock.patch.object(util, 'write_file') as mockwrite: |
994 | - # keep it side effect free and avoid permission errors |
995 | - with mock.patch.object(os, 'rename'): |
996 | - # make test independent to executing system |
997 | - with mock.patch.object(util, 'load_file', |
998 | - return_value=MOCKED_APT_SRC_LIST): |
999 | - with mock.patch.object(distro, 'lsb_release', |
1000 | - return_value={'codename': |
1001 | - 'fakerel'}): |
1002 | - apt_config.handle_apt(cfg, TARGET) |
1003 | - |
1004 | - mockga.assert_called_with("/") |
1005 | - |
1006 | - cloudfile = '/etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg' |
1007 | - cloudconf = yaml.dump({'apt_preserve_sources_list': True}, indent=1) |
1008 | + bpath = "curtin.commands.apt_config." |
1009 | + upath = bpath + "util." |
1010 | + self.add_patch(upath + "get_architecture", "mockga", return_value=arch) |
1011 | + self.add_patch(upath + "write_file", "mockwrite") |
1012 | + self.add_patch(bpath + "os.rename", "mockrename") |
1013 | + self.add_patch(upath + "load_file", "mockload_file", |
1014 | + return_value=MOCKED_APT_SRC_LIST) |
1015 | + self.add_patch(bpath + "distro.lsb_release", "mock_lsb_release", |
1016 | + return_value={'codename': 'fakerel'}) |
1017 | + self.add_patch(bpath + "apply_preserve_sources_list", |
1018 | + "mock_apply_preserve_sources_list") |
1019 | + |
1020 | + apt_config.handle_apt(cfg, TARGET) |
1021 | + |
1022 | + self.mockga.assert_called_with(TARGET) |
1023 | + self.mock_apply_preserve_sources_list.assert_called_with(TARGET) |
1024 | calls = [call(paths.target_path(TARGET, '/etc/apt/sources.list'), |
1025 | - expected, |
1026 | - mode=0o644), |
1027 | - call(paths.target_path(TARGET, cloudfile), |
1028 | - cloudconf, |
1029 | - mode=0o644)] |
1030 | - mockwrite.assert_has_calls(calls) |
1031 | + expected, mode=0o644)] |
1032 | + self.mockwrite.assert_has_calls(calls) |
1033 | |
1034 | def test_apt_source_list(self): |
1035 | """test_apt_source_list - Test with neither custom sources nor parms""" |
1036 | @@ -156,10 +150,6 @@ class TestAptSourceConfigSourceList(CiTestCase): |
1037 | self.assertEqual( |
1038 | EXPECTED_CONVERTED_CONTENT, |
1039 | util.load_file(paths.target_path(target, "/etc/apt/sources.list"))) |
1040 | - cloudfile = paths.target_path( |
1041 | - target, '/etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg') |
1042 | - self.assertEqual({'apt_preserve_sources_list': True}, |
1043 | - yaml.load(util.load_file(cloudfile))) |
1044 | |
1045 | @mock.patch("curtin.distro.lsb_release") |
1046 | @mock.patch("curtin.util.get_architecture", return_value="amd64") |
1047 | @@ -224,4 +214,46 @@ class TestAptSourceConfigSourceList(CiTestCase): |
1048 | apt_config.handle_apt(cfg, target) |
1049 | self.assertEqual(expected, util.load_file(easl)) |
1050 | |
1051 | + |
1052 | +class TestApplyPreserveSourcesList(CiTestCase): |
1053 | + """Test apply_preserve_sources_list.""" |
1054 | + |
1055 | + cloudfile = "/etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg" |
1056 | + |
1057 | + def setUp(self): |
1058 | + super(TestApplyPreserveSourcesList, self).setUp() |
1059 | + self.tmp = self.tmp_dir() |
1060 | + self.tmp_cfg = self.tmp_path(self.cloudfile, self.tmp) |
1061 | + |
1062 | + @mock.patch("curtin.commands.apt_config.distro.get_package_version") |
1063 | + def test_old_cloudinit_version(self, m_get_pkg_ver): |
1064 | + """Test installed old cloud-init version.""" |
1065 | + m_get_pkg_ver.return_value = distro.parse_dpkg_version('0.7.7-0') |
1066 | + apt_config.apply_preserve_sources_list(self.tmp) |
1067 | + m_get_pkg_ver.assert_has_calls( |
1068 | + [mock.call('cloud-init', target=self.tmp)]) |
1069 | + self.assertEqual( |
1070 | + yaml.load(util.load_file(self.tmp_cfg)), |
1071 | + {'apt_preserve_sources_list': True}) |
1072 | + |
1073 | + @mock.patch("curtin.commands.apt_config.distro.get_package_version") |
1074 | + def test_no_cloudinit(self, m_get_pkg_ver): |
1075 | + """Test where cloud-init is not installed.""" |
1076 | + m_get_pkg_ver.return_value = None |
1077 | + apt_config.apply_preserve_sources_list(self.tmp) |
1078 | + m_get_pkg_ver.assert_has_calls( |
1079 | + [mock.call('cloud-init', target=self.tmp)]) |
1080 | + self.assertFalse(os.path.exists(self.tmp_cfg)) |
1081 | + |
1082 | + @mock.patch("curtin.commands.apt_config.distro.get_package_version") |
1083 | + def test_new_cloudinit_version(self, m_get_pkg_ver): |
1084 | + """Test cloud-init > 1.0 with new apt format.""" |
1085 | + m_get_pkg_ver.return_value = distro.parse_dpkg_version('17.1-0ubuntu1') |
1086 | + apt_config.apply_preserve_sources_list(self.tmp) |
1087 | + m_get_pkg_ver.assert_has_calls( |
1088 | + [mock.call('cloud-init', target=self.tmp)]) |
1089 | + self.assertEqual( |
1090 | + yaml.load(util.load_file(self.tmp_cfg)), |
1091 | + {'apt': {'preserve_sources_list': True}}) |
1092 | + |
1093 | # vi: ts=4 expandtab syntax=python |
1094 | diff --git a/tests/unittests/test_commands_block_meta.py b/tests/unittests/test_commands_block_meta.py |
1095 | index e70d6ed..45b9906 100644 |
1096 | --- a/tests/unittests/test_commands_block_meta.py |
1097 | +++ b/tests/unittests/test_commands_block_meta.py |
1098 | @@ -85,8 +85,9 @@ class TestBlockMetaSimple(CiTestCase): |
1099 | self.mock_block_get_root_device.assert_called_with([devname], |
1100 | paths=paths) |
1101 | |
1102 | + @patch('curtin.commands.block_meta.meta_clear') |
1103 | @patch('curtin.commands.block_meta.write_image_to_disk') |
1104 | - def test_meta_simple_calls_write_img(self, mock_write_image): |
1105 | + def test_meta_simple_calls_write_img(self, mock_write_image, mock_clear): |
1106 | devname = "fakedisk1p1" |
1107 | devnode = "/dev/" + devname |
1108 | sources = { |
1109 | @@ -104,9 +105,9 @@ class TestBlockMetaSimple(CiTestCase): |
1110 | mock_write_image.return_value = devname |
1111 | |
1112 | args = Namespace(target=self.target, devices=None, mode=None, |
1113 | - boot_fstype=None, fstype=None) |
1114 | + boot_fstype=None, fstype=None, force_mode=False) |
1115 | |
1116 | - block_meta.meta_simple(args) |
1117 | + block_meta.block_meta(args) |
1118 | |
1119 | mock_write_image.assert_called_with(sources.get('unittest'), devname) |
1120 | self.mock_subp.assert_has_calls( |
1121 | diff --git a/tests/unittests/test_commands_extract.py b/tests/unittests/test_commands_extract.py |
1122 | index e604d7f..cc117bb 100644 |
1123 | --- a/tests/unittests/test_commands_extract.py |
1124 | +++ b/tests/unittests/test_commands_extract.py |
1125 | @@ -27,8 +27,9 @@ class TestExtractRootFsImageUrl(CiTestCase): |
1126 | tmpd = self.tmp_dir() |
1127 | target = self.tmp_path("target_d", tmpd) |
1128 | startdir = os.getcwd() |
1129 | + fname = "my.img" |
1130 | try: |
1131 | - fname = "my.img" |
1132 | + os.chdir(tmpd) |
1133 | util.write_file(fname, fname + " data\n") |
1134 | extract_root_fsimage_url("file://" + fname, target) |
1135 | finally: |
1136 | diff --git a/tests/unittests/test_make_dname.py b/tests/unittests/test_make_dname.py |
1137 | index 2b92a88..76c7b28 100644 |
1138 | --- a/tests/unittests/test_make_dname.py |
1139 | +++ b/tests/unittests/test_make_dname.py |
1140 | @@ -13,10 +13,16 @@ class TestMakeDname(CiTestCase): |
1141 | state = {'scratch': '/tmp/null'} |
1142 | rules_d = '/tmp/null/rules.d' |
1143 | rule_file = '/tmp/null/rules.d/{}.rules' |
1144 | + disk_serial = 'abcdefg' |
1145 | + disk_wwn = '0x1234567890' |
1146 | storage_config = { |
1147 | - 'disk1': {'type': 'disk', 'id': 'disk1', 'name': 'main_disk'}, |
1148 | + 'disk1': {'type': 'disk', 'id': 'disk1', 'name': 'main_disk', |
1149 | + 'serial': disk_serial}, |
1150 | + 'disk_noid': {'type': 'disk', 'id': 'disk_noid', 'name': 'main_disk'}, |
1151 | 'disk1p1': {'type': 'partition', 'id': 'disk1p1', 'device': 'disk1'}, |
1152 | - 'disk2': {'type': 'disk', 'id': 'disk2', |
1153 | + 'disk1p2': {'type': 'partition', 'id': 'disk1p2', 'device': 'disk1', |
1154 | + 'name': 'custom-partname'}, |
1155 | + 'disk2': {'type': 'disk', 'id': 'disk2', 'wwn': disk_wwn, |
1156 | 'name': 'in_valid/name!@#$% &*(+disk'}, |
1157 | 'disk2p1': {'type': 'partition', 'id': 'disk2p1', 'device': 'disk2'}, |
1158 | 'md_id': {'type': 'raid', 'id': 'md_id', 'name': 'mdadm_name'}, |
1159 | @@ -27,7 +33,8 @@ class TestMakeDname(CiTestCase): |
1160 | 'lpart2_id': {'type': 'lvm_partition', 'id': 'lpart2_id', |
1161 | 'name': 'lvm part/2', 'volgroup': 'lvol_id'}, |
1162 | 'bcache1_id': {'type': 'bcache', 'id': 'bcache1_id', |
1163 | - 'name': 'my-cached-data'} |
1164 | + 'name': 'my-cached-data'}, |
1165 | + 'iscsi1': {'type': 'disk', 'id': 'iscsi1', 'name': 'iscsi_disk1'} |
1166 | } |
1167 | bcache_super_show = { |
1168 | 'sb.version': '1 [backing device]', |
1169 | @@ -57,20 +64,37 @@ class TestMakeDname(CiTestCase): |
1170 | rule.append('SYMLINK+="disk/by-dname/{}"\n'.format(target)) |
1171 | return ', '.join(rule) |
1172 | |
1173 | + def _content(self, rules=[]): |
1174 | + return "\n".join(['# Written by curtin'] + rules) |
1175 | + |
1176 | + @mock.patch('curtin.commands.block_meta.udevadm_info') |
1177 | @mock.patch('curtin.commands.block_meta.LOG') |
1178 | @mock.patch('curtin.commands.block_meta.get_path_to_storage_volume') |
1179 | @mock.patch('curtin.commands.block_meta.util') |
1180 | - def test_make_dname_disk(self, mock_util, mock_get_path, mock_log): |
1181 | + def test_make_dname_disk(self, mock_util, mock_get_path, mock_log, |
1182 | + mock_udev): |
1183 | disk_ptuuid = str(uuid.uuid1()) |
1184 | mock_util.subp.side_effect = self._make_mock_subp_blkid( |
1185 | disk_ptuuid, self.disk_blkid) |
1186 | mock_util.load_command_environment.return_value = self.state |
1187 | - rule_identifiers = [ |
1188 | - ('DEVTYPE', 'disk'), |
1189 | - ('ID_PART_TABLE_UUID', disk_ptuuid) |
1190 | - ] |
1191 | + |
1192 | + rule_identifiers = [('ID_PART_TABLE_UUID', disk_ptuuid)] |
1193 | + id_rule_identifiers = [('ID_SERIAL', self.disk_serial)] |
1194 | + wwn_rule_identifiers = [('ID_WWN_WITH_EXTENSION', self.disk_wwn)] |
1195 | + |
1196 | + def _drule(devtype, match): |
1197 | + return [('DEVTYPE', devtype)] + [m for m in match] |
1198 | + |
1199 | + def drule(match): |
1200 | + return _drule('disk', match) |
1201 | + |
1202 | + def prule(match): |
1203 | + return _drule('partition', match) |
1204 | |
1205 | # simple run |
1206 | + mock_udev.side_effect = ( |
1207 | + [{'DEVTYPE': 'disk', 'ID_SERIAL': self.disk_serial}, |
1208 | + {'DEVTYPE': 'disk', 'ID_WWN_WITH_EXTENSION': self.disk_wwn}]) |
1209 | res_dname = 'main_disk' |
1210 | block_meta.make_dname('disk1', self.storage_config) |
1211 | mock_util.ensure_dir.assert_called_with(self.rules_d) |
1212 | @@ -78,7 +102,13 @@ class TestMakeDname(CiTestCase): |
1213 | self.assertFalse(mock_log.warning.called) |
1214 | mock_util.write_file.assert_called_with( |
1215 | self.rule_file.format(res_dname), |
1216 | - self._formatted_rule(rule_identifiers, res_dname)) |
1217 | + self._content( |
1218 | + [self._formatted_rule(drule(rule_identifiers), |
1219 | + res_dname), |
1220 | + self._formatted_rule(drule(id_rule_identifiers), |
1221 | + res_dname), |
1222 | + self._formatted_rule(prule(id_rule_identifiers), |
1223 | + "%s-part%%n" % res_dname)])) |
1224 | |
1225 | # run invalid dname |
1226 | res_dname = 'in_valid-name----------disk' |
1227 | @@ -86,12 +116,39 @@ class TestMakeDname(CiTestCase): |
1228 | self.assertTrue(mock_log.warning.called) |
1229 | mock_util.write_file.assert_called_with( |
1230 | self.rule_file.format(res_dname), |
1231 | - self._formatted_rule(rule_identifiers, res_dname)) |
1232 | + self._content( |
1233 | + [self._formatted_rule(drule(rule_identifiers), |
1234 | + res_dname), |
1235 | + self._formatted_rule(drule(wwn_rule_identifiers), |
1236 | + res_dname), |
1237 | + self._formatted_rule(prule(wwn_rule_identifiers), |
1238 | + "%s-part%%n" % res_dname)])) |
1239 | |
1240 | + # iscsi disk with no config, but info returns serial and wwn |
1241 | + mock_udev.side_effect = ( |
1242 | + [{'DEVTYPE': 'disk', 'ID_SERIAL': self.disk_serial, |
1243 | + 'DEVTYPE': 'disk', 'ID_WWN_WITH_EXTENSION': self.disk_wwn}]) |
1244 | + res_dname = 'iscsi_disk1' |
1245 | + block_meta.make_dname('iscsi1', self.storage_config) |
1246 | + mock_util.ensure_dir.assert_called_with(self.rules_d) |
1247 | + self.assertTrue(mock_log.debug.called) |
1248 | + both_rules = (id_rule_identifiers + wwn_rule_identifiers) |
1249 | + mock_util.write_file.assert_called_with( |
1250 | + self.rule_file.format(res_dname), |
1251 | + self._content( |
1252 | + [self._formatted_rule(drule(rule_identifiers), res_dname), |
1253 | + self._formatted_rule(drule(both_rules), res_dname), |
1254 | + self._formatted_rule(prule(both_rules), |
1255 | + "%s-part%%n" % res_dname)])) |
1256 | + |
1257 | + @mock.patch('curtin.commands.block_meta.udevadm_info') |
1258 | @mock.patch('curtin.commands.block_meta.LOG') |
1259 | @mock.patch('curtin.commands.block_meta.get_path_to_storage_volume') |
1260 | @mock.patch('curtin.commands.block_meta.util') |
1261 | - def test_make_dname_failures(self, mock_util, mock_get_path, mock_log): |
1262 | + def test_make_dname_failures(self, mock_util, mock_get_path, mock_log, |
1263 | + mock_udev): |
1264 | + mock_udev.side_effect = ([{'DEVTYPE': 'disk'}, {'DEVTYPE': 'disk'}]) |
1265 | + |
1266 | mock_util.subp.side_effect = self._make_mock_subp_blkid( |
1267 | '', self.trusty_blkid) |
1268 | mock_util.load_command_environment.return_value = self.state |
1269 | @@ -99,10 +156,14 @@ class TestMakeDname(CiTestCase): |
1270 | warning_msg = "Can't find a uuid for volume: {}. Skipping dname." |
1271 | |
1272 | # disk with no PT_UUID |
1273 | - block_meta.make_dname('disk1', self.storage_config) |
1274 | - mock_log.warning.assert_called_with(warning_msg.format('disk1')) |
1275 | - self.assertFalse(mock_util.write_file.called) |
1276 | + disk = 'disk_noid' |
1277 | + with self.assertRaises(RuntimeError): |
1278 | + block_meta.make_dname(disk, self.storage_config) |
1279 | + mock_log.warning.assert_called_with(warning_msg.format(disk)) |
1280 | + self.assertFalse(mock_util.write_file.called) |
1281 | |
1282 | + mock_util.subp.side_effect = self._make_mock_subp_blkid( |
1283 | + '', self.trusty_blkid) |
1284 | # partition with no PART_UUID |
1285 | block_meta.make_dname('disk1p1', self.storage_config) |
1286 | mock_log.warning.assert_called_with(warning_msg.format('disk1p1')) |
1287 | @@ -123,22 +184,15 @@ class TestMakeDname(CiTestCase): |
1288 | ] |
1289 | |
1290 | # simple run |
1291 | - res_dname = 'main_disk-part1' |
1292 | - block_meta.make_dname('disk1p1', self.storage_config) |
1293 | + res_dname = 'custom-partname' |
1294 | + block_meta.make_dname('disk1p2', self.storage_config) |
1295 | mock_util.ensure_dir.assert_called_with(self.rules_d) |
1296 | self.assertTrue(mock_log.debug.called) |
1297 | self.assertFalse(mock_log.warning.called) |
1298 | mock_util.write_file.assert_called_with( |
1299 | self.rule_file.format(res_dname), |
1300 | - self._formatted_rule(rule_identifiers, res_dname)) |
1301 | - |
1302 | - # run invalid dname |
1303 | - res_dname = 'in_valid-name----------disk-part1' |
1304 | - block_meta.make_dname('disk2p1', self.storage_config) |
1305 | - self.assertTrue(mock_log.warning.called) |
1306 | - mock_util.write_file.assert_called_with( |
1307 | - self.rule_file.format(res_dname), |
1308 | - self._formatted_rule(rule_identifiers, res_dname)) |
1309 | + self._content( |
1310 | + [self._formatted_rule(rule_identifiers, res_dname)])) |
1311 | |
1312 | @mock.patch('curtin.commands.block_meta.mdadm') |
1313 | @mock.patch('curtin.commands.block_meta.LOG') |
1314 | @@ -158,7 +212,8 @@ class TestMakeDname(CiTestCase): |
1315 | self.assertFalse(mock_log.warning.called) |
1316 | mock_util.write_file.assert_called_with( |
1317 | self.rule_file.format(res_dname), |
1318 | - self._formatted_rule(rule_identifiers, res_dname)) |
1319 | + self._content( |
1320 | + [self._formatted_rule(rule_identifiers, res_dname)])) |
1321 | |
1322 | # invalid name |
1323 | res_dname = 'mdadm-name' |
1324 | @@ -166,7 +221,8 @@ class TestMakeDname(CiTestCase): |
1325 | self.assertTrue(mock_log.warning.called) |
1326 | mock_util.write_file.assert_called_with( |
1327 | self.rule_file.format(res_dname), |
1328 | - self._formatted_rule(rule_identifiers, res_dname)) |
1329 | + self._content( |
1330 | + [self._formatted_rule(rule_identifiers, res_dname)])) |
1331 | |
1332 | @mock.patch('curtin.commands.block_meta.LOG') |
1333 | @mock.patch('curtin.commands.block_meta.get_path_to_storage_volume') |
1334 | @@ -183,7 +239,8 @@ class TestMakeDname(CiTestCase): |
1335 | self.assertFalse(mock_log.warning.called) |
1336 | mock_util.write_file.assert_called_with( |
1337 | self.rule_file.format(res_dname), |
1338 | - self._formatted_rule(rule_identifiers, res_dname)) |
1339 | + self._content( |
1340 | + [self._formatted_rule(rule_identifiers, res_dname)])) |
1341 | |
1342 | # with invalid name |
1343 | res_dname = 'vg1-lvm-part-2' |
1344 | @@ -192,7 +249,8 @@ class TestMakeDname(CiTestCase): |
1345 | self.assertTrue(mock_log.warning.called) |
1346 | mock_util.write_file.assert_called_with( |
1347 | self.rule_file.format(res_dname), |
1348 | - self._formatted_rule(rule_identifiers, res_dname)) |
1349 | + self._content( |
1350 | + [self._formatted_rule(rule_identifiers, res_dname)])) |
1351 | |
1352 | @mock.patch('curtin.commands.block_meta.LOG') |
1353 | @mock.patch('curtin.commands.block_meta.bcache') |
1354 | @@ -213,7 +271,8 @@ class TestMakeDname(CiTestCase): |
1355 | self.assertFalse(mock_log.warning.called) |
1356 | mock_util.write_file.assert_called_with( |
1357 | self.rule_file.format(res_dname), |
1358 | - self._formatted_rule(rule_identifiers, res_dname)) |
1359 | + self._content( |
1360 | + [self._formatted_rule(rule_identifiers, res_dname)])) |
1361 | |
1362 | def test_sanitize_dname(self): |
1363 | unsanitized_to_sanitized = [ |
1364 | @@ -226,4 +285,87 @@ class TestMakeDname(CiTestCase): |
1365 | for (unsanitized, sanitized) in unsanitized_to_sanitized: |
1366 | self.assertEqual(block_meta.sanitize_dname(unsanitized), sanitized) |
1367 | |
1368 | + |
1369 | +class TestMakeDnameById(CiTestCase): |
1370 | + |
1371 | + @mock.patch('curtin.commands.block_meta.udevadm_info') |
1372 | + def test_bad_path(self, m_udev): |
1373 | + """test dname_byid raises ValueError on invalid path.""" |
1374 | + mypath = None |
1375 | + with self.assertRaises(ValueError): |
1376 | + block_meta.make_dname_byid(mypath) |
1377 | + |
1378 | + @mock.patch('curtin.commands.block_meta.udevadm_info') |
1379 | + def test_non_disk(self, m_udev): |
1380 | + """test dname_byid raises ValueError on DEVTYPE != 'disk'""" |
1381 | + mypath = "/dev/" + self.random_string() |
1382 | + m_udev.return_value = {'DEVTYPE': 'not_a_disk'} |
1383 | + with self.assertRaises(ValueError): |
1384 | + block_meta.make_dname_byid(mypath) |
1385 | + |
1386 | + @mock.patch('curtin.commands.block_meta.udevadm_info') |
1387 | + def test_disk_with_no_id_wwn(self, m_udev): |
1388 | + """test dname_byid raises RuntimeError on device without ID or WWN.""" |
1389 | + mypath = "/dev/" + self.random_string() |
1390 | + m_udev.return_value = {'DEVTYPE': 'disk'} |
1391 | + with self.assertRaises(RuntimeError): |
1392 | + block_meta.make_dname_byid(mypath) |
1393 | + |
1394 | + @mock.patch('curtin.commands.block_meta.udevadm_info') |
1395 | + def test_udevinfo_not_called_if_info_provided(self, m_udev): |
1396 | + """dname_byid does not invoke udevadm_info if using info dict""" |
1397 | + myserial = self.random_string() |
1398 | + self.assertEqual( |
1399 | + [['ENV{ID_SERIAL}=="%s"' % myserial]], |
1400 | + block_meta.make_dname_byid( |
1401 | + self.random_string(), |
1402 | + info={'DEVTYPE': 'disk', 'ID_SERIAL': myserial})) |
1403 | + self.assertEqual(0, m_udev.call_count) |
1404 | + |
1405 | + @mock.patch('curtin.commands.block_meta.udevadm_info') |
1406 | + def test_udevinfo_called_if_info_not_provided(self, m_udev): |
1407 | + """dname_byid should call udevadm_info if no data given.""" |
1408 | + myserial = self.random_string() |
1409 | + mypath = "/dev/" + self.random_string() |
1410 | + m_udev.return_value = { |
1411 | + 'DEVTYPE': 'disk', 'ID_SERIAL': myserial, 'DEVNAME': mypath} |
1412 | + self.assertEqual( |
1413 | + [['ENV{ID_SERIAL}=="%s"' % myserial]], |
1414 | + block_meta.make_dname_byid(mypath)) |
1415 | + self.assertEqual( |
1416 | + [mock.call(path=mypath)], m_udev.call_args_list) |
1417 | + |
1418 | + def test_disk_with_only_serial(self): |
1419 | + """test dname_byid returns rules for ID_SERIAL""" |
1420 | + mypath = "/dev/" + self.random_string() |
1421 | + myserial = self.random_string() |
1422 | + info = {'DEVTYPE': 'disk', 'DEVNAME': mypath, 'ID_SERIAL': myserial} |
1423 | + self.assertEqual( |
1424 | + [['ENV{ID_SERIAL}=="%s"' % myserial]], |
1425 | + block_meta.make_dname_byid(mypath, info=info)) |
1426 | + |
1427 | + def test_disk_with_only_wwn(self): |
1428 | + """test dname_byid returns rules for ID_WWN_WITH_EXTENSION""" |
1429 | + mypath = "/dev/" + self.random_string() |
1430 | + mywwn = self.random_string() |
1431 | + info = {'DEVTYPE': 'disk', 'DEVNAME': mypath, |
1432 | + 'ID_WWN_WITH_EXTENSION': mywwn} |
1433 | + self.assertEqual( |
1434 | + [['ENV{ID_WWN_WITH_EXTENSION}=="%s"' % mywwn]], |
1435 | + block_meta.make_dname_byid(mypath, info=info)) |
1436 | + |
1437 | + def test_disk_with_both_id_wwn(self): |
1438 | + """test dname_byid returns rules with both ID_SERIAL and ID_WWN""" |
1439 | + mypath = "/dev/" + self.random_string() |
1440 | + myserial = self.random_string() |
1441 | + mywwn = self.random_string() |
1442 | + info = {'DEVTYPE': 'disk', 'ID_SERIAL': myserial, |
1443 | + 'ID_WWN_WITH_EXTENSION': mywwn, |
1444 | + 'DEVNAME': mypath} |
1445 | + self.assertEqual( |
1446 | + [['ENV{ID_SERIAL}=="%s"' % myserial, |
1447 | + 'ENV{ID_WWN_WITH_EXTENSION}=="%s"' % mywwn]], |
1448 | + block_meta.make_dname_byid(mypath, info=info)) |
1449 | + |
1450 | + |
1451 | # vi: ts=4 expandtab syntax=python |
1452 | diff --git a/tests/unittests/test_swap.py b/tests/unittests/test_swap.py |
1453 | index e12d12e..fd6c527 100644 |
1454 | --- a/tests/unittests/test_swap.py |
1455 | +++ b/tests/unittests/test_swap.py |
1456 | @@ -1,39 +1,53 @@ |
1457 | import mock |
1458 | |
1459 | from curtin import swap |
1460 | +from curtin import util |
1461 | from .helpers import CiTestCase |
1462 | |
1463 | |
1464 | class TestSwap(CiTestCase): |
1465 | - @mock.patch('curtin.swap.resource') |
1466 | - @mock.patch('curtin.swap.util') |
1467 | - def test_is_swap_device_read_offsets(self, mock_util, mock_resource): |
1468 | - """swap.is_swap_device() checks offsets based on system pagesize""" |
1469 | - path = '/mydev/dummydisk' |
1470 | + def _valid_swap_contents(self): |
1471 | + """Yields (pagesize, content) of things that should be considered |
1472 | + valid swap.""" |
1473 | # 4k and 64k page size |
1474 | for pagesize in [4096, 65536]: |
1475 | - magic_offset = pagesize - 10 |
1476 | - mock_resource.getpagesize.return_value = pagesize |
1477 | - swap.is_swap_device(path) |
1478 | - mock_util.load_file.assert_called_with(path, read_len=10, |
1479 | - offset=magic_offset, |
1480 | - decode=False) |
1481 | + for magic in [b'SWAPSPACE2', b'SWAP-SPACE']: |
1482 | + # yield content of 2 pages to trigger/avoid fence-post errors |
1483 | + yield (pagesize, |
1484 | + ((pagesize - len(magic)) * b'\0' + |
1485 | + magic + pagesize * b'\0')) |
1486 | |
1487 | - @mock.patch('curtin.swap.resource') |
1488 | - @mock.patch('curtin.swap.util') |
1489 | - def test_identify_swap_false(self, mock_util, mock_resource): |
1490 | - """swap.is_swap_device() returns false on non swap magic""" |
1491 | - mock_util.load_file.return_value = ( |
1492 | - b'\x00\x00c\x05\x00\x00\x11\x00\x19\x00') |
1493 | - is_swap = swap.is_swap_device('ignored') |
1494 | - self.assertFalse(is_swap) |
1495 | + @mock.patch('curtin.swap.resource.getpagesize') |
1496 | + def test_is_swap_device_read_offsets(self, mock_getpagesize): |
1497 | + """swap.is_swap_device() correctly identifies swap content.""" |
1498 | + tmpd = self.tmp_dir() |
1499 | + for num, (pagesize, content) in enumerate(self._valid_swap_contents()): |
1500 | + path = self.tmp_path("swap-file-%02d" % num, tmpd) |
1501 | + util.write_file(path, content, omode="wb") |
1502 | + mock_getpagesize.return_value = pagesize |
1503 | + self.assertTrue(swap.is_swap_device(path)) |
1504 | |
1505 | - @mock.patch('curtin.swap.resource') |
1506 | - @mock.patch('curtin.swap.util') |
1507 | - def test_identify_swap_true(self, mock_util, mock_resource): |
1508 | - """swap.is_swap_device() returns true on swap magic strings""" |
1509 | - path = '/mydev/dummydisk' |
1510 | - for magic in [b'SWAPSPACE2', b'SWAP-SPACE']: |
1511 | - mock_util.load_file.return_value = magic |
1512 | - is_swap = swap.is_swap_device(path) |
1513 | - self.assertTrue(is_swap) |
1514 | + @mock.patch('curtin.swap.resource.getpagesize', return_value=4096) |
1515 | + def test_identify_swap_false_if_tiny(self, mock_getpagesize): |
1516 | + """small files do not trip up is_swap_device().""" |
1517 | + path = self.tmp_path("tiny") |
1518 | + util.write_file(path, b'tinystuff', omode='wb') |
1519 | + self.assertFalse(swap.is_swap_device(path)) |
1520 | + |
1521 | + @mock.patch('curtin.swap.resource.getpagesize', return_value=4096) |
1522 | + def test_identify_zeros_are_swap(self, mock_getpagesize): |
1523 | + """swap.is_swap_device() returns false on all zeros""" |
1524 | + pagesize = mock_getpagesize() |
1525 | + path = self.tmp_path("notswap0") |
1526 | + util.write_file(path, pagesize * 2 * b'\0', omode="wb") |
1527 | + self.assertFalse(swap.is_swap_device(path)) |
1528 | + |
1529 | + @mock.patch('curtin.swap.resource.getpagesize', return_value=65536) |
1530 | + def test_identify_swap_false(self, mock_getpagesize): |
1531 | + """swap.is_swap_device() returns false on non swap content""" |
1532 | + pagesize = mock_getpagesize() |
1533 | + path = self.tmp_path("notswap1") |
1534 | + # this is just arbitrary content that is not swap content. |
1535 | + blob = b'\x00\x00c\x05\x00\x00\x11\x19' |
1536 | + util.write_file(path, int(pagesize * 2 / len(blob)) * blob, omode="wb") |
1537 | + self.assertFalse(swap.is_swap_device(path)) |
1538 | diff --git a/tests/unittests/test_udev.py b/tests/unittests/test_udev.py |
1539 | new file mode 100644 |
1540 | index 0000000..0a070d5 |
1541 | --- /dev/null |
1542 | +++ b/tests/unittests/test_udev.py |
1543 | @@ -0,0 +1,68 @@ |
1544 | +# This file is part of curtin. See LICENSE file for copyright and license info. |
1545 | + |
1546 | +import mock |
1547 | + |
1548 | +from curtin import udev |
1549 | +from curtin import util |
1550 | +from .helpers import CiTestCase |
1551 | + |
1552 | + |
1553 | +UDEVADM_INFO_QUERY = """\ |
1554 | +DEVLINKS=/dev/disk/by-id/nvme-eui.0025388b710116a1 |
1555 | +DEVNAME=/dev/nvme0n1 |
1556 | +DEVPATH=/devices/pci0000:00/0000:00:1c.4/0000:05:00.0/nvme/nvme0/nvme0n1 |
1557 | +DEVTYPE=disk |
1558 | +ID_PART_TABLE_TYPE=gpt |
1559 | +ID_PART_TABLE_UUID=ea0b9ddc-a114-4e01-b257-750d86e3a944 |
1560 | +ID_SERIAL=SAMSUNG MZVLB1T0HALR-000L7_S3TPNY0JB00151 |
1561 | +ID_SERIAL_SHORT=S3TPNY0JB00151 |
1562 | +MAJOR=259 |
1563 | +MINOR=0 |
1564 | +SUBSYSTEM=block |
1565 | +TAGS=:systemd: |
1566 | +USEC_INITIALIZED=2026691 |
1567 | +""" |
1568 | + |
1569 | +INFO_DICT = { |
1570 | + 'DEVLINKS': ['/dev/disk/by-id/nvme-eui.0025388b710116a1'], |
1571 | + 'DEVNAME': '/dev/nvme0n1', |
1572 | + 'DEVPATH': |
1573 | + '/devices/pci0000:00/0000:00:1c.4/0000:05:00.0/nvme/nvme0/nvme0n1', |
1574 | + 'DEVTYPE': 'disk', |
1575 | + 'ID_PART_TABLE_TYPE': 'gpt', |
1576 | + 'ID_PART_TABLE_UUID': 'ea0b9ddc-a114-4e01-b257-750d86e3a944', |
1577 | + 'ID_SERIAL': 'SAMSUNG MZVLB1T0HALR-000L7_S3TPNY0JB00151', |
1578 | + 'ID_SERIAL_SHORT': 'S3TPNY0JB00151', |
1579 | + 'MAJOR': '259', |
1580 | + 'MINOR': '0', |
1581 | + 'SUBSYSTEM': 'block', |
1582 | + 'TAGS': ':systemd:', |
1583 | + 'USEC_INITIALIZED': '2026691' |
1584 | +} |
1585 | + |
1586 | + |
1587 | +class TestUdevInfo(CiTestCase): |
1588 | + |
1589 | + @mock.patch('curtin.util.subp') |
1590 | + def test_udevadm_info(self, m_subp): |
1591 | + """ udevadm_info returns dictionary for specified device """ |
1592 | + mypath = '/dev/nvme0n1' |
1593 | + m_subp.return_value = (UDEVADM_INFO_QUERY, "") |
1594 | + info = udev.udevadm_info(mypath) |
1595 | + m_subp.assert_called_with( |
1596 | + ['udevadm', 'info', '--query=property', mypath], capture=True) |
1597 | + self.assertEqual(sorted(INFO_DICT), sorted(info)) |
1598 | + |
1599 | + def test_udevadm_info_no_path(self): |
1600 | + """ udevadm_info raises ValueError for invalid path value""" |
1601 | + mypath = None |
1602 | + with self.assertRaises(ValueError): |
1603 | + udev.udevadm_info(mypath) |
1604 | + |
1605 | + @mock.patch('curtin.util.subp') |
1606 | + def test_udevadm_info_path_not_exists(self, m_subp): |
1607 | + """ udevadm_info raises ProcessExecutionError for invalid path value""" |
1608 | + mypath = self.random_string() |
1609 | + m_subp.side_effect = util.ProcessExecutionError() |
1610 | + with self.assertRaises(util.ProcessExecutionError): |
1611 | + udev.udevadm_info(mypath) |
1612 | diff --git a/tests/vmtests/__init__.py b/tests/vmtests/__init__.py |
1613 | index 3823e39..bc4a87b 100644 |
1614 | --- a/tests/vmtests/__init__.py |
1615 | +++ b/tests/vmtests/__init__.py |
1616 | @@ -21,6 +21,7 @@ from curtin.block import iscsi |
1617 | |
1618 | from .report_webhook_logger import CaptureReporting |
1619 | from curtin.commands.install import INSTALL_PASS_MSG |
1620 | +from curtin.commands.block_meta import sanitize_dname |
1621 | |
1622 | from .image_sync import query as imagesync_query |
1623 | from .image_sync import mirror as imagesync_mirror |
1624 | @@ -344,12 +345,17 @@ class TempDir(object): |
1625 | |
1626 | def collect_output(self): |
1627 | logger.debug('extracting output disk') |
1628 | - subprocess.check_call(['tar', '-C', self.collect, '-xf', |
1629 | - self.output_disk], |
1630 | - stdout=DEVNULL, stderr=subprocess.STDOUT) |
1631 | - # make sure collect output dir is usable by non-root |
1632 | - subprocess.check_call(['chmod', '-R', 'u+rwX', self.collect], |
1633 | - stdout=DEVNULL, stderr=subprocess.STDOUT) |
1634 | + try: |
1635 | + subprocess.check_call(['tar', '-C', self.collect, '-xf', |
1636 | + self.output_disk], |
1637 | + stdout=DEVNULL, stderr=subprocess.STDOUT) |
1638 | + except subprocess.CalledProcessError as e: |
1639 | + logger.error('Failed unpacking collect output: %s', e) |
1640 | + finally: |
1641 | + logger.debug('Fixing collect output dir permissions.') |
1642 | + # make sure collect output dir is usable by non-root |
1643 | + subprocess.check_call(['chmod', '-R', 'u+rwX', self.collect], |
1644 | + stdout=DEVNULL, stderr=subprocess.STDOUT) |
1645 | |
1646 | |
1647 | def skip_if_flag(flag): |
1648 | @@ -515,11 +521,17 @@ DEFAULT_COLLECT_SCRIPTS = { |
1649 | ls -al /dev/disk/by-dname/ | cat >ls_al_bydname |
1650 | ls -al /dev/disk/by-id/ | cat >ls_al_byid |
1651 | ls -al /dev/disk/by-uuid/ | cat >ls_al_byuuid |
1652 | + ls -al /dev/disk/by-partuuid/ | cat >ls_al_bypartuuid |
1653 | blkid -o export | cat >blkid.out |
1654 | find /boot | cat > find_boot.out |
1655 | - [ -e /sys/firmware/efi ] && { |
1656 | + if [ -e /sys/firmware/efi ]; then |
1657 | efibootmgr -v | cat >efibootmgr.out; |
1658 | - } |
1659 | + fi |
1660 | + [ ! -d /etc/default/grub.d ] || |
1661 | + cp -a /etc/default/grub.d etc_default_grub_d |
1662 | + [ ! -f /etc/default/grub ] || cp /etc/default/grub etc_default_grub |
1663 | + |
1664 | + exit 0 |
1665 | """)], |
1666 | 'centos': [textwrap.dedent(""" |
1667 | # XXX: command | cat >output is required for Centos under SELinux |
1668 | @@ -530,6 +542,8 @@ DEFAULT_COLLECT_SCRIPTS = { |
1669 | rpm -q --queryformat '%{VERSION}\n' cloud-init |tee rpm_ci_version |
1670 | rpm -E '%rhel' > rpm_dist_version_major |
1671 | cp -a /etc/centos-release . |
1672 | + |
1673 | + exit 0 |
1674 | """)], |
1675 | 'ubuntu': [textwrap.dedent(""" |
1676 | cd OUTPUT_COLLECT_D |
1677 | @@ -543,6 +557,8 @@ DEFAULT_COLLECT_SCRIPTS = { |
1678 | out=$(apt-config shell v Acquire::HTTP::Proxy) |
1679 | eval "$out" |
1680 | echo "$v" > apt-proxy |
1681 | + |
1682 | + exit 0 |
1683 | """)] |
1684 | } |
1685 | |
1686 | @@ -890,6 +906,11 @@ class VMBaseClass(TestCase): |
1687 | "--append=ro", |
1688 | ]) |
1689 | |
1690 | + # Avoid LP: #1797218 and make vms boot faster |
1691 | + cmd.extend(['--append=%s' % service for service in |
1692 | + ["systemd.mask=snapd.seeded.service", |
1693 | + "systemd.mask=snapd.service"]]) |
1694 | + |
1695 | # getting resolvconf configured is only fixed in bionic |
1696 | # the iscsi_auto handles resolvconf setup via call to |
1697 | # configure_networking in initramfs |
1698 | @@ -935,13 +956,21 @@ class VMBaseClass(TestCase): |
1699 | |
1700 | # build disk arguments |
1701 | disks = [] |
1702 | - sc = cls.load_conf_file() |
1703 | - storage_config = yaml.load(sc).get('storage', {}).get('config', {}) |
1704 | + storage_config = cls.get_class_storage_config() |
1705 | cls.disk_wwns = ["wwn=%s" % x.get('wwn') for x in storage_config |
1706 | if 'wwn' in x] |
1707 | - cls.disk_serials = ["serial=%s" % x.get('serial') |
1708 | - for x in storage_config if 'serial' in x] |
1709 | + cls.disk_serials = [] |
1710 | + cls.nvme_serials = [] |
1711 | + for x in storage_config: |
1712 | + if 'serial' in x: |
1713 | + serial = x.get('serial') |
1714 | + if serial.startswith('nvme'): |
1715 | + cls.nvme_serials.append("serial=%s" % serial) |
1716 | + else: |
1717 | + cls.disk_serials.append("serial=%s" % serial) |
1718 | |
1719 | + logger.info("disk_serials: %s", cls.disk_serials) |
1720 | + logger.info("nvme_serials: %s", cls.nvme_serials) |
1721 | target_disk = "{}:{}:{}:{}:".format(cls.td.target_disk, |
1722 | "", |
1723 | cls.disk_driver, |
1724 | @@ -973,11 +1002,13 @@ class VMBaseClass(TestCase): |
1725 | disks.extend(['--disk', extra_disk]) |
1726 | |
1727 | # build nvme disk args if needed |
1728 | + logger.info('nvme disks: %s', cls.nvme_disks) |
1729 | for (disk_no, disk_sz) in enumerate(cls.nvme_disks): |
1730 | dpath = os.path.join(cls.td.disks, 'nvme_disk_%d.img' % disk_no) |
1731 | + nvme_serial = cls.nvme_serials[disk_no] |
1732 | nvme_disk = '{}:{}:nvme:{}:{}'.format(dpath, disk_sz, |
1733 | cls.disk_block_size, |
1734 | - "serial=nvme-%d" % disk_no) |
1735 | + "%s" % nvme_serial) |
1736 | disks.extend(['--disk', nvme_disk]) |
1737 | |
1738 | # build iscsi disk args if needed |
1739 | @@ -1206,6 +1237,8 @@ class VMBaseClass(TestCase): |
1740 | dpath = os.path.join(cls.td.disks, 'nvme_disk_%d.img' % disk_no) |
1741 | disk = '--disk={},driver={},format={},{}'.format( |
1742 | dpath, disk_driver, TARGET_IMAGE_FORMAT, bsize_args) |
1743 | + if len(cls.nvme_serials): |
1744 | + disk += ",%s" % cls.nvme_serials[disk_no] |
1745 | nvme_disks.extend([disk]) |
1746 | |
1747 | # unlike NVMe disks, we do not want to configure the iSCSI disks |
1748 | @@ -1525,8 +1558,41 @@ class VMBaseClass(TestCase): |
1749 | for diskname, part in self.disk_to_check: |
1750 | if part is not 0: |
1751 | link = diskname + "-part" + str(part) |
1752 | - self.assertIn(link, contents) |
1753 | - self.assertIn(diskname, contents) |
1754 | + self.assertIn(link, contents.splitlines()) |
1755 | + self.assertIn(diskname, contents.splitlines()) |
1756 | + |
1757 | + @skip_if_flag('expected_failure') |
1758 | + def test_dname_rules(self, disk_to_check=None): |
1759 | + if self.target_distro != "ubuntu": |
1760 | + raise SkipTest("dname not present in non-ubuntu releases") |
1761 | + |
1762 | + if not disk_to_check: |
1763 | + disk_to_check = self.disk_to_check |
1764 | + if disk_to_check is None: |
1765 | + logger.debug('test_dname_rules: no disks to check') |
1766 | + return |
1767 | + logger.debug('test_dname_rules: checking disks: %s', disk_to_check) |
1768 | + self.output_files_exist(["udev_rules.d"]) |
1769 | + |
1770 | + cfg = yaml.load(self.load_collect_file("root/curtin-install-cfg.yaml")) |
1771 | + stgcfg = cfg.get("storage", {}).get("config", []) |
1772 | + disks = [ent for ent in stgcfg if (ent.get('type') == 'disk' and |
1773 | + 'name' in ent)] |
1774 | + key_to_udev = { |
1775 | + 'serial': 'ID_SERIAL', |
1776 | + 'wwn': 'ID_WWN_WITH_EXTENSION', |
1777 | + } |
1778 | + for disk in disks: |
1779 | + dname_file = "%s.rules" % sanitize_dname(disk.get('name')) |
1780 | + contents = self.load_collect_file("udev_rules.d/%s" % dname_file) |
1781 | + for key, key_name in key_to_udev.items(): |
1782 | + value = disk.get(key) |
1783 | + if value: |
1784 | + # serials may include spaces, udev replaces them with # _ |
1785 | + if ' ' in value: |
1786 | + value = value.replace(' ', '_') |
1787 | + self.assertIn(key_name, contents) |
1788 | + self.assertIn(value, contents) |
1789 | |
1790 | @skip_if_flag('expected_failure') |
1791 | def test_reporting_data(self): |
1792 | @@ -1570,6 +1636,29 @@ class VMBaseClass(TestCase): |
1793 | kpackage = self.get_kernel_package() |
1794 | self.assertIn(kpackage, self.debian_packages) |
1795 | |
1796 | + @skip_if_flag('expected_failure') |
1797 | + def test_clear_holders_ran(self): |
1798 | + """ Test curtin install runs block-meta/clear-holders. """ |
1799 | + if not self.has_storage_config(): |
1800 | + raise SkipTest("This test does not use storage config.") |
1801 | + |
1802 | + install_logfile = 'root/curtin-install.log' |
1803 | + self.output_files_exist([install_logfile]) |
1804 | + install_log = self.load_collect_file(install_logfile) |
1805 | + |
1806 | + # validate block-meta called clear-holders at least once |
1807 | + # We match both 'start' and 'finish' strings, so for each |
1808 | + # call we'll have 2 matches. |
1809 | + clear_holders_re = 'cmd-install/.*cmd-block-meta/clear-holders' |
1810 | + events = re.findall(clear_holders_re, install_log) |
1811 | + print('Matched clear-holder events:\n%s' % events) |
1812 | + self.assertGreaterEqual(len(events), 2) |
1813 | + |
1814 | + # dirty_disks mode runs an early block-meta command which |
1815 | + # also runs clear-holders |
1816 | + if self.dirty_disks is True: |
1817 | + self.assertGreaterEqual(len(events), 4) |
1818 | + |
1819 | def run(self, result): |
1820 | super(VMBaseClass, self).run(result) |
1821 | self.record_result(result) |
1822 | @@ -1609,14 +1698,25 @@ class VMBaseClass(TestCase): |
1823 | self._debian_packages = pkgs |
1824 | return self._debian_packages |
1825 | |
1826 | + @classmethod |
1827 | + def get_class_storage_config(cls): |
1828 | + sc = cls.load_conf_file() |
1829 | + return yaml.load(sc).get('storage', {}).get('config', {}) |
1830 | + |
1831 | + def get_storage_config(self): |
1832 | + cfg = yaml.load(self.load_collect_file("root/curtin-install-cfg.yaml")) |
1833 | + return cfg.get("storage", {}).get("config", []) |
1834 | + |
1835 | + def has_storage_config(self): |
1836 | + '''check if test used storage config''' |
1837 | + return len(self.get_storage_config()) > 0 |
1838 | + |
1839 | @skip_if_flag('expected_failure') |
1840 | def test_swaps_used(self): |
1841 | - cfg = yaml.load(self.load_collect_file("root/curtin-install-cfg.yaml")) |
1842 | - stgcfg = cfg.get("storage", {}).get("config", []) |
1843 | - if len(stgcfg) == 0: |
1844 | - logger.debug("This test does not use storage config.") |
1845 | - return |
1846 | + if not self.has_storage_config(): |
1847 | + raise SkipTest("This test does not use storage config.") |
1848 | |
1849 | + stgcfg = self.get_storage_config() |
1850 | swap_ids = [d["id"] for d in stgcfg if d.get("fstype") == "swap"] |
1851 | swap_mounts = [d for d in stgcfg if d.get("device") in swap_ids] |
1852 | self.assertEqual(len(swap_ids), len(swap_mounts), |
1853 | @@ -1715,6 +1815,9 @@ class PsuedoVMBaseClass(VMBaseClass): |
1854 | def test_dname(self, disk_to_check=None): |
1855 | pass |
1856 | |
1857 | + def test_dname_rules(self, disk_to_check=None): |
1858 | + pass |
1859 | + |
1860 | def test_interfacesd_eth0_removed(self): |
1861 | pass |
1862 | |
1863 | @@ -1772,6 +1875,7 @@ def check_install_log(install_log, nrchars=200): |
1864 | install_fail = "({})".format("|".join([ |
1865 | 'Installation failed', |
1866 | 'ImportError: No module named.*', |
1867 | + 'Out of memory:', |
1868 | 'Unexpected error while running command', |
1869 | 'E: Unable to locate package.*', |
1870 | 'cloud-init.*: Traceback.*'])) |
1871 | diff --git a/tests/vmtests/releases.py b/tests/vmtests/releases.py |
1872 | index 7be8feb..5dfb2d2 100644 |
1873 | --- a/tests/vmtests/releases.py |
1874 | +++ b/tests/vmtests/releases.py |
1875 | @@ -113,6 +113,11 @@ class _CosmicBase(_UbuntuBase): |
1876 | target_release = "cosmic" |
1877 | |
1878 | |
1879 | +class _DiscoBase(_UbuntuBase): |
1880 | + release = "disco" |
1881 | + target_release = "disco" |
1882 | + |
1883 | + |
1884 | class _Releases(object): |
1885 | trusty = _TrustyBase |
1886 | precise = _PreciseBase |
1887 | @@ -128,6 +133,7 @@ class _Releases(object): |
1888 | xenial_edge = _XenialEdge |
1889 | bionic = _BionicBase |
1890 | cosmic = _CosmicBase |
1891 | + disco = _DiscoBase |
1892 | |
1893 | |
1894 | class _CentosReleases(object): |
1895 | diff --git a/tests/vmtests/test_apt_config_cmd.py b/tests/vmtests/test_apt_config_cmd.py |
1896 | index f9b6a09..1296240 100644 |
1897 | --- a/tests/vmtests/test_apt_config_cmd.py |
1898 | +++ b/tests/vmtests/test_apt_config_cmd.py |
1899 | @@ -5,6 +5,7 @@ |
1900 | apt-config standalone command. |
1901 | """ |
1902 | import textwrap |
1903 | +import yaml |
1904 | |
1905 | from . import VMBaseClass |
1906 | from .releases import base_vm_classes as relbase |
1907 | @@ -23,6 +24,8 @@ class TestAptConfigCMD(VMBaseClass): |
1908 | cp /etc/apt/sources.list.d/curtin-dev-ubuntu-test-archive-*.list . |
1909 | cp /etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg . |
1910 | apt-cache policy | grep proposed > proposed-enabled |
1911 | + |
1912 | + exit 0 |
1913 | """)] |
1914 | |
1915 | def test_cmd_proposed_enabled(self): |
1916 | @@ -44,8 +47,10 @@ class TestAptConfigCMD(VMBaseClass): |
1917 | def test_cmd_preserve_source(self): |
1918 | """check if cloud-init was prevented from overwriting""" |
1919 | self.output_files_exist(["curtin-preserve-sources.cfg"]) |
1920 | - self.check_file_regex("curtin-preserve-sources.cfg", |
1921 | - "apt_preserve_sources_list.*true") |
1922 | + # For earlier than xenial 'apt_preserve_sources_list' is expected |
1923 | + self.assertEqual( |
1924 | + {'apt': {'preserve_sources_list': True}}, |
1925 | + yaml.load(self.load_collect_file("curtin-preserve-sources.cfg"))) |
1926 | |
1927 | |
1928 | class XenialTestAptConfigCMDCMD(relbase.xenial, TestAptConfigCMD): |
1929 | @@ -62,4 +67,8 @@ class BionicTestAptConfigCMDCMD(relbase.bionic, TestAptConfigCMD): |
1930 | class CosmicTestAptConfigCMDCMD(relbase.cosmic, TestAptConfigCMD): |
1931 | __test__ = True |
1932 | |
1933 | + |
1934 | +class DiscoTestAptConfigCMDCMD(relbase.disco, TestAptConfigCMD): |
1935 | + __test__ = True |
1936 | + |
1937 | # vi: ts=4 expandtab syntax=python |
1938 | diff --git a/tests/vmtests/test_apt_source.py b/tests/vmtests/test_apt_source.py |
1939 | index bb502b2..2cd7267 100644 |
1940 | --- a/tests/vmtests/test_apt_source.py |
1941 | +++ b/tests/vmtests/test_apt_source.py |
1942 | @@ -4,6 +4,7 @@ |
1943 | Collection of tests for the apt configuration features |
1944 | """ |
1945 | import textwrap |
1946 | +import yaml |
1947 | |
1948 | from . import VMBaseClass |
1949 | from .releases import base_vm_classes as relbase |
1950 | @@ -34,6 +35,8 @@ class TestAptSrcAbs(VMBaseClass): |
1951 | apt-config dump | grep Retries > aptconf |
1952 | cp /etc/apt/sources.list sources.list |
1953 | cp /etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg . |
1954 | + |
1955 | + exit 0 |
1956 | """)] |
1957 | mirror = "http://us.archive.ubuntu.com/ubuntu" |
1958 | secmirror = "http://security.ubuntu.com/ubuntu" |
1959 | @@ -61,8 +64,10 @@ class TestAptSrcAbs(VMBaseClass): |
1960 | def test_preserve_source(self): |
1961 | """test_preserve_source - no clobbering sources.list by cloud-init""" |
1962 | self.output_files_exist(["curtin-preserve-sources.cfg"]) |
1963 | - self.check_file_regex("curtin-preserve-sources.cfg", |
1964 | - "apt_preserve_sources_list.*true") |
1965 | + # For earlier than xenial 'apt_preserve_sources_list' is expected |
1966 | + self.assertEqual( |
1967 | + {'apt': {'preserve_sources_list': True}}, |
1968 | + yaml.load(self.load_collect_file("curtin-preserve-sources.cfg"))) |
1969 | |
1970 | def test_source_files(self): |
1971 | """test_source_files - Check generated .lists for correct content""" |
1972 | diff --git a/tests/vmtests/test_basic.py b/tests/vmtests/test_basic.py |
1973 | index 54e3df8..48f07d6 100644 |
1974 | --- a/tests/vmtests/test_basic.py |
1975 | +++ b/tests/vmtests/test_basic.py |
1976 | @@ -17,9 +17,14 @@ class TestBasicAbs(VMBaseClass): |
1977 | dirty_disks = True |
1978 | conf_file = "examples/tests/basic.yaml" |
1979 | extra_disks = ['128G', '128G', '4G'] |
1980 | - nvme_disks = ['4G'] |
1981 | - disk_to_check = [('main_disk_with_in---valid--dname', 1), |
1982 | - ('main_disk_with_in---valid--dname', 2)] |
1983 | + disk_to_check = [('btrfs_volume', 0), |
1984 | + ('main_disk_with_in---valid--dname', 0), |
1985 | + ('main_disk_with_in---valid--dname', 1), |
1986 | + ('main_disk_with_in---valid--dname', 2), |
1987 | + ('pnum_disk', 0), |
1988 | + ('pnum_disk', 1), |
1989 | + ('pnum_disk', 10), |
1990 | + ('sparedisk', 0)] |
1991 | extra_collect_scripts = [textwrap.dedent(""" |
1992 | cd OUTPUT_COLLECT_D |
1993 | blkid -o export /dev/vda | cat >blkid_output_vda |
1994 | @@ -32,6 +37,13 @@ class TestBasicAbs(VMBaseClass): |
1995 | btrfs inspect-internal dump-super $dev | |
1996 | awk '/^dev_item.fsid/ {print $2}' |
1997 | fi | cat >$f |
1998 | + |
1999 | + # compare via /dev/zero 8MB |
2000 | + cmp --bytes=8388608 /dev/zero /dev/vde2; echo "$?" > cmp_prep.out |
2001 | + # extract partition info |
2002 | + udevadm info --export --query=property /dev/vde2 | cat >udev_info.out |
2003 | + |
2004 | + exit 0 |
2005 | """)] |
2006 | |
2007 | def _kname_to_uuid(self, kname): |
2008 | @@ -108,6 +120,21 @@ class TestBasicAbs(VMBaseClass): |
2009 | # compare them |
2010 | self.assertEqual(kname_uuid, btrfs_uuid) |
2011 | |
2012 | + def _test_partition_is_prep(self, info_file): |
2013 | + udev_info = self.load_collect_file(info_file).rstrip() |
2014 | + entry_type = '' |
2015 | + for line in udev_info.splitlines(): |
2016 | + if line.startswith('ID_PART_ENTRY_TYPE'): |
2017 | + entry_type = line.split("=", 1)[1].replace("'", "") |
2018 | + break |
2019 | + # https://en.wikipedia.org/wiki/GUID_Partition_Table |
2020 | + # GPT PReP boot UUID |
2021 | + self.assertEqual('9e1a2d38-c612-4316-aa26-8b49521e5a8b'.lower(), |
2022 | + entry_type.lower()) |
2023 | + |
2024 | + def _test_partition_is_zero(self, cmp_file): |
2025 | + self.assertEqual(0, int(self.load_collect_file(cmp_file).rstrip())) |
2026 | + |
2027 | # class specific input |
2028 | def test_output_files_exist(self): |
2029 | self.output_files_exist( |
2030 | @@ -118,9 +145,9 @@ class TestBasicAbs(VMBaseClass): |
2031 | self._test_ptable("blkid_output_vda", "dos") |
2032 | |
2033 | def test_partition_numbers(self): |
2034 | - # vde should have partitions 1 and 10 |
2035 | + # vde should have partitions 1 2, and 10 |
2036 | disk = "vde" |
2037 | - expected = [disk + s for s in ["", "1", "10"]] |
2038 | + expected = [disk + s for s in ["", "1", "2", "10"]] |
2039 | self._test_partition_numbers(disk, expected) |
2040 | |
2041 | def test_fstab_entries(self): |
2042 | @@ -157,6 +184,12 @@ class TestBasicAbs(VMBaseClass): |
2043 | print('Source repo version: %s' % source_version) |
2044 | self.assertEqual(source_version, installed_version) |
2045 | |
2046 | + def test_partition_is_prep(self): |
2047 | + self._test_partition_is_prep("udev_info.out") |
2048 | + |
2049 | + def test_partition_is_zero(self): |
2050 | + self._test_partition_is_zero("cmp_prep.out") |
2051 | + |
2052 | |
2053 | class CentosTestBasicAbs(TestBasicAbs): |
2054 | def test_centos_release(self): |
2055 | @@ -213,11 +246,14 @@ class CosmicTestBasic(relbase.cosmic, TestBasicAbs): |
2056 | __test__ = True |
2057 | |
2058 | |
2059 | +class DiscoTestBasic(relbase.disco, TestBasicAbs): |
2060 | + __test__ = True |
2061 | + |
2062 | + |
2063 | class TestBasicScsiAbs(TestBasicAbs): |
2064 | conf_file = "examples/tests/basic_scsi.yaml" |
2065 | disk_driver = 'scsi-hd' |
2066 | extra_disks = ['128G', '128G', '4G'] |
2067 | - nvme_disks = ['4G'] |
2068 | extra_collect_scripts = [textwrap.dedent(""" |
2069 | cd OUTPUT_COLLECT_D |
2070 | blkid -o export /dev/sda | cat >blkid_output_sda |
2071 | @@ -230,15 +266,22 @@ class TestBasicScsiAbs(TestBasicAbs): |
2072 | btrfs inspect-internal dump-super $dev | |
2073 | awk '/^dev_item.fsid/ {print $2}' |
2074 | fi | cat >$f |
2075 | + |
2076 | + # compare via /dev/zero 8MB |
2077 | + cmp --bytes=8388608 /dev/zero /dev/sdd2; echo "$?" > cmp_prep.out |
2078 | + # extract partition info |
2079 | + udevadm info --export --query=property /dev/sdd2 | cat >udev_info.out |
2080 | + |
2081 | + exit 0 |
2082 | """)] |
2083 | |
2084 | def test_ptable(self): |
2085 | self._test_ptable("blkid_output_sda", "dos") |
2086 | |
2087 | def test_partition_numbers(self): |
2088 | - # sdd should have partitions 1 and 10 |
2089 | + # sdd should have partitions 1, 2, and 10 |
2090 | disk = "sdd" |
2091 | - expected = [disk + s for s in ["", "1", "10"]] |
2092 | + expected = [disk + s for s in ["", "1", "2", "10"]] |
2093 | self._test_partition_numbers(disk, expected) |
2094 | |
2095 | def test_fstab_entries(self): |
2096 | @@ -255,6 +298,12 @@ class TestBasicScsiAbs(TestBasicAbs): |
2097 | def test_whole_disk_uuid(self): |
2098 | self._test_whole_disk_uuid("sdc", "btrfs_uuid_sdc") |
2099 | |
2100 | + def test_partition_is_prep(self): |
2101 | + self._test_partition_is_prep("udev_info.out") |
2102 | + |
2103 | + def test_partition_is_zero(self): |
2104 | + self._test_partition_is_zero("cmp_prep.out") |
2105 | + |
2106 | |
2107 | class Centos70XenialTestScsiBasic(centos_relbase.centos70_xenial, |
2108 | TestBasicScsiAbs, CentosTestBasicAbs): |
2109 | @@ -280,4 +329,8 @@ class BionicTestScsiBasic(relbase.bionic, TestBasicScsiAbs): |
2110 | class CosmicTestScsiBasic(relbase.cosmic, TestBasicScsiAbs): |
2111 | __test__ = True |
2112 | |
2113 | + |
2114 | +class DiscoTestScsiBasic(relbase.disco, TestBasicScsiAbs): |
2115 | + __test__ = True |
2116 | + |
2117 | # vi: ts=4 expandtab syntax=python |
2118 | diff --git a/tests/vmtests/test_bcache_basic.py b/tests/vmtests/test_bcache_basic.py |
2119 | index b4191b6..a62fb17 100644 |
2120 | --- a/tests/vmtests/test_bcache_basic.py |
2121 | +++ b/tests/vmtests/test_bcache_basic.py |
2122 | @@ -20,6 +20,8 @@ class TestBcacheBasic(VMBaseClass): |
2123 | bcache-super-show /dev/vda2 > bcache_super_vda2 |
2124 | ls /sys/fs/bcache > bcache_ls |
2125 | cat /sys/block/bcache0/bcache/cache_mode > bcache_cache_mode |
2126 | + |
2127 | + exit 0 |
2128 | """)] |
2129 | |
2130 | def test_bcache_output_files_exist(self): |
2131 | @@ -69,4 +71,8 @@ class BionicBcacheBasic(relbase.bionic, TestBcacheBasic): |
2132 | class CosmicBcacheBasic(relbase.cosmic, TestBcacheBasic): |
2133 | __test__ = True |
2134 | |
2135 | + |
2136 | +class DiscoBcacheBasic(relbase.disco, TestBcacheBasic): |
2137 | + __test__ = True |
2138 | + |
2139 | # vi: ts=4 expandtab syntax=python |
2140 | diff --git a/tests/vmtests/test_bcache_bug1718699.py b/tests/vmtests/test_bcache_bug1718699.py |
2141 | index bc0f1e0..5410dc3 100644 |
2142 | --- a/tests/vmtests/test_bcache_bug1718699.py |
2143 | +++ b/tests/vmtests/test_bcache_bug1718699.py |
2144 | @@ -22,4 +22,8 @@ class BionicTestBcacheBug1718699(relbase.bionic, TestBcacheBug1718699): |
2145 | class CosmicTestBcacheBug1718699(relbase.cosmic, TestBcacheBug1718699): |
2146 | __test__ = True |
2147 | |
2148 | + |
2149 | +class DiscoTestBcacheBug1718699(relbase.disco, TestBcacheBug1718699): |
2150 | + __test__ = True |
2151 | + |
2152 | # vi: ts=4 expandtab syntax=python |
2153 | diff --git a/tests/vmtests/test_fs_battery.py b/tests/vmtests/test_fs_battery.py |
2154 | index defdf1a..347b62a 100644 |
2155 | --- a/tests/vmtests/test_fs_battery.py |
2156 | +++ b/tests/vmtests/test_fs_battery.py |
2157 | @@ -100,6 +100,8 @@ class TestFsBattery(VMBaseClass): |
2158 | echo "$part umount: PASS" || |
2159 | echo "$part umount: FAIL: $out" |
2160 | done >> battery-mount-umount |
2161 | + |
2162 | + exit 0 |
2163 | """)] |
2164 | |
2165 | def get_fs_entries(self): |
2166 | @@ -243,4 +245,7 @@ class CosmicTestFsBattery(relbase.cosmic, TestFsBattery): |
2167 | __test__ = True |
2168 | |
2169 | |
2170 | +class DiscoTestFsBattery(relbase.disco, TestFsBattery): |
2171 | + __test__ = True |
2172 | + |
2173 | # vi: ts=4 expandtab syntax=python |
2174 | diff --git a/tests/vmtests/test_iscsi.py b/tests/vmtests/test_iscsi.py |
2175 | index a800df5..2707d40 100644 |
2176 | --- a/tests/vmtests/test_iscsi.py |
2177 | +++ b/tests/vmtests/test_iscsi.py |
2178 | @@ -23,6 +23,8 @@ class TestBasicIscsiAbs(VMBaseClass): |
2179 | cp -a /etc/iscsi ./etc_iscsi |
2180 | bash -c \ |
2181 | 'for f in /mnt/iscsi*; do cp $f/testfile testfile${f: -1}; done' |
2182 | + |
2183 | + exit 0 |
2184 | """)] |
2185 | |
2186 | def test_fstab_has_netdev_option(self): |
2187 | @@ -76,4 +78,8 @@ class BionicTestIscsiBasic(relbase.bionic, TestBasicIscsiAbs): |
2188 | class CosmicTestIscsiBasic(relbase.cosmic, TestBasicIscsiAbs): |
2189 | __test__ = True |
2190 | |
2191 | + |
2192 | +class DiscoTestIscsiBasic(relbase.disco, TestBasicIscsiAbs): |
2193 | + __test__ = True |
2194 | + |
2195 | # vi: ts=4 expandtab syntax=python |
2196 | diff --git a/tests/vmtests/test_journald_reporter.py b/tests/vmtests/test_journald_reporter.py |
2197 | index c60a862..80af61e 100644 |
2198 | --- a/tests/vmtests/test_journald_reporter.py |
2199 | +++ b/tests/vmtests/test_journald_reporter.py |
2200 | @@ -39,4 +39,8 @@ class BionicTestJournaldReporter(relbase.bionic, TestJournaldReporter): |
2201 | class CosmicTestJournaldReporter(relbase.cosmic, TestJournaldReporter): |
2202 | __test__ = True |
2203 | |
2204 | + |
2205 | +class DiscoTestJournaldReporter(relbase.disco, TestJournaldReporter): |
2206 | + __test__ = True |
2207 | + |
2208 | # vi: ts=4 expandtab syntax=python |
2209 | diff --git a/tests/vmtests/test_lvm.py b/tests/vmtests/test_lvm.py |
2210 | index 37053fe..fdb5314 100644 |
2211 | --- a/tests/vmtests/test_lvm.py |
2212 | +++ b/tests/vmtests/test_lvm.py |
2213 | @@ -17,6 +17,8 @@ class TestLvmAbs(VMBaseClass): |
2214 | cd OUTPUT_COLLECT_D |
2215 | pvdisplay -C --separator = -o vg_name,pv_name --noheadings > pvs |
2216 | lvdisplay -C --separator = -o lv_name,vg_name --noheadings > lvs |
2217 | + |
2218 | + exit 0 |
2219 | """)] |
2220 | fstab_expected = { |
2221 | '/dev/vg1/lv1': '/srv/data', |
2222 | @@ -73,4 +75,7 @@ class CosmicTestLvm(relbase.cosmic, TestLvmAbs): |
2223 | __test__ = True |
2224 | |
2225 | |
2226 | +class DiscoTestLvm(relbase.disco, TestLvmAbs): |
2227 | + __test__ = True |
2228 | + |
2229 | # vi: ts=4 expandtab syntax=python |
2230 | diff --git a/tests/vmtests/test_lvm_iscsi.py b/tests/vmtests/test_lvm_iscsi.py |
2231 | index 091461e..6cdcab2 100644 |
2232 | --- a/tests/vmtests/test_lvm_iscsi.py |
2233 | +++ b/tests/vmtests/test_lvm_iscsi.py |
2234 | @@ -17,13 +17,15 @@ class TestLvmIscsiAbs(TestLvmAbs, TestBasicIscsiAbs): |
2235 | conf_file = "examples/tests/lvm_iscsi.yaml" |
2236 | nr_testfiles = 4 |
2237 | |
2238 | - extra_collect_scripts = TestLvmAbs.extra_collect_scripts |
2239 | - extra_collect_scripts += TestBasicIscsiAbs.extra_collect_scripts |
2240 | - extra_collect_scripts += [textwrap.dedent( |
2241 | - """ |
2242 | - cd OUTPUT_COLLECT_D |
2243 | - ls -al /sys/class/block/dm*/slaves/ > dm_slaves |
2244 | - """)] |
2245 | + extra_collect_scripts = ( |
2246 | + TestLvmAbs.extra_collect_scripts + |
2247 | + TestBasicIscsiAbs.extra_collect_scripts + |
2248 | + [textwrap.dedent(""" |
2249 | + cd OUTPUT_COLLECT_D |
2250 | + ls -al /sys/class/block/dm*/slaves/ > dm_slaves |
2251 | + |
2252 | + exit 0 |
2253 | + """)]) |
2254 | |
2255 | fstab_expected = { |
2256 | 'UUID=6de56115-9500-424b-8151-221b270ec708': '/mnt/iscsi1', |
2257 | @@ -87,4 +89,8 @@ class BionicTestIscsiLvm(relbase.bionic, TestLvmIscsiAbs): |
2258 | class CosmicTestIscsiLvm(relbase.cosmic, TestLvmIscsiAbs): |
2259 | __test__ = True |
2260 | |
2261 | + |
2262 | +class DiscoTestIscsiLvm(relbase.disco, TestLvmIscsiAbs): |
2263 | + __test__ = True |
2264 | + |
2265 | # vi: ts=4 expandtab syntax=python |
2266 | diff --git a/tests/vmtests/test_lvm_raid.py b/tests/vmtests/test_lvm_raid.py |
2267 | index 99a33f2..d70f3ef 100644 |
2268 | --- a/tests/vmtests/test_lvm_raid.py |
2269 | +++ b/tests/vmtests/test_lvm_raid.py |
2270 | @@ -14,14 +14,18 @@ class TestLvmOverRaidAbs(TestMdadmAbs, TestLvmAbs): |
2271 | dirty_disks = True |
2272 | extra_disks = ['10G'] * 4 |
2273 | |
2274 | - extra_collect_scripts = TestLvmAbs.extra_collect_scripts |
2275 | - extra_collect_scripts += TestMdadmAbs.extra_collect_scripts |
2276 | - extra_collect_scripts += [textwrap.dedent(""" |
2277 | - cd OUTPUT_COLLECT_D |
2278 | - ls -al /dev/md* > dev_md |
2279 | - cp -a /etc/mdadm etc_mdadm |
2280 | - cp -a /etc/lvm etc_lvm |
2281 | - """)] |
2282 | + extra_collect_scripts = ( |
2283 | + TestLvmAbs.extra_collect_scripts + |
2284 | + TestMdadmAbs.extra_collect_scripts + |
2285 | + [textwrap.dedent(""" |
2286 | + cd OUTPUT_COLLECT_D |
2287 | + ls -al /dev/md* > dev_md |
2288 | + cp -a /etc/mdadm etc_mdadm |
2289 | + cp -a /etc/lvm etc_lvm |
2290 | + |
2291 | + exit 0 |
2292 | + """)] |
2293 | + ) |
2294 | |
2295 | fstab_expected = { |
2296 | '/dev/vg1/lv1': '/srv/data', |
2297 | @@ -39,6 +43,10 @@ class TestLvmOverRaidAbs(TestMdadmAbs, TestLvmAbs): |
2298 | self.check_file_strippedline("pvs", "vg0=/dev/md1") |
2299 | |
2300 | |
2301 | +class DiscoTestLvmOverRaid(relbase.disco, TestLvmOverRaidAbs): |
2302 | + __test__ = True |
2303 | + |
2304 | + |
2305 | class CosmicTestLvmOverRaid(relbase.cosmic, TestLvmOverRaidAbs): |
2306 | __test__ = True |
2307 | |
2308 | diff --git a/tests/vmtests/test_lvm_root.py b/tests/vmtests/test_lvm_root.py |
2309 | index 7e7472d..d726a45 100644 |
2310 | --- a/tests/vmtests/test_lvm_root.py |
2311 | +++ b/tests/vmtests/test_lvm_root.py |
2312 | @@ -26,6 +26,8 @@ class TestLvmRootAbs(VMBaseClass): |
2313 | vgdisplay > vgdisplay |
2314 | lvdisplay > lvdisplay |
2315 | ls -al /dev/root_vg/ > dev_root_vg |
2316 | + |
2317 | + exit 0 |
2318 | """)] |
2319 | fstab_expected = { |
2320 | 'UUID=04836770-e989-460f-8774-8e277ddcb40f': '/', |
2321 | diff --git a/tests/vmtests/test_mdadm_bcache.py b/tests/vmtests/test_mdadm_bcache.py |
2322 | index adfa55b..f38d4f7 100644 |
2323 | --- a/tests/vmtests/test_mdadm_bcache.py |
2324 | +++ b/tests/vmtests/test_mdadm_bcache.py |
2325 | @@ -23,6 +23,8 @@ class TestMdadmAbs(VMBaseClass): |
2326 | ls -al /sys/fs/bcache/* > lsal_sys_fs_bcache_star |
2327 | ls -al /dev/bcache* > lsal_dev_bcache_star |
2328 | ls -al /dev/bcache/by_uuid/* > lsal_dev_bcache_byuuid_star |
2329 | + |
2330 | + exit 0 |
2331 | """)] |
2332 | |
2333 | def test_mdadm_output_files_exist(self): |
2334 | @@ -63,6 +65,8 @@ class TestMdadmBcacheAbs(TestMdadmAbs): |
2335 | cat /sys/block/bcache0/bcache/cache_mode > bcache_cache_mode |
2336 | cat /sys/block/bcache1/bcache/cache_mode >> bcache_cache_mode |
2337 | cat /sys/block/bcache2/bcache/cache_mode >> bcache_cache_mode |
2338 | + |
2339 | + exit 0 |
2340 | """)] |
2341 | fstab_expected = { |
2342 | '/dev/vda1': '/media/sda1', |
2343 | @@ -151,6 +155,10 @@ class CosmicTestMdadmBcache(relbase.cosmic, TestMdadmBcacheAbs): |
2344 | __test__ = True |
2345 | |
2346 | |
2347 | +class DiscoTestMdadmBcache(relbase.disco, TestMdadmBcacheAbs): |
2348 | + __test__ = True |
2349 | + |
2350 | + |
2351 | class TestMirrorbootAbs(TestMdadmAbs): |
2352 | # alternative config for more complex setup |
2353 | conf_file = "examples/tests/mirrorboot.yaml" |
2354 | @@ -196,6 +204,10 @@ class CosmicTestMirrorboot(relbase.cosmic, TestMirrorbootAbs): |
2355 | __test__ = True |
2356 | |
2357 | |
2358 | +class DiscoTestMirrorboot(relbase.disco, TestMirrorbootAbs): |
2359 | + __test__ = True |
2360 | + |
2361 | + |
2362 | class TestMirrorbootPartitionsAbs(TestMdadmAbs): |
2363 | # alternative config for more complex setup |
2364 | conf_file = "examples/tests/mirrorboot-msdos-partition.yaml" |
2365 | @@ -247,6 +259,11 @@ class CosmicTestMirrorbootPartitions(relbase.cosmic, |
2366 | __test__ = True |
2367 | |
2368 | |
2369 | +class DiscoTestMirrorbootPartitions(relbase.disco, |
2370 | + TestMirrorbootPartitionsAbs): |
2371 | + __test__ = True |
2372 | + |
2373 | + |
2374 | class TestMirrorbootPartitionsUEFIAbs(TestMdadmAbs): |
2375 | # alternative config for more complex setup |
2376 | conf_file = "examples/tests/mirrorboot-uefi.yaml" |
2377 | @@ -297,6 +314,11 @@ class CosmicTestMirrorbootPartitionsUEFI(relbase.cosmic, |
2378 | __test__ = True |
2379 | |
2380 | |
2381 | +class DiscoTestMirrorbootPartitionsUEFI(relbase.disco, |
2382 | + TestMirrorbootPartitionsUEFIAbs): |
2383 | + __test__ = True |
2384 | + |
2385 | + |
2386 | class TestRaid5bootAbs(TestMdadmAbs): |
2387 | # alternative config for more complex setup |
2388 | conf_file = "examples/tests/raid5boot.yaml" |
2389 | @@ -342,6 +364,10 @@ class CosmicTestRaid5boot(relbase.cosmic, TestRaid5bootAbs): |
2390 | __test__ = True |
2391 | |
2392 | |
2393 | +class DiscoTestRaid5boot(relbase.disco, TestRaid5bootAbs): |
2394 | + __test__ = True |
2395 | + |
2396 | + |
2397 | class TestRaid6bootAbs(TestMdadmAbs): |
2398 | # alternative config for more complex setup |
2399 | conf_file = "examples/tests/raid6boot.yaml" |
2400 | @@ -357,6 +383,8 @@ class TestRaid6bootAbs(TestMdadmAbs): |
2401 | TestMdadmAbs.extra_collect_scripts + [textwrap.dedent(""" |
2402 | cd OUTPUT_COLLECT_D |
2403 | mdadm --detail --scan > mdadm_detail |
2404 | + |
2405 | + exit 0 |
2406 | """)]) |
2407 | |
2408 | def test_raid6_output_files_exist(self): |
2409 | @@ -400,6 +428,10 @@ class CosmicTestRaid6boot(relbase.cosmic, TestRaid6bootAbs): |
2410 | __test__ = True |
2411 | |
2412 | |
2413 | +class DiscoTestRaid6boot(relbase.disco, TestRaid6bootAbs): |
2414 | + __test__ = True |
2415 | + |
2416 | + |
2417 | class TestRaid10bootAbs(TestMdadmAbs): |
2418 | # alternative config for more complex setup |
2419 | conf_file = "examples/tests/raid10boot.yaml" |
2420 | @@ -446,6 +478,10 @@ class CosmicTestRaid10boot(relbase.cosmic, TestRaid10bootAbs): |
2421 | __test__ = True |
2422 | |
2423 | |
2424 | +class DiscoTestRaid10boot(relbase.disco, TestRaid10bootAbs): |
2425 | + __test__ = True |
2426 | + |
2427 | + |
2428 | class TestAllindataAbs(TestMdadmAbs): |
2429 | # more complex, needs more time |
2430 | # alternative config for more complex setup |
2431 | @@ -493,6 +529,8 @@ class TestAllindataAbs(TestMdadmAbs): |
2432 | mkdir -p /tmp/xfstest |
2433 | mount /dev/mapper/dmcrypt0 /tmp/xfstest |
2434 | xfs_info /tmp/xfstest/ > xfs_info |
2435 | + |
2436 | + exit 0 |
2437 | """)]) |
2438 | fstab_expected = { |
2439 | '/dev/vg1/lv1': '/srv/data', |
2440 | @@ -547,4 +585,8 @@ class BionicTestAllindata(relbase.bionic, TestAllindataAbs): |
2441 | class CosmicTestAllindata(relbase.cosmic, TestAllindataAbs): |
2442 | __test__ = True |
2443 | |
2444 | + |
2445 | +class DiscoTestAllindata(relbase.disco, TestAllindataAbs): |
2446 | + __test__ = True |
2447 | + |
2448 | # vi: ts=4 expandtab syntax=python |
2449 | diff --git a/tests/vmtests/test_mdadm_iscsi.py b/tests/vmtests/test_mdadm_iscsi.py |
2450 | index 537baec..b43855d 100644 |
2451 | --- a/tests/vmtests/test_mdadm_iscsi.py |
2452 | +++ b/tests/vmtests/test_mdadm_iscsi.py |
2453 | @@ -18,12 +18,15 @@ class TestMdadmIscsiAbs(TestMdadmAbs, TestBasicIscsiAbs): |
2454 | conf_file = "examples/tests/mdadm_iscsi.yaml" |
2455 | nr_testfiles = 1 |
2456 | |
2457 | - extra_collect_scripts = TestMdadmAbs.extra_collect_scripts |
2458 | - extra_collect_scripts += TestBasicIscsiAbs.extra_collect_scripts |
2459 | - extra_collect_scripts += [textwrap.dedent(""" |
2460 | - cd OUTPUT_COLLECT_D |
2461 | - ls -al /sys/class/block/md*/slaves/ > md_slaves |
2462 | - """)] |
2463 | + extra_collect_scripts = ( |
2464 | + TestMdadmAbs.extra_collect_scripts + |
2465 | + TestBasicIscsiAbs.extra_collect_scripts + |
2466 | + [textwrap.dedent(""" |
2467 | + cd OUTPUT_COLLECT_D |
2468 | + ls -al /sys/class/block/md*/slaves/ > md_slaves |
2469 | + |
2470 | + exit 0 |
2471 | + """)]) |
2472 | |
2473 | |
2474 | class Centos70TestIscsiMdadm(centos_relbase.centos70_xenial, |
2475 | @@ -54,4 +57,8 @@ class BionicTestIscsiMdadm(relbase.bionic, TestMdadmIscsiAbs): |
2476 | class CosmicTestIscsiMdadm(relbase.cosmic, TestMdadmIscsiAbs): |
2477 | __test__ = True |
2478 | |
2479 | + |
2480 | +class DiscoTestIscsiMdadm(relbase.disco, TestMdadmIscsiAbs): |
2481 | + __test__ = True |
2482 | + |
2483 | # vi: ts=4 expandtab syntax=python |
2484 | diff --git a/tests/vmtests/test_multipath.py b/tests/vmtests/test_multipath.py |
2485 | index a6bd8ce..a22bc0f 100644 |
2486 | --- a/tests/vmtests/test_multipath.py |
2487 | +++ b/tests/vmtests/test_multipath.py |
2488 | @@ -4,6 +4,8 @@ from . import VMBaseClass |
2489 | from .releases import base_vm_classes as relbase |
2490 | from .releases import centos_base_vm_classes as centos_relbase |
2491 | |
2492 | +from unittest import SkipTest |
2493 | +import os |
2494 | import textwrap |
2495 | |
2496 | |
2497 | @@ -22,6 +24,11 @@ class TestMultipathBasicAbs(VMBaseClass): |
2498 | cp -a /etc/multipath* . |
2499 | readlink -f /sys/class/block/sda/holders/dm-0 > holders_sda |
2500 | readlink -f /sys/class/block/sdb/holders/dm-0 > holders_sdb |
2501 | + command -v systemctl && { |
2502 | + systemctl show -- home.mount > systemctl_show_home.mount; |
2503 | + systemctl status --full home.mount > systemctl_status_home.mount |
2504 | + } |
2505 | + exit 0 |
2506 | """)] |
2507 | |
2508 | def test_multipath_disks_match(self): |
2509 | @@ -31,6 +38,26 @@ class TestMultipathBasicAbs(VMBaseClass): |
2510 | print('sdb holders:\n%s' % sdb_data) |
2511 | self.assertEqual(sda_data, sdb_data) |
2512 | |
2513 | + def test_home_mount_unit(self): |
2514 | + unit_file = 'systemctl_show_home.mount' |
2515 | + if not os.path.exists(self.collect_path(unit_file)): |
2516 | + raise SkipTest( |
2517 | + 'target_release=%s does not use systemd' % self.target_release) |
2518 | + |
2519 | + # We can't use load_shell_content as systemctl show output |
2520 | + # does not quote values even though it's in Key=Value format |
2521 | + content = self.load_collect_file(unit_file) |
2522 | + expected_results = { |
2523 | + 'ActiveState': 'active', |
2524 | + 'Result': 'success', |
2525 | + 'SubState': 'mounted', |
2526 | + } |
2527 | + show = {key: value for key, value in |
2528 | + [line.split('=') for line in content.splitlines() |
2529 | + if line.split('=')[0] in expected_results.keys()]} |
2530 | + |
2531 | + self.assertEqual(sorted(expected_results), sorted(show)) |
2532 | + |
2533 | |
2534 | class Centos70TestMultipathBasic(centos_relbase.centos70_xenial, |
2535 | TestMultipathBasicAbs): |
2536 | @@ -65,4 +92,8 @@ class BionicTestMultipathBasic(relbase.bionic, TestMultipathBasicAbs): |
2537 | class CosmicTestMultipathBasic(relbase.cosmic, TestMultipathBasicAbs): |
2538 | __test__ = True |
2539 | |
2540 | + |
2541 | +class DiscoTestMultipathBasic(relbase.disco, TestMultipathBasicAbs): |
2542 | + __test__ = True |
2543 | + |
2544 | # vi: ts=4 expandtab syntax=python |
2545 | diff --git a/tests/vmtests/test_network.py b/tests/vmtests/test_network.py |
2546 | index ce4cdb9..1b0e41c 100644 |
2547 | --- a/tests/vmtests/test_network.py |
2548 | +++ b/tests/vmtests/test_network.py |
2549 | @@ -49,6 +49,8 @@ class TestNetworkBaseTestsAbs(VMBaseClass): |
2550 | cp -a /etc/systemd ./etc_systemd ||: |
2551 | journalctl --no-pager -b -x | tee journalctl_out |
2552 | sleep 10 && ip a | tee ip_a |
2553 | + |
2554 | + exit 0 |
2555 | """)] |
2556 | |
2557 | def test_output_files_exist(self): |
2558 | @@ -473,6 +475,10 @@ class CosmicTestNetworkBasic(relbase.cosmic, TestNetworkBasicAbs): |
2559 | __test__ = True |
2560 | |
2561 | |
2562 | +class DiscoTestNetworkBasic(relbase.disco, TestNetworkBasicAbs): |
2563 | + __test__ = True |
2564 | + |
2565 | + |
2566 | class Centos66TestNetworkBasic(centos_relbase.centos66_xenial, |
2567 | CentosTestNetworkBasicAbs): |
2568 | __test__ = True |
2569 | diff --git a/tests/vmtests/test_network_alias.py b/tests/vmtests/test_network_alias.py |
2570 | index 1d1837f..b2c4ed7 100644 |
2571 | --- a/tests/vmtests/test_network_alias.py |
2572 | +++ b/tests/vmtests/test_network_alias.py |
2573 | @@ -26,6 +26,8 @@ class CentosTestNetworkAliasAbs(TestNetworkAliasAbs): |
2574 | cp -a /var/log/cloud-init* . |
2575 | cp -a /var/lib/cloud ./var_lib_cloud |
2576 | cp -a /run/cloud-init ./run_cloud-init |
2577 | + |
2578 | + exit 0 |
2579 | """)] |
2580 | |
2581 | def test_etc_resolvconf(self): |
2582 | @@ -76,4 +78,8 @@ class BionicTestNetworkAlias(relbase.bionic, TestNetworkAliasAbs): |
2583 | class CosmicTestNetworkAlias(relbase.cosmic, TestNetworkAliasAbs): |
2584 | __test__ = True |
2585 | |
2586 | + |
2587 | +class DiscoTestNetworkAlias(relbase.disco, TestNetworkAliasAbs): |
2588 | + __test__ = True |
2589 | + |
2590 | # vi: ts=4 expandtab syntax=python |
2591 | diff --git a/tests/vmtests/test_network_bonding.py b/tests/vmtests/test_network_bonding.py |
2592 | index 572f240..fe3abe9 100644 |
2593 | --- a/tests/vmtests/test_network_bonding.py |
2594 | +++ b/tests/vmtests/test_network_bonding.py |
2595 | @@ -33,6 +33,8 @@ class CentosTestNetworkBondingAbs(TestNetworkBondingAbs): |
2596 | cp -a /var/lib/cloud ./var_lib_cloud |
2597 | cp -a /run/cloud-init ./run_cloud-init |
2598 | rpm -qf `which ifenslave` |tee ifenslave_installed |
2599 | + |
2600 | + exit 0 |
2601 | """)] |
2602 | |
2603 | def test_ifenslave_package_status(self): |
2604 | @@ -79,6 +81,10 @@ class CosmicTestBonding(relbase.cosmic, TestNetworkBondingAbs): |
2605 | __test__ = True |
2606 | |
2607 | |
2608 | +class DiscoTestBonding(relbase.disco, TestNetworkBondingAbs): |
2609 | + __test__ = True |
2610 | + |
2611 | + |
2612 | class Centos66TestNetworkBonding(centos_relbase.centos66_xenial, |
2613 | CentosTestNetworkBondingAbs): |
2614 | __test__ = True |
2615 | diff --git a/tests/vmtests/test_network_bridging.py b/tests/vmtests/test_network_bridging.py |
2616 | index ed9b728..8576d60 100644 |
2617 | --- a/tests/vmtests/test_network_bridging.py |
2618 | +++ b/tests/vmtests/test_network_bridging.py |
2619 | @@ -102,6 +102,8 @@ class TestBridgeNetworkAbs(TestNetworkBaseTestsAbs): |
2620 | grep -r . /sys/class/net/br0 > sysfs_br0 |
2621 | grep -r . /sys/class/net/br0/brif/eth1 > sysfs_br0_eth1 |
2622 | grep -r . /sys/class/net/br0/brif/eth2 > sysfs_br0_eth2 |
2623 | + |
2624 | + exit 0 |
2625 | """)] |
2626 | |
2627 | def test_output_files_exist_bridge(self): |
2628 | @@ -200,6 +202,8 @@ class CentosTestBridgeNetworkAbs(TestBridgeNetworkAbs): |
2629 | cp -a /var/lib/cloud ./var_lib_cloud |
2630 | cp -a /run/cloud-init ./run_cloud-init |
2631 | rpm -qf `which brctl` |tee bridge-utils_installed |
2632 | + |
2633 | + exit 0 |
2634 | """)] |
2635 | |
2636 | def test_etc_network_interfaces(self): |
2637 | @@ -236,4 +240,7 @@ class CosmicTestBridging(relbase.cosmic, TestBridgeNetworkAbs): |
2638 | __test__ = True |
2639 | |
2640 | |
2641 | +class DiscoTestBridging(relbase.disco, TestBridgeNetworkAbs): |
2642 | + __test__ = True |
2643 | + |
2644 | # vi: ts=4 expandtab syntax=python |
2645 | diff --git a/tests/vmtests/test_network_ipv6.py b/tests/vmtests/test_network_ipv6.py |
2646 | index b8a69e8..42adb20 100644 |
2647 | --- a/tests/vmtests/test_network_ipv6.py |
2648 | +++ b/tests/vmtests/test_network_ipv6.py |
2649 | @@ -21,6 +21,8 @@ class TestNetworkIPV6Abs(TestNetworkBaseTestsAbs): |
2650 | grep . -r /sys/class/net/bond0/ > sysfs_bond0 || : |
2651 | grep . -r /sys/class/net/bond0.108/ > sysfs_bond0.108 || : |
2652 | grep . -r /sys/class/net/bond0.208/ > sysfs_bond0.208 || : |
2653 | + |
2654 | + exit 0 |
2655 | """)] |
2656 | |
2657 | |
2658 | @@ -32,6 +34,8 @@ class CentosTestNetworkIPV6Abs(TestNetworkIPV6Abs): |
2659 | cp -a /var/log/cloud-init* . |
2660 | cp -a /var/lib/cloud ./var_lib_cloud |
2661 | cp -a /run/cloud-init ./run_cloud-init |
2662 | + |
2663 | + exit 0 |
2664 | """)] |
2665 | |
2666 | def test_etc_network_interfaces(self): |
2667 | @@ -72,6 +76,10 @@ class CosmicTestNetworkIPV6(relbase.cosmic, TestNetworkIPV6Abs): |
2668 | __test__ = True |
2669 | |
2670 | |
2671 | +class DiscoTestNetworkIPV6(relbase.disco, TestNetworkIPV6Abs): |
2672 | + __test__ = True |
2673 | + |
2674 | + |
2675 | class Centos66TestNetworkIPV6(centos_relbase.centos66_xenial, |
2676 | CentosTestNetworkIPV6Abs): |
2677 | __test__ = True |
2678 | diff --git a/tests/vmtests/test_network_ipv6_static.py b/tests/vmtests/test_network_ipv6_static.py |
2679 | index 7ebc5eb..bbd90c9 100644 |
2680 | --- a/tests/vmtests/test_network_ipv6_static.py |
2681 | +++ b/tests/vmtests/test_network_ipv6_static.py |
2682 | @@ -54,6 +54,10 @@ class CosmicTestNetworkIPV6Static(relbase.cosmic, TestNetworkIPV6StaticAbs): |
2683 | __test__ = True |
2684 | |
2685 | |
2686 | +class DiscoTestNetworkIPV6Static(relbase.disco, TestNetworkIPV6StaticAbs): |
2687 | + __test__ = True |
2688 | + |
2689 | + |
2690 | class Centos66TestNetworkIPV6Static(centos_relbase.centos66_xenial, |
2691 | CentosTestNetworkIPV6StaticAbs): |
2692 | __test__ = True |
2693 | diff --git a/tests/vmtests/test_network_ipv6_vlan.py b/tests/vmtests/test_network_ipv6_vlan.py |
2694 | index 2f7bd49..7401d2c 100644 |
2695 | --- a/tests/vmtests/test_network_ipv6_vlan.py |
2696 | +++ b/tests/vmtests/test_network_ipv6_vlan.py |
2697 | @@ -35,6 +35,10 @@ class CosmicTestNetworkIPV6Vlan(relbase.cosmic, TestNetworkIPV6VlanAbs): |
2698 | __test__ = True |
2699 | |
2700 | |
2701 | +class DiscoTestNetworkIPV6Vlan(relbase.disco, TestNetworkIPV6VlanAbs): |
2702 | + __test__ = True |
2703 | + |
2704 | + |
2705 | class Centos66TestNetworkIPV6Vlan(centos_relbase.centos66_xenial, |
2706 | CentosTestNetworkIPV6VlanAbs): |
2707 | __test__ = True |
2708 | diff --git a/tests/vmtests/test_network_mtu.py b/tests/vmtests/test_network_mtu.py |
2709 | index eaa383b..ffde5c7 100644 |
2710 | --- a/tests/vmtests/test_network_mtu.py |
2711 | +++ b/tests/vmtests/test_network_mtu.py |
2712 | @@ -36,6 +36,8 @@ class TestNetworkMtuAbs(TestNetworkIPV6Abs): |
2713 | if [ -e /var/log/upstart ]; then |
2714 | cp -a /var/log/upstart ./var_log_upstart |
2715 | fi |
2716 | + |
2717 | + exit 0 |
2718 | """)] |
2719 | |
2720 | def _load_mtu_data(self, ifname): |
2721 | @@ -128,6 +130,8 @@ class CentosTestNetworkMtuAbs(TestNetworkMtuAbs): |
2722 | cp -a /var/log/cloud-init* . |
2723 | cp -a /var/lib/cloud ./var_lib_cloud |
2724 | cp -a /run/cloud-init ./run_cloud-init |
2725 | + |
2726 | + exit 0 |
2727 | """)] |
2728 | |
2729 | def test_etc_network_interfaces(self): |
2730 | @@ -200,6 +204,11 @@ class CosmicTestNetworkMtu(relbase.cosmic, TestNetworkMtuAbs): |
2731 | __test__ = True |
2732 | |
2733 | |
2734 | +@TestNetworkMtuAbs.skip_by_date("1671951", fixby="2019-01-02") |
2735 | +class DiscoTestNetworkMtu(relbase.disco, TestNetworkMtuAbs): |
2736 | + __test__ = True |
2737 | + |
2738 | + |
2739 | class Centos66TestNetworkMtu(centos_relbase.centos66_xenial, |
2740 | CentosTestNetworkMtuAbs): |
2741 | __test__ = True |
2742 | diff --git a/tests/vmtests/test_network_static.py b/tests/vmtests/test_network_static.py |
2743 | index 9e042a5..6820c33 100644 |
2744 | --- a/tests/vmtests/test_network_static.py |
2745 | +++ b/tests/vmtests/test_network_static.py |
2746 | @@ -59,6 +59,10 @@ class CosmicTestNetworkStatic(relbase.cosmic, TestNetworkStaticAbs): |
2747 | __test__ = True |
2748 | |
2749 | |
2750 | +class DiscoTestNetworkStatic(relbase.disco, TestNetworkStaticAbs): |
2751 | + __test__ = True |
2752 | + |
2753 | + |
2754 | class Centos66TestNetworkStatic(centos_relbase.centos66_xenial, |
2755 | CentosTestNetworkStaticAbs): |
2756 | __test__ = True |
2757 | diff --git a/tests/vmtests/test_network_static_routes.py b/tests/vmtests/test_network_static_routes.py |
2758 | index ad18eef..405a730 100644 |
2759 | --- a/tests/vmtests/test_network_static_routes.py |
2760 | +++ b/tests/vmtests/test_network_static_routes.py |
2761 | @@ -59,6 +59,11 @@ class CosmicTestNetworkStaticRoutes(relbase.cosmic, |
2762 | __test__ = True |
2763 | |
2764 | |
2765 | +class DiscoTestNetworkStaticRoutes(relbase.disco, |
2766 | + TestNetworkStaticRoutesAbs): |
2767 | + __test__ = True |
2768 | + |
2769 | + |
2770 | class Centos66TestNetworkStaticRoutes(centos_relbase.centos66_xenial, |
2771 | CentosTestNetworkStaticRoutesAbs): |
2772 | __test__ = False |
2773 | diff --git a/tests/vmtests/test_network_vlan.py b/tests/vmtests/test_network_vlan.py |
2774 | index 9e55bfa..904f8c2 100644 |
2775 | --- a/tests/vmtests/test_network_vlan.py |
2776 | +++ b/tests/vmtests/test_network_vlan.py |
2777 | @@ -18,6 +18,8 @@ class TestNetworkVlanAbs(TestNetworkBaseTestsAbs): |
2778 | ip -d link show interface1.2668 |tee ip_link_show_interface1.2668 |
2779 | ip -d link show interface1.2669 |tee ip_link_show_interface1.2669 |
2780 | ip -d link show interface1.2670 |tee ip_link_show_interface1.2670 |
2781 | + |
2782 | + exit 0 |
2783 | """)] |
2784 | |
2785 | def get_vlans(self): |
2786 | @@ -86,6 +88,10 @@ class CosmicTestNetworkVlan(relbase.cosmic, TestNetworkVlanAbs): |
2787 | __test__ = True |
2788 | |
2789 | |
2790 | +class DiscoTestNetworkVlan(relbase.disco, TestNetworkVlanAbs): |
2791 | + __test__ = True |
2792 | + |
2793 | + |
2794 | class Centos66TestNetworkVlan(centos_relbase.centos66_xenial, |
2795 | CentosTestNetworkVlanAbs): |
2796 | __test__ = True |
2797 | diff --git a/tests/vmtests/test_nvme.py b/tests/vmtests/test_nvme.py |
2798 | index f3d1ae2..60d9d86 100644 |
2799 | --- a/tests/vmtests/test_nvme.py |
2800 | +++ b/tests/vmtests/test_nvme.py |
2801 | @@ -19,14 +19,17 @@ class TestNvmeAbs(VMBaseClass): |
2802 | conf_file = "examples/tests/nvme.yaml" |
2803 | extra_disks = [] |
2804 | nvme_disks = ['4G', '4G'] |
2805 | - disk_to_check = [('main_disk', 1), ('main_disk', 2), ('main_disk', 15), |
2806 | - ('nvme_disk', 1), ('nvme_disk', 2), ('nvme_disk', 3), |
2807 | - ('second_nvme', 1)] |
2808 | + disk_to_check = [ |
2809 | + ('main_disk', 1), ('main_disk', 2), ('main_disk', 15), |
2810 | + ('nvme_disk', 0), ('nvme_disk', 1), ('nvme_disk', 2), ('nvme_disk', 3), |
2811 | + ('second_nvme', 0), ('second_nvme', 1)] |
2812 | extra_collect_scripts = [textwrap.dedent(""" |
2813 | cd OUTPUT_COLLECT_D |
2814 | ls /sys/class/ > sys_class |
2815 | ls /sys/class/nvme/ > ls_nvme |
2816 | ls /dev/nvme* > ls_dev_nvme |
2817 | + |
2818 | + exit 0 |
2819 | """)] |
2820 | |
2821 | def _test_nvme_device_names(self, expected): |
2822 | @@ -82,6 +85,10 @@ class CosmicTestNvme(relbase.cosmic, TestNvmeAbs): |
2823 | __test__ = True |
2824 | |
2825 | |
2826 | +class DiscoTestNvme(relbase.cosmic, TestNvmeAbs): |
2827 | + __test__ = True |
2828 | + |
2829 | + |
2830 | class TestNvmeBcacheAbs(TestNvmeAbs): |
2831 | arch_skip = [ |
2832 | "s390x", # nvme is a pci device, no pci on s390x |
2833 | @@ -91,7 +98,8 @@ class TestNvmeBcacheAbs(TestNvmeAbs): |
2834 | extra_disks = ['10G'] |
2835 | nvme_disks = ['6G'] |
2836 | uefi = True |
2837 | - disk_to_check = [('sda', 1), ('sda', 2), ('sda', 3)] |
2838 | + disk_to_check = [('sda', 1), ('sda', 2), ('sda', 3), |
2839 | + ('sdb', 0), ('nvme0n1', 0)] |
2840 | |
2841 | extra_collect_scripts = [textwrap.dedent(""" |
2842 | cd OUTPUT_COLLECT_D |
2843 | @@ -102,6 +110,8 @@ class TestNvmeBcacheAbs(TestNvmeAbs): |
2844 | bcache-super-show /dev/nvme0n1p1 > bcache_super_nvme0n1p1 |
2845 | ls /sys/fs/bcache > bcache_ls |
2846 | cat /sys/block/bcache0/bcache/cache_mode > bcache_cache_mode |
2847 | + |
2848 | + exit 0 |
2849 | """)] |
2850 | |
2851 | def test_bcache_output_files_exist(self): |
2852 | @@ -145,4 +155,7 @@ class CosmicTestNvmeBcache(relbase.cosmic, TestNvmeBcacheAbs): |
2853 | __test__ = True |
2854 | |
2855 | |
2856 | +class DiscoTestNvmeBcache(relbase.disco, TestNvmeBcacheAbs): |
2857 | + __test__ = True |
2858 | + |
2859 | # vi: ts=4 expandtab syntax=python |
2860 | diff --git a/tests/vmtests/test_old_apt_features.py b/tests/vmtests/test_old_apt_features.py |
2861 | index 33561a3..ec3765c 100644 |
2862 | --- a/tests/vmtests/test_old_apt_features.py |
2863 | +++ b/tests/vmtests/test_old_apt_features.py |
2864 | @@ -5,6 +5,7 @@ |
2865 | """ |
2866 | import re |
2867 | import textwrap |
2868 | +import yaml |
2869 | |
2870 | from . import VMBaseClass |
2871 | from .releases import base_vm_classes as relbase |
2872 | @@ -50,6 +51,8 @@ class TestOldAptAbs(VMBaseClass): |
2873 | cp /etc/apt/sources.list . |
2874 | cp /etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg . |
2875 | cp /etc/cloud/cloud.cfg.d/90_dpkg.cfg . |
2876 | + |
2877 | + exit 0 |
2878 | """)] |
2879 | arch = util.get_architecture() |
2880 | if arch in ['amd64', 'i386']: |
2881 | @@ -69,8 +72,10 @@ class TestOldAptAbs(VMBaseClass): |
2882 | |
2883 | def test_preserve_source(self): |
2884 | """test_preserve_source - no clobbering sources.list by cloud-init""" |
2885 | - self.check_file_regex("curtin-preserve-sources.cfg", |
2886 | - "apt_preserve_sources_list.*true") |
2887 | + # For earlier than xenial 'apt_preserve_sources_list' is expected |
2888 | + self.assertEqual( |
2889 | + {'apt': {'preserve_sources_list': True}}, |
2890 | + yaml.load(self.load_collect_file("curtin-preserve-sources.cfg"))) |
2891 | |
2892 | def test_debconf(self): |
2893 | """test_debconf - Check if debconf is in place""" |
2894 | diff --git a/tests/vmtests/test_pollinate_useragent.py b/tests/vmtests/test_pollinate_useragent.py |
2895 | index 13dd28c..c1c6c36 100644 |
2896 | --- a/tests/vmtests/test_pollinate_useragent.py |
2897 | +++ b/tests/vmtests/test_pollinate_useragent.py |
2898 | @@ -18,6 +18,8 @@ class TestPollinateUserAgent(VMBaseClass): |
2899 | cd OUTPUT_COLLECT_D |
2900 | cp -a /etc/pollinate etc_pollinate |
2901 | pollinate --print-user-agent > pollinate_print_user_agent |
2902 | + |
2903 | + exit 0 |
2904 | """)] |
2905 | |
2906 | def test_pollinate_user_agent(self): |
2907 | @@ -66,4 +68,8 @@ class BionicTestPollinateUserAgent(relbase.bionic, TestPollinateUserAgent): |
2908 | class CosmicTestPollinateUserAgent(relbase.cosmic, TestPollinateUserAgent): |
2909 | __test__ = True |
2910 | |
2911 | + |
2912 | +class DiscoTestPollinateUserAgent(relbase.disco, TestPollinateUserAgent): |
2913 | + __test__ = True |
2914 | + |
2915 | # vi: ts=4 expandtab syntax=python |
2916 | diff --git a/tests/vmtests/test_raid5_bcache.py b/tests/vmtests/test_raid5_bcache.py |
2917 | index 8d24f8e..76d0eed 100644 |
2918 | --- a/tests/vmtests/test_raid5_bcache.py |
2919 | +++ b/tests/vmtests/test_raid5_bcache.py |
2920 | @@ -16,6 +16,8 @@ class TestMdadmAbs(VMBaseClass): |
2921 | mdadm --detail --scan > mdadm_status |
2922 | mdadm --detail --scan | grep -c ubuntu > mdadm_active1 |
2923 | grep -c active /proc/mdstat > mdadm_active2 |
2924 | + |
2925 | + exit 0 |
2926 | """)] |
2927 | |
2928 | def test_mdadm_output_files_exist(self): |
2929 | @@ -33,13 +35,15 @@ class TestMdadmBcacheAbs(TestMdadmAbs): |
2930 | conf_file = "examples/tests/raid5bcache.yaml" |
2931 | disk_to_check = [('md0', 0), ('sda', 2)] |
2932 | |
2933 | - extra_collect_scripts = TestMdadmAbs.extra_collect_scripts |
2934 | - extra_collect_scripts += [textwrap.dedent(""" |
2935 | - cd OUTPUT_COLLECT_D |
2936 | - bcache-super-show /dev/vda2 > bcache_super_vda2 |
2937 | - ls /sys/fs/bcache > bcache_ls |
2938 | - cat /sys/block/bcache0/bcache/cache_mode > bcache_cache_mode |
2939 | - """)] |
2940 | + extra_collect_scripts = ( |
2941 | + TestMdadmAbs.extra_collect_scripts + |
2942 | + [textwrap.dedent("""\ |
2943 | + cd OUTPUT_COLLECT_D |
2944 | + bcache-super-show /dev/vda2 > bcache_super_vda2 |
2945 | + ls /sys/fs/bcache > bcache_ls |
2946 | + cat /sys/block/bcache0/bcache/cache_mode > bcache_cache_mode |
2947 | + |
2948 | + exit 0""")]) |
2949 | fstab_expected = { |
2950 | '/dev/bcache0': '/', |
2951 | '/dev/md0': '/srv/data', |
2952 | @@ -101,4 +105,8 @@ class BionicTestRaid5Bcache(relbase.bionic, TestMdadmBcacheAbs): |
2953 | class CosmicTestRaid5Bcache(relbase.cosmic, TestMdadmBcacheAbs): |
2954 | __test__ = True |
2955 | |
2956 | + |
2957 | +class DiscoTestRaid5Bcache(relbase.disco, TestMdadmBcacheAbs): |
2958 | + __test__ = True |
2959 | + |
2960 | # vi: ts=4 expandtab syntax=python |
2961 | diff --git a/tests/vmtests/test_simple.py b/tests/vmtests/test_simple.py |
2962 | index 8948f72..625f841 100644 |
2963 | --- a/tests/vmtests/test_simple.py |
2964 | +++ b/tests/vmtests/test_simple.py |
2965 | @@ -8,13 +8,15 @@ import textwrap |
2966 | |
2967 | |
2968 | class TestSimple(VMBaseClass): |
2969 | - # Test that curtin with no config does the right thing |
2970 | + """ Test that curtin runs block-meta simple mode correctly. """ |
2971 | conf_file = "examples/tests/simple.yaml" |
2972 | extra_disks = [] |
2973 | extra_nics = [] |
2974 | extra_collect_scripts = [textwrap.dedent(""" |
2975 | cd OUTPUT_COLLECT_D |
2976 | cp /etc/netplan/50-cloud-init.yaml netplan.yaml |
2977 | + |
2978 | + exit 0 |
2979 | """)] |
2980 | |
2981 | |
2982 | @@ -43,4 +45,68 @@ class CosmicTestSimple(relbase.cosmic, TestSimple): |
2983 | def test_output_files_exist(self): |
2984 | self.output_files_exist(["netplan.yaml"]) |
2985 | |
2986 | + |
2987 | +class DiscoTestSimple(relbase.disco, TestSimple): |
2988 | + __test__ = True |
2989 | + |
2990 | + def test_output_files_exist(self): |
2991 | + self.output_files_exist(["netplan.yaml"]) |
2992 | + |
2993 | + |
2994 | +class TestSimpleStorage(VMBaseClass): |
2995 | + """ Test curtin runs clear-holders when mode=simple with storage cfg. """ |
2996 | + conf_file = "examples/tests/simple-storage.yaml" |
2997 | + dirty_disks = True |
2998 | + extra_disks = ['5G', '5G'] |
2999 | + extra_nics = [] |
3000 | + extra_collect_scripts = [textwrap.dedent(""" |
3001 | + cd OUTPUT_COLLECT_D |
3002 | + sfdisk --list > sfdisk_list |
3003 | + for d in /dev/[sv]d[a-z] /dev/xvd?; do |
3004 | + [ -b "$d" ] || continue |
3005 | + echo == $d == |
3006 | + sgdisk --print $d |
3007 | + done > sgdisk_list |
3008 | + blkid > blkid |
3009 | + cat /proc/partitions > proc_partitions |
3010 | + cp /etc/network/interfaces interfaces |
3011 | + cp /etc/netplan/50-cloud-init.yaml netplan.yaml |
3012 | + if [ -f /var/log/cloud-init-output.log ]; then |
3013 | + cp /var/log/cloud-init-output.log . |
3014 | + fi |
3015 | + cp /var/log/cloud-init.log . |
3016 | + find /etc/network/interfaces.d > find_interfacesd |
3017 | + exit 0 |
3018 | + """)] |
3019 | + |
3020 | + def test_output_files_exist(self): |
3021 | + self.output_files_exist(["sfdisk_list", "blkid", |
3022 | + "proc_partitions"]) |
3023 | + |
3024 | + |
3025 | +class XenialGATestSimpleStorage(relbase.xenial, TestSimpleStorage): |
3026 | + __test__ = True |
3027 | + |
3028 | + |
3029 | +class BionicTestSimpleStorage(relbase.bionic, TestSimpleStorage): |
3030 | + __test__ = True |
3031 | + |
3032 | + def test_output_files_exist(self): |
3033 | + self.output_files_exist(["netplan.yaml"]) |
3034 | + |
3035 | + |
3036 | +class CosmicTestSimpleStorage(relbase.cosmic, TestSimpleStorage): |
3037 | + __test__ = True |
3038 | + |
3039 | + def test_output_files_exist(self): |
3040 | + self.output_files_exist(["netplan.yaml"]) |
3041 | + |
3042 | + |
3043 | +class DiscoTestSimpleStorage(relbase.disco, TestSimpleStorage): |
3044 | + __test__ = True |
3045 | + |
3046 | + def test_output_files_exist(self): |
3047 | + self.output_files_exist(["netplan.yaml"]) |
3048 | + |
3049 | + |
3050 | # vi: ts=4 expandtab syntax=python |
3051 | diff --git a/tests/vmtests/test_ubuntu_core.py b/tests/vmtests/test_ubuntu_core.py |
3052 | index 732399b..a282940 100644 |
3053 | --- a/tests/vmtests/test_ubuntu_core.py |
3054 | +++ b/tests/vmtests/test_ubuntu_core.py |
3055 | @@ -18,6 +18,8 @@ class TestUbuntuCoreAbs(VMBaseClass): |
3056 | cp -a /etc/cloud ./etc_cloud |: |
3057 | cp -a /home . |: |
3058 | cp -a /var/lib/extrausers . |: |
3059 | + |
3060 | + exit 0 |
3061 | """)] |
3062 | |
3063 | def test_ubuntu_core_snaps_installed(self): |
3064 | diff --git a/tests/vmtests/test_uefi_basic.py b/tests/vmtests/test_uefi_basic.py |
3065 | index 40ece65..f0e48c6 100644 |
3066 | --- a/tests/vmtests/test_uefi_basic.py |
3067 | +++ b/tests/vmtests/test_uefi_basic.py |
3068 | @@ -24,6 +24,8 @@ class TestBasicAbs(VMBaseClass): |
3069 | blockdev --getss /dev/vda | cat >vda_blockdev_getss |
3070 | blockdev --getpbsz /dev/vda | cat >vda_blockdev_getpbsz |
3071 | blockdev --getbsz /dev/vda | cat >vda_blockdev_getbsz |
3072 | + |
3073 | + exit 0 |
3074 | """)] |
3075 | |
3076 | def test_sys_firmware_efi(self): |
3077 | @@ -91,7 +93,7 @@ class TrustyUefiTestBasic(relbase.trusty, TestBasicAbs): |
3078 | __test__ = True |
3079 | |
3080 | |
3081 | -class TrustyHWEXUefiTestBasic(relbase.trusty_hwe_x, TrustyUefiTestBasic): |
3082 | +class TrustyHWEXUefiTestBasic(relbase.trusty_hwe_x, TestBasicAbs): |
3083 | __test__ = True |
3084 | |
3085 | |
3086 | @@ -115,27 +117,35 @@ class CosmicUefiTestBasic(relbase.cosmic, TestBasicAbs): |
3087 | __test__ = True |
3088 | |
3089 | |
3090 | +class DiscoUefiTestBasic(relbase.disco, TestBasicAbs): |
3091 | + __test__ = True |
3092 | + |
3093 | + |
3094 | class Centos70UefiTestBasic4k(centos_relbase.centos70_xenial, TestBasicAbs): |
3095 | disk_block_size = 4096 |
3096 | |
3097 | |
3098 | -class TrustyUefiTestBasic4k(TrustyUefiTestBasic): |
3099 | +class TrustyUefiTestBasic4k(relbase.trusty, TestBasicAbs): |
3100 | disk_block_size = 4096 |
3101 | |
3102 | |
3103 | -class TrustyHWEXUefiTestBasic4k(relbase.trusty_hwe_x, TrustyUefiTestBasic4k): |
3104 | - __test__ = True |
3105 | +class TrustyHWEXUefiTestBasic4k(relbase.trusty_hwe_x, TestBasicAbs): |
3106 | + disk_block_size = 4096 |
3107 | + |
3108 | + |
3109 | +class XenialGAUefiTestBasic4k(relbase.xenial_ga, TestBasicAbs): |
3110 | + disk_block_size = 4096 |
3111 | |
3112 | |
3113 | -class XenialGAUefiTestBasic4k(XenialGAUefiTestBasic): |
3114 | +class BionicUefiTestBasic4k(relbase.bionic, TestBasicAbs): |
3115 | disk_block_size = 4096 |
3116 | |
3117 | |
3118 | -class BionicUefiTestBasic4k(BionicUefiTestBasic): |
3119 | +class CosmicUefiTestBasic4k(relbase.cosmic, TestBasicAbs): |
3120 | disk_block_size = 4096 |
3121 | |
3122 | |
3123 | -class CosmicUefiTestBasic4k(CosmicUefiTestBasic): |
3124 | +class DiscoUefiTestBasic4k(relbase.disco, TestBasicAbs): |
3125 | disk_block_size = 4096 |
3126 | |
3127 | # vi: ts=4 expandtab syntax=python |
3128 | diff --git a/tests/vmtests/test_zfsroot.py b/tests/vmtests/test_zfsroot.py |
3129 | index 4e257ae..473c9e3 100644 |
3130 | --- a/tests/vmtests/test_zfsroot.py |
3131 | +++ b/tests/vmtests/test_zfsroot.py |
3132 | @@ -17,6 +17,8 @@ class TestZfsRootAbs(VMBaseClass): |
3133 | zfs list > zfs_list |
3134 | zpool list > zpool_list |
3135 | zpool status > zpool_status |
3136 | + |
3137 | + exit 0 |
3138 | """)] |
3139 | |
3140 | @skip_if_flag('expected_failure') |
3141 | @@ -88,6 +90,10 @@ class CosmicTestZfsRoot(relbase.cosmic, TestZfsRootAbs): |
3142 | __test__ = True |
3143 | |
3144 | |
3145 | +class DiscoTestZfsRoot(relbase.disco, TestZfsRootAbs): |
3146 | + __test__ = True |
3147 | + |
3148 | + |
3149 | class TestZfsRootFsTypeAbs(TestZfsRootAbs): |
3150 | conf_file = "examples/tests/basic-zfsroot.yaml" |
3151 | |
3152 | @@ -108,3 +114,7 @@ class BionicTestZfsRootFsType(relbase.bionic, TestZfsRootFsTypeAbs): |
3153 | |
3154 | class CosmicTestZfsRootFsType(relbase.cosmic, TestZfsRootFsTypeAbs): |
3155 | __test__ = True |
3156 | + |
3157 | + |
3158 | +class DiscoTestZfsRootFsType(relbase.disco, TestZfsRootFsTypeAbs): |
3159 | + __test__ = True |
3160 | diff --git a/tools/jenkins-runner b/tools/jenkins-runner |
3161 | index c1cef8e..bf8ea0a 100755 |
3162 | --- a/tools/jenkins-runner |
3163 | +++ b/tools/jenkins-runner |
3164 | @@ -79,14 +79,14 @@ if [ "${#tests[@]}" -ne 0 -a "${#ntfilters[@]}" -ne 0 ]; then |
3165 | error "Passing test tests and --filter are incompatible." |
3166 | error "test arguments provided were: ${#tests[*]}" |
3167 | fail |
3168 | -elif [ "${#tests[@]}" -eq 0 ]; then |
3169 | +elif [ "${#tests[@]}" -eq 0 -a "${#ntfilters[@]}" -eq 0 ]; then |
3170 | tests=( tests/vmtests ) |
3171 | elif [ "${#ntfilters[@]}" -ne 0 ]; then |
3172 | - tests=( $(./tools/vmtest-filter "${ntfilters[@]}") ) |
3173 | - if [ "${#tests[@]}" -eq 0 ]; then |
3174 | - error "Failed to find any tests with filter(s): \"${ntfilters[*]}\"" |
3175 | - fail "Try testing filters with: ./tools/vmtest-filter ${ntfilters[*]}" |
3176 | - fi |
3177 | + tests=( $(./tools/vmtest-filter "${ntfilters[@]}") ) |
3178 | + if [ "${#tests[@]}" -eq 0 ]; then |
3179 | + error "Failed to find any tests with filter(s): \"${ntfilters[*]}\"" |
3180 | + fail "Try testing filters with: ./tools/vmtest-filter ${ntfilters[*]}" |
3181 | + fi |
3182 | fi |
3183 | |
3184 | CURTIN_VMTEST_PARALLEL=$parallel |
PASSED: Continuous integration, rev:ce1e3c1d2e9 cc1927b07a2dad1 05820ac80c8948 /jenkins. ubuntu. com/server/ job/curtin- ci/1144/ /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-arm64/ 1144 /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-ppc64el/ 1144 /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-s390x/ 1144 /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= torkoal/ 1144
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild: /jenkins. ubuntu. com/server/ job/curtin- ci/1144/ rebuild
https:/