Merge ~raharper/curtin:ubuntu/devel/release-18.2 into curtin:ubuntu/devel

Proposed by Ryan Harper
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)
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_LINUX_DEFAULT in place.
      (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_source_list changes
    - apt: Use new format apt config when writing preserve_sources_list.
      (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

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/curtin/__init__.py b/curtin/__init__.py
index ee35ca3..a307e84 100644
--- a/curtin/__init__.py
+++ b/curtin/__init__.py
@@ -30,6 +30,6 @@ FEATURES = [
30 'HAS_VERSION_MODULE',30 'HAS_VERSION_MODULE',
31]31]
3232
33__version__ = "18.1"33__version__ = "18.2"
3434
35# vi: ts=4 expandtab syntax=python35# vi: ts=4 expandtab syntax=python
diff --git a/curtin/commands/apt_config.py b/curtin/commands/apt_config.py
index 9ce25b3..8bd6e79 100644
--- a/curtin/commands/apt_config.py
+++ b/curtin/commands/apt_config.py
@@ -10,7 +10,6 @@ import glob
10import os10import os
11import re11import re
12import sys12import sys
13import yaml
1413
15from curtin.log import LOG14from curtin.log import LOG
16from curtin import (config, distro, gpg, paths, util)15from curtin import (config, distro, gpg, paths, util)
@@ -71,6 +70,7 @@ def handle_apt(cfg, target=None):
71 if not config.value_as_boolean(cfg.get('preserve_sources_list',70 if not config.value_as_boolean(cfg.get('preserve_sources_list',
72 True)):71 True)):
73 generate_sources_list(cfg, release, mirrors, target)72 generate_sources_list(cfg, release, mirrors, target)
73 apply_preserve_sources_list(target)
74 rename_apt_lists(mirrors, target)74 rename_apt_lists(mirrors, target)
7575
76 try:76 try:
@@ -318,16 +318,32 @@ def generate_sources_list(cfg, release, mirrors, target=None):
318 disabled = disable_suites(cfg.get('disable_suites'), rendered, release)318 disabled = disable_suites(cfg.get('disable_suites'), rendered, release)
319 util.write_file(paths.target_path(target, aptsrc), disabled, mode=0o644)319 util.write_file(paths.target_path(target, aptsrc), disabled, mode=0o644)
320320
321
322def apply_preserve_sources_list(target):
321 # protect the just generated sources.list from cloud-init323 # protect the just generated sources.list from cloud-init
322 cloudfile = "/etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg"324 cloudfile = "/etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg"
323 # this has to work with older cloud-init as well, so use old key325
324 cloudconf = yaml.dump({'apt_preserve_sources_list': True}, indent=1)326 target_ver = distro.get_package_version('cloud-init', target=target)
327 if not target_ver:
328 LOG.info("Attempt to read cloud-init version from target returned "
329 "'%s', not writing preserve_sources_list config.",
330 target_ver)
331 return
332
333 cfg = {'apt': {'preserve_sources_list': True}}
334 if target_ver['major'] < 1:
335 # anything cloud-init 0.X.X will get the old config key.
336 cfg = {'apt_preserve_sources_list': True}
337
325 try:338 try:
326 util.write_file(paths.target_path(target, cloudfile),339 util.write_file(paths.target_path(target, cloudfile),
327 cloudconf, mode=0o644)340 config.dump_config(cfg), mode=0o644)
341 LOG.debug("Set preserve_sources_list to True in %s with: %s",
342 cloudfile, cfg)
328 except IOError:343 except IOError:
329 LOG.exception("Failed to protect source.list from cloud-init in (%s)",344 LOG.exception(
330 paths.target_path(target, cloudfile))345 "Failed to protect /etc/apt/sources.list from cloud-init in '%s'",
346 cloudfile)
331 raise347 raise
332348
333349
diff --git a/curtin/commands/block_meta.py b/curtin/commands/block_meta.py
index 197c1fd..8decab5 100644
--- a/curtin/commands/block_meta.py
+++ b/curtin/commands/block_meta.py
@@ -8,7 +8,8 @@ from curtin.log import LOG, logged_time
8from curtin.reporter import events8from curtin.reporter import events
99
10from . import populate_one_subcmd10from . import populate_one_subcmd
11from curtin.udev import compose_udev_equality, udevadm_settle, udevadm_trigger11from curtin.udev import (compose_udev_equality, udevadm_settle,
12 udevadm_trigger, udevadm_info)
1213
13import glob14import glob
14import os15import os
@@ -35,6 +36,8 @@ CMD_ARGUMENTS = (
35 'metavar': 'DEVICE', 'default': None, }),36 'metavar': 'DEVICE', 'default': None, }),
36 ('--fstype', {'help': 'root partition filesystem type',37 ('--fstype', {'help': 'root partition filesystem type',
37 'choices': ['ext4', 'ext3'], 'default': 'ext4'}),38 'choices': ['ext4', 'ext3'], 'default': 'ext4'}),
39 ('--force-mode', {'help': 'force mode, disable mode detection',
40 'action': 'store_true', 'default': False}),
38 (('-t', '--target'),41 (('-t', '--target'),
39 {'help': 'chroot to target. default is env[TARGET_MOUNT_POINT]',42 {'help': 'chroot to target. default is env[TARGET_MOUNT_POINT]',
40 'action': 'store', 'metavar': 'TARGET',43 'action': 'store', 'metavar': 'TARGET',
@@ -55,11 +58,35 @@ def block_meta(args):
55 state = util.load_command_environment()58 state = util.load_command_environment()
56 cfg = config.load_command_config(args, state)59 cfg = config.load_command_config(args, state)
57 dd_images = util.get_dd_images(cfg.get('sources', {}))60 dd_images = util.get_dd_images(cfg.get('sources', {}))
58 if ((args.mode == CUSTOM or cfg.get("storage") is not None) and61
59 len(dd_images) == 0):62 # run clear holders on potential devices
60 meta_custom(args)63 devices = args.devices
61 elif args.mode in (SIMPLE, SIMPLE_BOOT) or len(dd_images) > 0:64 if devices is None:
62 meta_simple(args)65 devices = []
66 if 'storage' in cfg:
67 devices = get_disk_paths_from_storage_config(
68 extract_storage_ordered_dict(cfg))
69 if len(devices) == 0:
70 devices = cfg.get('block-meta', {}).get('devices', [])
71 LOG.debug('Declared block devices: %s', devices)
72 args.devices = devices
73
74 meta_clear(devices, state.get('report_stack_prefix', ''))
75
76 # dd-images requires use of meta_simple
77 if len(dd_images) > 0 and args.force_mode is False:
78 LOG.info('blockmeta: detected dd-images, using mode=simple')
79 return meta_simple(args)
80
81 if cfg.get("storage") and args.force_mode is False:
82 LOG.info('blockmeta: detected storage config, using mode=custom')
83 return meta_custom(args)
84
85 LOG.info('blockmeta: mode=%s force=%s', args.mode, args.force_mode)
86 if args.mode == CUSTOM:
87 return meta_custom(args)
88 elif args.mode in (SIMPLE, SIMPLE_BOOT):
89 return meta_simple(args)
63 else:90 else:
64 raise NotImplementedError("mode=%s is not implemented" % args.mode)91 raise NotImplementedError("mode=%s is not implemented" % args.mode)
6592
@@ -195,12 +222,43 @@ def sanitize_dname(dname):
195 return ''.join(c if c in valid else '-' for c in dname)222 return ''.join(c if c in valid else '-' for c in dname)
196223
197224
225def make_dname_byid(path, error_msg=None, info=None):
226 """ Returns a list of udev equalities for a given disk path
227
228 :param path: string of a kernel device path to a block device
229 :param error_msg: more information about path for log/errors
230 :param info: dict of udevadm info key, value pairs of device specified by
231 path.
232 :returns: list of udev equalities (lists)
233 :raises: ValueError if path is not a disk.
234 :raises: RuntimeError if there is no serial or wwn.
235 """
236 error_msg = str(path) + ("" if not error_msg else " [%s]" % error_msg)
237 if info is None:
238 info = udevadm_info(path=path)
239 devtype = info.get('DEVTYPE')
240 if devtype != "disk":
241 raise ValueError(
242 "Disk tag udev rules are only for disks, %s has devtype=%s" %
243 (error_msg, devtype))
244
245 byid_keys = ['ID_SERIAL', 'ID_WWN_WITH_EXTENSION']
246 present = [k for k in byid_keys if info.get(k)]
247 if not present:
248 raise RuntimeError(
249 "Cannot create disk tag udev rule for %s, "
250 "missing 'serial' or 'wwn' value" % error_msg)
251
252 return [[compose_udev_equality('ENV{%s}' % k, info[k]) for k in present]]
253
254
198def make_dname(volume, storage_config):255def make_dname(volume, storage_config):
199 state = util.load_command_environment()256 state = util.load_command_environment()
200 rules_dir = os.path.join(state['scratch'], "rules.d")257 rules_dir = os.path.join(state['scratch'], "rules.d")
201 vol = storage_config.get(volume)258 vol = storage_config.get(volume)
202 path = get_path_to_storage_volume(volume, storage_config)259 path = get_path_to_storage_volume(volume, storage_config)
203 ptuuid = None260 ptuuid = None
261 byid = None
204 dname = vol.get('name')262 dname = vol.get('name')
205 if vol.get('type') in ["partition", "disk"]:263 if vol.get('type') in ["partition", "disk"]:
206 (out, _err) = util.subp(["blkid", "-o", "export", path], capture=True,264 (out, _err) = util.subp(["blkid", "-o", "export", path], capture=True,
@@ -209,28 +267,41 @@ def make_dname(volume, storage_config):
209 if "PTUUID" in line or "PARTUUID" in line:267 if "PTUUID" in line or "PARTUUID" in line:
210 ptuuid = line.split('=')[-1]268 ptuuid = line.split('=')[-1]
211 break269 break
270 if vol.get('type') == 'disk':
271 byid = make_dname_byid(path, error_msg="id=%s" % vol.get('id'))
212 # we may not always be able to find a uniq identifier on devices with names272 # we may not always be able to find a uniq identifier on devices with names
213 if not ptuuid and vol.get('type') in ["disk", "partition"]:273 if (not ptuuid and not byid) and vol.get('type') in ["disk", "partition"]:
214 LOG.warning("Can't find a uuid for volume: {}. Skipping dname.".format(274 LOG.warning("Can't find a uuid for volume: {}. Skipping dname.".format(
215 volume))275 volume))
216 return276 return
217277
218 rule = [278 matches = []
279 base_rule = [
219 compose_udev_equality("SUBSYSTEM", "block"),280 compose_udev_equality("SUBSYSTEM", "block"),
220 compose_udev_equality("ACTION", "add|change"),281 compose_udev_equality("ACTION", "add|change"),
221 ]282 ]
222 if vol.get('type') == "disk":283 if vol.get('type') == "disk":
223 rule.append(compose_udev_equality('ENV{DEVTYPE}', "disk"))284 if ptuuid:
224 rule.append(compose_udev_equality('ENV{ID_PART_TABLE_UUID}', ptuuid))285 matches += [[compose_udev_equality('ENV{DEVTYPE}', "disk"),
286 compose_udev_equality('ENV{ID_PART_TABLE_UUID}',
287 ptuuid)]]
288 for rule in byid:
289 matches += [
290 [compose_udev_equality('ENV{DEVTYPE}', "disk")] + rule]
225 elif vol.get('type') == "partition":291 elif vol.get('type') == "partition":
226 rule.append(compose_udev_equality('ENV{DEVTYPE}', "partition"))292 # if partition has its own name, bind that to the existing PTUUID
227 dname = storage_config.get(vol.get('device')).get('name') + \293 if dname:
228 "-part%s" % determine_partition_number(volume, storage_config)294 matches += [[compose_udev_equality('ENV{DEVTYPE}', "partition"),
229 rule.append(compose_udev_equality('ENV{ID_PART_ENTRY_UUID}', ptuuid))295 compose_udev_equality('ENV{ID_PART_ENTRY_UUID}',
296 ptuuid)]]
297 else:
298 # disks generate dname-part%n rules automatically
299 LOG.debug('No partition-specific dname')
300 return
230 elif vol.get('type') == "raid":301 elif vol.get('type') == "raid":
231 md_data = mdadm.mdadm_query_detail(path)302 md_data = mdadm.mdadm_query_detail(path)
232 md_uuid = md_data.get('MD_UUID')303 md_uuid = md_data.get('MD_UUID')
233 rule.append(compose_udev_equality("ENV{MD_UUID}", md_uuid))304 matches += [[compose_udev_equality("ENV{MD_UUID}", md_uuid)]]
234 elif vol.get('type') == "bcache":305 elif vol.get('type') == "bcache":
235 # bind dname to bcache backing device's dev.uuid as the bcache minor306 # bind dname to bcache backing device's dev.uuid as the bcache minor
236 # device numbers are not stable across reboots.307 # device numbers are not stable across reboots.
@@ -239,12 +310,12 @@ def make_dname(volume, storage_config):
239 bcache_super = bcache.superblock_asdict(device=backing_dev)310 bcache_super = bcache.superblock_asdict(device=backing_dev)
240 if bcache_super and bcache_super['sb.version'].startswith('1'):311 if bcache_super and bcache_super['sb.version'].startswith('1'):
241 bdev_uuid = bcache_super['dev.uuid']312 bdev_uuid = bcache_super['dev.uuid']
242 rule.append(compose_udev_equality("ENV{CACHED_UUID}", bdev_uuid))313 matches += [[compose_udev_equality("ENV{CACHED_UUID}", bdev_uuid)]]
243 bcache.write_label(sanitize_dname(dname), backing_dev)314 bcache.write_label(sanitize_dname(dname), backing_dev)
244 elif vol.get('type') == "lvm_partition":315 elif vol.get('type') == "lvm_partition":
245 volgroup_name = storage_config.get(vol.get('volgroup')).get('name')316 volgroup_name = storage_config.get(vol.get('volgroup')).get('name')
246 dname = "%s-%s" % (volgroup_name, dname)317 dname = "%s-%s" % (volgroup_name, dname)
247 rule.append(compose_udev_equality("ENV{DM_NAME}", dname))318 matches += [[compose_udev_equality("ENV{DM_NAME}", dname)]]
248 else:319 else:
249 raise ValueError('cannot make dname for device with type: {}'320 raise ValueError('cannot make dname for device with type: {}'
250 .format(vol.get('type')))321 .format(vol.get('type')))
@@ -257,11 +328,25 @@ def make_dname(volume, storage_config):
257 LOG.warning(328 LOG.warning(
258 "dname modified to remove invalid chars. old: '{}' new: '{}'"329 "dname modified to remove invalid chars. old: '{}' new: '{}'"
259 .format(dname, sanitized))330 .format(dname, sanitized))
260 rule.append("SYMLINK+=\"disk/by-dname/%s\"\n" % sanitized)331 content = ['# Written by curtin']
261 LOG.debug("Writing dname udev rule '{}'".format(str(rule)))332 for match in matches:
333 rule = (base_rule + match +
334 ["SYMLINK+=\"disk/by-dname/%s\"\n" % sanitized])
335 LOG.debug("Creating dname udev rule '{}'".format(str(rule)))
336 content.append(', '.join(rule))
337
338 if vol.get('type') == 'disk':
339 for brule in byid:
340 rule = (base_rule +
341 [compose_udev_equality('ENV{DEVTYPE}', 'partition')] +
342 brule +
343 ['SYMLINK+="disk/by-dname/%s-part%%n"\n' % sanitized])
344 LOG.debug("Creating dname udev rule '{}'".format(str(rule)))
345 content.append(', '.join(rule))
346
262 util.ensure_dir(rules_dir)347 util.ensure_dir(rules_dir)
263 rule_file = os.path.join(rules_dir, '{}.rules'.format(sanitized))348 rule_file = os.path.join(rules_dir, '{}.rules'.format(sanitized))
264 util.write_file(rule_file, ', '.join(rule))349 util.write_file(rule_file, '\n'.join(content))
265350
266351
267def get_poolname(info, storage_config):352def get_poolname(info, storage_config):
@@ -584,6 +669,9 @@ def partition_handler(info, storage_config):
584 block.zero_file_at_offsets(disk, [wipe_offset], exclusive=False)669 block.zero_file_at_offsets(disk, [wipe_offset], exclusive=False)
585670
586 if disk_ptable == "msdos":671 if disk_ptable == "msdos":
672 if flag and flag == 'prep':
673 raise ValueError('PReP partitions require a GPT partition table')
674
587 if flag in ["extended", "logical", "primary"]:675 if flag in ["extended", "logical", "primary"]:
588 partition_type = flag676 partition_type = flag
589 else:677 else:
@@ -604,6 +692,16 @@ def partition_handler(info, storage_config):
604 else:692 else:
605 raise ValueError("parent partition has invalid partition table")693 raise ValueError("parent partition has invalid partition table")
606694
695 # wipe the created partition if needed, superblocks have already been wiped
696 wipe_mode = info.get('wipe', 'superblock')
697 if wipe_mode != 'superblock':
698 part_path = block.dev_path(block.partition_kname(disk_kname,
699 partnumber))
700 block.rescan_block_devices([disk])
701 udevadm_settle(exists=part_path)
702 LOG.debug('Wiping partition %s mode=%s', part_path, wipe_mode)
703 block.wipe_volume(part_path, mode=wipe_mode, exclusive=False)
704
607 # Make the name if needed705 # Make the name if needed
608 if storage_config.get(device).get('name') and partition_type != 'extended':706 if storage_config.get(device).get('name') and partition_type != 'extended':
609 make_dname(info.get('id'), storage_config)707 make_dname(info.get('id'), storage_config)
@@ -1335,6 +1433,19 @@ def extract_storage_ordered_dict(config):
1335 return OrderedDict((d["id"], d) for (i, d) in enumerate(scfg))1433 return OrderedDict((d["id"], d) for (i, d) in enumerate(scfg))
13361434
13371435
1436def get_disk_paths_from_storage_config(storage_config):
1437 """Returns a list of disk paths in a storage config filtering out
1438 preserved or disks which do not have wipe configuration.
1439
1440 :param: storage_config: Ordered dict of storage configation
1441 """
1442 return [get_path_to_storage_volume(k, storage_config)
1443 for (k, v) in storage_config.items()
1444 if v.get('type') == 'disk' and
1445 config.value_as_boolean(v.get('wipe')) and
1446 not config.value_as_boolean(v.get('preserve'))]
1447
1448
1338def zfsroot_update_storage_config(storage_config):1449def zfsroot_update_storage_config(storage_config):
1339 """Return an OrderedDict that has 'zfsroot' format expanded into1450 """Return an OrderedDict that has 'zfsroot' format expanded into
1340 zpool and zfs commands to enable ZFS on rootfs.1451 zpool and zfs commands to enable ZFS on rootfs.
@@ -1429,6 +1540,24 @@ def zfsroot_update_storage_config(storage_config):
1429 return ret1540 return ret
14301541
14311542
1543def meta_clear(devices, report_prefix=''):
1544 """ Run clear_holders on specified list of devices.
1545
1546 :param: devices: a list of block devices (/dev/XXX) to be cleared
1547 :param: report_prefix: a string to pass to the ReportEventStack
1548 """
1549 # shut down any already existing storage layers above any disks used in
1550 # config that have 'wipe' set
1551 with events.ReportEventStack(
1552 name=report_prefix + '/clear-holders',
1553 reporting_enabled=True, level='INFO',
1554 description="removing previous storage devices"):
1555 clear_holders.start_clear_holders_deps()
1556 clear_holders.clear_holders(devices)
1557 # if anything was not properly shut down, stop installation
1558 clear_holders.assert_clear(devices)
1559
1560
1432def meta_custom(args):1561def meta_custom(args):
1433 """Does custom partitioning based on the layout provided in the config1562 """Does custom partitioning based on the layout provided in the config
1434 file. Section with the name storage contains information on which1563 file. Section with the name storage contains information on which
@@ -1460,21 +1589,6 @@ def meta_custom(args):
1460 # set up reportstack1589 # set up reportstack
1461 stack_prefix = state.get('report_stack_prefix', '')1590 stack_prefix = state.get('report_stack_prefix', '')
14621591
1463 # shut down any already existing storage layers above any disks used in
1464 # config that have 'wipe' set
1465 with events.ReportEventStack(
1466 name=stack_prefix, reporting_enabled=True, level='INFO',
1467 description="removing previous storage devices"):
1468 clear_holders.start_clear_holders_deps()
1469 disk_paths = [get_path_to_storage_volume(k, storage_config_dict)
1470 for (k, v) in storage_config_dict.items()
1471 if v.get('type') == 'disk' and
1472 config.value_as_boolean(v.get('wipe')) and
1473 not config.value_as_boolean(v.get('preserve'))]
1474 clear_holders.clear_holders(disk_paths)
1475 # if anything was not properly shut down, stop installation
1476 clear_holders.assert_clear(disk_paths)
1477
1478 for item_id, command in storage_config_dict.items():1592 for item_id, command in storage_config_dict.items():
1479 handler = command_handlers.get(command['type'])1593 handler = command_handlers.get(command['type'])
1480 if not handler:1594 if not handler:
@@ -1500,8 +1614,15 @@ def meta_simple(args):
1500 create a separate /boot partition.1614 create a separate /boot partition.
1501 """1615 """
1502 state = util.load_command_environment()1616 state = util.load_command_environment()
1503
1504 cfg = config.load_command_config(args, state)1617 cfg = config.load_command_config(args, state)
1618 if args.target is not None:
1619 state['target'] = args.target
1620
1621 if state['target'] is None:
1622 sys.stderr.write("Unable to find target. "
1623 "Use --target or set TARGET_MOUNT_POINT\n")
1624 sys.exit(2)
1625
1505 devpath = None1626 devpath = None
1506 if cfg.get("storage") is not None:1627 if cfg.get("storage") is not None:
1507 for i in cfg["storage"]["config"]:1628 for i in cfg["storage"]["config"]:
@@ -1512,21 +1633,8 @@ def meta_simple(args):
1512 diskPath = block.lookup_disk(serial)1633 diskPath = block.lookup_disk(serial)
1513 if grub is True:1634 if grub is True:
1514 devpath = diskPath1635 devpath = diskPath
1515 if config.value_as_boolean(i.get('wipe')):
1516 block.wipe_volume(diskPath, mode=i.get('wipe'))
1517
1518 if args.target is not None:
1519 state['target'] = args.target
1520
1521 if state['target'] is None:
1522 sys.stderr.write("Unable to find target. "
1523 "Use --target or set TARGET_MOUNT_POINT\n")
1524 sys.exit(2)
15251636
1526 devices = args.devices1637 devices = args.devices
1527 if devices is None:
1528 devices = cfg.get('block-meta', {}).get('devices', [])
1529
1530 bootpt = get_bootpt_cfg(1638 bootpt = get_bootpt_cfg(
1531 cfg.get('block-meta', {}).get('boot-partition', {}),1639 cfg.get('block-meta', {}).get('boot-partition', {}),
1532 enabled=args.mode == SIMPLE_BOOT, fstype=args.boot_fstype,1640 enabled=args.mode == SIMPLE_BOOT, fstype=args.boot_fstype,
diff --git a/curtin/swap.py b/curtin/swap.py
index 5f77b03..d3f29dc 100644
--- a/curtin/swap.py
+++ b/curtin/swap.py
@@ -103,9 +103,15 @@ def is_swap_device(path):
103 https://github.com/torvalds/linux/blob/master/include/linux/swap.h#L111103 https://github.com/torvalds/linux/blob/master/include/linux/swap.h#L111
104 """104 """
105 LOG.debug('Checking if %s is a swap device', path)105 LOG.debug('Checking if %s is a swap device', path)
106 swap_magic_offset = resource.getpagesize() - 10106 pagesize = resource.getpagesize()
107 magic = util.load_file(path, read_len=10, offset=swap_magic_offset,107 magic_offset = pagesize - 10
108 decode=False)108 size = util.file_size(path)
109 if size < magic_offset:
110 LOG.debug("%s is to small for swap (size=%d < pagesize=%d)",
111 path, size, pagesize)
112 return False
113 magic = util.load_file(
114 path, read_len=10, offset=magic_offset, decode=False)
109 LOG.debug('Found swap magic: %s' % magic)115 LOG.debug('Found swap magic: %s' % magic)
110 return magic in [b'SWAPSPACE2', b'SWAP-SPACE']116 return magic in [b'SWAPSPACE2', b'SWAP-SPACE']
111# vi: ts=4 expandtab syntax=python117# vi: ts=4 expandtab syntax=python
diff --git a/curtin/udev.py b/curtin/udev.py
index 13d9cc5..106a7e7 100644
--- a/curtin/udev.py
+++ b/curtin/udev.py
@@ -61,4 +61,41 @@ def udevadm_trigger(devices):
61 util.subp(['udevadm', 'trigger'] + list(devices))61 util.subp(['udevadm', 'trigger'] + list(devices))
62 udevadm_settle()62 udevadm_settle()
6363
64
65def udevadm_info(path=None):
66 """ Return a dictionary populated by properties of the device specified
67 in the `path` variable via querying udev 'property' database.
68
69 :params: path: path to device, either /dev or /sys
70 :returns: dictionary of key=value pairs as exported from the udev database
71 :raises: ValueError path is None, ProcessExecutionError on exec error.
72 """
73 if not path:
74 raise ValueError('Invalid path: "%s"' % path)
75
76 info_cmd = ['udevadm', 'info', '--query=property', path]
77 output, _ = util.subp(info_cmd, capture=True)
78
79 # strip for trailing empty line
80 info = {}
81 for line in output.splitlines():
82 if not line:
83 continue
84 # maxsplit=2 gives us key and remaininng part of line is value
85 # py2.7 on Trusty doesn't have keyword, pass as argument
86 key, value = line.split('=', 2)
87 if not value:
88 value = None
89 if value:
90 # devlinks is a list of paths separated by space
91 # convert to a list for easy use
92 if key == 'DEVLINKS':
93 info[key] = value.split()
94 else:
95 # preserve spaces in values, to match udev database
96 info[key] = value
97
98 return info
99
100
64# vi: ts=4 expandtab syntax=python101# vi: ts=4 expandtab syntax=python
diff --git a/debian/changelog b/debian/changelog
index 89bb20f..ef377fe 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,30 @@
1curtin (18.2-0ubuntu1) disco; urgency=medium
2
3 * New upstream release (18.2).
4 - Release 18.2
5 - Adjust helpers/common to edit GRUB_CMDLINE_LINUX_DEFAULT in place.
6 (LP: #1527664)
7 - dname: persistent names based on serial or wwn (LP: #1735839)
8 - Fix bug in is_swap_device if a device was smaller than page_size.
9 (LP: #1803672)
10 - vmtest: add disco tests [Joshua Powers]
11 - unittest: change directory to tmpdir for testing relative files.
12 - Add clear-holders to meta-simple (LP: #1786736)
13 - vmtests: check install log for Out of memory kernel messages and fail
14 - unittest: correctly use tmpdir for my.img [Joshua Powers] (LP: #1803611)
15 - block_meta: use wipe config when clearing partitions (LP: #1800153)
16 - tests: fix vmtests for apt perserve_source_list changes
17 - apt: Use new format apt config when writing preserve_sources_list.
18 (LP: #1735950)
19 - vmtests: multipath mount /home with nofail and validate in unittest
20 - vmtests: fix common collect scripts to not exit failure.
21 - vmtest: handle collect disk unpack failure
22 - vmtests: dont use multiple subclasses in uefi 4k tests
23 - vmtests: disable snapd/seeding to avoid boot hang
24 - jenkins-runner: fix when using --filter only
25
26 -- Ryan Harper <ryan.harper@canonical.com> Thu, 06 Dec 2018 12:11:01 -0600
27
1curtin (18.1-59-g0f993084-0ubuntu1) cosmic; urgency=medium28curtin (18.1-59-g0f993084-0ubuntu1) cosmic; urgency=medium
229
3 * New upstream snapshot.30 * New upstream snapshot.
diff --git a/doc/topics/apt_source.rst b/doc/topics/apt_source.rst
index 9795361..f996c53 100644
--- a/doc/topics/apt_source.rst
+++ b/doc/topics/apt_source.rst
@@ -165,3 +165,60 @@ Dependencies
165~~~~~~~~~~~~165~~~~~~~~~~~~
166Cloud-init might need to resolve dependencies and install packages in the ephemeral environment to run curtin.166Cloud-init might need to resolve dependencies and install packages in the ephemeral environment to run curtin.
167Therefore 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.167Therefore 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.
168
169
170apt preserve_sources_list setting
171~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
172cloud-init and curtin treat the ``preserve_sources_list`` setting slightly differently, and thus this setting deserves its own section.
173
174Interpretation / Meaning
175------------------------
176curtin 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...).
177
178cloud-init reads ``preserve_sources_list`` to indicate whether or not it should *render* ``/etc/apt/sources.list`` from its built-in template.
179
180defaults
181--------
182Just for reference, the ``preserve_sources_list`` defaults in curtin and cloud-init are:
183
184 * curtin: **true**
185 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.
186 * cloud-init: **false**
187 * cloud-init in ephemeral environment: **false**
188 * cloud-init system installed by curtin: **true**
189 (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.
190
191preserve_sources_list in MAAS
192-----------------------------
193Curtin and cloud-init use the same ``apt`` configuration language.
194MAAS provides apt config in three different scenarios.
195
196 1. To cloud-init in ephemeral environment (rescue, install or commissioning)
197 Here MAAS **should not send a value**. If it wants to be explicit it should send ``preserve_sources_list: false``.
198
199 2. To curtin in curtin config
200 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.
201
202 3. To cloud-init via curtin config in debconf_selections.
203 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.
204
205 4. To installed system via vendor-data or user-data.
206 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.
207
208Legacy format
209-------------
210
211Versions of cloud-init in 14.04 and older only support:
212
213.. code-block:: yaml
214
215 apt_preserve_sources_list: VALUE
216
217Versions of cloud-init present 16.04+ read the "new" style apt configuration, but support the old style configuration also. The new style configuration is:
218
219.. code-block:: yaml
220
221 apt:
222 preserve_sources_list: VALUE
223
224**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.
diff --git a/doc/topics/storage.rst b/doc/topics/storage.rst
index b28964b..f9a9fe8 100644
--- a/doc/topics/storage.rst
+++ b/doc/topics/storage.rst
@@ -175,7 +175,15 @@ not need to be preserved.
175If the ``name`` key is present, curtin will create a udev rule that makes a175If the ``name`` key is present, curtin will create a udev rule that makes a
176symbolic link to the disk with the given name value. This makes it easy to find176symbolic link to the disk with the given name value. This makes it easy to find
177disks on an installed system. The links are created in177disks on an installed system. The links are created in
178``/dev/disk/by-dname/<name>``.178``/dev/disk/by-dname/<name>``. The udev rules will utilize two types of disk
179metadata to construct the link. For disks with ``serial`` and/or ``wwn`` values
180these will be used to ensure the name persists even if the contents of the disk
181change. For legacy purposes, curtin also emits a rule utilizing metadata on
182the disk contents, typically a partition UUID value, this also preserves these
183links for disks which lack persistent attributes such as a ``serial`` or
184``wwn``, typically found on virtualized environments where such values are left
185unset.
186
179A link to each partition on the disk will also be created at187A link to each partition on the disk will also be created at
180``/dev/disk/by-dname/<name>-part<number>``, so if ``name: maindisk`` is set,188``/dev/disk/by-dname/<name>-part<number>``, so if ``name: maindisk`` is set,
181the disk will be at ``/dev/disk/by-dname/maindisk`` and the first partition on189the disk will be at ``/dev/disk/by-dname/maindisk`` and the first partition on
@@ -237,6 +245,14 @@ After the partition is added to the disk's partition table, curtin can run a
237wipe command on the partition. The wipe command values are the sames as for245wipe command on the partition. The wipe command values are the sames as for
238disks.246disks.
239247
248.. note::
249
250 Curtin will automatically wipe 1MB at the starting location of the partition
251 prior to creating the partition to ensure that other block layers or devices
252 do not enable themselves and prevent accessing the partition. Wipe
253 and other destructive operations only occur if the ``preserve`` value
254 is not set to ``True``.
255
240**flag**: *logical, extended, boot, bios_grub, swap, lvm, raid, home, prep*256**flag**: *logical, extended, boot, bios_grub, swap, lvm, raid, home, prep*
241257
242If the ``flag`` key is present, curtin will set the specified flag on the258If the ``flag`` key is present, curtin will set the specified flag on the
@@ -268,6 +284,17 @@ filesystem or be mounted anywhere on the system.
268If the preserve flag is set to true, curtin will verify that the partition284If the preserve flag is set to true, curtin will verify that the partition
269exists and will not modify the partition.285exists and will not modify the partition.
270286
287**name**: *<name>*
288
289If the ``name`` key is present, curtin will create a udev rule that makes a
290symbolic link to the partition with the given name value. The links are created
291in ``/dev/disk/by-dname/<name>``.
292
293For partitions, the udev rule created relies upon disk contents, in this case
294the partition entry UUID. This will remain in effect unless the underlying disk
295on which the partition resides has the partition table modified or wiped.
296
297
271**Config Example**::298**Config Example**::
272299
273 - id: disk0-part1300 - id: disk0-part1
@@ -276,6 +303,7 @@ exists and will not modify the partition.
276 size: 8GB303 size: 8GB
277 device: disk0304 device: disk0
278 flag: boot305 flag: boot
306 name: boot_partition
279307
280.. _format:308.. _format:
281309
@@ -496,6 +524,12 @@ scheme for Logical Volumes follows the pattern
496with ``name`` *lv1* on a ``lvm_volgroup`` named *vg1* would have the path524with ``name`` *lv1* on a ``lvm_volgroup`` named *vg1* would have the path
497``/dev/disk/by-dname/vg1-lv1``.525``/dev/disk/by-dname/vg1-lv1``.
498526
527.. note::
528
529 dname values for contructed devices (such as lvm) only remain persistent
530 as long as the device metadata does not change. If users modify the device
531 such that device metadata is changed then the udev rule may no longer apply.
532
499**volgroup**: *<volgroup id>*533**volgroup**: *<volgroup id>*
500534
501The ``volgroup`` key specifies the ``id`` of the Volume Group in which to535The ``volgroup`` key specifies the ``id`` of the Volume Group in which to
@@ -592,7 +626,9 @@ The ``name`` key specifies the name of the md device.
592.. note::626.. note::
593627
594 Curtin creates a udev rule to create a link to the md device in628 Curtin creates a udev rule to create a link to the md device in
595 ``/dev/disk/by-dname/<name>`` using the specified name.629 ``/dev/disk/by-dname/<name>`` using the specified name. The dname
630 symbolic link is only persistent as long as the raid metadata is
631 not modifed or destroyed.
596632
597**raidlevel**: *0, 1, 5, 6, 10*633**raidlevel**: *0, 1, 5, 6, 10*
598634
@@ -669,6 +705,13 @@ reads/writes from the cache. None effectively disables bcache.
669If the ``name`` key is present, curtin will create a link to the device at705If the ``name`` key is present, curtin will create a link to the device at
670``/dev/disk/by-dname/<name>``.706``/dev/disk/by-dname/<name>``.
671707
708.. note::
709
710 dname values for contructed devices (such as bcache) only remain persistent
711 as long as the device metadata does not change. If users modify the device
712 such that device metadata is changed then the udev rule may no longer apply.
713
714
672**Config Example**::715**Config Example**::
673716
674 - id: bcache0717 - id: bcache0
diff --git a/examples/tests/basic.yaml b/examples/tests/basic.yaml
index 089e776..71730c0 100644
--- a/examples/tests/basic.yaml
+++ b/examples/tests/basic.yaml
@@ -26,6 +26,7 @@ storage:
26 number: 326 number: 3
27 size: 1GB27 size: 1GB
28 device: sda28 device: sda
29 name: swap
29 - id: sda1_root30 - id: sda1_root
30 type: format31 type: format
31 fstype: ext432 fstype: ext4
@@ -83,6 +84,14 @@ storage:
83 device: pnum_disk84 device: pnum_disk
84 - id: pnum_disk_p285 - id: pnum_disk_p2
85 type: partition86 type: partition
87 number: 2
88 size: 8MB
89 device: pnum_disk
90 flag: prep
91 wipe: zero
92 name: prep
93 - id: pnum_disk_p3
94 type: partition
86 number: 1095 number: 10
87 size: 1GB96 size: 1GB
88 device: pnum_disk97 device: pnum_disk
diff --git a/examples/tests/basic_scsi.yaml b/examples/tests/basic_scsi.yaml
index 5f60459..aa62137 100644
--- a/examples/tests/basic_scsi.yaml
+++ b/examples/tests/basic_scsi.yaml
@@ -20,14 +20,25 @@ storage:
20 number: 220 number: 2
21 size: 1GB21 size: 1GB
22 device: sda22 device: sda
23 - id: sda3
24 type: partition
25 number: 3
26 size: 1GB
27 device: sda
28 name: swap
23 - id: sda1_root29 - id: sda1_root
24 type: format30 type: format
25 fstype: ext431 fstype: ext4
26 volume: sda132 volume: sda1
33 label: 'cloudimg-rootfs'
27 - id: sda2_home34 - id: sda2_home
28 type: format35 type: format
29 fstype: ext436 fstype: ext4
30 volume: sda237 volume: sda2
38 - id: sda3_swap
39 type: format
40 fstype: swap
41 volume: sda3
31 - id: sda1_mount42 - id: sda1_mount
32 type: mount43 type: mount
33 path: /44 path: /
@@ -41,6 +52,10 @@ storage:
41 wwn: '0x080258d13ea95ae5'52 wwn: '0x080258d13ea95ae5'
42 name: sparedisk53 name: sparedisk
43 wipe: superblock54 wipe: superblock
55 - id: sparedisk_fat_fmt_id
56 type: format
57 fstype: fat32
58 volume: sparedisk_id
44 - id: btrfs_disk_id59 - id: btrfs_disk_id
45 type: disk60 type: disk
46 wwn: '0x22dc58dc023c7008'61 wwn: '0x22dc58dc023c7008'
@@ -68,6 +83,18 @@ storage:
68 device: pnum_disk83 device: pnum_disk
69 - id: pnum_disk_p284 - id: pnum_disk_p2
70 type: partition85 type: partition
86 number: 2
87 size: 8MB
88 device: pnum_disk
89 flag: prep
90 wipe: zero
91 name: prep
92 - id: pnum_disk_p3
93 type: partition
71 number: 1094 number: 10
72 size: 1GB95 size: 1GB
73 device: pnum_disk96 device: pnum_disk
97 - id: swap_mnt
98 type: mount
99 path: "none"
100 device: sda3_swap
diff --git a/examples/tests/multipath.yaml b/examples/tests/multipath.yaml
index d4b928c..8447d55 100644
--- a/examples/tests/multipath.yaml
+++ b/examples/tests/multipath.yaml
@@ -36,3 +36,4 @@ storage:
36 type: mount36 type: mount
37 path: /home37 path: /home
38 device: sda2_home38 device: sda2_home
39 options: 'defaults,nofail'
diff --git a/examples/tests/nvme.yaml b/examples/tests/nvme.yaml
index b2a1276..4cf7735 100644
--- a/examples/tests/nvme.yaml
+++ b/examples/tests/nvme.yaml
@@ -44,7 +44,7 @@ storage:
44 device: main_disk_home44 device: main_disk_home
45 - id: nvme_disk45 - id: nvme_disk
46 type: disk46 type: disk
47 path: /dev/nvme0n147 serial: nvme-a
48 name: nvme_disk48 name: nvme_disk
49 wipe: superblock49 wipe: superblock
50 ptable: gpt50 ptable: gpt
@@ -63,7 +63,7 @@ storage:
63 device: nvme_disk63 device: nvme_disk
64 - id: nvme_disk264 - id: nvme_disk2
65 type: disk65 type: disk
66 path: /dev/nvme1n166 serial: nvme-b
67 wipe: superblock67 wipe: superblock
68 ptable: msdos68 ptable: msdos
69 name: second_nvme69 name: second_nvme
diff --git a/examples/tests/nvme_bcache.yaml b/examples/tests/nvme_bcache.yaml
index 2ee0ad3..4fefd94 100644
--- a/examples/tests/nvme_bcache.yaml
+++ b/examples/tests/nvme_bcache.yaml
@@ -19,7 +19,7 @@ storage:
19 model: INTEL SSDPEDME400G419 model: INTEL SSDPEDME400G4
20 name: nvme0n120 name: nvme0n1
21 ptable: gpt21 ptable: gpt
22 path: /dev/nvme0n122 serial: nvme-a
23 type: disk23 type: disk
24 wipe: superblock24 wipe: superblock
25 - device: sda25 - device: sda
diff --git a/examples/tests/simple-storage.yaml b/examples/tests/simple-storage.yaml
26new file mode 10064426new file mode 100644
index 0000000..644c5aa
--- /dev/null
+++ b/examples/tests/simple-storage.yaml
@@ -0,0 +1,48 @@
1# MAAS will send storage config to dd and windows to help pick boot device
2# this test forces curtin down a block-meta simple path along with storage cfg
3partitioning_commands:
4 builtin: [curtin, block-meta, simple, --force-mode]
5showtrace: true
6storage:
7 version: 1
8 config:
9 - id: sda
10 type: disk
11 wipe: superblock
12 ptable: msdos
13 model: QEMU HARDDISK
14 serial: disk-a
15 grub_device: true
16 - id: sdb
17 type: disk
18 wipe: superblock
19 ptable: msdos
20 model: QEMU HARDDISK
21 serial: disk-b
22 wipe: superblock
23 - id: sdc
24 type: disk
25 wipe: superblock
26 ptable: msdos
27 model: QEMU HARDDISK
28 serial: disk-c
29 wipe: superblock
30# This partition config is here to "dirty" the disk
31 - id: sda-part1
32 type: partition
33 device: sda
34 name: sda-part1
35 number: 1
36 size: 3G
37 uuid: ecc1ec63-e8d2-4719-8cee-dd7f4e2b390e
38 wipe: superblock
39 - id: sda-part1_format
40 type: format
41 fstype: ext4
42 label: root
43 uuid: f793b242-e812-44df-91c0-c245a55ffd59
44 volume: sda-part1
45 - id: sda-part1_mount
46 type: mount
47 path: /
48 device: sda-part1_format
diff --git a/helpers/common b/helpers/common
index f9217b7..34f0870 100644
--- a/helpers/common
+++ b/helpers/common
@@ -537,7 +537,44 @@ get_carryover_params() {
537 done537 done
538 echo "${c# }"538 echo "${c# }"
539 )539 )
540 _RET="${carry_lead:+${carry_lead} }${carry_extra}"540 [ -n "${carry_lead}" -a -n "${carry_extra}" ] &&
541 carry_lead="${carry_lead} "
542 _RET="${carry_lead}${carry_extra}"
543}
544
545shell_config_update() {
546 # shell_config_update(file, name, value)
547 # update variable 'name' setting value to 'val' in shell syntax 'file'.
548 # if 'name' is not present, then append declaration.
549 local file="$1" name="$2" val="$3"
550 if ! [ -f "$file" ] || ! grep -q "^$name=" "$file"; then
551 debug 2 "appending to $file shell $name=\"$val\""
552 echo "$name=\"$val\"" >> "$file"
553 return
554 fi
555 local cand="" del=""
556 for cand in "|" "," "/"; do
557 [ "${val#*${del}}" = "${val}" ] && del="$cand" && break
558 done
559 [ -n "$del" ] || {
560 error "Couldn't find a sed delimiter for '$val'";
561 return 1;
562 }
563
564 sed -i -e "s${del}^$name=.*${del}$name=\"$val\"${del}" "$file" ||
565 { error "Failed editing '$file' to set $name=$val"; return 1; }
566 debug 2 "updated $file to set $name=\"$val\""
567 return 0
568}
569
570apply_grub_cmdline_linux_default() {
571 local mp="$1" newargs="$2" edg="${3:-etc/default/grub}"
572 local gcld="GRUB_CMDLINE_LINUX_DEFAULT"
573 debug 1 "setting $gcld to '$newargs' in $edg"
574 shell_config_update "$mp/$edg" "$gcld" "$newargs" || {
575 error "Failed to set '$gcld=$newargs' in $edg"
576 return 1
577 }
541}578}
542579
543install_grub() {580install_grub() {
@@ -709,6 +746,8 @@ install_grub() {
709 esac746 esac
710747
711 local grub_d="etc/default/grub.d"748 local grub_d="etc/default/grub.d"
749 # ubuntu writes to /etc/default/grub.d/50-curtin-settings.cfg
750 # to avoid tripping prompts on upgrade LP: #564853
712 local mygrub_cfg="$grub_d/50-curtin-settings.cfg"751 local mygrub_cfg="$grub_d/50-curtin-settings.cfg"
713 case $os_family in752 case $os_family in
714 redhat)753 redhat)
@@ -736,9 +775,9 @@ install_grub() {
736 # always append rd.auto=1 for centos775 # always append rd.auto=1 for centos
737 case $os_family in776 case $os_family in
738 redhat)777 redhat)
739 newargs="$newargs rd.auto=1";;778 newargs="${newargs:+${newargs} }rd.auto=1";;
740 esac779 esac
741 debug 1 "carryover command line params: $newargs"780 debug 1 "carryover command line params '$newargs'"
742781
743 case $os_family in782 case $os_family in
744 debian)783 debian)
@@ -746,9 +785,15 @@ install_grub() {
746 { error "Failed to write '$mygrub_cfg'"; return 1; }785 { error "Failed to write '$mygrub_cfg'"; return 1; }
747 ;;786 ;;
748 esac787 esac
788
789 if [ "${REPLACE_GRUB_LINUX_DEFAULT:-1}" != "0" ]; then
790 apply_grub_cmdline_linux_default "$mp" "$newargs" || {
791 error "Failed to apply grub cmdline."
792 return 1
793 }
794 fi
795
749 {796 {
750 [ "${REPLACE_GRUB_LINUX_DEFAULT:-1}" = "0" ] ||
751 echo "GRUB_CMDLINE_LINUX_DEFAULT=\"$newargs\""
752 echo "# Curtin disable grub os prober that might find other OS installs."797 echo "# Curtin disable grub os prober that might find other OS installs."
753 echo "GRUB_DISABLE_OS_PROBER=true"798 echo "GRUB_DISABLE_OS_PROBER=true"
754 echo "GRUB_TERMINAL=console"799 echo "GRUB_TERMINAL=console"
diff --git a/tests/unittests/helpers.py b/tests/unittests/helpers.py
index 58e068b..1268880 100644
--- a/tests/unittests/helpers.py
+++ b/tests/unittests/helpers.py
@@ -5,7 +5,9 @@ import imp
5import importlib5import importlib
6import mock6import mock
7import os7import os
8import random
8import shutil9import shutil
10import string
9import tempfile11import tempfile
10from unittest import TestCase12from unittest import TestCase
1113
@@ -67,6 +69,11 @@ class CiTestCase(TestCase):
67 return os.path.normpath(69 return os.path.normpath(
68 os.path.abspath(os.path.sep.join((_dir, path))))70 os.path.abspath(os.path.sep.join((_dir, path))))
6971
72 def random_string(self, length=8):
73 """ return a random lowercase string with default length of 8"""
74 return ''.join(
75 random.choice(string.ascii_lowercase) for _ in range(length))
76
7077
71def dir2dict(startdir, prefix=None):78def dir2dict(startdir, prefix=None):
72 flist = {}79 flist = {}
diff --git a/tests/unittests/test_apt_custom_sources_list.py b/tests/unittests/test_apt_custom_sources_list.py
index a427ae9..d77c750 100644
--- a/tests/unittests/test_apt_custom_sources_list.py
+++ b/tests/unittests/test_apt_custom_sources_list.py
@@ -94,36 +94,30 @@ class TestAptSourceConfigSourceList(CiTestCase):
94 self.new_root = self.tmp_dir()94 self.new_root = self.tmp_dir()
95 # self.patchUtils(self.new_root)95 # self.patchUtils(self.new_root)
9696
97 @staticmethod97 def _apt_source_list(self, cfg, expected):
98 def _apt_source_list(cfg, expected):
99 "_apt_source_list - Test rendering from template (generic)"98 "_apt_source_list - Test rendering from template (generic)"
10099
101 arch = util.get_architecture()100 arch = util.get_architecture()
102 # would fail inside the unittest context101 # would fail inside the unittest context
103 with mock.patch.object(util, 'get_architecture',102 bpath = "curtin.commands.apt_config."
104 return_value=arch) as mockga:103 upath = bpath + "util."
105 with mock.patch.object(util, 'write_file') as mockwrite:104 self.add_patch(upath + "get_architecture", "mockga", return_value=arch)
106 # keep it side effect free and avoid permission errors105 self.add_patch(upath + "write_file", "mockwrite")
107 with mock.patch.object(os, 'rename'):106 self.add_patch(bpath + "os.rename", "mockrename")
108 # make test independent to executing system107 self.add_patch(upath + "load_file", "mockload_file",
109 with mock.patch.object(util, 'load_file',108 return_value=MOCKED_APT_SRC_LIST)
110 return_value=MOCKED_APT_SRC_LIST):109 self.add_patch(bpath + "distro.lsb_release", "mock_lsb_release",
111 with mock.patch.object(distro, 'lsb_release',110 return_value={'codename': 'fakerel'})
112 return_value={'codename':111 self.add_patch(bpath + "apply_preserve_sources_list",
113 'fakerel'}):112 "mock_apply_preserve_sources_list")
114 apt_config.handle_apt(cfg, TARGET)113
115114 apt_config.handle_apt(cfg, TARGET)
116 mockga.assert_called_with("/")115
117116 self.mockga.assert_called_with(TARGET)
118 cloudfile = '/etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg'117 self.mock_apply_preserve_sources_list.assert_called_with(TARGET)
119 cloudconf = yaml.dump({'apt_preserve_sources_list': True}, indent=1)
120 calls = [call(paths.target_path(TARGET, '/etc/apt/sources.list'),118 calls = [call(paths.target_path(TARGET, '/etc/apt/sources.list'),
121 expected,119 expected, mode=0o644)]
122 mode=0o644),120 self.mockwrite.assert_has_calls(calls)
123 call(paths.target_path(TARGET, cloudfile),
124 cloudconf,
125 mode=0o644)]
126 mockwrite.assert_has_calls(calls)
127121
128 def test_apt_source_list(self):122 def test_apt_source_list(self):
129 """test_apt_source_list - Test with neither custom sources nor parms"""123 """test_apt_source_list - Test with neither custom sources nor parms"""
@@ -156,10 +150,6 @@ class TestAptSourceConfigSourceList(CiTestCase):
156 self.assertEqual(150 self.assertEqual(
157 EXPECTED_CONVERTED_CONTENT,151 EXPECTED_CONVERTED_CONTENT,
158 util.load_file(paths.target_path(target, "/etc/apt/sources.list")))152 util.load_file(paths.target_path(target, "/etc/apt/sources.list")))
159 cloudfile = paths.target_path(
160 target, '/etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg')
161 self.assertEqual({'apt_preserve_sources_list': True},
162 yaml.load(util.load_file(cloudfile)))
163153
164 @mock.patch("curtin.distro.lsb_release")154 @mock.patch("curtin.distro.lsb_release")
165 @mock.patch("curtin.util.get_architecture", return_value="amd64")155 @mock.patch("curtin.util.get_architecture", return_value="amd64")
@@ -224,4 +214,46 @@ class TestAptSourceConfigSourceList(CiTestCase):
224 apt_config.handle_apt(cfg, target)214 apt_config.handle_apt(cfg, target)
225 self.assertEqual(expected, util.load_file(easl))215 self.assertEqual(expected, util.load_file(easl))
226216
217
218class TestApplyPreserveSourcesList(CiTestCase):
219 """Test apply_preserve_sources_list."""
220
221 cloudfile = "/etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg"
222
223 def setUp(self):
224 super(TestApplyPreserveSourcesList, self).setUp()
225 self.tmp = self.tmp_dir()
226 self.tmp_cfg = self.tmp_path(self.cloudfile, self.tmp)
227
228 @mock.patch("curtin.commands.apt_config.distro.get_package_version")
229 def test_old_cloudinit_version(self, m_get_pkg_ver):
230 """Test installed old cloud-init version."""
231 m_get_pkg_ver.return_value = distro.parse_dpkg_version('0.7.7-0')
232 apt_config.apply_preserve_sources_list(self.tmp)
233 m_get_pkg_ver.assert_has_calls(
234 [mock.call('cloud-init', target=self.tmp)])
235 self.assertEqual(
236 yaml.load(util.load_file(self.tmp_cfg)),
237 {'apt_preserve_sources_list': True})
238
239 @mock.patch("curtin.commands.apt_config.distro.get_package_version")
240 def test_no_cloudinit(self, m_get_pkg_ver):
241 """Test where cloud-init is not installed."""
242 m_get_pkg_ver.return_value = None
243 apt_config.apply_preserve_sources_list(self.tmp)
244 m_get_pkg_ver.assert_has_calls(
245 [mock.call('cloud-init', target=self.tmp)])
246 self.assertFalse(os.path.exists(self.tmp_cfg))
247
248 @mock.patch("curtin.commands.apt_config.distro.get_package_version")
249 def test_new_cloudinit_version(self, m_get_pkg_ver):
250 """Test cloud-init > 1.0 with new apt format."""
251 m_get_pkg_ver.return_value = distro.parse_dpkg_version('17.1-0ubuntu1')
252 apt_config.apply_preserve_sources_list(self.tmp)
253 m_get_pkg_ver.assert_has_calls(
254 [mock.call('cloud-init', target=self.tmp)])
255 self.assertEqual(
256 yaml.load(util.load_file(self.tmp_cfg)),
257 {'apt': {'preserve_sources_list': True}})
258
227# vi: ts=4 expandtab syntax=python259# vi: ts=4 expandtab syntax=python
diff --git a/tests/unittests/test_commands_block_meta.py b/tests/unittests/test_commands_block_meta.py
index e70d6ed..45b9906 100644
--- a/tests/unittests/test_commands_block_meta.py
+++ b/tests/unittests/test_commands_block_meta.py
@@ -85,8 +85,9 @@ class TestBlockMetaSimple(CiTestCase):
85 self.mock_block_get_root_device.assert_called_with([devname],85 self.mock_block_get_root_device.assert_called_with([devname],
86 paths=paths)86 paths=paths)
8787
88 @patch('curtin.commands.block_meta.meta_clear')
88 @patch('curtin.commands.block_meta.write_image_to_disk')89 @patch('curtin.commands.block_meta.write_image_to_disk')
89 def test_meta_simple_calls_write_img(self, mock_write_image):90 def test_meta_simple_calls_write_img(self, mock_write_image, mock_clear):
90 devname = "fakedisk1p1"91 devname = "fakedisk1p1"
91 devnode = "/dev/" + devname92 devnode = "/dev/" + devname
92 sources = {93 sources = {
@@ -104,9 +105,9 @@ class TestBlockMetaSimple(CiTestCase):
104 mock_write_image.return_value = devname105 mock_write_image.return_value = devname
105106
106 args = Namespace(target=self.target, devices=None, mode=None,107 args = Namespace(target=self.target, devices=None, mode=None,
107 boot_fstype=None, fstype=None)108 boot_fstype=None, fstype=None, force_mode=False)
108109
109 block_meta.meta_simple(args)110 block_meta.block_meta(args)
110111
111 mock_write_image.assert_called_with(sources.get('unittest'), devname)112 mock_write_image.assert_called_with(sources.get('unittest'), devname)
112 self.mock_subp.assert_has_calls(113 self.mock_subp.assert_has_calls(
diff --git a/tests/unittests/test_commands_extract.py b/tests/unittests/test_commands_extract.py
index e604d7f..cc117bb 100644
--- a/tests/unittests/test_commands_extract.py
+++ b/tests/unittests/test_commands_extract.py
@@ -27,8 +27,9 @@ class TestExtractRootFsImageUrl(CiTestCase):
27 tmpd = self.tmp_dir()27 tmpd = self.tmp_dir()
28 target = self.tmp_path("target_d", tmpd)28 target = self.tmp_path("target_d", tmpd)
29 startdir = os.getcwd()29 startdir = os.getcwd()
30 fname = "my.img"
30 try:31 try:
31 fname = "my.img"32 os.chdir(tmpd)
32 util.write_file(fname, fname + " data\n")33 util.write_file(fname, fname + " data\n")
33 extract_root_fsimage_url("file://" + fname, target)34 extract_root_fsimage_url("file://" + fname, target)
34 finally:35 finally:
diff --git a/tests/unittests/test_make_dname.py b/tests/unittests/test_make_dname.py
index 2b92a88..76c7b28 100644
--- a/tests/unittests/test_make_dname.py
+++ b/tests/unittests/test_make_dname.py
@@ -13,10 +13,16 @@ class TestMakeDname(CiTestCase):
13 state = {'scratch': '/tmp/null'}13 state = {'scratch': '/tmp/null'}
14 rules_d = '/tmp/null/rules.d'14 rules_d = '/tmp/null/rules.d'
15 rule_file = '/tmp/null/rules.d/{}.rules'15 rule_file = '/tmp/null/rules.d/{}.rules'
16 disk_serial = 'abcdefg'
17 disk_wwn = '0x1234567890'
16 storage_config = {18 storage_config = {
17 'disk1': {'type': 'disk', 'id': 'disk1', 'name': 'main_disk'},19 'disk1': {'type': 'disk', 'id': 'disk1', 'name': 'main_disk',
20 'serial': disk_serial},
21 'disk_noid': {'type': 'disk', 'id': 'disk_noid', 'name': 'main_disk'},
18 'disk1p1': {'type': 'partition', 'id': 'disk1p1', 'device': 'disk1'},22 'disk1p1': {'type': 'partition', 'id': 'disk1p1', 'device': 'disk1'},
19 'disk2': {'type': 'disk', 'id': 'disk2',23 'disk1p2': {'type': 'partition', 'id': 'disk1p2', 'device': 'disk1',
24 'name': 'custom-partname'},
25 'disk2': {'type': 'disk', 'id': 'disk2', 'wwn': disk_wwn,
20 'name': 'in_valid/name!@#$% &*(+disk'},26 'name': 'in_valid/name!@#$% &*(+disk'},
21 'disk2p1': {'type': 'partition', 'id': 'disk2p1', 'device': 'disk2'},27 'disk2p1': {'type': 'partition', 'id': 'disk2p1', 'device': 'disk2'},
22 'md_id': {'type': 'raid', 'id': 'md_id', 'name': 'mdadm_name'},28 'md_id': {'type': 'raid', 'id': 'md_id', 'name': 'mdadm_name'},
@@ -27,7 +33,8 @@ class TestMakeDname(CiTestCase):
27 'lpart2_id': {'type': 'lvm_partition', 'id': 'lpart2_id',33 'lpart2_id': {'type': 'lvm_partition', 'id': 'lpart2_id',
28 'name': 'lvm part/2', 'volgroup': 'lvol_id'},34 'name': 'lvm part/2', 'volgroup': 'lvol_id'},
29 'bcache1_id': {'type': 'bcache', 'id': 'bcache1_id',35 'bcache1_id': {'type': 'bcache', 'id': 'bcache1_id',
30 'name': 'my-cached-data'}36 'name': 'my-cached-data'},
37 'iscsi1': {'type': 'disk', 'id': 'iscsi1', 'name': 'iscsi_disk1'}
31 }38 }
32 bcache_super_show = {39 bcache_super_show = {
33 'sb.version': '1 [backing device]',40 'sb.version': '1 [backing device]',
@@ -57,20 +64,37 @@ class TestMakeDname(CiTestCase):
57 rule.append('SYMLINK+="disk/by-dname/{}"\n'.format(target))64 rule.append('SYMLINK+="disk/by-dname/{}"\n'.format(target))
58 return ', '.join(rule)65 return ', '.join(rule)
5966
67 def _content(self, rules=[]):
68 return "\n".join(['# Written by curtin'] + rules)
69
70 @mock.patch('curtin.commands.block_meta.udevadm_info')
60 @mock.patch('curtin.commands.block_meta.LOG')71 @mock.patch('curtin.commands.block_meta.LOG')
61 @mock.patch('curtin.commands.block_meta.get_path_to_storage_volume')72 @mock.patch('curtin.commands.block_meta.get_path_to_storage_volume')
62 @mock.patch('curtin.commands.block_meta.util')73 @mock.patch('curtin.commands.block_meta.util')
63 def test_make_dname_disk(self, mock_util, mock_get_path, mock_log):74 def test_make_dname_disk(self, mock_util, mock_get_path, mock_log,
75 mock_udev):
64 disk_ptuuid = str(uuid.uuid1())76 disk_ptuuid = str(uuid.uuid1())
65 mock_util.subp.side_effect = self._make_mock_subp_blkid(77 mock_util.subp.side_effect = self._make_mock_subp_blkid(
66 disk_ptuuid, self.disk_blkid)78 disk_ptuuid, self.disk_blkid)
67 mock_util.load_command_environment.return_value = self.state79 mock_util.load_command_environment.return_value = self.state
68 rule_identifiers = [80
69 ('DEVTYPE', 'disk'),81 rule_identifiers = [('ID_PART_TABLE_UUID', disk_ptuuid)]
70 ('ID_PART_TABLE_UUID', disk_ptuuid)82 id_rule_identifiers = [('ID_SERIAL', self.disk_serial)]
71 ]83 wwn_rule_identifiers = [('ID_WWN_WITH_EXTENSION', self.disk_wwn)]
84
85 def _drule(devtype, match):
86 return [('DEVTYPE', devtype)] + [m for m in match]
87
88 def drule(match):
89 return _drule('disk', match)
90
91 def prule(match):
92 return _drule('partition', match)
7293
73 # simple run94 # simple run
95 mock_udev.side_effect = (
96 [{'DEVTYPE': 'disk', 'ID_SERIAL': self.disk_serial},
97 {'DEVTYPE': 'disk', 'ID_WWN_WITH_EXTENSION': self.disk_wwn}])
74 res_dname = 'main_disk'98 res_dname = 'main_disk'
75 block_meta.make_dname('disk1', self.storage_config)99 block_meta.make_dname('disk1', self.storage_config)
76 mock_util.ensure_dir.assert_called_with(self.rules_d)100 mock_util.ensure_dir.assert_called_with(self.rules_d)
@@ -78,7 +102,13 @@ class TestMakeDname(CiTestCase):
78 self.assertFalse(mock_log.warning.called)102 self.assertFalse(mock_log.warning.called)
79 mock_util.write_file.assert_called_with(103 mock_util.write_file.assert_called_with(
80 self.rule_file.format(res_dname),104 self.rule_file.format(res_dname),
81 self._formatted_rule(rule_identifiers, res_dname))105 self._content(
106 [self._formatted_rule(drule(rule_identifiers),
107 res_dname),
108 self._formatted_rule(drule(id_rule_identifiers),
109 res_dname),
110 self._formatted_rule(prule(id_rule_identifiers),
111 "%s-part%%n" % res_dname)]))
82112
83 # run invalid dname113 # run invalid dname
84 res_dname = 'in_valid-name----------disk'114 res_dname = 'in_valid-name----------disk'
@@ -86,12 +116,39 @@ class TestMakeDname(CiTestCase):
86 self.assertTrue(mock_log.warning.called)116 self.assertTrue(mock_log.warning.called)
87 mock_util.write_file.assert_called_with(117 mock_util.write_file.assert_called_with(
88 self.rule_file.format(res_dname),118 self.rule_file.format(res_dname),
89 self._formatted_rule(rule_identifiers, res_dname))119 self._content(
120 [self._formatted_rule(drule(rule_identifiers),
121 res_dname),
122 self._formatted_rule(drule(wwn_rule_identifiers),
123 res_dname),
124 self._formatted_rule(prule(wwn_rule_identifiers),
125 "%s-part%%n" % res_dname)]))
90126
127 # iscsi disk with no config, but info returns serial and wwn
128 mock_udev.side_effect = (
129 [{'DEVTYPE': 'disk', 'ID_SERIAL': self.disk_serial,
130 'DEVTYPE': 'disk', 'ID_WWN_WITH_EXTENSION': self.disk_wwn}])
131 res_dname = 'iscsi_disk1'
132 block_meta.make_dname('iscsi1', self.storage_config)
133 mock_util.ensure_dir.assert_called_with(self.rules_d)
134 self.assertTrue(mock_log.debug.called)
135 both_rules = (id_rule_identifiers + wwn_rule_identifiers)
136 mock_util.write_file.assert_called_with(
137 self.rule_file.format(res_dname),
138 self._content(
139 [self._formatted_rule(drule(rule_identifiers), res_dname),
140 self._formatted_rule(drule(both_rules), res_dname),
141 self._formatted_rule(prule(both_rules),
142 "%s-part%%n" % res_dname)]))
143
144 @mock.patch('curtin.commands.block_meta.udevadm_info')
91 @mock.patch('curtin.commands.block_meta.LOG')145 @mock.patch('curtin.commands.block_meta.LOG')
92 @mock.patch('curtin.commands.block_meta.get_path_to_storage_volume')146 @mock.patch('curtin.commands.block_meta.get_path_to_storage_volume')
93 @mock.patch('curtin.commands.block_meta.util')147 @mock.patch('curtin.commands.block_meta.util')
94 def test_make_dname_failures(self, mock_util, mock_get_path, mock_log):148 def test_make_dname_failures(self, mock_util, mock_get_path, mock_log,
149 mock_udev):
150 mock_udev.side_effect = ([{'DEVTYPE': 'disk'}, {'DEVTYPE': 'disk'}])
151
95 mock_util.subp.side_effect = self._make_mock_subp_blkid(152 mock_util.subp.side_effect = self._make_mock_subp_blkid(
96 '', self.trusty_blkid)153 '', self.trusty_blkid)
97 mock_util.load_command_environment.return_value = self.state154 mock_util.load_command_environment.return_value = self.state
@@ -99,10 +156,14 @@ class TestMakeDname(CiTestCase):
99 warning_msg = "Can't find a uuid for volume: {}. Skipping dname."156 warning_msg = "Can't find a uuid for volume: {}. Skipping dname."
100157
101 # disk with no PT_UUID158 # disk with no PT_UUID
102 block_meta.make_dname('disk1', self.storage_config)159 disk = 'disk_noid'
103 mock_log.warning.assert_called_with(warning_msg.format('disk1'))160 with self.assertRaises(RuntimeError):
104 self.assertFalse(mock_util.write_file.called)161 block_meta.make_dname(disk, self.storage_config)
162 mock_log.warning.assert_called_with(warning_msg.format(disk))
163 self.assertFalse(mock_util.write_file.called)
105164
165 mock_util.subp.side_effect = self._make_mock_subp_blkid(
166 '', self.trusty_blkid)
106 # partition with no PART_UUID167 # partition with no PART_UUID
107 block_meta.make_dname('disk1p1', self.storage_config)168 block_meta.make_dname('disk1p1', self.storage_config)
108 mock_log.warning.assert_called_with(warning_msg.format('disk1p1'))169 mock_log.warning.assert_called_with(warning_msg.format('disk1p1'))
@@ -123,22 +184,15 @@ class TestMakeDname(CiTestCase):
123 ]184 ]
124185
125 # simple run186 # simple run
126 res_dname = 'main_disk-part1'187 res_dname = 'custom-partname'
127 block_meta.make_dname('disk1p1', self.storage_config)188 block_meta.make_dname('disk1p2', self.storage_config)
128 mock_util.ensure_dir.assert_called_with(self.rules_d)189 mock_util.ensure_dir.assert_called_with(self.rules_d)
129 self.assertTrue(mock_log.debug.called)190 self.assertTrue(mock_log.debug.called)
130 self.assertFalse(mock_log.warning.called)191 self.assertFalse(mock_log.warning.called)
131 mock_util.write_file.assert_called_with(192 mock_util.write_file.assert_called_with(
132 self.rule_file.format(res_dname),193 self.rule_file.format(res_dname),
133 self._formatted_rule(rule_identifiers, res_dname))194 self._content(
134195 [self._formatted_rule(rule_identifiers, res_dname)]))
135 # run invalid dname
136 res_dname = 'in_valid-name----------disk-part1'
137 block_meta.make_dname('disk2p1', self.storage_config)
138 self.assertTrue(mock_log.warning.called)
139 mock_util.write_file.assert_called_with(
140 self.rule_file.format(res_dname),
141 self._formatted_rule(rule_identifiers, res_dname))
142196
143 @mock.patch('curtin.commands.block_meta.mdadm')197 @mock.patch('curtin.commands.block_meta.mdadm')
144 @mock.patch('curtin.commands.block_meta.LOG')198 @mock.patch('curtin.commands.block_meta.LOG')
@@ -158,7 +212,8 @@ class TestMakeDname(CiTestCase):
158 self.assertFalse(mock_log.warning.called)212 self.assertFalse(mock_log.warning.called)
159 mock_util.write_file.assert_called_with(213 mock_util.write_file.assert_called_with(
160 self.rule_file.format(res_dname),214 self.rule_file.format(res_dname),
161 self._formatted_rule(rule_identifiers, res_dname))215 self._content(
216 [self._formatted_rule(rule_identifiers, res_dname)]))
162217
163 # invalid name218 # invalid name
164 res_dname = 'mdadm-name'219 res_dname = 'mdadm-name'
@@ -166,7 +221,8 @@ class TestMakeDname(CiTestCase):
166 self.assertTrue(mock_log.warning.called)221 self.assertTrue(mock_log.warning.called)
167 mock_util.write_file.assert_called_with(222 mock_util.write_file.assert_called_with(
168 self.rule_file.format(res_dname),223 self.rule_file.format(res_dname),
169 self._formatted_rule(rule_identifiers, res_dname))224 self._content(
225 [self._formatted_rule(rule_identifiers, res_dname)]))
170226
171 @mock.patch('curtin.commands.block_meta.LOG')227 @mock.patch('curtin.commands.block_meta.LOG')
172 @mock.patch('curtin.commands.block_meta.get_path_to_storage_volume')228 @mock.patch('curtin.commands.block_meta.get_path_to_storage_volume')
@@ -183,7 +239,8 @@ class TestMakeDname(CiTestCase):
183 self.assertFalse(mock_log.warning.called)239 self.assertFalse(mock_log.warning.called)
184 mock_util.write_file.assert_called_with(240 mock_util.write_file.assert_called_with(
185 self.rule_file.format(res_dname),241 self.rule_file.format(res_dname),
186 self._formatted_rule(rule_identifiers, res_dname))242 self._content(
243 [self._formatted_rule(rule_identifiers, res_dname)]))
187244
188 # with invalid name245 # with invalid name
189 res_dname = 'vg1-lvm-part-2'246 res_dname = 'vg1-lvm-part-2'
@@ -192,7 +249,8 @@ class TestMakeDname(CiTestCase):
192 self.assertTrue(mock_log.warning.called)249 self.assertTrue(mock_log.warning.called)
193 mock_util.write_file.assert_called_with(250 mock_util.write_file.assert_called_with(
194 self.rule_file.format(res_dname),251 self.rule_file.format(res_dname),
195 self._formatted_rule(rule_identifiers, res_dname))252 self._content(
253 [self._formatted_rule(rule_identifiers, res_dname)]))
196254
197 @mock.patch('curtin.commands.block_meta.LOG')255 @mock.patch('curtin.commands.block_meta.LOG')
198 @mock.patch('curtin.commands.block_meta.bcache')256 @mock.patch('curtin.commands.block_meta.bcache')
@@ -213,7 +271,8 @@ class TestMakeDname(CiTestCase):
213 self.assertFalse(mock_log.warning.called)271 self.assertFalse(mock_log.warning.called)
214 mock_util.write_file.assert_called_with(272 mock_util.write_file.assert_called_with(
215 self.rule_file.format(res_dname),273 self.rule_file.format(res_dname),
216 self._formatted_rule(rule_identifiers, res_dname))274 self._content(
275 [self._formatted_rule(rule_identifiers, res_dname)]))
217276
218 def test_sanitize_dname(self):277 def test_sanitize_dname(self):
219 unsanitized_to_sanitized = [278 unsanitized_to_sanitized = [
@@ -226,4 +285,87 @@ class TestMakeDname(CiTestCase):
226 for (unsanitized, sanitized) in unsanitized_to_sanitized:285 for (unsanitized, sanitized) in unsanitized_to_sanitized:
227 self.assertEqual(block_meta.sanitize_dname(unsanitized), sanitized)286 self.assertEqual(block_meta.sanitize_dname(unsanitized), sanitized)
228287
288
289class TestMakeDnameById(CiTestCase):
290
291 @mock.patch('curtin.commands.block_meta.udevadm_info')
292 def test_bad_path(self, m_udev):
293 """test dname_byid raises ValueError on invalid path."""
294 mypath = None
295 with self.assertRaises(ValueError):
296 block_meta.make_dname_byid(mypath)
297
298 @mock.patch('curtin.commands.block_meta.udevadm_info')
299 def test_non_disk(self, m_udev):
300 """test dname_byid raises ValueError on DEVTYPE != 'disk'"""
301 mypath = "/dev/" + self.random_string()
302 m_udev.return_value = {'DEVTYPE': 'not_a_disk'}
303 with self.assertRaises(ValueError):
304 block_meta.make_dname_byid(mypath)
305
306 @mock.patch('curtin.commands.block_meta.udevadm_info')
307 def test_disk_with_no_id_wwn(self, m_udev):
308 """test dname_byid raises RuntimeError on device without ID or WWN."""
309 mypath = "/dev/" + self.random_string()
310 m_udev.return_value = {'DEVTYPE': 'disk'}
311 with self.assertRaises(RuntimeError):
312 block_meta.make_dname_byid(mypath)
313
314 @mock.patch('curtin.commands.block_meta.udevadm_info')
315 def test_udevinfo_not_called_if_info_provided(self, m_udev):
316 """dname_byid does not invoke udevadm_info if using info dict"""
317 myserial = self.random_string()
318 self.assertEqual(
319 [['ENV{ID_SERIAL}=="%s"' % myserial]],
320 block_meta.make_dname_byid(
321 self.random_string(),
322 info={'DEVTYPE': 'disk', 'ID_SERIAL': myserial}))
323 self.assertEqual(0, m_udev.call_count)
324
325 @mock.patch('curtin.commands.block_meta.udevadm_info')
326 def test_udevinfo_called_if_info_not_provided(self, m_udev):
327 """dname_byid should call udevadm_info if no data given."""
328 myserial = self.random_string()
329 mypath = "/dev/" + self.random_string()
330 m_udev.return_value = {
331 'DEVTYPE': 'disk', 'ID_SERIAL': myserial, 'DEVNAME': mypath}
332 self.assertEqual(
333 [['ENV{ID_SERIAL}=="%s"' % myserial]],
334 block_meta.make_dname_byid(mypath))
335 self.assertEqual(
336 [mock.call(path=mypath)], m_udev.call_args_list)
337
338 def test_disk_with_only_serial(self):
339 """test dname_byid returns rules for ID_SERIAL"""
340 mypath = "/dev/" + self.random_string()
341 myserial = self.random_string()
342 info = {'DEVTYPE': 'disk', 'DEVNAME': mypath, 'ID_SERIAL': myserial}
343 self.assertEqual(
344 [['ENV{ID_SERIAL}=="%s"' % myserial]],
345 block_meta.make_dname_byid(mypath, info=info))
346
347 def test_disk_with_only_wwn(self):
348 """test dname_byid returns rules for ID_WWN_WITH_EXTENSION"""
349 mypath = "/dev/" + self.random_string()
350 mywwn = self.random_string()
351 info = {'DEVTYPE': 'disk', 'DEVNAME': mypath,
352 'ID_WWN_WITH_EXTENSION': mywwn}
353 self.assertEqual(
354 [['ENV{ID_WWN_WITH_EXTENSION}=="%s"' % mywwn]],
355 block_meta.make_dname_byid(mypath, info=info))
356
357 def test_disk_with_both_id_wwn(self):
358 """test dname_byid returns rules with both ID_SERIAL and ID_WWN"""
359 mypath = "/dev/" + self.random_string()
360 myserial = self.random_string()
361 mywwn = self.random_string()
362 info = {'DEVTYPE': 'disk', 'ID_SERIAL': myserial,
363 'ID_WWN_WITH_EXTENSION': mywwn,
364 'DEVNAME': mypath}
365 self.assertEqual(
366 [['ENV{ID_SERIAL}=="%s"' % myserial,
367 'ENV{ID_WWN_WITH_EXTENSION}=="%s"' % mywwn]],
368 block_meta.make_dname_byid(mypath, info=info))
369
370
229# vi: ts=4 expandtab syntax=python371# vi: ts=4 expandtab syntax=python
diff --git a/tests/unittests/test_swap.py b/tests/unittests/test_swap.py
index e12d12e..fd6c527 100644
--- a/tests/unittests/test_swap.py
+++ b/tests/unittests/test_swap.py
@@ -1,39 +1,53 @@
1import mock1import mock
22
3from curtin import swap3from curtin import swap
4from curtin import util
4from .helpers import CiTestCase5from .helpers import CiTestCase
56
67
7class TestSwap(CiTestCase):8class TestSwap(CiTestCase):
8 @mock.patch('curtin.swap.resource')9 def _valid_swap_contents(self):
9 @mock.patch('curtin.swap.util')10 """Yields (pagesize, content) of things that should be considered
10 def test_is_swap_device_read_offsets(self, mock_util, mock_resource):11 valid swap."""
11 """swap.is_swap_device() checks offsets based on system pagesize"""
12 path = '/mydev/dummydisk'
13 # 4k and 64k page size12 # 4k and 64k page size
14 for pagesize in [4096, 65536]:13 for pagesize in [4096, 65536]:
15 magic_offset = pagesize - 1014 for magic in [b'SWAPSPACE2', b'SWAP-SPACE']:
16 mock_resource.getpagesize.return_value = pagesize15 # yield content of 2 pages to trigger/avoid fence-post errors
17 swap.is_swap_device(path)16 yield (pagesize,
18 mock_util.load_file.assert_called_with(path, read_len=10,17 ((pagesize - len(magic)) * b'\0' +
19 offset=magic_offset,18 magic + pagesize * b'\0'))
20 decode=False)
2119
22 @mock.patch('curtin.swap.resource')20 @mock.patch('curtin.swap.resource.getpagesize')
23 @mock.patch('curtin.swap.util')21 def test_is_swap_device_read_offsets(self, mock_getpagesize):
24 def test_identify_swap_false(self, mock_util, mock_resource):22 """swap.is_swap_device() correctly identifies swap content."""
25 """swap.is_swap_device() returns false on non swap magic"""23 tmpd = self.tmp_dir()
26 mock_util.load_file.return_value = (24 for num, (pagesize, content) in enumerate(self._valid_swap_contents()):
27 b'\x00\x00c\x05\x00\x00\x11\x00\x19\x00')25 path = self.tmp_path("swap-file-%02d" % num, tmpd)
28 is_swap = swap.is_swap_device('ignored')26 util.write_file(path, content, omode="wb")
29 self.assertFalse(is_swap)27 mock_getpagesize.return_value = pagesize
28 self.assertTrue(swap.is_swap_device(path))
3029
31 @mock.patch('curtin.swap.resource')30 @mock.patch('curtin.swap.resource.getpagesize', return_value=4096)
32 @mock.patch('curtin.swap.util')31 def test_identify_swap_false_if_tiny(self, mock_getpagesize):
33 def test_identify_swap_true(self, mock_util, mock_resource):32 """small files do not trip up is_swap_device()."""
34 """swap.is_swap_device() returns true on swap magic strings"""33 path = self.tmp_path("tiny")
35 path = '/mydev/dummydisk'34 util.write_file(path, b'tinystuff', omode='wb')
36 for magic in [b'SWAPSPACE2', b'SWAP-SPACE']:35 self.assertFalse(swap.is_swap_device(path))
37 mock_util.load_file.return_value = magic36
38 is_swap = swap.is_swap_device(path)37 @mock.patch('curtin.swap.resource.getpagesize', return_value=4096)
39 self.assertTrue(is_swap)38 def test_identify_zeros_are_swap(self, mock_getpagesize):
39 """swap.is_swap_device() returns false on all zeros"""
40 pagesize = mock_getpagesize()
41 path = self.tmp_path("notswap0")
42 util.write_file(path, pagesize * 2 * b'\0', omode="wb")
43 self.assertFalse(swap.is_swap_device(path))
44
45 @mock.patch('curtin.swap.resource.getpagesize', return_value=65536)
46 def test_identify_swap_false(self, mock_getpagesize):
47 """swap.is_swap_device() returns false on non swap content"""
48 pagesize = mock_getpagesize()
49 path = self.tmp_path("notswap1")
50 # this is just arbitrary content that is not swap content.
51 blob = b'\x00\x00c\x05\x00\x00\x11\x19'
52 util.write_file(path, int(pagesize * 2 / len(blob)) * blob, omode="wb")
53 self.assertFalse(swap.is_swap_device(path))
diff --git a/tests/unittests/test_udev.py b/tests/unittests/test_udev.py
40new file mode 10064454new file mode 100644
index 0000000..0a070d5
--- /dev/null
+++ b/tests/unittests/test_udev.py
@@ -0,0 +1,68 @@
1# This file is part of curtin. See LICENSE file for copyright and license info.
2
3import mock
4
5from curtin import udev
6from curtin import util
7from .helpers import CiTestCase
8
9
10UDEVADM_INFO_QUERY = """\
11DEVLINKS=/dev/disk/by-id/nvme-eui.0025388b710116a1
12DEVNAME=/dev/nvme0n1
13DEVPATH=/devices/pci0000:00/0000:00:1c.4/0000:05:00.0/nvme/nvme0/nvme0n1
14DEVTYPE=disk
15ID_PART_TABLE_TYPE=gpt
16ID_PART_TABLE_UUID=ea0b9ddc-a114-4e01-b257-750d86e3a944
17ID_SERIAL=SAMSUNG MZVLB1T0HALR-000L7_S3TPNY0JB00151
18ID_SERIAL_SHORT=S3TPNY0JB00151
19MAJOR=259
20MINOR=0
21SUBSYSTEM=block
22TAGS=:systemd:
23USEC_INITIALIZED=2026691
24"""
25
26INFO_DICT = {
27 'DEVLINKS': ['/dev/disk/by-id/nvme-eui.0025388b710116a1'],
28 'DEVNAME': '/dev/nvme0n1',
29 'DEVPATH':
30 '/devices/pci0000:00/0000:00:1c.4/0000:05:00.0/nvme/nvme0/nvme0n1',
31 'DEVTYPE': 'disk',
32 'ID_PART_TABLE_TYPE': 'gpt',
33 'ID_PART_TABLE_UUID': 'ea0b9ddc-a114-4e01-b257-750d86e3a944',
34 'ID_SERIAL': 'SAMSUNG MZVLB1T0HALR-000L7_S3TPNY0JB00151',
35 'ID_SERIAL_SHORT': 'S3TPNY0JB00151',
36 'MAJOR': '259',
37 'MINOR': '0',
38 'SUBSYSTEM': 'block',
39 'TAGS': ':systemd:',
40 'USEC_INITIALIZED': '2026691'
41}
42
43
44class TestUdevInfo(CiTestCase):
45
46 @mock.patch('curtin.util.subp')
47 def test_udevadm_info(self, m_subp):
48 """ udevadm_info returns dictionary for specified device """
49 mypath = '/dev/nvme0n1'
50 m_subp.return_value = (UDEVADM_INFO_QUERY, "")
51 info = udev.udevadm_info(mypath)
52 m_subp.assert_called_with(
53 ['udevadm', 'info', '--query=property', mypath], capture=True)
54 self.assertEqual(sorted(INFO_DICT), sorted(info))
55
56 def test_udevadm_info_no_path(self):
57 """ udevadm_info raises ValueError for invalid path value"""
58 mypath = None
59 with self.assertRaises(ValueError):
60 udev.udevadm_info(mypath)
61
62 @mock.patch('curtin.util.subp')
63 def test_udevadm_info_path_not_exists(self, m_subp):
64 """ udevadm_info raises ProcessExecutionError for invalid path value"""
65 mypath = self.random_string()
66 m_subp.side_effect = util.ProcessExecutionError()
67 with self.assertRaises(util.ProcessExecutionError):
68 udev.udevadm_info(mypath)
diff --git a/tests/vmtests/__init__.py b/tests/vmtests/__init__.py
index 3823e39..bc4a87b 100644
--- a/tests/vmtests/__init__.py
+++ b/tests/vmtests/__init__.py
@@ -21,6 +21,7 @@ from curtin.block import iscsi
2121
22from .report_webhook_logger import CaptureReporting22from .report_webhook_logger import CaptureReporting
23from curtin.commands.install import INSTALL_PASS_MSG23from curtin.commands.install import INSTALL_PASS_MSG
24from curtin.commands.block_meta import sanitize_dname
2425
25from .image_sync import query as imagesync_query26from .image_sync import query as imagesync_query
26from .image_sync import mirror as imagesync_mirror27from .image_sync import mirror as imagesync_mirror
@@ -344,12 +345,17 @@ class TempDir(object):
344345
345 def collect_output(self):346 def collect_output(self):
346 logger.debug('extracting output disk')347 logger.debug('extracting output disk')
347 subprocess.check_call(['tar', '-C', self.collect, '-xf',348 try:
348 self.output_disk],349 subprocess.check_call(['tar', '-C', self.collect, '-xf',
349 stdout=DEVNULL, stderr=subprocess.STDOUT)350 self.output_disk],
350 # make sure collect output dir is usable by non-root351 stdout=DEVNULL, stderr=subprocess.STDOUT)
351 subprocess.check_call(['chmod', '-R', 'u+rwX', self.collect],352 except subprocess.CalledProcessError as e:
352 stdout=DEVNULL, stderr=subprocess.STDOUT)353 logger.error('Failed unpacking collect output: %s', e)
354 finally:
355 logger.debug('Fixing collect output dir permissions.')
356 # make sure collect output dir is usable by non-root
357 subprocess.check_call(['chmod', '-R', 'u+rwX', self.collect],
358 stdout=DEVNULL, stderr=subprocess.STDOUT)
353359
354360
355def skip_if_flag(flag):361def skip_if_flag(flag):
@@ -515,11 +521,17 @@ DEFAULT_COLLECT_SCRIPTS = {
515 ls -al /dev/disk/by-dname/ | cat >ls_al_bydname521 ls -al /dev/disk/by-dname/ | cat >ls_al_bydname
516 ls -al /dev/disk/by-id/ | cat >ls_al_byid522 ls -al /dev/disk/by-id/ | cat >ls_al_byid
517 ls -al /dev/disk/by-uuid/ | cat >ls_al_byuuid523 ls -al /dev/disk/by-uuid/ | cat >ls_al_byuuid
524 ls -al /dev/disk/by-partuuid/ | cat >ls_al_bypartuuid
518 blkid -o export | cat >blkid.out525 blkid -o export | cat >blkid.out
519 find /boot | cat > find_boot.out526 find /boot | cat > find_boot.out
520 [ -e /sys/firmware/efi ] && {527 if [ -e /sys/firmware/efi ]; then
521 efibootmgr -v | cat >efibootmgr.out;528 efibootmgr -v | cat >efibootmgr.out;
522 }529 fi
530 [ ! -d /etc/default/grub.d ] ||
531 cp -a /etc/default/grub.d etc_default_grub_d
532 [ ! -f /etc/default/grub ] || cp /etc/default/grub etc_default_grub
533
534 exit 0
523 """)],535 """)],
524 'centos': [textwrap.dedent("""536 'centos': [textwrap.dedent("""
525 # XXX: command | cat >output is required for Centos under SELinux537 # XXX: command | cat >output is required for Centos under SELinux
@@ -530,6 +542,8 @@ DEFAULT_COLLECT_SCRIPTS = {
530 rpm -q --queryformat '%{VERSION}\n' cloud-init |tee rpm_ci_version542 rpm -q --queryformat '%{VERSION}\n' cloud-init |tee rpm_ci_version
531 rpm -E '%rhel' > rpm_dist_version_major543 rpm -E '%rhel' > rpm_dist_version_major
532 cp -a /etc/centos-release .544 cp -a /etc/centos-release .
545
546 exit 0
533 """)],547 """)],
534 'ubuntu': [textwrap.dedent("""548 'ubuntu': [textwrap.dedent("""
535 cd OUTPUT_COLLECT_D549 cd OUTPUT_COLLECT_D
@@ -543,6 +557,8 @@ DEFAULT_COLLECT_SCRIPTS = {
543 out=$(apt-config shell v Acquire::HTTP::Proxy)557 out=$(apt-config shell v Acquire::HTTP::Proxy)
544 eval "$out"558 eval "$out"
545 echo "$v" > apt-proxy559 echo "$v" > apt-proxy
560
561 exit 0
546 """)]562 """)]
547}563}
548564
@@ -890,6 +906,11 @@ class VMBaseClass(TestCase):
890 "--append=ro",906 "--append=ro",
891 ])907 ])
892908
909 # Avoid LP: #1797218 and make vms boot faster
910 cmd.extend(['--append=%s' % service for service in
911 ["systemd.mask=snapd.seeded.service",
912 "systemd.mask=snapd.service"]])
913
893 # getting resolvconf configured is only fixed in bionic914 # getting resolvconf configured is only fixed in bionic
894 # the iscsi_auto handles resolvconf setup via call to915 # the iscsi_auto handles resolvconf setup via call to
895 # configure_networking in initramfs916 # configure_networking in initramfs
@@ -935,13 +956,21 @@ class VMBaseClass(TestCase):
935956
936 # build disk arguments957 # build disk arguments
937 disks = []958 disks = []
938 sc = cls.load_conf_file()959 storage_config = cls.get_class_storage_config()
939 storage_config = yaml.load(sc).get('storage', {}).get('config', {})
940 cls.disk_wwns = ["wwn=%s" % x.get('wwn') for x in storage_config960 cls.disk_wwns = ["wwn=%s" % x.get('wwn') for x in storage_config
941 if 'wwn' in x]961 if 'wwn' in x]
942 cls.disk_serials = ["serial=%s" % x.get('serial')962 cls.disk_serials = []
943 for x in storage_config if 'serial' in x]963 cls.nvme_serials = []
964 for x in storage_config:
965 if 'serial' in x:
966 serial = x.get('serial')
967 if serial.startswith('nvme'):
968 cls.nvme_serials.append("serial=%s" % serial)
969 else:
970 cls.disk_serials.append("serial=%s" % serial)
944971
972 logger.info("disk_serials: %s", cls.disk_serials)
973 logger.info("nvme_serials: %s", cls.nvme_serials)
945 target_disk = "{}:{}:{}:{}:".format(cls.td.target_disk,974 target_disk = "{}:{}:{}:{}:".format(cls.td.target_disk,
946 "",975 "",
947 cls.disk_driver,976 cls.disk_driver,
@@ -973,11 +1002,13 @@ class VMBaseClass(TestCase):
973 disks.extend(['--disk', extra_disk])1002 disks.extend(['--disk', extra_disk])
9741003
975 # build nvme disk args if needed1004 # build nvme disk args if needed
1005 logger.info('nvme disks: %s', cls.nvme_disks)
976 for (disk_no, disk_sz) in enumerate(cls.nvme_disks):1006 for (disk_no, disk_sz) in enumerate(cls.nvme_disks):
977 dpath = os.path.join(cls.td.disks, 'nvme_disk_%d.img' % disk_no)1007 dpath = os.path.join(cls.td.disks, 'nvme_disk_%d.img' % disk_no)
1008 nvme_serial = cls.nvme_serials[disk_no]
978 nvme_disk = '{}:{}:nvme:{}:{}'.format(dpath, disk_sz,1009 nvme_disk = '{}:{}:nvme:{}:{}'.format(dpath, disk_sz,
979 cls.disk_block_size,1010 cls.disk_block_size,
980 "serial=nvme-%d" % disk_no)1011 "%s" % nvme_serial)
981 disks.extend(['--disk', nvme_disk])1012 disks.extend(['--disk', nvme_disk])
9821013
983 # build iscsi disk args if needed1014 # build iscsi disk args if needed
@@ -1206,6 +1237,8 @@ class VMBaseClass(TestCase):
1206 dpath = os.path.join(cls.td.disks, 'nvme_disk_%d.img' % disk_no)1237 dpath = os.path.join(cls.td.disks, 'nvme_disk_%d.img' % disk_no)
1207 disk = '--disk={},driver={},format={},{}'.format(1238 disk = '--disk={},driver={},format={},{}'.format(
1208 dpath, disk_driver, TARGET_IMAGE_FORMAT, bsize_args)1239 dpath, disk_driver, TARGET_IMAGE_FORMAT, bsize_args)
1240 if len(cls.nvme_serials):
1241 disk += ",%s" % cls.nvme_serials[disk_no]
1209 nvme_disks.extend([disk])1242 nvme_disks.extend([disk])
12101243
1211 # unlike NVMe disks, we do not want to configure the iSCSI disks1244 # unlike NVMe disks, we do not want to configure the iSCSI disks
@@ -1525,8 +1558,41 @@ class VMBaseClass(TestCase):
1525 for diskname, part in self.disk_to_check:1558 for diskname, part in self.disk_to_check:
1526 if part is not 0:1559 if part is not 0:
1527 link = diskname + "-part" + str(part)1560 link = diskname + "-part" + str(part)
1528 self.assertIn(link, contents)1561 self.assertIn(link, contents.splitlines())
1529 self.assertIn(diskname, contents)1562 self.assertIn(diskname, contents.splitlines())
1563
1564 @skip_if_flag('expected_failure')
1565 def test_dname_rules(self, disk_to_check=None):
1566 if self.target_distro != "ubuntu":
1567 raise SkipTest("dname not present in non-ubuntu releases")
1568
1569 if not disk_to_check:
1570 disk_to_check = self.disk_to_check
1571 if disk_to_check is None:
1572 logger.debug('test_dname_rules: no disks to check')
1573 return
1574 logger.debug('test_dname_rules: checking disks: %s', disk_to_check)
1575 self.output_files_exist(["udev_rules.d"])
1576
1577 cfg = yaml.load(self.load_collect_file("root/curtin-install-cfg.yaml"))
1578 stgcfg = cfg.get("storage", {}).get("config", [])
1579 disks = [ent for ent in stgcfg if (ent.get('type') == 'disk' and
1580 'name' in ent)]
1581 key_to_udev = {
1582 'serial': 'ID_SERIAL',
1583 'wwn': 'ID_WWN_WITH_EXTENSION',
1584 }
1585 for disk in disks:
1586 dname_file = "%s.rules" % sanitize_dname(disk.get('name'))
1587 contents = self.load_collect_file("udev_rules.d/%s" % dname_file)
1588 for key, key_name in key_to_udev.items():
1589 value = disk.get(key)
1590 if value:
1591 # serials may include spaces, udev replaces them with # _
1592 if ' ' in value:
1593 value = value.replace(' ', '_')
1594 self.assertIn(key_name, contents)
1595 self.assertIn(value, contents)
15301596
1531 @skip_if_flag('expected_failure')1597 @skip_if_flag('expected_failure')
1532 def test_reporting_data(self):1598 def test_reporting_data(self):
@@ -1570,6 +1636,29 @@ class VMBaseClass(TestCase):
1570 kpackage = self.get_kernel_package()1636 kpackage = self.get_kernel_package()
1571 self.assertIn(kpackage, self.debian_packages)1637 self.assertIn(kpackage, self.debian_packages)
15721638
1639 @skip_if_flag('expected_failure')
1640 def test_clear_holders_ran(self):
1641 """ Test curtin install runs block-meta/clear-holders. """
1642 if not self.has_storage_config():
1643 raise SkipTest("This test does not use storage config.")
1644
1645 install_logfile = 'root/curtin-install.log'
1646 self.output_files_exist([install_logfile])
1647 install_log = self.load_collect_file(install_logfile)
1648
1649 # validate block-meta called clear-holders at least once
1650 # We match both 'start' and 'finish' strings, so for each
1651 # call we'll have 2 matches.
1652 clear_holders_re = 'cmd-install/.*cmd-block-meta/clear-holders'
1653 events = re.findall(clear_holders_re, install_log)
1654 print('Matched clear-holder events:\n%s' % events)
1655 self.assertGreaterEqual(len(events), 2)
1656
1657 # dirty_disks mode runs an early block-meta command which
1658 # also runs clear-holders
1659 if self.dirty_disks is True:
1660 self.assertGreaterEqual(len(events), 4)
1661
1573 def run(self, result):1662 def run(self, result):
1574 super(VMBaseClass, self).run(result)1663 super(VMBaseClass, self).run(result)
1575 self.record_result(result)1664 self.record_result(result)
@@ -1609,14 +1698,25 @@ class VMBaseClass(TestCase):
1609 self._debian_packages = pkgs1698 self._debian_packages = pkgs
1610 return self._debian_packages1699 return self._debian_packages
16111700
1701 @classmethod
1702 def get_class_storage_config(cls):
1703 sc = cls.load_conf_file()
1704 return yaml.load(sc).get('storage', {}).get('config', {})
1705
1706 def get_storage_config(self):
1707 cfg = yaml.load(self.load_collect_file("root/curtin-install-cfg.yaml"))
1708 return cfg.get("storage", {}).get("config", [])
1709
1710 def has_storage_config(self):
1711 '''check if test used storage config'''
1712 return len(self.get_storage_config()) > 0
1713
1612 @skip_if_flag('expected_failure')1714 @skip_if_flag('expected_failure')
1613 def test_swaps_used(self):1715 def test_swaps_used(self):
1614 cfg = yaml.load(self.load_collect_file("root/curtin-install-cfg.yaml"))1716 if not self.has_storage_config():
1615 stgcfg = cfg.get("storage", {}).get("config", [])1717 raise SkipTest("This test does not use storage config.")
1616 if len(stgcfg) == 0:
1617 logger.debug("This test does not use storage config.")
1618 return
16191718
1719 stgcfg = self.get_storage_config()
1620 swap_ids = [d["id"] for d in stgcfg if d.get("fstype") == "swap"]1720 swap_ids = [d["id"] for d in stgcfg if d.get("fstype") == "swap"]
1621 swap_mounts = [d for d in stgcfg if d.get("device") in swap_ids]1721 swap_mounts = [d for d in stgcfg if d.get("device") in swap_ids]
1622 self.assertEqual(len(swap_ids), len(swap_mounts),1722 self.assertEqual(len(swap_ids), len(swap_mounts),
@@ -1715,6 +1815,9 @@ class PsuedoVMBaseClass(VMBaseClass):
1715 def test_dname(self, disk_to_check=None):1815 def test_dname(self, disk_to_check=None):
1716 pass1816 pass
17171817
1818 def test_dname_rules(self, disk_to_check=None):
1819 pass
1820
1718 def test_interfacesd_eth0_removed(self):1821 def test_interfacesd_eth0_removed(self):
1719 pass1822 pass
17201823
@@ -1772,6 +1875,7 @@ def check_install_log(install_log, nrchars=200):
1772 install_fail = "({})".format("|".join([1875 install_fail = "({})".format("|".join([
1773 'Installation failed',1876 'Installation failed',
1774 'ImportError: No module named.*',1877 'ImportError: No module named.*',
1878 'Out of memory:',
1775 'Unexpected error while running command',1879 'Unexpected error while running command',
1776 'E: Unable to locate package.*',1880 'E: Unable to locate package.*',
1777 'cloud-init.*: Traceback.*']))1881 'cloud-init.*: Traceback.*']))
diff --git a/tests/vmtests/releases.py b/tests/vmtests/releases.py
index 7be8feb..5dfb2d2 100644
--- a/tests/vmtests/releases.py
+++ b/tests/vmtests/releases.py
@@ -113,6 +113,11 @@ class _CosmicBase(_UbuntuBase):
113 target_release = "cosmic"113 target_release = "cosmic"
114114
115115
116class _DiscoBase(_UbuntuBase):
117 release = "disco"
118 target_release = "disco"
119
120
116class _Releases(object):121class _Releases(object):
117 trusty = _TrustyBase122 trusty = _TrustyBase
118 precise = _PreciseBase123 precise = _PreciseBase
@@ -128,6 +133,7 @@ class _Releases(object):
128 xenial_edge = _XenialEdge133 xenial_edge = _XenialEdge
129 bionic = _BionicBase134 bionic = _BionicBase
130 cosmic = _CosmicBase135 cosmic = _CosmicBase
136 disco = _DiscoBase
131137
132138
133class _CentosReleases(object):139class _CentosReleases(object):
diff --git a/tests/vmtests/test_apt_config_cmd.py b/tests/vmtests/test_apt_config_cmd.py
index f9b6a09..1296240 100644
--- a/tests/vmtests/test_apt_config_cmd.py
+++ b/tests/vmtests/test_apt_config_cmd.py
@@ -5,6 +5,7 @@
5 apt-config standalone command.5 apt-config standalone command.
6"""6"""
7import textwrap7import textwrap
8import yaml
89
9from . import VMBaseClass10from . import VMBaseClass
10from .releases import base_vm_classes as relbase11from .releases import base_vm_classes as relbase
@@ -23,6 +24,8 @@ class TestAptConfigCMD(VMBaseClass):
23 cp /etc/apt/sources.list.d/curtin-dev-ubuntu-test-archive-*.list .24 cp /etc/apt/sources.list.d/curtin-dev-ubuntu-test-archive-*.list .
24 cp /etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg .25 cp /etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg .
25 apt-cache policy | grep proposed > proposed-enabled26 apt-cache policy | grep proposed > proposed-enabled
27
28 exit 0
26 """)]29 """)]
2730
28 def test_cmd_proposed_enabled(self):31 def test_cmd_proposed_enabled(self):
@@ -44,8 +47,10 @@ class TestAptConfigCMD(VMBaseClass):
44 def test_cmd_preserve_source(self):47 def test_cmd_preserve_source(self):
45 """check if cloud-init was prevented from overwriting"""48 """check if cloud-init was prevented from overwriting"""
46 self.output_files_exist(["curtin-preserve-sources.cfg"])49 self.output_files_exist(["curtin-preserve-sources.cfg"])
47 self.check_file_regex("curtin-preserve-sources.cfg",50 # For earlier than xenial 'apt_preserve_sources_list' is expected
48 "apt_preserve_sources_list.*true")51 self.assertEqual(
52 {'apt': {'preserve_sources_list': True}},
53 yaml.load(self.load_collect_file("curtin-preserve-sources.cfg")))
4954
5055
51class XenialTestAptConfigCMDCMD(relbase.xenial, TestAptConfigCMD):56class XenialTestAptConfigCMDCMD(relbase.xenial, TestAptConfigCMD):
@@ -62,4 +67,8 @@ class BionicTestAptConfigCMDCMD(relbase.bionic, TestAptConfigCMD):
62class CosmicTestAptConfigCMDCMD(relbase.cosmic, TestAptConfigCMD):67class CosmicTestAptConfigCMDCMD(relbase.cosmic, TestAptConfigCMD):
63 __test__ = True68 __test__ = True
6469
70
71class DiscoTestAptConfigCMDCMD(relbase.disco, TestAptConfigCMD):
72 __test__ = True
73
65# vi: ts=4 expandtab syntax=python74# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_apt_source.py b/tests/vmtests/test_apt_source.py
index bb502b2..2cd7267 100644
--- a/tests/vmtests/test_apt_source.py
+++ b/tests/vmtests/test_apt_source.py
@@ -4,6 +4,7 @@
4 Collection of tests for the apt configuration features4 Collection of tests for the apt configuration features
5"""5"""
6import textwrap6import textwrap
7import yaml
78
8from . import VMBaseClass9from . import VMBaseClass
9from .releases import base_vm_classes as relbase10from .releases import base_vm_classes as relbase
@@ -34,6 +35,8 @@ class TestAptSrcAbs(VMBaseClass):
34 apt-config dump | grep Retries > aptconf35 apt-config dump | grep Retries > aptconf
35 cp /etc/apt/sources.list sources.list36 cp /etc/apt/sources.list sources.list
36 cp /etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg .37 cp /etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg .
38
39 exit 0
37 """)]40 """)]
38 mirror = "http://us.archive.ubuntu.com/ubuntu"41 mirror = "http://us.archive.ubuntu.com/ubuntu"
39 secmirror = "http://security.ubuntu.com/ubuntu"42 secmirror = "http://security.ubuntu.com/ubuntu"
@@ -61,8 +64,10 @@ class TestAptSrcAbs(VMBaseClass):
61 def test_preserve_source(self):64 def test_preserve_source(self):
62 """test_preserve_source - no clobbering sources.list by cloud-init"""65 """test_preserve_source - no clobbering sources.list by cloud-init"""
63 self.output_files_exist(["curtin-preserve-sources.cfg"])66 self.output_files_exist(["curtin-preserve-sources.cfg"])
64 self.check_file_regex("curtin-preserve-sources.cfg",67 # For earlier than xenial 'apt_preserve_sources_list' is expected
65 "apt_preserve_sources_list.*true")68 self.assertEqual(
69 {'apt': {'preserve_sources_list': True}},
70 yaml.load(self.load_collect_file("curtin-preserve-sources.cfg")))
6671
67 def test_source_files(self):72 def test_source_files(self):
68 """test_source_files - Check generated .lists for correct content"""73 """test_source_files - Check generated .lists for correct content"""
diff --git a/tests/vmtests/test_basic.py b/tests/vmtests/test_basic.py
index 54e3df8..48f07d6 100644
--- a/tests/vmtests/test_basic.py
+++ b/tests/vmtests/test_basic.py
@@ -17,9 +17,14 @@ class TestBasicAbs(VMBaseClass):
17 dirty_disks = True17 dirty_disks = True
18 conf_file = "examples/tests/basic.yaml"18 conf_file = "examples/tests/basic.yaml"
19 extra_disks = ['128G', '128G', '4G']19 extra_disks = ['128G', '128G', '4G']
20 nvme_disks = ['4G']20 disk_to_check = [('btrfs_volume', 0),
21 disk_to_check = [('main_disk_with_in---valid--dname', 1),21 ('main_disk_with_in---valid--dname', 0),
22 ('main_disk_with_in---valid--dname', 2)]22 ('main_disk_with_in---valid--dname', 1),
23 ('main_disk_with_in---valid--dname', 2),
24 ('pnum_disk', 0),
25 ('pnum_disk', 1),
26 ('pnum_disk', 10),
27 ('sparedisk', 0)]
23 extra_collect_scripts = [textwrap.dedent("""28 extra_collect_scripts = [textwrap.dedent("""
24 cd OUTPUT_COLLECT_D29 cd OUTPUT_COLLECT_D
25 blkid -o export /dev/vda | cat >blkid_output_vda30 blkid -o export /dev/vda | cat >blkid_output_vda
@@ -32,6 +37,13 @@ class TestBasicAbs(VMBaseClass):
32 btrfs inspect-internal dump-super $dev |37 btrfs inspect-internal dump-super $dev |
33 awk '/^dev_item.fsid/ {print $2}'38 awk '/^dev_item.fsid/ {print $2}'
34 fi | cat >$f39 fi | cat >$f
40
41 # compare via /dev/zero 8MB
42 cmp --bytes=8388608 /dev/zero /dev/vde2; echo "$?" > cmp_prep.out
43 # extract partition info
44 udevadm info --export --query=property /dev/vde2 | cat >udev_info.out
45
46 exit 0
35 """)]47 """)]
3648
37 def _kname_to_uuid(self, kname):49 def _kname_to_uuid(self, kname):
@@ -108,6 +120,21 @@ class TestBasicAbs(VMBaseClass):
108 # compare them120 # compare them
109 self.assertEqual(kname_uuid, btrfs_uuid)121 self.assertEqual(kname_uuid, btrfs_uuid)
110122
123 def _test_partition_is_prep(self, info_file):
124 udev_info = self.load_collect_file(info_file).rstrip()
125 entry_type = ''
126 for line in udev_info.splitlines():
127 if line.startswith('ID_PART_ENTRY_TYPE'):
128 entry_type = line.split("=", 1)[1].replace("'", "")
129 break
130 # https://en.wikipedia.org/wiki/GUID_Partition_Table
131 # GPT PReP boot UUID
132 self.assertEqual('9e1a2d38-c612-4316-aa26-8b49521e5a8b'.lower(),
133 entry_type.lower())
134
135 def _test_partition_is_zero(self, cmp_file):
136 self.assertEqual(0, int(self.load_collect_file(cmp_file).rstrip()))
137
111 # class specific input138 # class specific input
112 def test_output_files_exist(self):139 def test_output_files_exist(self):
113 self.output_files_exist(140 self.output_files_exist(
@@ -118,9 +145,9 @@ class TestBasicAbs(VMBaseClass):
118 self._test_ptable("blkid_output_vda", "dos")145 self._test_ptable("blkid_output_vda", "dos")
119146
120 def test_partition_numbers(self):147 def test_partition_numbers(self):
121 # vde should have partitions 1 and 10148 # vde should have partitions 1 2, and 10
122 disk = "vde"149 disk = "vde"
123 expected = [disk + s for s in ["", "1", "10"]]150 expected = [disk + s for s in ["", "1", "2", "10"]]
124 self._test_partition_numbers(disk, expected)151 self._test_partition_numbers(disk, expected)
125152
126 def test_fstab_entries(self):153 def test_fstab_entries(self):
@@ -157,6 +184,12 @@ class TestBasicAbs(VMBaseClass):
157 print('Source repo version: %s' % source_version)184 print('Source repo version: %s' % source_version)
158 self.assertEqual(source_version, installed_version)185 self.assertEqual(source_version, installed_version)
159186
187 def test_partition_is_prep(self):
188 self._test_partition_is_prep("udev_info.out")
189
190 def test_partition_is_zero(self):
191 self._test_partition_is_zero("cmp_prep.out")
192
160193
161class CentosTestBasicAbs(TestBasicAbs):194class CentosTestBasicAbs(TestBasicAbs):
162 def test_centos_release(self):195 def test_centos_release(self):
@@ -213,11 +246,14 @@ class CosmicTestBasic(relbase.cosmic, TestBasicAbs):
213 __test__ = True246 __test__ = True
214247
215248
249class DiscoTestBasic(relbase.disco, TestBasicAbs):
250 __test__ = True
251
252
216class TestBasicScsiAbs(TestBasicAbs):253class TestBasicScsiAbs(TestBasicAbs):
217 conf_file = "examples/tests/basic_scsi.yaml"254 conf_file = "examples/tests/basic_scsi.yaml"
218 disk_driver = 'scsi-hd'255 disk_driver = 'scsi-hd'
219 extra_disks = ['128G', '128G', '4G']256 extra_disks = ['128G', '128G', '4G']
220 nvme_disks = ['4G']
221 extra_collect_scripts = [textwrap.dedent("""257 extra_collect_scripts = [textwrap.dedent("""
222 cd OUTPUT_COLLECT_D258 cd OUTPUT_COLLECT_D
223 blkid -o export /dev/sda | cat >blkid_output_sda259 blkid -o export /dev/sda | cat >blkid_output_sda
@@ -230,15 +266,22 @@ class TestBasicScsiAbs(TestBasicAbs):
230 btrfs inspect-internal dump-super $dev |266 btrfs inspect-internal dump-super $dev |
231 awk '/^dev_item.fsid/ {print $2}'267 awk '/^dev_item.fsid/ {print $2}'
232 fi | cat >$f268 fi | cat >$f
269
270 # compare via /dev/zero 8MB
271 cmp --bytes=8388608 /dev/zero /dev/sdd2; echo "$?" > cmp_prep.out
272 # extract partition info
273 udevadm info --export --query=property /dev/sdd2 | cat >udev_info.out
274
275 exit 0
233 """)]276 """)]
234277
235 def test_ptable(self):278 def test_ptable(self):
236 self._test_ptable("blkid_output_sda", "dos")279 self._test_ptable("blkid_output_sda", "dos")
237280
238 def test_partition_numbers(self):281 def test_partition_numbers(self):
239 # sdd should have partitions 1 and 10282 # sdd should have partitions 1, 2, and 10
240 disk = "sdd"283 disk = "sdd"
241 expected = [disk + s for s in ["", "1", "10"]]284 expected = [disk + s for s in ["", "1", "2", "10"]]
242 self._test_partition_numbers(disk, expected)285 self._test_partition_numbers(disk, expected)
243286
244 def test_fstab_entries(self):287 def test_fstab_entries(self):
@@ -255,6 +298,12 @@ class TestBasicScsiAbs(TestBasicAbs):
255 def test_whole_disk_uuid(self):298 def test_whole_disk_uuid(self):
256 self._test_whole_disk_uuid("sdc", "btrfs_uuid_sdc")299 self._test_whole_disk_uuid("sdc", "btrfs_uuid_sdc")
257300
301 def test_partition_is_prep(self):
302 self._test_partition_is_prep("udev_info.out")
303
304 def test_partition_is_zero(self):
305 self._test_partition_is_zero("cmp_prep.out")
306
258307
259class Centos70XenialTestScsiBasic(centos_relbase.centos70_xenial,308class Centos70XenialTestScsiBasic(centos_relbase.centos70_xenial,
260 TestBasicScsiAbs, CentosTestBasicAbs):309 TestBasicScsiAbs, CentosTestBasicAbs):
@@ -280,4 +329,8 @@ class BionicTestScsiBasic(relbase.bionic, TestBasicScsiAbs):
280class CosmicTestScsiBasic(relbase.cosmic, TestBasicScsiAbs):329class CosmicTestScsiBasic(relbase.cosmic, TestBasicScsiAbs):
281 __test__ = True330 __test__ = True
282331
332
333class DiscoTestScsiBasic(relbase.disco, TestBasicScsiAbs):
334 __test__ = True
335
283# vi: ts=4 expandtab syntax=python336# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_bcache_basic.py b/tests/vmtests/test_bcache_basic.py
index b4191b6..a62fb17 100644
--- a/tests/vmtests/test_bcache_basic.py
+++ b/tests/vmtests/test_bcache_basic.py
@@ -20,6 +20,8 @@ class TestBcacheBasic(VMBaseClass):
20 bcache-super-show /dev/vda2 > bcache_super_vda220 bcache-super-show /dev/vda2 > bcache_super_vda2
21 ls /sys/fs/bcache > bcache_ls21 ls /sys/fs/bcache > bcache_ls
22 cat /sys/block/bcache0/bcache/cache_mode > bcache_cache_mode22 cat /sys/block/bcache0/bcache/cache_mode > bcache_cache_mode
23
24 exit 0
23 """)]25 """)]
2426
25 def test_bcache_output_files_exist(self):27 def test_bcache_output_files_exist(self):
@@ -69,4 +71,8 @@ class BionicBcacheBasic(relbase.bionic, TestBcacheBasic):
69class CosmicBcacheBasic(relbase.cosmic, TestBcacheBasic):71class CosmicBcacheBasic(relbase.cosmic, TestBcacheBasic):
70 __test__ = True72 __test__ = True
7173
74
75class DiscoBcacheBasic(relbase.disco, TestBcacheBasic):
76 __test__ = True
77
72# vi: ts=4 expandtab syntax=python78# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_bcache_bug1718699.py b/tests/vmtests/test_bcache_bug1718699.py
index bc0f1e0..5410dc3 100644
--- a/tests/vmtests/test_bcache_bug1718699.py
+++ b/tests/vmtests/test_bcache_bug1718699.py
@@ -22,4 +22,8 @@ class BionicTestBcacheBug1718699(relbase.bionic, TestBcacheBug1718699):
22class CosmicTestBcacheBug1718699(relbase.cosmic, TestBcacheBug1718699):22class CosmicTestBcacheBug1718699(relbase.cosmic, TestBcacheBug1718699):
23 __test__ = True23 __test__ = True
2424
25
26class DiscoTestBcacheBug1718699(relbase.disco, TestBcacheBug1718699):
27 __test__ = True
28
25# vi: ts=4 expandtab syntax=python29# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_fs_battery.py b/tests/vmtests/test_fs_battery.py
index defdf1a..347b62a 100644
--- a/tests/vmtests/test_fs_battery.py
+++ b/tests/vmtests/test_fs_battery.py
@@ -100,6 +100,8 @@ class TestFsBattery(VMBaseClass):
100 echo "$part umount: PASS" ||100 echo "$part umount: PASS" ||
101 echo "$part umount: FAIL: $out"101 echo "$part umount: FAIL: $out"
102 done >> battery-mount-umount102 done >> battery-mount-umount
103
104 exit 0
103 """)]105 """)]
104106
105 def get_fs_entries(self):107 def get_fs_entries(self):
@@ -243,4 +245,7 @@ class CosmicTestFsBattery(relbase.cosmic, TestFsBattery):
243 __test__ = True245 __test__ = True
244246
245247
248class DiscoTestFsBattery(relbase.disco, TestFsBattery):
249 __test__ = True
250
246# vi: ts=4 expandtab syntax=python251# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_iscsi.py b/tests/vmtests/test_iscsi.py
index a800df5..2707d40 100644
--- a/tests/vmtests/test_iscsi.py
+++ b/tests/vmtests/test_iscsi.py
@@ -23,6 +23,8 @@ class TestBasicIscsiAbs(VMBaseClass):
23 cp -a /etc/iscsi ./etc_iscsi23 cp -a /etc/iscsi ./etc_iscsi
24 bash -c \24 bash -c \
25 'for f in /mnt/iscsi*; do cp $f/testfile testfile${f: -1}; done'25 'for f in /mnt/iscsi*; do cp $f/testfile testfile${f: -1}; done'
26
27 exit 0
26 """)]28 """)]
2729
28 def test_fstab_has_netdev_option(self):30 def test_fstab_has_netdev_option(self):
@@ -76,4 +78,8 @@ class BionicTestIscsiBasic(relbase.bionic, TestBasicIscsiAbs):
76class CosmicTestIscsiBasic(relbase.cosmic, TestBasicIscsiAbs):78class CosmicTestIscsiBasic(relbase.cosmic, TestBasicIscsiAbs):
77 __test__ = True79 __test__ = True
7880
81
82class DiscoTestIscsiBasic(relbase.disco, TestBasicIscsiAbs):
83 __test__ = True
84
79# vi: ts=4 expandtab syntax=python85# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_journald_reporter.py b/tests/vmtests/test_journald_reporter.py
index c60a862..80af61e 100644
--- a/tests/vmtests/test_journald_reporter.py
+++ b/tests/vmtests/test_journald_reporter.py
@@ -39,4 +39,8 @@ class BionicTestJournaldReporter(relbase.bionic, TestJournaldReporter):
39class CosmicTestJournaldReporter(relbase.cosmic, TestJournaldReporter):39class CosmicTestJournaldReporter(relbase.cosmic, TestJournaldReporter):
40 __test__ = True40 __test__ = True
4141
42
43class DiscoTestJournaldReporter(relbase.disco, TestJournaldReporter):
44 __test__ = True
45
42# vi: ts=4 expandtab syntax=python46# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_lvm.py b/tests/vmtests/test_lvm.py
index 37053fe..fdb5314 100644
--- a/tests/vmtests/test_lvm.py
+++ b/tests/vmtests/test_lvm.py
@@ -17,6 +17,8 @@ class TestLvmAbs(VMBaseClass):
17 cd OUTPUT_COLLECT_D17 cd OUTPUT_COLLECT_D
18 pvdisplay -C --separator = -o vg_name,pv_name --noheadings > pvs18 pvdisplay -C --separator = -o vg_name,pv_name --noheadings > pvs
19 lvdisplay -C --separator = -o lv_name,vg_name --noheadings > lvs19 lvdisplay -C --separator = -o lv_name,vg_name --noheadings > lvs
20
21 exit 0
20 """)]22 """)]
21 fstab_expected = {23 fstab_expected = {
22 '/dev/vg1/lv1': '/srv/data',24 '/dev/vg1/lv1': '/srv/data',
@@ -73,4 +75,7 @@ class CosmicTestLvm(relbase.cosmic, TestLvmAbs):
73 __test__ = True75 __test__ = True
7476
7577
78class DiscoTestLvm(relbase.disco, TestLvmAbs):
79 __test__ = True
80
76# vi: ts=4 expandtab syntax=python81# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_lvm_iscsi.py b/tests/vmtests/test_lvm_iscsi.py
index 091461e..6cdcab2 100644
--- a/tests/vmtests/test_lvm_iscsi.py
+++ b/tests/vmtests/test_lvm_iscsi.py
@@ -17,13 +17,15 @@ class TestLvmIscsiAbs(TestLvmAbs, TestBasicIscsiAbs):
17 conf_file = "examples/tests/lvm_iscsi.yaml"17 conf_file = "examples/tests/lvm_iscsi.yaml"
18 nr_testfiles = 418 nr_testfiles = 4
1919
20 extra_collect_scripts = TestLvmAbs.extra_collect_scripts20 extra_collect_scripts = (
21 extra_collect_scripts += TestBasicIscsiAbs.extra_collect_scripts21 TestLvmAbs.extra_collect_scripts +
22 extra_collect_scripts += [textwrap.dedent(22 TestBasicIscsiAbs.extra_collect_scripts +
23 """23 [textwrap.dedent("""
24 cd OUTPUT_COLLECT_D24 cd OUTPUT_COLLECT_D
25 ls -al /sys/class/block/dm*/slaves/ > dm_slaves25 ls -al /sys/class/block/dm*/slaves/ > dm_slaves
26 """)]26
27 exit 0
28 """)])
2729
28 fstab_expected = {30 fstab_expected = {
29 'UUID=6de56115-9500-424b-8151-221b270ec708': '/mnt/iscsi1',31 'UUID=6de56115-9500-424b-8151-221b270ec708': '/mnt/iscsi1',
@@ -87,4 +89,8 @@ class BionicTestIscsiLvm(relbase.bionic, TestLvmIscsiAbs):
87class CosmicTestIscsiLvm(relbase.cosmic, TestLvmIscsiAbs):89class CosmicTestIscsiLvm(relbase.cosmic, TestLvmIscsiAbs):
88 __test__ = True90 __test__ = True
8991
92
93class DiscoTestIscsiLvm(relbase.disco, TestLvmIscsiAbs):
94 __test__ = True
95
90# vi: ts=4 expandtab syntax=python96# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_lvm_raid.py b/tests/vmtests/test_lvm_raid.py
index 99a33f2..d70f3ef 100644
--- a/tests/vmtests/test_lvm_raid.py
+++ b/tests/vmtests/test_lvm_raid.py
@@ -14,14 +14,18 @@ class TestLvmOverRaidAbs(TestMdadmAbs, TestLvmAbs):
14 dirty_disks = True14 dirty_disks = True
15 extra_disks = ['10G'] * 415 extra_disks = ['10G'] * 4
1616
17 extra_collect_scripts = TestLvmAbs.extra_collect_scripts17 extra_collect_scripts = (
18 extra_collect_scripts += TestMdadmAbs.extra_collect_scripts18 TestLvmAbs.extra_collect_scripts +
19 extra_collect_scripts += [textwrap.dedent("""19 TestMdadmAbs.extra_collect_scripts +
20 cd OUTPUT_COLLECT_D20 [textwrap.dedent("""
21 ls -al /dev/md* > dev_md21 cd OUTPUT_COLLECT_D
22 cp -a /etc/mdadm etc_mdadm22 ls -al /dev/md* > dev_md
23 cp -a /etc/lvm etc_lvm23 cp -a /etc/mdadm etc_mdadm
24 """)]24 cp -a /etc/lvm etc_lvm
25
26 exit 0
27 """)]
28 )
2529
26 fstab_expected = {30 fstab_expected = {
27 '/dev/vg1/lv1': '/srv/data',31 '/dev/vg1/lv1': '/srv/data',
@@ -39,6 +43,10 @@ class TestLvmOverRaidAbs(TestMdadmAbs, TestLvmAbs):
39 self.check_file_strippedline("pvs", "vg0=/dev/md1")43 self.check_file_strippedline("pvs", "vg0=/dev/md1")
4044
4145
46class DiscoTestLvmOverRaid(relbase.disco, TestLvmOverRaidAbs):
47 __test__ = True
48
49
42class CosmicTestLvmOverRaid(relbase.cosmic, TestLvmOverRaidAbs):50class CosmicTestLvmOverRaid(relbase.cosmic, TestLvmOverRaidAbs):
43 __test__ = True51 __test__ = True
4452
diff --git a/tests/vmtests/test_lvm_root.py b/tests/vmtests/test_lvm_root.py
index 7e7472d..d726a45 100644
--- a/tests/vmtests/test_lvm_root.py
+++ b/tests/vmtests/test_lvm_root.py
@@ -26,6 +26,8 @@ class TestLvmRootAbs(VMBaseClass):
26 vgdisplay > vgdisplay26 vgdisplay > vgdisplay
27 lvdisplay > lvdisplay27 lvdisplay > lvdisplay
28 ls -al /dev/root_vg/ > dev_root_vg28 ls -al /dev/root_vg/ > dev_root_vg
29
30 exit 0
29 """)]31 """)]
30 fstab_expected = {32 fstab_expected = {
31 'UUID=04836770-e989-460f-8774-8e277ddcb40f': '/',33 'UUID=04836770-e989-460f-8774-8e277ddcb40f': '/',
diff --git a/tests/vmtests/test_mdadm_bcache.py b/tests/vmtests/test_mdadm_bcache.py
index adfa55b..f38d4f7 100644
--- a/tests/vmtests/test_mdadm_bcache.py
+++ b/tests/vmtests/test_mdadm_bcache.py
@@ -23,6 +23,8 @@ class TestMdadmAbs(VMBaseClass):
23 ls -al /sys/fs/bcache/* > lsal_sys_fs_bcache_star23 ls -al /sys/fs/bcache/* > lsal_sys_fs_bcache_star
24 ls -al /dev/bcache* > lsal_dev_bcache_star24 ls -al /dev/bcache* > lsal_dev_bcache_star
25 ls -al /dev/bcache/by_uuid/* > lsal_dev_bcache_byuuid_star25 ls -al /dev/bcache/by_uuid/* > lsal_dev_bcache_byuuid_star
26
27 exit 0
26 """)]28 """)]
2729
28 def test_mdadm_output_files_exist(self):30 def test_mdadm_output_files_exist(self):
@@ -63,6 +65,8 @@ class TestMdadmBcacheAbs(TestMdadmAbs):
63 cat /sys/block/bcache0/bcache/cache_mode > bcache_cache_mode65 cat /sys/block/bcache0/bcache/cache_mode > bcache_cache_mode
64 cat /sys/block/bcache1/bcache/cache_mode >> bcache_cache_mode66 cat /sys/block/bcache1/bcache/cache_mode >> bcache_cache_mode
65 cat /sys/block/bcache2/bcache/cache_mode >> bcache_cache_mode67 cat /sys/block/bcache2/bcache/cache_mode >> bcache_cache_mode
68
69 exit 0
66 """)]70 """)]
67 fstab_expected = {71 fstab_expected = {
68 '/dev/vda1': '/media/sda1',72 '/dev/vda1': '/media/sda1',
@@ -151,6 +155,10 @@ class CosmicTestMdadmBcache(relbase.cosmic, TestMdadmBcacheAbs):
151 __test__ = True155 __test__ = True
152156
153157
158class DiscoTestMdadmBcache(relbase.disco, TestMdadmBcacheAbs):
159 __test__ = True
160
161
154class TestMirrorbootAbs(TestMdadmAbs):162class TestMirrorbootAbs(TestMdadmAbs):
155 # alternative config for more complex setup163 # alternative config for more complex setup
156 conf_file = "examples/tests/mirrorboot.yaml"164 conf_file = "examples/tests/mirrorboot.yaml"
@@ -196,6 +204,10 @@ class CosmicTestMirrorboot(relbase.cosmic, TestMirrorbootAbs):
196 __test__ = True204 __test__ = True
197205
198206
207class DiscoTestMirrorboot(relbase.disco, TestMirrorbootAbs):
208 __test__ = True
209
210
199class TestMirrorbootPartitionsAbs(TestMdadmAbs):211class TestMirrorbootPartitionsAbs(TestMdadmAbs):
200 # alternative config for more complex setup212 # alternative config for more complex setup
201 conf_file = "examples/tests/mirrorboot-msdos-partition.yaml"213 conf_file = "examples/tests/mirrorboot-msdos-partition.yaml"
@@ -247,6 +259,11 @@ class CosmicTestMirrorbootPartitions(relbase.cosmic,
247 __test__ = True259 __test__ = True
248260
249261
262class DiscoTestMirrorbootPartitions(relbase.disco,
263 TestMirrorbootPartitionsAbs):
264 __test__ = True
265
266
250class TestMirrorbootPartitionsUEFIAbs(TestMdadmAbs):267class TestMirrorbootPartitionsUEFIAbs(TestMdadmAbs):
251 # alternative config for more complex setup268 # alternative config for more complex setup
252 conf_file = "examples/tests/mirrorboot-uefi.yaml"269 conf_file = "examples/tests/mirrorboot-uefi.yaml"
@@ -297,6 +314,11 @@ class CosmicTestMirrorbootPartitionsUEFI(relbase.cosmic,
297 __test__ = True314 __test__ = True
298315
299316
317class DiscoTestMirrorbootPartitionsUEFI(relbase.disco,
318 TestMirrorbootPartitionsUEFIAbs):
319 __test__ = True
320
321
300class TestRaid5bootAbs(TestMdadmAbs):322class TestRaid5bootAbs(TestMdadmAbs):
301 # alternative config for more complex setup323 # alternative config for more complex setup
302 conf_file = "examples/tests/raid5boot.yaml"324 conf_file = "examples/tests/raid5boot.yaml"
@@ -342,6 +364,10 @@ class CosmicTestRaid5boot(relbase.cosmic, TestRaid5bootAbs):
342 __test__ = True364 __test__ = True
343365
344366
367class DiscoTestRaid5boot(relbase.disco, TestRaid5bootAbs):
368 __test__ = True
369
370
345class TestRaid6bootAbs(TestMdadmAbs):371class TestRaid6bootAbs(TestMdadmAbs):
346 # alternative config for more complex setup372 # alternative config for more complex setup
347 conf_file = "examples/tests/raid6boot.yaml"373 conf_file = "examples/tests/raid6boot.yaml"
@@ -357,6 +383,8 @@ class TestRaid6bootAbs(TestMdadmAbs):
357 TestMdadmAbs.extra_collect_scripts + [textwrap.dedent("""383 TestMdadmAbs.extra_collect_scripts + [textwrap.dedent("""
358 cd OUTPUT_COLLECT_D384 cd OUTPUT_COLLECT_D
359 mdadm --detail --scan > mdadm_detail385 mdadm --detail --scan > mdadm_detail
386
387 exit 0
360 """)])388 """)])
361389
362 def test_raid6_output_files_exist(self):390 def test_raid6_output_files_exist(self):
@@ -400,6 +428,10 @@ class CosmicTestRaid6boot(relbase.cosmic, TestRaid6bootAbs):
400 __test__ = True428 __test__ = True
401429
402430
431class DiscoTestRaid6boot(relbase.disco, TestRaid6bootAbs):
432 __test__ = True
433
434
403class TestRaid10bootAbs(TestMdadmAbs):435class TestRaid10bootAbs(TestMdadmAbs):
404 # alternative config for more complex setup436 # alternative config for more complex setup
405 conf_file = "examples/tests/raid10boot.yaml"437 conf_file = "examples/tests/raid10boot.yaml"
@@ -446,6 +478,10 @@ class CosmicTestRaid10boot(relbase.cosmic, TestRaid10bootAbs):
446 __test__ = True478 __test__ = True
447479
448480
481class DiscoTestRaid10boot(relbase.disco, TestRaid10bootAbs):
482 __test__ = True
483
484
449class TestAllindataAbs(TestMdadmAbs):485class TestAllindataAbs(TestMdadmAbs):
450 # more complex, needs more time486 # more complex, needs more time
451 # alternative config for more complex setup487 # alternative config for more complex setup
@@ -493,6 +529,8 @@ class TestAllindataAbs(TestMdadmAbs):
493 mkdir -p /tmp/xfstest529 mkdir -p /tmp/xfstest
494 mount /dev/mapper/dmcrypt0 /tmp/xfstest530 mount /dev/mapper/dmcrypt0 /tmp/xfstest
495 xfs_info /tmp/xfstest/ > xfs_info531 xfs_info /tmp/xfstest/ > xfs_info
532
533 exit 0
496 """)])534 """)])
497 fstab_expected = {535 fstab_expected = {
498 '/dev/vg1/lv1': '/srv/data',536 '/dev/vg1/lv1': '/srv/data',
@@ -547,4 +585,8 @@ class BionicTestAllindata(relbase.bionic, TestAllindataAbs):
547class CosmicTestAllindata(relbase.cosmic, TestAllindataAbs):585class CosmicTestAllindata(relbase.cosmic, TestAllindataAbs):
548 __test__ = True586 __test__ = True
549587
588
589class DiscoTestAllindata(relbase.disco, TestAllindataAbs):
590 __test__ = True
591
550# vi: ts=4 expandtab syntax=python592# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_mdadm_iscsi.py b/tests/vmtests/test_mdadm_iscsi.py
index 537baec..b43855d 100644
--- a/tests/vmtests/test_mdadm_iscsi.py
+++ b/tests/vmtests/test_mdadm_iscsi.py
@@ -18,12 +18,15 @@ class TestMdadmIscsiAbs(TestMdadmAbs, TestBasicIscsiAbs):
18 conf_file = "examples/tests/mdadm_iscsi.yaml"18 conf_file = "examples/tests/mdadm_iscsi.yaml"
19 nr_testfiles = 119 nr_testfiles = 1
2020
21 extra_collect_scripts = TestMdadmAbs.extra_collect_scripts21 extra_collect_scripts = (
22 extra_collect_scripts += TestBasicIscsiAbs.extra_collect_scripts22 TestMdadmAbs.extra_collect_scripts +
23 extra_collect_scripts += [textwrap.dedent("""23 TestBasicIscsiAbs.extra_collect_scripts +
24 cd OUTPUT_COLLECT_D24 [textwrap.dedent("""
25 ls -al /sys/class/block/md*/slaves/ > md_slaves25 cd OUTPUT_COLLECT_D
26 """)]26 ls -al /sys/class/block/md*/slaves/ > md_slaves
27
28 exit 0
29 """)])
2730
2831
29class Centos70TestIscsiMdadm(centos_relbase.centos70_xenial,32class Centos70TestIscsiMdadm(centos_relbase.centos70_xenial,
@@ -54,4 +57,8 @@ class BionicTestIscsiMdadm(relbase.bionic, TestMdadmIscsiAbs):
54class CosmicTestIscsiMdadm(relbase.cosmic, TestMdadmIscsiAbs):57class CosmicTestIscsiMdadm(relbase.cosmic, TestMdadmIscsiAbs):
55 __test__ = True58 __test__ = True
5659
60
61class DiscoTestIscsiMdadm(relbase.disco, TestMdadmIscsiAbs):
62 __test__ = True
63
57# vi: ts=4 expandtab syntax=python64# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_multipath.py b/tests/vmtests/test_multipath.py
index a6bd8ce..a22bc0f 100644
--- a/tests/vmtests/test_multipath.py
+++ b/tests/vmtests/test_multipath.py
@@ -4,6 +4,8 @@ from . import VMBaseClass
4from .releases import base_vm_classes as relbase4from .releases import base_vm_classes as relbase
5from .releases import centos_base_vm_classes as centos_relbase5from .releases import centos_base_vm_classes as centos_relbase
66
7from unittest import SkipTest
8import os
7import textwrap9import textwrap
810
911
@@ -22,6 +24,11 @@ class TestMultipathBasicAbs(VMBaseClass):
22 cp -a /etc/multipath* .24 cp -a /etc/multipath* .
23 readlink -f /sys/class/block/sda/holders/dm-0 > holders_sda25 readlink -f /sys/class/block/sda/holders/dm-0 > holders_sda
24 readlink -f /sys/class/block/sdb/holders/dm-0 > holders_sdb26 readlink -f /sys/class/block/sdb/holders/dm-0 > holders_sdb
27 command -v systemctl && {
28 systemctl show -- home.mount > systemctl_show_home.mount;
29 systemctl status --full home.mount > systemctl_status_home.mount
30 }
31 exit 0
25 """)]32 """)]
2633
27 def test_multipath_disks_match(self):34 def test_multipath_disks_match(self):
@@ -31,6 +38,26 @@ class TestMultipathBasicAbs(VMBaseClass):
31 print('sdb holders:\n%s' % sdb_data)38 print('sdb holders:\n%s' % sdb_data)
32 self.assertEqual(sda_data, sdb_data)39 self.assertEqual(sda_data, sdb_data)
3340
41 def test_home_mount_unit(self):
42 unit_file = 'systemctl_show_home.mount'
43 if not os.path.exists(self.collect_path(unit_file)):
44 raise SkipTest(
45 'target_release=%s does not use systemd' % self.target_release)
46
47 # We can't use load_shell_content as systemctl show output
48 # does not quote values even though it's in Key=Value format
49 content = self.load_collect_file(unit_file)
50 expected_results = {
51 'ActiveState': 'active',
52 'Result': 'success',
53 'SubState': 'mounted',
54 }
55 show = {key: value for key, value in
56 [line.split('=') for line in content.splitlines()
57 if line.split('=')[0] in expected_results.keys()]}
58
59 self.assertEqual(sorted(expected_results), sorted(show))
60
3461
35class Centos70TestMultipathBasic(centos_relbase.centos70_xenial,62class Centos70TestMultipathBasic(centos_relbase.centos70_xenial,
36 TestMultipathBasicAbs):63 TestMultipathBasicAbs):
@@ -65,4 +92,8 @@ class BionicTestMultipathBasic(relbase.bionic, TestMultipathBasicAbs):
65class CosmicTestMultipathBasic(relbase.cosmic, TestMultipathBasicAbs):92class CosmicTestMultipathBasic(relbase.cosmic, TestMultipathBasicAbs):
66 __test__ = True93 __test__ = True
6794
95
96class DiscoTestMultipathBasic(relbase.disco, TestMultipathBasicAbs):
97 __test__ = True
98
68# vi: ts=4 expandtab syntax=python99# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_network.py b/tests/vmtests/test_network.py
index ce4cdb9..1b0e41c 100644
--- a/tests/vmtests/test_network.py
+++ b/tests/vmtests/test_network.py
@@ -49,6 +49,8 @@ class TestNetworkBaseTestsAbs(VMBaseClass):
49 cp -a /etc/systemd ./etc_systemd ||:49 cp -a /etc/systemd ./etc_systemd ||:
50 journalctl --no-pager -b -x | tee journalctl_out50 journalctl --no-pager -b -x | tee journalctl_out
51 sleep 10 && ip a | tee ip_a51 sleep 10 && ip a | tee ip_a
52
53 exit 0
52 """)]54 """)]
5355
54 def test_output_files_exist(self):56 def test_output_files_exist(self):
@@ -473,6 +475,10 @@ class CosmicTestNetworkBasic(relbase.cosmic, TestNetworkBasicAbs):
473 __test__ = True475 __test__ = True
474476
475477
478class DiscoTestNetworkBasic(relbase.disco, TestNetworkBasicAbs):
479 __test__ = True
480
481
476class Centos66TestNetworkBasic(centos_relbase.centos66_xenial,482class Centos66TestNetworkBasic(centos_relbase.centos66_xenial,
477 CentosTestNetworkBasicAbs):483 CentosTestNetworkBasicAbs):
478 __test__ = True484 __test__ = True
diff --git a/tests/vmtests/test_network_alias.py b/tests/vmtests/test_network_alias.py
index 1d1837f..b2c4ed7 100644
--- a/tests/vmtests/test_network_alias.py
+++ b/tests/vmtests/test_network_alias.py
@@ -26,6 +26,8 @@ class CentosTestNetworkAliasAbs(TestNetworkAliasAbs):
26 cp -a /var/log/cloud-init* .26 cp -a /var/log/cloud-init* .
27 cp -a /var/lib/cloud ./var_lib_cloud27 cp -a /var/lib/cloud ./var_lib_cloud
28 cp -a /run/cloud-init ./run_cloud-init28 cp -a /run/cloud-init ./run_cloud-init
29
30 exit 0
29 """)]31 """)]
3032
31 def test_etc_resolvconf(self):33 def test_etc_resolvconf(self):
@@ -76,4 +78,8 @@ class BionicTestNetworkAlias(relbase.bionic, TestNetworkAliasAbs):
76class CosmicTestNetworkAlias(relbase.cosmic, TestNetworkAliasAbs):78class CosmicTestNetworkAlias(relbase.cosmic, TestNetworkAliasAbs):
77 __test__ = True79 __test__ = True
7880
81
82class DiscoTestNetworkAlias(relbase.disco, TestNetworkAliasAbs):
83 __test__ = True
84
79# vi: ts=4 expandtab syntax=python85# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_network_bonding.py b/tests/vmtests/test_network_bonding.py
index 572f240..fe3abe9 100644
--- a/tests/vmtests/test_network_bonding.py
+++ b/tests/vmtests/test_network_bonding.py
@@ -33,6 +33,8 @@ class CentosTestNetworkBondingAbs(TestNetworkBondingAbs):
33 cp -a /var/lib/cloud ./var_lib_cloud33 cp -a /var/lib/cloud ./var_lib_cloud
34 cp -a /run/cloud-init ./run_cloud-init34 cp -a /run/cloud-init ./run_cloud-init
35 rpm -qf `which ifenslave` |tee ifenslave_installed35 rpm -qf `which ifenslave` |tee ifenslave_installed
36
37 exit 0
36 """)]38 """)]
3739
38 def test_ifenslave_package_status(self):40 def test_ifenslave_package_status(self):
@@ -79,6 +81,10 @@ class CosmicTestBonding(relbase.cosmic, TestNetworkBondingAbs):
79 __test__ = True81 __test__ = True
8082
8183
84class DiscoTestBonding(relbase.disco, TestNetworkBondingAbs):
85 __test__ = True
86
87
82class Centos66TestNetworkBonding(centos_relbase.centos66_xenial,88class Centos66TestNetworkBonding(centos_relbase.centos66_xenial,
83 CentosTestNetworkBondingAbs):89 CentosTestNetworkBondingAbs):
84 __test__ = True90 __test__ = True
diff --git a/tests/vmtests/test_network_bridging.py b/tests/vmtests/test_network_bridging.py
index ed9b728..8576d60 100644
--- a/tests/vmtests/test_network_bridging.py
+++ b/tests/vmtests/test_network_bridging.py
@@ -102,6 +102,8 @@ class TestBridgeNetworkAbs(TestNetworkBaseTestsAbs):
102 grep -r . /sys/class/net/br0 > sysfs_br0102 grep -r . /sys/class/net/br0 > sysfs_br0
103 grep -r . /sys/class/net/br0/brif/eth1 > sysfs_br0_eth1103 grep -r . /sys/class/net/br0/brif/eth1 > sysfs_br0_eth1
104 grep -r . /sys/class/net/br0/brif/eth2 > sysfs_br0_eth2104 grep -r . /sys/class/net/br0/brif/eth2 > sysfs_br0_eth2
105
106 exit 0
105 """)]107 """)]
106108
107 def test_output_files_exist_bridge(self):109 def test_output_files_exist_bridge(self):
@@ -200,6 +202,8 @@ class CentosTestBridgeNetworkAbs(TestBridgeNetworkAbs):
200 cp -a /var/lib/cloud ./var_lib_cloud202 cp -a /var/lib/cloud ./var_lib_cloud
201 cp -a /run/cloud-init ./run_cloud-init203 cp -a /run/cloud-init ./run_cloud-init
202 rpm -qf `which brctl` |tee bridge-utils_installed204 rpm -qf `which brctl` |tee bridge-utils_installed
205
206 exit 0
203 """)]207 """)]
204208
205 def test_etc_network_interfaces(self):209 def test_etc_network_interfaces(self):
@@ -236,4 +240,7 @@ class CosmicTestBridging(relbase.cosmic, TestBridgeNetworkAbs):
236 __test__ = True240 __test__ = True
237241
238242
243class DiscoTestBridging(relbase.disco, TestBridgeNetworkAbs):
244 __test__ = True
245
239# vi: ts=4 expandtab syntax=python246# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_network_ipv6.py b/tests/vmtests/test_network_ipv6.py
index b8a69e8..42adb20 100644
--- a/tests/vmtests/test_network_ipv6.py
+++ b/tests/vmtests/test_network_ipv6.py
@@ -21,6 +21,8 @@ class TestNetworkIPV6Abs(TestNetworkBaseTestsAbs):
21 grep . -r /sys/class/net/bond0/ > sysfs_bond0 || :21 grep . -r /sys/class/net/bond0/ > sysfs_bond0 || :
22 grep . -r /sys/class/net/bond0.108/ > sysfs_bond0.108 || :22 grep . -r /sys/class/net/bond0.108/ > sysfs_bond0.108 || :
23 grep . -r /sys/class/net/bond0.208/ > sysfs_bond0.208 || :23 grep . -r /sys/class/net/bond0.208/ > sysfs_bond0.208 || :
24
25 exit 0
24 """)]26 """)]
2527
2628
@@ -32,6 +34,8 @@ class CentosTestNetworkIPV6Abs(TestNetworkIPV6Abs):
32 cp -a /var/log/cloud-init* .34 cp -a /var/log/cloud-init* .
33 cp -a /var/lib/cloud ./var_lib_cloud35 cp -a /var/lib/cloud ./var_lib_cloud
34 cp -a /run/cloud-init ./run_cloud-init36 cp -a /run/cloud-init ./run_cloud-init
37
38 exit 0
35 """)]39 """)]
3640
37 def test_etc_network_interfaces(self):41 def test_etc_network_interfaces(self):
@@ -72,6 +76,10 @@ class CosmicTestNetworkIPV6(relbase.cosmic, TestNetworkIPV6Abs):
72 __test__ = True76 __test__ = True
7377
7478
79class DiscoTestNetworkIPV6(relbase.disco, TestNetworkIPV6Abs):
80 __test__ = True
81
82
75class Centos66TestNetworkIPV6(centos_relbase.centos66_xenial,83class Centos66TestNetworkIPV6(centos_relbase.centos66_xenial,
76 CentosTestNetworkIPV6Abs):84 CentosTestNetworkIPV6Abs):
77 __test__ = True85 __test__ = True
diff --git a/tests/vmtests/test_network_ipv6_static.py b/tests/vmtests/test_network_ipv6_static.py
index 7ebc5eb..bbd90c9 100644
--- a/tests/vmtests/test_network_ipv6_static.py
+++ b/tests/vmtests/test_network_ipv6_static.py
@@ -54,6 +54,10 @@ class CosmicTestNetworkIPV6Static(relbase.cosmic, TestNetworkIPV6StaticAbs):
54 __test__ = True54 __test__ = True
5555
5656
57class DiscoTestNetworkIPV6Static(relbase.disco, TestNetworkIPV6StaticAbs):
58 __test__ = True
59
60
57class Centos66TestNetworkIPV6Static(centos_relbase.centos66_xenial,61class Centos66TestNetworkIPV6Static(centos_relbase.centos66_xenial,
58 CentosTestNetworkIPV6StaticAbs):62 CentosTestNetworkIPV6StaticAbs):
59 __test__ = True63 __test__ = True
diff --git a/tests/vmtests/test_network_ipv6_vlan.py b/tests/vmtests/test_network_ipv6_vlan.py
index 2f7bd49..7401d2c 100644
--- a/tests/vmtests/test_network_ipv6_vlan.py
+++ b/tests/vmtests/test_network_ipv6_vlan.py
@@ -35,6 +35,10 @@ class CosmicTestNetworkIPV6Vlan(relbase.cosmic, TestNetworkIPV6VlanAbs):
35 __test__ = True35 __test__ = True
3636
3737
38class DiscoTestNetworkIPV6Vlan(relbase.disco, TestNetworkIPV6VlanAbs):
39 __test__ = True
40
41
38class Centos66TestNetworkIPV6Vlan(centos_relbase.centos66_xenial,42class Centos66TestNetworkIPV6Vlan(centos_relbase.centos66_xenial,
39 CentosTestNetworkIPV6VlanAbs):43 CentosTestNetworkIPV6VlanAbs):
40 __test__ = True44 __test__ = True
diff --git a/tests/vmtests/test_network_mtu.py b/tests/vmtests/test_network_mtu.py
index eaa383b..ffde5c7 100644
--- a/tests/vmtests/test_network_mtu.py
+++ b/tests/vmtests/test_network_mtu.py
@@ -36,6 +36,8 @@ class TestNetworkMtuAbs(TestNetworkIPV6Abs):
36 if [ -e /var/log/upstart ]; then36 if [ -e /var/log/upstart ]; then
37 cp -a /var/log/upstart ./var_log_upstart37 cp -a /var/log/upstart ./var_log_upstart
38 fi38 fi
39
40 exit 0
39 """)]41 """)]
4042
41 def _load_mtu_data(self, ifname):43 def _load_mtu_data(self, ifname):
@@ -128,6 +130,8 @@ class CentosTestNetworkMtuAbs(TestNetworkMtuAbs):
128 cp -a /var/log/cloud-init* .130 cp -a /var/log/cloud-init* .
129 cp -a /var/lib/cloud ./var_lib_cloud131 cp -a /var/lib/cloud ./var_lib_cloud
130 cp -a /run/cloud-init ./run_cloud-init132 cp -a /run/cloud-init ./run_cloud-init
133
134 exit 0
131 """)]135 """)]
132136
133 def test_etc_network_interfaces(self):137 def test_etc_network_interfaces(self):
@@ -200,6 +204,11 @@ class CosmicTestNetworkMtu(relbase.cosmic, TestNetworkMtuAbs):
200 __test__ = True204 __test__ = True
201205
202206
207@TestNetworkMtuAbs.skip_by_date("1671951", fixby="2019-01-02")
208class DiscoTestNetworkMtu(relbase.disco, TestNetworkMtuAbs):
209 __test__ = True
210
211
203class Centos66TestNetworkMtu(centos_relbase.centos66_xenial,212class Centos66TestNetworkMtu(centos_relbase.centos66_xenial,
204 CentosTestNetworkMtuAbs):213 CentosTestNetworkMtuAbs):
205 __test__ = True214 __test__ = True
diff --git a/tests/vmtests/test_network_static.py b/tests/vmtests/test_network_static.py
index 9e042a5..6820c33 100644
--- a/tests/vmtests/test_network_static.py
+++ b/tests/vmtests/test_network_static.py
@@ -59,6 +59,10 @@ class CosmicTestNetworkStatic(relbase.cosmic, TestNetworkStaticAbs):
59 __test__ = True59 __test__ = True
6060
6161
62class DiscoTestNetworkStatic(relbase.disco, TestNetworkStaticAbs):
63 __test__ = True
64
65
62class Centos66TestNetworkStatic(centos_relbase.centos66_xenial,66class Centos66TestNetworkStatic(centos_relbase.centos66_xenial,
63 CentosTestNetworkStaticAbs):67 CentosTestNetworkStaticAbs):
64 __test__ = True68 __test__ = True
diff --git a/tests/vmtests/test_network_static_routes.py b/tests/vmtests/test_network_static_routes.py
index ad18eef..405a730 100644
--- a/tests/vmtests/test_network_static_routes.py
+++ b/tests/vmtests/test_network_static_routes.py
@@ -59,6 +59,11 @@ class CosmicTestNetworkStaticRoutes(relbase.cosmic,
59 __test__ = True59 __test__ = True
6060
6161
62class DiscoTestNetworkStaticRoutes(relbase.disco,
63 TestNetworkStaticRoutesAbs):
64 __test__ = True
65
66
62class Centos66TestNetworkStaticRoutes(centos_relbase.centos66_xenial,67class Centos66TestNetworkStaticRoutes(centos_relbase.centos66_xenial,
63 CentosTestNetworkStaticRoutesAbs):68 CentosTestNetworkStaticRoutesAbs):
64 __test__ = False69 __test__ = False
diff --git a/tests/vmtests/test_network_vlan.py b/tests/vmtests/test_network_vlan.py
index 9e55bfa..904f8c2 100644
--- a/tests/vmtests/test_network_vlan.py
+++ b/tests/vmtests/test_network_vlan.py
@@ -18,6 +18,8 @@ class TestNetworkVlanAbs(TestNetworkBaseTestsAbs):
18 ip -d link show interface1.2668 |tee ip_link_show_interface1.266818 ip -d link show interface1.2668 |tee ip_link_show_interface1.2668
19 ip -d link show interface1.2669 |tee ip_link_show_interface1.266919 ip -d link show interface1.2669 |tee ip_link_show_interface1.2669
20 ip -d link show interface1.2670 |tee ip_link_show_interface1.267020 ip -d link show interface1.2670 |tee ip_link_show_interface1.2670
21
22 exit 0
21 """)]23 """)]
2224
23 def get_vlans(self):25 def get_vlans(self):
@@ -86,6 +88,10 @@ class CosmicTestNetworkVlan(relbase.cosmic, TestNetworkVlanAbs):
86 __test__ = True88 __test__ = True
8789
8890
91class DiscoTestNetworkVlan(relbase.disco, TestNetworkVlanAbs):
92 __test__ = True
93
94
89class Centos66TestNetworkVlan(centos_relbase.centos66_xenial,95class Centos66TestNetworkVlan(centos_relbase.centos66_xenial,
90 CentosTestNetworkVlanAbs):96 CentosTestNetworkVlanAbs):
91 __test__ = True97 __test__ = True
diff --git a/tests/vmtests/test_nvme.py b/tests/vmtests/test_nvme.py
index f3d1ae2..60d9d86 100644
--- a/tests/vmtests/test_nvme.py
+++ b/tests/vmtests/test_nvme.py
@@ -19,14 +19,17 @@ class TestNvmeAbs(VMBaseClass):
19 conf_file = "examples/tests/nvme.yaml"19 conf_file = "examples/tests/nvme.yaml"
20 extra_disks = []20 extra_disks = []
21 nvme_disks = ['4G', '4G']21 nvme_disks = ['4G', '4G']
22 disk_to_check = [('main_disk', 1), ('main_disk', 2), ('main_disk', 15),22 disk_to_check = [
23 ('nvme_disk', 1), ('nvme_disk', 2), ('nvme_disk', 3),23 ('main_disk', 1), ('main_disk', 2), ('main_disk', 15),
24 ('second_nvme', 1)]24 ('nvme_disk', 0), ('nvme_disk', 1), ('nvme_disk', 2), ('nvme_disk', 3),
25 ('second_nvme', 0), ('second_nvme', 1)]
25 extra_collect_scripts = [textwrap.dedent("""26 extra_collect_scripts = [textwrap.dedent("""
26 cd OUTPUT_COLLECT_D27 cd OUTPUT_COLLECT_D
27 ls /sys/class/ > sys_class28 ls /sys/class/ > sys_class
28 ls /sys/class/nvme/ > ls_nvme29 ls /sys/class/nvme/ > ls_nvme
29 ls /dev/nvme* > ls_dev_nvme30 ls /dev/nvme* > ls_dev_nvme
31
32 exit 0
30 """)]33 """)]
3134
32 def _test_nvme_device_names(self, expected):35 def _test_nvme_device_names(self, expected):
@@ -82,6 +85,10 @@ class CosmicTestNvme(relbase.cosmic, TestNvmeAbs):
82 __test__ = True85 __test__ = True
8386
8487
88class DiscoTestNvme(relbase.cosmic, TestNvmeAbs):
89 __test__ = True
90
91
85class TestNvmeBcacheAbs(TestNvmeAbs):92class TestNvmeBcacheAbs(TestNvmeAbs):
86 arch_skip = [93 arch_skip = [
87 "s390x", # nvme is a pci device, no pci on s390x94 "s390x", # nvme is a pci device, no pci on s390x
@@ -91,7 +98,8 @@ class TestNvmeBcacheAbs(TestNvmeAbs):
91 extra_disks = ['10G']98 extra_disks = ['10G']
92 nvme_disks = ['6G']99 nvme_disks = ['6G']
93 uefi = True100 uefi = True
94 disk_to_check = [('sda', 1), ('sda', 2), ('sda', 3)]101 disk_to_check = [('sda', 1), ('sda', 2), ('sda', 3),
102 ('sdb', 0), ('nvme0n1', 0)]
95103
96 extra_collect_scripts = [textwrap.dedent("""104 extra_collect_scripts = [textwrap.dedent("""
97 cd OUTPUT_COLLECT_D105 cd OUTPUT_COLLECT_D
@@ -102,6 +110,8 @@ class TestNvmeBcacheAbs(TestNvmeAbs):
102 bcache-super-show /dev/nvme0n1p1 > bcache_super_nvme0n1p1110 bcache-super-show /dev/nvme0n1p1 > bcache_super_nvme0n1p1
103 ls /sys/fs/bcache > bcache_ls111 ls /sys/fs/bcache > bcache_ls
104 cat /sys/block/bcache0/bcache/cache_mode > bcache_cache_mode112 cat /sys/block/bcache0/bcache/cache_mode > bcache_cache_mode
113
114 exit 0
105 """)]115 """)]
106116
107 def test_bcache_output_files_exist(self):117 def test_bcache_output_files_exist(self):
@@ -145,4 +155,7 @@ class CosmicTestNvmeBcache(relbase.cosmic, TestNvmeBcacheAbs):
145 __test__ = True155 __test__ = True
146156
147157
158class DiscoTestNvmeBcache(relbase.disco, TestNvmeBcacheAbs):
159 __test__ = True
160
148# vi: ts=4 expandtab syntax=python161# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_old_apt_features.py b/tests/vmtests/test_old_apt_features.py
index 33561a3..ec3765c 100644
--- a/tests/vmtests/test_old_apt_features.py
+++ b/tests/vmtests/test_old_apt_features.py
@@ -5,6 +5,7 @@
5"""5"""
6import re6import re
7import textwrap7import textwrap
8import yaml
89
9from . import VMBaseClass10from . import VMBaseClass
10from .releases import base_vm_classes as relbase11from .releases import base_vm_classes as relbase
@@ -50,6 +51,8 @@ class TestOldAptAbs(VMBaseClass):
50 cp /etc/apt/sources.list .51 cp /etc/apt/sources.list .
51 cp /etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg .52 cp /etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg .
52 cp /etc/cloud/cloud.cfg.d/90_dpkg.cfg .53 cp /etc/cloud/cloud.cfg.d/90_dpkg.cfg .
54
55 exit 0
53 """)]56 """)]
54 arch = util.get_architecture()57 arch = util.get_architecture()
55 if arch in ['amd64', 'i386']:58 if arch in ['amd64', 'i386']:
@@ -69,8 +72,10 @@ class TestOldAptAbs(VMBaseClass):
6972
70 def test_preserve_source(self):73 def test_preserve_source(self):
71 """test_preserve_source - no clobbering sources.list by cloud-init"""74 """test_preserve_source - no clobbering sources.list by cloud-init"""
72 self.check_file_regex("curtin-preserve-sources.cfg",75 # For earlier than xenial 'apt_preserve_sources_list' is expected
73 "apt_preserve_sources_list.*true")76 self.assertEqual(
77 {'apt': {'preserve_sources_list': True}},
78 yaml.load(self.load_collect_file("curtin-preserve-sources.cfg")))
7479
75 def test_debconf(self):80 def test_debconf(self):
76 """test_debconf - Check if debconf is in place"""81 """test_debconf - Check if debconf is in place"""
diff --git a/tests/vmtests/test_pollinate_useragent.py b/tests/vmtests/test_pollinate_useragent.py
index 13dd28c..c1c6c36 100644
--- a/tests/vmtests/test_pollinate_useragent.py
+++ b/tests/vmtests/test_pollinate_useragent.py
@@ -18,6 +18,8 @@ class TestPollinateUserAgent(VMBaseClass):
18 cd OUTPUT_COLLECT_D18 cd OUTPUT_COLLECT_D
19 cp -a /etc/pollinate etc_pollinate19 cp -a /etc/pollinate etc_pollinate
20 pollinate --print-user-agent > pollinate_print_user_agent20 pollinate --print-user-agent > pollinate_print_user_agent
21
22 exit 0
21 """)]23 """)]
2224
23 def test_pollinate_user_agent(self):25 def test_pollinate_user_agent(self):
@@ -66,4 +68,8 @@ class BionicTestPollinateUserAgent(relbase.bionic, TestPollinateUserAgent):
66class CosmicTestPollinateUserAgent(relbase.cosmic, TestPollinateUserAgent):68class CosmicTestPollinateUserAgent(relbase.cosmic, TestPollinateUserAgent):
67 __test__ = True69 __test__ = True
6870
71
72class DiscoTestPollinateUserAgent(relbase.disco, TestPollinateUserAgent):
73 __test__ = True
74
69# vi: ts=4 expandtab syntax=python75# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_raid5_bcache.py b/tests/vmtests/test_raid5_bcache.py
index 8d24f8e..76d0eed 100644
--- a/tests/vmtests/test_raid5_bcache.py
+++ b/tests/vmtests/test_raid5_bcache.py
@@ -16,6 +16,8 @@ class TestMdadmAbs(VMBaseClass):
16 mdadm --detail --scan > mdadm_status16 mdadm --detail --scan > mdadm_status
17 mdadm --detail --scan | grep -c ubuntu > mdadm_active117 mdadm --detail --scan | grep -c ubuntu > mdadm_active1
18 grep -c active /proc/mdstat > mdadm_active218 grep -c active /proc/mdstat > mdadm_active2
19
20 exit 0
19 """)]21 """)]
2022
21 def test_mdadm_output_files_exist(self):23 def test_mdadm_output_files_exist(self):
@@ -33,13 +35,15 @@ class TestMdadmBcacheAbs(TestMdadmAbs):
33 conf_file = "examples/tests/raid5bcache.yaml"35 conf_file = "examples/tests/raid5bcache.yaml"
34 disk_to_check = [('md0', 0), ('sda', 2)]36 disk_to_check = [('md0', 0), ('sda', 2)]
3537
36 extra_collect_scripts = TestMdadmAbs.extra_collect_scripts38 extra_collect_scripts = (
37 extra_collect_scripts += [textwrap.dedent("""39 TestMdadmAbs.extra_collect_scripts +
38 cd OUTPUT_COLLECT_D40 [textwrap.dedent("""\
39 bcache-super-show /dev/vda2 > bcache_super_vda241 cd OUTPUT_COLLECT_D
40 ls /sys/fs/bcache > bcache_ls42 bcache-super-show /dev/vda2 > bcache_super_vda2
41 cat /sys/block/bcache0/bcache/cache_mode > bcache_cache_mode43 ls /sys/fs/bcache > bcache_ls
42 """)]44 cat /sys/block/bcache0/bcache/cache_mode > bcache_cache_mode
45
46 exit 0""")])
43 fstab_expected = {47 fstab_expected = {
44 '/dev/bcache0': '/',48 '/dev/bcache0': '/',
45 '/dev/md0': '/srv/data',49 '/dev/md0': '/srv/data',
@@ -101,4 +105,8 @@ class BionicTestRaid5Bcache(relbase.bionic, TestMdadmBcacheAbs):
101class CosmicTestRaid5Bcache(relbase.cosmic, TestMdadmBcacheAbs):105class CosmicTestRaid5Bcache(relbase.cosmic, TestMdadmBcacheAbs):
102 __test__ = True106 __test__ = True
103107
108
109class DiscoTestRaid5Bcache(relbase.disco, TestMdadmBcacheAbs):
110 __test__ = True
111
104# vi: ts=4 expandtab syntax=python112# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_simple.py b/tests/vmtests/test_simple.py
index 8948f72..625f841 100644
--- a/tests/vmtests/test_simple.py
+++ b/tests/vmtests/test_simple.py
@@ -8,13 +8,15 @@ import textwrap
88
99
10class TestSimple(VMBaseClass):10class TestSimple(VMBaseClass):
11 # Test that curtin with no config does the right thing11 """ Test that curtin runs block-meta simple mode correctly. """
12 conf_file = "examples/tests/simple.yaml"12 conf_file = "examples/tests/simple.yaml"
13 extra_disks = []13 extra_disks = []
14 extra_nics = []14 extra_nics = []
15 extra_collect_scripts = [textwrap.dedent("""15 extra_collect_scripts = [textwrap.dedent("""
16 cd OUTPUT_COLLECT_D16 cd OUTPUT_COLLECT_D
17 cp /etc/netplan/50-cloud-init.yaml netplan.yaml17 cp /etc/netplan/50-cloud-init.yaml netplan.yaml
18
19 exit 0
18 """)]20 """)]
1921
2022
@@ -43,4 +45,68 @@ class CosmicTestSimple(relbase.cosmic, TestSimple):
43 def test_output_files_exist(self):45 def test_output_files_exist(self):
44 self.output_files_exist(["netplan.yaml"])46 self.output_files_exist(["netplan.yaml"])
4547
48
49class DiscoTestSimple(relbase.disco, TestSimple):
50 __test__ = True
51
52 def test_output_files_exist(self):
53 self.output_files_exist(["netplan.yaml"])
54
55
56class TestSimpleStorage(VMBaseClass):
57 """ Test curtin runs clear-holders when mode=simple with storage cfg. """
58 conf_file = "examples/tests/simple-storage.yaml"
59 dirty_disks = True
60 extra_disks = ['5G', '5G']
61 extra_nics = []
62 extra_collect_scripts = [textwrap.dedent("""
63 cd OUTPUT_COLLECT_D
64 sfdisk --list > sfdisk_list
65 for d in /dev/[sv]d[a-z] /dev/xvd?; do
66 [ -b "$d" ] || continue
67 echo == $d ==
68 sgdisk --print $d
69 done > sgdisk_list
70 blkid > blkid
71 cat /proc/partitions > proc_partitions
72 cp /etc/network/interfaces interfaces
73 cp /etc/netplan/50-cloud-init.yaml netplan.yaml
74 if [ -f /var/log/cloud-init-output.log ]; then
75 cp /var/log/cloud-init-output.log .
76 fi
77 cp /var/log/cloud-init.log .
78 find /etc/network/interfaces.d > find_interfacesd
79 exit 0
80 """)]
81
82 def test_output_files_exist(self):
83 self.output_files_exist(["sfdisk_list", "blkid",
84 "proc_partitions"])
85
86
87class XenialGATestSimpleStorage(relbase.xenial, TestSimpleStorage):
88 __test__ = True
89
90
91class BionicTestSimpleStorage(relbase.bionic, TestSimpleStorage):
92 __test__ = True
93
94 def test_output_files_exist(self):
95 self.output_files_exist(["netplan.yaml"])
96
97
98class CosmicTestSimpleStorage(relbase.cosmic, TestSimpleStorage):
99 __test__ = True
100
101 def test_output_files_exist(self):
102 self.output_files_exist(["netplan.yaml"])
103
104
105class DiscoTestSimpleStorage(relbase.disco, TestSimpleStorage):
106 __test__ = True
107
108 def test_output_files_exist(self):
109 self.output_files_exist(["netplan.yaml"])
110
111
46# vi: ts=4 expandtab syntax=python112# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_ubuntu_core.py b/tests/vmtests/test_ubuntu_core.py
index 732399b..a282940 100644
--- a/tests/vmtests/test_ubuntu_core.py
+++ b/tests/vmtests/test_ubuntu_core.py
@@ -18,6 +18,8 @@ class TestUbuntuCoreAbs(VMBaseClass):
18 cp -a /etc/cloud ./etc_cloud |:18 cp -a /etc/cloud ./etc_cloud |:
19 cp -a /home . |:19 cp -a /home . |:
20 cp -a /var/lib/extrausers . |:20 cp -a /var/lib/extrausers . |:
21
22 exit 0
21 """)]23 """)]
2224
23 def test_ubuntu_core_snaps_installed(self):25 def test_ubuntu_core_snaps_installed(self):
diff --git a/tests/vmtests/test_uefi_basic.py b/tests/vmtests/test_uefi_basic.py
index 40ece65..f0e48c6 100644
--- a/tests/vmtests/test_uefi_basic.py
+++ b/tests/vmtests/test_uefi_basic.py
@@ -24,6 +24,8 @@ class TestBasicAbs(VMBaseClass):
24 blockdev --getss /dev/vda | cat >vda_blockdev_getss24 blockdev --getss /dev/vda | cat >vda_blockdev_getss
25 blockdev --getpbsz /dev/vda | cat >vda_blockdev_getpbsz25 blockdev --getpbsz /dev/vda | cat >vda_blockdev_getpbsz
26 blockdev --getbsz /dev/vda | cat >vda_blockdev_getbsz26 blockdev --getbsz /dev/vda | cat >vda_blockdev_getbsz
27
28 exit 0
27 """)]29 """)]
2830
29 def test_sys_firmware_efi(self):31 def test_sys_firmware_efi(self):
@@ -91,7 +93,7 @@ class TrustyUefiTestBasic(relbase.trusty, TestBasicAbs):
91 __test__ = True93 __test__ = True
9294
9395
94class TrustyHWEXUefiTestBasic(relbase.trusty_hwe_x, TrustyUefiTestBasic):96class TrustyHWEXUefiTestBasic(relbase.trusty_hwe_x, TestBasicAbs):
95 __test__ = True97 __test__ = True
9698
9799
@@ -115,27 +117,35 @@ class CosmicUefiTestBasic(relbase.cosmic, TestBasicAbs):
115 __test__ = True117 __test__ = True
116118
117119
120class DiscoUefiTestBasic(relbase.disco, TestBasicAbs):
121 __test__ = True
122
123
118class Centos70UefiTestBasic4k(centos_relbase.centos70_xenial, TestBasicAbs):124class Centos70UefiTestBasic4k(centos_relbase.centos70_xenial, TestBasicAbs):
119 disk_block_size = 4096125 disk_block_size = 4096
120126
121127
122class TrustyUefiTestBasic4k(TrustyUefiTestBasic):128class TrustyUefiTestBasic4k(relbase.trusty, TestBasicAbs):
123 disk_block_size = 4096129 disk_block_size = 4096
124130
125131
126class TrustyHWEXUefiTestBasic4k(relbase.trusty_hwe_x, TrustyUefiTestBasic4k):132class TrustyHWEXUefiTestBasic4k(relbase.trusty_hwe_x, TestBasicAbs):
127 __test__ = True133 disk_block_size = 4096
134
135
136class XenialGAUefiTestBasic4k(relbase.xenial_ga, TestBasicAbs):
137 disk_block_size = 4096
128138
129139
130class XenialGAUefiTestBasic4k(XenialGAUefiTestBasic):140class BionicUefiTestBasic4k(relbase.bionic, TestBasicAbs):
131 disk_block_size = 4096141 disk_block_size = 4096
132142
133143
134class BionicUefiTestBasic4k(BionicUefiTestBasic):144class CosmicUefiTestBasic4k(relbase.cosmic, TestBasicAbs):
135 disk_block_size = 4096145 disk_block_size = 4096
136146
137147
138class CosmicUefiTestBasic4k(CosmicUefiTestBasic):148class DiscoUefiTestBasic4k(relbase.disco, TestBasicAbs):
139 disk_block_size = 4096149 disk_block_size = 4096
140150
141# vi: ts=4 expandtab syntax=python151# vi: ts=4 expandtab syntax=python
diff --git a/tests/vmtests/test_zfsroot.py b/tests/vmtests/test_zfsroot.py
index 4e257ae..473c9e3 100644
--- a/tests/vmtests/test_zfsroot.py
+++ b/tests/vmtests/test_zfsroot.py
@@ -17,6 +17,8 @@ class TestZfsRootAbs(VMBaseClass):
17 zfs list > zfs_list17 zfs list > zfs_list
18 zpool list > zpool_list18 zpool list > zpool_list
19 zpool status > zpool_status19 zpool status > zpool_status
20
21 exit 0
20 """)]22 """)]
2123
22 @skip_if_flag('expected_failure')24 @skip_if_flag('expected_failure')
@@ -88,6 +90,10 @@ class CosmicTestZfsRoot(relbase.cosmic, TestZfsRootAbs):
88 __test__ = True90 __test__ = True
8991
9092
93class DiscoTestZfsRoot(relbase.disco, TestZfsRootAbs):
94 __test__ = True
95
96
91class TestZfsRootFsTypeAbs(TestZfsRootAbs):97class TestZfsRootFsTypeAbs(TestZfsRootAbs):
92 conf_file = "examples/tests/basic-zfsroot.yaml"98 conf_file = "examples/tests/basic-zfsroot.yaml"
9399
@@ -108,3 +114,7 @@ class BionicTestZfsRootFsType(relbase.bionic, TestZfsRootFsTypeAbs):
108114
109class CosmicTestZfsRootFsType(relbase.cosmic, TestZfsRootFsTypeAbs):115class CosmicTestZfsRootFsType(relbase.cosmic, TestZfsRootFsTypeAbs):
110 __test__ = True116 __test__ = True
117
118
119class DiscoTestZfsRootFsType(relbase.disco, TestZfsRootFsTypeAbs):
120 __test__ = True
diff --git a/tools/jenkins-runner b/tools/jenkins-runner
index c1cef8e..bf8ea0a 100755
--- a/tools/jenkins-runner
+++ b/tools/jenkins-runner
@@ -79,14 +79,14 @@ if [ "${#tests[@]}" -ne 0 -a "${#ntfilters[@]}" -ne 0 ]; then
79 error "Passing test tests and --filter are incompatible."79 error "Passing test tests and --filter are incompatible."
80 error "test arguments provided were: ${#tests[*]}"80 error "test arguments provided were: ${#tests[*]}"
81 fail81 fail
82elif [ "${#tests[@]}" -eq 0 ]; then82elif [ "${#tests[@]}" -eq 0 -a "${#ntfilters[@]}" -eq 0 ]; then
83 tests=( tests/vmtests )83 tests=( tests/vmtests )
84elif [ "${#ntfilters[@]}" -ne 0 ]; then84elif [ "${#ntfilters[@]}" -ne 0 ]; then
85 tests=( $(./tools/vmtest-filter "${ntfilters[@]}") )85 tests=( $(./tools/vmtest-filter "${ntfilters[@]}") )
86 if [ "${#tests[@]}" -eq 0 ]; then86 if [ "${#tests[@]}" -eq 0 ]; then
87 error "Failed to find any tests with filter(s): \"${ntfilters[*]}\""87 error "Failed to find any tests with filter(s): \"${ntfilters[*]}\""
88 fail "Try testing filters with: ./tools/vmtest-filter ${ntfilters[*]}"88 fail "Try testing filters with: ./tools/vmtest-filter ${ntfilters[*]}"
89 fi89 fi
90fi90fi
9191
92CURTIN_VMTEST_PARALLEL=$parallel92CURTIN_VMTEST_PARALLEL=$parallel

Subscribers

People subscribed via source and target branches