Merge lp:~raharper/curtin/new-bionic-upstream-snapshot-v2 into lp:~curtin-dev/curtin/bionic

Proposed by Ryan Harper
Status: Merged
Merged at revision: 85
Proposed branch: lp:~raharper/curtin/new-bionic-upstream-snapshot-v2
Merge into: lp:~curtin-dev/curtin/bionic
Diff against target: 2108 lines (+1141/-114)
57 files modified
curtin/__init__.py (+1/-1)
curtin/block/mkfs.py (+35/-23)
curtin/commands/block_meta.py (+62/-27)
curtin/commands/curthooks.py (+5/-6)
curtin/commands/install.py (+8/-1)
curtin/commands/main.py (+5/-5)
curtin/commands/unmount.py (+60/-0)
curtin/futil.py (+13/-0)
curtin/util.py (+26/-8)
debian/changelog (+17/-0)
debian/changelog.trunk (+5/-0)
doc/topics/config.rst (+20/-0)
doc/topics/storage.rst (+18/-0)
examples/tests/basic.yaml (+1/-0)
examples/tests/basic_scsi.yaml (+1/-0)
examples/tests/install_disable_unmount.yaml (+18/-0)
examples/tests/lvmroot.yaml (+42/-0)
examples/tests/uefi_lvmroot.yaml (+82/-0)
tests/unittests/helpers.py (+13/-0)
tests/unittests/test_block_mkfs.py (+7/-0)
tests/unittests/test_commands_block_meta.py (+118/-2)
tests/unittests/test_commands_unmount.py (+47/-0)
tests/unittests/test_curthooks.py (+47/-1)
tests/vmtests/__init__.py (+50/-24)
tests/vmtests/releases.py (+5/-0)
tests/vmtests/test_apt_config_cmd.py (+4/-0)
tests/vmtests/test_basic.py (+11/-1)
tests/vmtests/test_bcache_basic.py (+4/-0)
tests/vmtests/test_bcache_bug1718699.py (+4/-0)
tests/vmtests/test_install_umount.py (+56/-0)
tests/vmtests/test_iscsi.py (+4/-0)
tests/vmtests/test_journald_reporter.py (+4/-0)
tests/vmtests/test_lvm.py (+4/-0)
tests/vmtests/test_lvm_iscsi.py (+4/-0)
tests/vmtests/test_lvm_root.py (+155/-0)
tests/vmtests/test_mdadm_bcache.py (+34/-0)
tests/vmtests/test_mdadm_iscsi.py (+4/-0)
tests/vmtests/test_multipath.py (+4/-0)
tests/vmtests/test_network.py (+4/-0)
tests/vmtests/test_network_alias.py (+4/-0)
tests/vmtests/test_network_bonding.py (+14/-0)
tests/vmtests/test_network_bridging.py (+13/-6)
tests/vmtests/test_network_enisource.py (+5/-1)
tests/vmtests/test_network_ipv6.py (+4/-0)
tests/vmtests/test_network_ipv6_enisource.py (+1/-3)
tests/vmtests/test_network_ipv6_static.py (+4/-0)
tests/vmtests/test_network_ipv6_vlan.py (+4/-0)
tests/vmtests/test_network_mtu.py (+10/-0)
tests/vmtests/test_network_static.py (+4/-0)
tests/vmtests/test_network_static_routes.py (+5/-0)
tests/vmtests/test_network_vlan.py (+4/-0)
tests/vmtests/test_nvme.py (+8/-0)
tests/vmtests/test_raid5_bcache.py (+4/-0)
tests/vmtests/test_simple.py (+4/-0)
tests/vmtests/test_uefi_basic.py (+8/-0)
tools/build-deb (+41/-3)
tox.ini (+2/-2)
To merge this branch: bzr merge lp:~raharper/curtin/new-bionic-upstream-snapshot-v2
Reviewer Review Type Date Requested Status
curtin developers Pending
Review via email: mp+334990@code.launchpad.net

Description of the change

  * New upstream snapshot.
    - packaging: update debian/build-deb to remove ~bzrREV when using equal tag
    - pack: fix packing when curtin is installed inside a snap.
    - tox: move to pylint 1.7.4
    - vmtests: iscsi minor cleanup.
    - vmtests: exercise rootfs over an lvm logical volume
    - Switch network dep filter to test for ifupdown/nplan instead of release name
    - Allow control of curtin install unmounting
    - vmtests: Add Bionic release to tests and update classes.
    - storage: add 'options' key mount type to specify mount parameters for filesystems (LP: #1709284)
    - Re-add curthooks.write_files method for backwards compat (LP: #1731709)
    - vmtest: Remove ArtfulTestBridging skip_by_date check, bug fixed

 -- Ryan Harper <email address hidden> Fri, 08 Dec 2017 14:48:42 -0600

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'curtin/__init__.py'
2--- curtin/__init__.py 2017-08-03 19:48:07 +0000
3+++ curtin/__init__.py 2017-12-08 20:52:11 +0000
4@@ -43,6 +43,6 @@
5 'HAS_VERSION_MODULE',
6 ]
7
8-__version__ = "0.1.0"
9+__version__ = "17.0"
10
11 # vi: ts=4 expandtab syntax=python
12
13=== modified file 'curtin/block/mkfs.py'
14--- curtin/block/mkfs.py 2017-02-08 20:25:39 +0000
15+++ curtin/block/mkfs.py 2017-12-08 20:52:11 +0000
16@@ -64,18 +64,8 @@
17 }
18
19 family_flag_mappings = {
20- "label": {"btrfs": "--label",
21- "ext": "-L",
22- "fat": "-n",
23- "jfs": "-L",
24- "ntfs": "--label",
25- "reiserfs": "--label",
26- "swap": "--label",
27- "xfs": "-L"},
28- "uuid": {"btrfs": "--uuid",
29- "ext": "-U",
30- "reiserfs": "--uuid",
31- "swap": "--uuid"},
32+ "fatsize": {"fat": ("-F", "{fatsize}")},
33+ # flag with no parameter
34 "force": {"btrfs": "--force",
35 "ext": "-F",
36 "fat": "-I",
37@@ -83,18 +73,31 @@
38 "reiserfs": "-f",
39 "swap": "--force",
40 "xfs": "-f"},
41- "fatsize": {"fat": "-F"},
42+ "label": {"btrfs": ("--label", "{label}"),
43+ "ext": ("-L", "{label}"),
44+ "fat": ("-n", "{label}"),
45+ "jfs": ("-L", "{label}"),
46+ "ntfs": ("--label", "{label}"),
47+ "reiserfs": ("--label", "{label}"),
48+ "swap": ("--label", "{label}"),
49+ "xfs": ("-L", "{label}")},
50+ # flag with no parameter, N.B: this isn't used/exposed
51 "quiet": {"ext": "-q",
52 "ntfs": "-q",
53 "reiserfs": "-q",
54 "xfs": "--quiet"},
55 "sectorsize": {
56- "btrfs": "--sectorsize",
57- "ext": "-b",
58- "fat": "-S",
59- "xfs": "-s",
60- "ntfs": "--sector-size",
61- "reiserfs": "--block-size"}
62+ "btrfs": ("--sectorsize", "{sectorsize}",),
63+ "ext": ("-b", "{sectorsize}"),
64+ "fat": ("-S", "{sectorsize}"),
65+ "ntfs": ("--sector-size", "{sectorsize}"),
66+ "reiserfs": ("--block-size", "{sectorsize}"),
67+ "xfs": ("-s", "{sectorsize}")},
68+ "uuid": {"btrfs": ("--uuid", "{uuid}"),
69+ "ext": ("-U", "{uuid}"),
70+ "reiserfs": ("--uuid", "{uuid}"),
71+ "swap": ("--uuid", "{uuid}"),
72+ "xfs": ("-m", "uuid={uuid}")},
73 }
74
75 release_flag_mapping_overrides = {
76@@ -102,7 +105,8 @@
77 "force": {"btrfs": None},
78 "uuid": {"btrfs": None}},
79 "trusty": {
80- "uuid": {"btrfs": None}},
81+ "uuid": {"btrfs": None,
82+ "xfs": None}},
83 }
84
85
86@@ -126,10 +130,18 @@
87 if strict:
88 raise ValueError("flag '%s' not supported by fs family '%s'" %
89 flag_name, fs_family)
90+ else:
91+ return ret
92+
93+ if param is None:
94+ ret.append(flag_sym)
95 else:
96- ret = [flag_sym]
97- if param is not None:
98- ret.append(param)
99+ params = [k.format(**{flag_name: param}) for k in flag_sym]
100+ if list(params) == list(flag_sym):
101+ raise ValueError("Param %s not used for flag_name=%s and "
102+ "fs_family=%s." % (param, flag_name, fs_family))
103+
104+ ret.extend(params)
105 return ret
106
107
108
109=== modified file 'curtin/commands/block_meta.py'
110--- curtin/commands/block_meta.py 2017-11-07 17:11:12 +0000
111+++ curtin/commands/block_meta.py 2017-12-08 20:52:11 +0000
112@@ -620,9 +620,27 @@
113
114
115 def mount_handler(info, storage_config):
116+ """ Handle storage config type: mount
117+
118+ info = {
119+ 'id': 'rootfs_mount',
120+ 'type': 'mount',
121+ 'path': '/',
122+ 'options': 'defaults,errors=remount-ro',
123+ 'device': 'rootfs',
124+ }
125+
126+ Mount specified device under target at 'path' and generate
127+ fstab entry.
128+ """
129 state = util.load_command_environment()
130 path = info.get('path')
131 filesystem = storage_config.get(info.get('device'))
132+ mount_options = info.get('options')
133+ # handle unset, or empty('') strings
134+ if not mount_options:
135+ mount_options = 'defaults'
136+
137 if not path and filesystem.get('fstype') != "swap":
138 raise ValueError("path to mountpoint must be specified")
139 volume = storage_config.get(filesystem.get('volume'))
140@@ -638,41 +656,50 @@
141 mount_point = os.path.sep.join([state['target'], path])
142 mount_point = os.path.normpath(mount_point)
143
144- # Create mount point if does not exist
145- util.ensure_dir(mount_point)
146-
147- # Mount volume
148- util.subp(['mount', volume_path, mount_point])
149-
150- path = "/%s" % path
151-
152- options = ["defaults"]
153+ options = mount_options.split(",")
154 # If the volume_path's kname is backed by iSCSI or (in the case of
155 # LVM/DM) if any of its slaves are backed by iSCSI, then we need to
156 # append _netdev to the fstab line
157 if iscsi.volpath_is_iscsi(volume_path):
158 LOG.debug("Marking volume_path:%s as '_netdev'", volume_path)
159 options.append("_netdev")
160+
161+ # Create mount point if does not exist
162+ util.ensure_dir(mount_point)
163+
164+ # Mount volume, with options
165+ try:
166+ opts = ['-o', ','.join(options)]
167+ util.subp(['mount', volume_path, mount_point] + opts, capture=True)
168+ except util.ProcessExecutionError as e:
169+ LOG.exception(e)
170+ msg = ('Mount failed: %s @ %s with options %s' % (volume_path,
171+ mount_point,
172+ ",".join(opts)))
173+ LOG.error(msg)
174+ raise RuntimeError(msg)
175+
176+ # set path
177+ path = "/%s" % path
178+
179 else:
180 path = "none"
181 options = ["sw"]
182
183 # Add volume to fstab
184 if state['fstab']:
185- with open(state['fstab'], "a") as fp:
186- location = get_path_to_storage_volume(volume.get('id'),
187- storage_config)
188- uuid = block.get_volume_uuid(volume_path)
189- if len(uuid) > 0:
190- location = "UUID=%s" % uuid
191-
192- if filesystem.get('fstype') in ["fat", "fat12", "fat16", "fat32",
193- "fat64"]:
194- fstype = "vfat"
195- else:
196- fstype = filesystem.get('fstype')
197- fp.write("%s %s %s %s 0 0\n" % (location, path, fstype,
198- ",".join(options)))
199+ uuid = block.get_volume_uuid(volume_path)
200+ location = ("UUID=%s" % uuid) if uuid else (
201+ get_path_to_storage_volume(volume.get('id'),
202+ storage_config))
203+
204+ fstype = filesystem.get('fstype')
205+ if fstype in ["fat", "fat12", "fat16", "fat32", "fat64"]:
206+ fstype = "vfat"
207+
208+ fstab_entry = "%s %s %s %s 0 0\n" % (location, path, fstype,
209+ ",".join(options))
210+ util.write_file(state['fstab'], fstab_entry, omode='a')
211 else:
212 LOG.info("fstab not in environment, so not writing")
213
214@@ -707,7 +734,9 @@
215 else:
216 # Create vgrcreate command and run
217 # capture output to avoid printing it to log
218- util.subp(['vgcreate', name] + device_paths, capture=True)
219+ # Use zero to clear target devices of any metadata
220+ util.subp(['vgcreate', '--force', '--zero=y', '--yes',
221+ name] + device_paths, capture=True)
222
223 # refresh lvmetad
224 lvm.lvm_scan()
225@@ -738,11 +767,17 @@
226 "possibility of damaging lvm partitions intended to be "
227 "preserved." % (info.get('id'), volgroup))
228 else:
229- cmd = ["lvcreate", volgroup, "-n", name]
230+ # Use 'wipesignatures' (if available) and 'zero' to clear target lv
231+ # of any fs metadata
232+ cmd = ["lvcreate", volgroup, "--name", name, "--zero=y"]
233+ release = util.lsb_release()['codename']
234+ if release not in ['precise', 'trusty']:
235+ cmd.extend(["--wipesignatures=y"])
236+
237 if info.get('size'):
238- cmd.extend(["-L", info.get('size')])
239+ cmd.extend(["--size", info.get('size')])
240 else:
241- cmd.extend(["-l", "100%FREE"])
242+ cmd.extend(["--extents", "100%FREE"])
243
244 util.subp(cmd)
245
246
247=== modified file 'curtin/commands/curthooks.py'
248--- curtin/commands/curthooks.py 2017-10-06 02:20:08 +0000
249+++ curtin/commands/curthooks.py 2017-12-08 20:52:11 +0000
250@@ -36,6 +36,8 @@
251
252 from . import populate_one_subcmd
253
254+write_files = futil._legacy_write_files # LP: #1731709
255+
256 CMD_ARGUMENTS = (
257 ((('-t', '--target'),
258 {'help': 'operate on target. default is env[TARGET_MOUNT_POINT]',
259@@ -689,14 +691,11 @@
260 if pkg not in needed_packages:
261 needed_packages.add(pkg)
262
263- # FIXME: This needs cleaning up.
264- # do not install certain packages on artful as they are no longer needed.
265- # ifenslave specifically causes issuse due to dependency on ifupdown.
266- codename = util.lsb_release(target=target).get('codename')
267- if codename == 'artful':
268+ # Filter out ifupdown network packages on netplan enabled systems.
269+ if 'ifupdown' not in installed_packages and 'nplan' in installed_packages:
270 drops = set(['bridge-utils', 'ifenslave', 'vlan'])
271 if needed_packages.union(drops):
272- LOG.debug("Skipping install of %s. Not needed on artful.",
273+ LOG.debug("Skipping install of %s. Not needed on netplan system.",
274 needed_packages.union(drops))
275 needed_packages = needed_packages.difference(drops)
276
277
278=== modified file 'curtin/commands/install.py'
279--- curtin/commands/install.py 2017-10-06 02:20:08 +0000
280+++ curtin/commands/install.py 2017-12-08 20:52:11 +0000
281@@ -120,7 +120,9 @@
282 def __init__(self, config):
283 top_d = tempfile.mkdtemp()
284 state_d = os.path.join(top_d, 'state')
285- target_d = os.path.join(top_d, 'target')
286+ target_d = config.get('install', {}).get('target')
287+ if not target_d:
288+ target_d = os.path.join(top_d, 'target')
289 scratch_d = os.path.join(top_d, 'scratch')
290 for p in (state_d, target_d, scratch_d):
291 os.mkdir(p)
292@@ -477,6 +479,11 @@
293 '/root/curtin-install.log')
294 if log_target_path:
295 copy_install_log(logfile, workingd.target, log_target_path)
296+
297+ if instcfg.get('unmount', "") == "disabled":
298+ LOG.info('Skipping unmount: config disabled target unmounting')
299+ return
300+
301 # unmount everything (including iscsi disks)
302 util.do_umount(workingd.target, recursive=True)
303
304
305=== modified file 'curtin/commands/main.py'
306--- curtin/commands/main.py 2017-02-28 15:26:03 +0000
307+++ curtin/commands/main.py 2017-12-08 20:52:11 +0000
308@@ -29,11 +29,11 @@
309 VERSIONSTR = version.version_string()
310
311 SUB_COMMAND_MODULES = [
312- 'apply_net', 'block-attach-iscsi', 'block-detach-iscsi',
313- 'block-info', 'block-meta', 'block-wipe', 'curthooks',
314- 'clear-holders', 'extract', 'hook', 'in-target', 'install', 'mkfs',
315- 'net-meta', 'apt-config', 'pack', 'swap', 'system-install',
316- 'system-upgrade', 'version']
317+ 'apply_net', 'apt-config', 'block-attach-iscsi', 'block-detach-iscsi',
318+ 'block-info', 'block-meta', 'block-wipe', 'clear-holders', 'curthooks',
319+ 'extract', 'hook', 'install', 'mkfs', 'in-target', 'net-meta', 'pack',
320+ 'swap', 'system-install', 'system-upgrade', 'unmount', 'version',
321+]
322
323
324 def add_subcmd(subparser, subcmd):
325
326=== added file 'curtin/commands/unmount.py'
327--- curtin/commands/unmount.py 1970-01-01 00:00:00 +0000
328+++ curtin/commands/unmount.py 2017-12-08 20:52:11 +0000
329@@ -0,0 +1,60 @@
330+# Copyright (C) 2017 Canonical Ltd.
331+#
332+# Author: Ryan Harper <ryan.harper@canonical.com>
333+#
334+# Curtin is free software: you can redistribute it and/or modify it under
335+# the terms of the GNU Affero General Public License as published by the
336+# Free Software Foundation, either version 3 of the License, or (at your
337+# option) any later version.
338+#
339+# Curtin is distributed in the hope that it will be useful, but WITHOUT ANY
340+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
341+# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
342+# more details.
343+#
344+# You should have received a copy of the GNU Affero General Public License
345+# along with Curtin. If not, see <http://www.gnu.org/licenses/>.
346+
347+from curtin.log import LOG
348+from curtin import util
349+from . import populate_one_subcmd
350+
351+import os
352+
353+try:
354+ FileMissingError = FileNotFoundError
355+except NameError:
356+ FileMissingError = IOError
357+
358+
359+def unmount_main(args):
360+ """
361+ run util.umount(target, recursive=True)
362+ """
363+ if args.target is None:
364+ msg = "Missing target. Please provide target path parameter"
365+ raise ValueError(msg)
366+
367+ if not os.path.exists(args.target):
368+ msg = "Cannot unmount target path %s: it does not exist" % args.target
369+ raise FileMissingError(msg)
370+
371+ LOG.info("Unmounting devices from target path: %s", args.target)
372+ recursive_mode = not args.disable_recursive_mounts
373+ util.do_umount(args.target, recursive=recursive_mode)
374+
375+
376+CMD_ARGUMENTS = (
377+ (('-t', '--target'),
378+ {'help': ('Path to mountpoint to be unmounted.'
379+ 'The default is env variable "TARGET_MOUNT_POINT"'),
380+ 'metavar': 'TARGET', 'action': 'store',
381+ 'default': os.environ.get('TARGET_MOUNT_POINT')}),
382+ (('-d', '--disable-recursive-mounts'),
383+ {'help': 'Disable unmounting recursively under target',
384+ 'default': False, 'action': 'store_true'}),
385+)
386+
387+
388+def POPULATE_SUBCMD(parser):
389+ populate_one_subcmd(parser, CMD_ARGUMENTS, unmount_main)
390
391=== modified file 'curtin/futil.py'
392--- curtin/futil.py 2017-08-03 19:48:07 +0000
393+++ curtin/futil.py 2017-12-08 20:52:11 +0000
394@@ -18,6 +18,7 @@
395 import grp
396 import pwd
397 import os
398+import warnings
399
400 from .util import write_file, target_path
401 from .log import LOG
402@@ -101,3 +102,15 @@
403 content=info.get('content', ''),
404 owner=info.get('owner', "-1:-1"),
405 perms=info.get('permissions', info.get('perms', "0644")))
406+
407+
408+def _legacy_write_files(cfg, base_dir=None):
409+ """Backwards compatibility for curthooks.write_files (LP: #1731709)
410+ It needs to work like:
411+ curthooks.write_files(cfg, target)
412+ cfg is a 'cfg' dictionary with a 'write_files' entry in it.
413+ """
414+ warnings.warn(
415+ "write_files use from curtin.util is deprecated. "
416+ "Please use curtin.futil.write_files.", DeprecationWarning)
417+ return write_files(cfg.get('write_files', {}), base_dir=base_dir)
418
419=== modified file 'curtin/util.py'
420--- curtin/util.py 2017-10-06 02:20:08 +0000
421+++ curtin/util.py 2017-12-08 20:52:11 +0000
422@@ -54,8 +54,8 @@
423
424 from .log import LOG
425
426-_INSTALLED_HELPERS_PATH = '/usr/lib/curtin/helpers'
427-_INSTALLED_MAIN = '/usr/bin/curtin'
428+_INSTALLED_HELPERS_PATH = 'usr/lib/curtin/helpers'
429+_INSTALLED_MAIN = 'usr/bin/curtin'
430
431 _LSB_RELEASE = {}
432 _USES_SYSTEMD = None
433@@ -677,6 +677,24 @@
434 return None
435
436
437+def _installed_file_path(path, check_file=None):
438+ # check the install root for the file 'path'.
439+ # if 'check_file', then path is a directory that contains file.
440+ # return absolute path or None.
441+ inst_pre = "/"
442+ if os.environ.get('SNAP'):
443+ inst_pre = os.path.abspath(os.environ['SNAP'])
444+ inst_path = os.path.join(inst_pre, path)
445+ if check_file:
446+ check_path = os.path.sep.join((inst_path, check_file))
447+ else:
448+ check_path = inst_path
449+
450+ if os.path.isfile(check_path):
451+ return os.path.abspath(inst_path)
452+ return None
453+
454+
455 def get_paths(curtin_exe=None, lib=None, helpers=None):
456 # return a dictionary with paths for 'curtin_exe', 'helpers' and 'lib'
457 # that represent where 'curtin' executable lives, where the 'curtin' module
458@@ -698,17 +716,17 @@
459 if found:
460 curtin_exe = found
461
462- if (curtin_exe is None and os.path.exists(_INSTALLED_MAIN)):
463- curtin_exe = _INSTALLED_MAIN
464+ if curtin_exe is None:
465+ curtin_exe = _installed_file_path(_INSTALLED_MAIN)
466
467- cfile = "common" # a file in 'helpers'
468+ # "common" is a file in helpers
469+ cfile = "common"
470 if (helpers is None and
471 os.path.isfile(os.path.join(tld, "helpers", cfile))):
472 helpers = os.path.join(tld, "helpers")
473
474- if (helpers is None and
475- os.path.isfile(os.path.join(_INSTALLED_HELPERS_PATH, cfile))):
476- helpers = _INSTALLED_HELPERS_PATH
477+ if helpers is None:
478+ helpers = _installed_file_path(_INSTALLED_HELPERS_PATH, cfile)
479
480 return({'curtin_exe': curtin_exe, 'lib': mydir, 'helpers': helpers})
481
482
483=== modified file 'debian/changelog'
484--- debian/changelog 2017-11-07 17:13:15 +0000
485+++ debian/changelog 2017-12-08 20:52:11 +0000
486@@ -1,3 +1,20 @@
487+curtin (17.0~bzr552-0ubuntu1) bionic; urgency=medium
488+
489+ * New upstream snapshot.
490+ - packaging: update debian/build-deb to remove ~bzrREV when using equal tag
491+ - pack: fix packing when curtin is installed inside a snap.
492+ - tox: move to pylint 1.7.4
493+ - vmtests: iscsi minor cleanup.
494+ - vmtests: exercise rootfs over an lvm logical volume
495+ - Switch network dep filter to test for ifupdown/nplan instead of release name
496+ - Allow control of curtin install unmounting
497+ - vmtests: Add Bionic release to tests and update classes.
498+ - storage: add 'options' key mount type to specify mount parameters for filesystems (LP: #1709284)
499+ - Re-add curthooks.write_files method for backwards compat (LP: #1731709)
500+ - vmtest: Remove ArtfulTestBridging skip_by_date check, bug fixed
501+
502+ -- Ryan Harper <ryan.harper@canonical.com> Fri, 08 Dec 2017 14:48:42 -0600
503+
504 curtin (0.1.0~bzr541-0ubuntu1) bionic; urgency=medium
505
506 * New upstream snapshot.
507
508=== added file 'debian/changelog.trunk'
509--- debian/changelog.trunk 1970-01-01 00:00:00 +0000
510+++ debian/changelog.trunk 2017-12-08 20:52:11 +0000
511@@ -0,0 +1,5 @@
512+curtin (17.0~bzrREVNO-0ubuntu1) UNRELEASED; urgency=low
513+
514+ * Initial release
515+
516+ -- Scott Moser <smoser@ubuntu.com> Mon, 29 Jul 2013 16:12:09 -0400
517
518=== modified file 'doc/topics/config.rst'
519--- doc/topics/config.rst 2017-08-03 19:48:07 +0000
520+++ doc/topics/config.rst 2017-12-08 20:52:11 +0000
521@@ -205,6 +205,23 @@
522 Curtin will save the merged configuration data into the target OS at
523 the path of ``save_install_config``. This defaults to /root/curtin-install-cfg.yaml
524
525+**save_install_logs**: *<Path to save curtin install log>*
526+
527+Curtin will copy the install log to a specific path in the target
528+filesystem. This defaults to /root/install.log
529+
530+**target**: *<path to mount install target>*
531+
532+Control where curtin mounts the target device for installing the OS. If this
533+value is unset, curtin picks a suitable path under a temporary directory. If
534+a value is set, then curtin will utilize the ``target`` value instead.
535+
536+**unmount**: *disabled*
537+
538+If this key is set to the string 'disabled' then curtin will not
539+unmount the target filesystem when install is complete. This
540+skips unmounting in all cases of install success or failure.
541+
542 **Example**::
543
544 install:
545@@ -213,6 +230,9 @@
546 - /tmp/install.log
547 - /var/log/syslog
548 save_install_config: /root/myconf.yaml
549+ save_install_log: /var/log/curtin-install.log
550+ target: /my_mount_point
551+ unmount: disabled
552
553
554 kernel
555
556=== modified file 'doc/topics/storage.rst'
557--- doc/topics/storage.rst 2017-10-06 02:20:08 +0000
558+++ doc/topics/storage.rst 2017-12-08 20:52:11 +0000
559@@ -366,12 +366,30 @@
560 fstab entry will contain ``_netdev`` to indicate networking is
561 required to mount this filesystem.
562
563+**options**: *<mount(8) comma-separated options string>*
564+
565+The ``options`` key will replace the default options value of ``defaults``.
566+
567+.. warning::
568+ The kernel and user-space utilities may differ between the install
569+ environment and the runtime environment. Not all kernels and user-space
570+ combinations will support all options. Providing options for a mount point
571+ will have both of the following effects:
572+
573+ - ``curtin`` will mount the filesystems with the provided options during the installation.
574+
575+ - ``curtin`` will ensure the target OS uses the provided mount options by updating the target OS (/etc/fstab).
576+
577+ If either of the environments (install or target) do not have support for
578+ the provided options, the behavior is undefined.
579+
580 **Config Example**::
581
582 - id: disk0-part1-fs1-mount0
583 type: mount
584 path: /home
585 device: disk0-part1-fs1
586+ options: 'noatime,errors=remount-ro'
587
588 Lvm Volgroup Command
589 ~~~~~~~~~~~~~~~~~~~~
590
591=== modified file 'examples/tests/basic.yaml'
592--- examples/tests/basic.yaml 2017-05-19 20:56:27 +0000
593+++ examples/tests/basic.yaml 2017-12-08 20:52:11 +0000
594@@ -59,6 +59,7 @@
595 - id: btrfs_disk_mnt_id
596 type: mount
597 path: /btrfs
598+ options: 'defaults,noatime'
599 device: btrfs_disk_fmt_id
600 - id: pnum_disk
601 type: disk
602
603=== modified file 'examples/tests/basic_scsi.yaml'
604--- examples/tests/basic_scsi.yaml 2016-08-05 20:47:14 +0000
605+++ examples/tests/basic_scsi.yaml 2017-12-08 20:52:11 +0000
606@@ -53,6 +53,7 @@
607 - id: btrfs_disk_mnt_id
608 type: mount
609 path: /btrfs
610+ options: 'defaults,noatime'
611 device: btrfs_disk_fmt_id
612 - id: pnum_disk
613 type: disk
614
615=== added file 'examples/tests/install_disable_unmount.yaml'
616--- examples/tests/install_disable_unmount.yaml 1970-01-01 00:00:00 +0000
617+++ examples/tests/install_disable_unmount.yaml 2017-12-08 20:52:11 +0000
618@@ -0,0 +1,18 @@
619+install:
620+ unmount: disabled
621+
622+showtrace: true
623+
624+post_cmds:
625+ - &cat_proc_mounts |-
626+ cat /proc/mounts | tee ${TARGET_MOUNT_POINT}/root/postinst_mounts.out
627+
628+ - &echo_target_mp |-
629+ echo ${TARGET_MOUNT_POINT} | tee ${TARGET_MOUNT_POINT}/root/target.out
630+
631+# capture proc/mounts, unmount, capture proc/mounts
632+late_commands:
633+ 01_get_proc_mounts: [sh, -c, *cat_proc_mounts]
634+ 02_write_out_target: [sh, -c, *echo_target_mp]
635+ 03_unmount_target: [curtin, unmount]
636+ 04_get_proc_mounts: [cat, /proc/mounts]
637
638=== added file 'examples/tests/lvmroot.yaml'
639--- examples/tests/lvmroot.yaml 1970-01-01 00:00:00 +0000
640+++ examples/tests/lvmroot.yaml 2017-12-08 20:52:11 +0000
641@@ -0,0 +1,42 @@
642+showtrace: true
643+storage:
644+ version: 1
645+ config:
646+ - id: main_disk
647+ type: disk
648+ ptable: gpt
649+ name: root_disk
650+ wipe: superblock
651+ serial: '4cd98c5c-7761-4179'
652+ grub_device: true
653+ - id: main_disk_p1
654+ type: partition
655+ number: 1
656+ size: 4GB
657+ device: main_disk
658+ flag: boot
659+ - id: bios_boot
660+ type: partition
661+ size: 1MB
662+ number: 15
663+ device: main_disk
664+ flag: bios_grub
665+ - id: root_vg
666+ type: lvm_volgroup
667+ name: root_vg
668+ devices:
669+ - main_disk_p1
670+ - id: root_vg_lv1
671+ type: lvm_partition
672+ name: lv1_root
673+ size: 3.5G
674+ volgroup: root_vg
675+ - id: lv1_root_fs
676+ type: format
677+ fstype: __ROOTFS_FORMAT__
678+ uuid: 04836770-e989-460f-8774-8e277ddcb40f
679+ volume: root_vg_lv1
680+ - id: lvroot_mount
681+ path: /
682+ type: mount
683+ device: lv1_root_fs
684
685=== added file 'examples/tests/uefi_lvmroot.yaml'
686--- examples/tests/uefi_lvmroot.yaml 1970-01-01 00:00:00 +0000
687+++ examples/tests/uefi_lvmroot.yaml 2017-12-08 20:52:11 +0000
688@@ -0,0 +1,82 @@
689+showtrace: true
690+storage:
691+ config:
692+ - grub_device: true
693+ id: sda
694+ model: PERC 6/i
695+ name: sda
696+ ptable: gpt
697+ serial: '4cd98c5c-7761-4179'
698+ type: disk
699+ wipe: superblock
700+ - device: sda
701+ id: sda-part1
702+ name: sda-part1
703+ number: 1
704+ offset: 4194304B
705+ size: 96468992B
706+ type: partition
707+ uuid: 88a0df20-c224-4637-ad93-1d932e065a77
708+ wipe: superblock
709+ - device: sda
710+ id: sda-part2
711+ name: sda-part2
712+ number: 2
713+ size: 499122176B
714+ type: partition
715+ uuid: 1df4c42e-a94c-41d9-887f-9f2139425029
716+ wipe: superblock
717+ - device: sda
718+ id: sda-part3
719+ name: sda-part3
720+ number: 3
721+ size: 8G
722+ type: partition
723+ uuid: 14870e21-4ef7-4058-baf1-8ae1148bb1b0
724+ wipe: superblock
725+ - devices:
726+ - sda-part3
727+ id: vg0
728+ name: vg0
729+ type: lvm_volgroup
730+ uuid: 1cae2f4d-ea47-45df-9d33-d1b77f23ee3f
731+ - id: vg0-lv0
732+ name: lv0
733+ size: 7.5G
734+ type: lvm_partition
735+ volgroup: vg0
736+ - fstype: vfat
737+ id: sda-part1_format
738+ label: ''
739+ type: format
740+ uuid: 0bea118f-558e-4235-8547-644c76078066
741+ volume: sda-part1
742+ - fstype: __BOOTFS_FORMAT__
743+ id: sda-part2_format
744+ label: ''
745+ type: format
746+ uuid: a45a16b8-018c-4a24-b9d8-aee19ca4566e
747+ volume: sda-part2
748+ - fstype: __ROOTFS_FORMAT__
749+ id: vg0-lv0_format
750+ label: ''
751+ type: format
752+ uuid: 48691520-0025-4e6c-a7c0-50bb0ac30713
753+ volume: vg0-lv0
754+ - device: vg0-lv0_format
755+ id: vg0-lv0_mount
756+ options: ''
757+ path: /
758+ type: mount
759+ - device: sda-part2_format
760+ id: sda-part2_mount
761+ options: ''
762+ path: /boot
763+ type: mount
764+ - device: sda-part1_format
765+ id: sda-part1_mount
766+ options: ''
767+ path: /boot/efi
768+ type: mount
769+ version: 1
770+
771
772=== modified file 'tests/unittests/helpers.py'
773--- tests/unittests/helpers.py 2017-08-03 19:48:07 +0000
774+++ tests/unittests/helpers.py 2017-12-08 20:52:11 +0000
775@@ -79,3 +79,16 @@
776 if _dir is None:
777 _dir = self.tmp_dir()
778 return os.path.normpath(os.path.abspath(os.path.join(_dir, path)))
779+
780+
781+def dir2dict(startdir, prefix=None):
782+ flist = {}
783+ if prefix is None:
784+ prefix = startdir
785+ for root, dirs, files in os.walk(startdir):
786+ for fname in files:
787+ fpath = os.path.join(root, fname)
788+ key = fpath[len(prefix):]
789+ with open(fpath, "r") as fp:
790+ flist[key] = fp.read()
791+ return flist
792
793=== modified file 'tests/unittests/test_block_mkfs.py'
794--- tests/unittests/test_block_mkfs.py 2017-08-03 19:48:07 +0000
795+++ tests/unittests/test_block_mkfs.py 2017-12-08 20:52:11 +0000
796@@ -66,6 +66,13 @@
797 ["--uuid", self.test_uuid]]
798 self._run_mkfs_with_config(conf, "mkfs.btrfs", expected_flags)
799
800+ def test_mkfs_xfs(self):
801+ """ mkfs.xfs passes uuid parameter """
802+ conf = self._get_config("xfs")
803+ expected_flags = ['-f', ['-L', 'format1'],
804+ ['-m', 'uuid=%s' % self.test_uuid]]
805+ self._run_mkfs_with_config(conf, "mkfs.xfs", expected_flags)
806+
807 def test_mkfs_btrfs_on_precise(self):
808 # Test precise+btrfs where there is no force or uuid
809 conf = self._get_config("btrfs")
810
811=== modified file 'tests/unittests/test_commands_block_meta.py'
812--- tests/unittests/test_commands_block_meta.py 2017-11-07 17:11:12 +0000
813+++ tests/unittests/test_commands_block_meta.py 2017-12-08 20:52:11 +0000
814@@ -115,7 +115,10 @@
815 basepath = 'curtin.commands.block_meta.'
816 self.add_patch(basepath + 'get_path_to_storage_volume', 'mock_getpath')
817 self.add_patch(basepath + 'make_dname', 'mock_make_dname')
818+ self.add_patch('curtin.util.load_command_environment',
819+ 'mock_load_env')
820 self.add_patch('curtin.util.subp', 'mock_subp')
821+ self.add_patch('curtin.util.ensure_dir', 'mock_ensure_dir')
822 self.add_patch('curtin.block.get_part_table_type',
823 'mock_block_get_part_table_type')
824 self.add_patch('curtin.block.wipe_volume',
825@@ -130,6 +133,10 @@
826 'mock_clear_holders')
827 self.add_patch('curtin.block.clear_holders.assert_clear',
828 'mock_assert_clear')
829+ self.add_patch('curtin.block.iscsi.volpath_is_iscsi',
830+ 'mock_volpath_is_iscsi')
831+ self.add_patch('curtin.block.get_volume_uuid',
832+ 'mock_block_get_volume_uuid')
833 self.add_patch('curtin.block.zero_file_at_offsets',
834 'mock_block_zero_file')
835
836@@ -154,7 +161,20 @@
837 'size': '511705088B',
838 'type': 'partition',
839 'uuid': 'fc7ab24c-b6bf-460f-8446-d3ac362c0625',
840- 'wipe': 'superblock'}
841+ 'wipe': 'superblock'},
842+ {'id': 'sda1-root',
843+ 'type': 'format',
844+ 'fstype': 'xfs',
845+ 'volume': 'sda-part1'},
846+ {'id': 'sda-part1-mnt-root',
847+ 'type': 'mount',
848+ 'path': '/',
849+ 'device': 'sda1-root'},
850+ {'id': 'sda-part1-mnt-root-ro',
851+ 'type': 'mount',
852+ 'path': '/readonly',
853+ 'options': 'ro',
854+ 'device': 'sda1-root'}
855 ],
856 }
857 }
858@@ -195,10 +215,106 @@
859 self.mock_block_sys_block_path.return_value = '/sys/class/block/xxx'
860
861 block_meta.partition_handler(part_info, self.storage_config)
862-
863 part_offset = 2048 * 512
864 self.mock_block_zero_file.assert_called_with(disk_kname, [part_offset],
865 exclusive=False)
866 self.mock_subp.assert_called_with(['parted', disk_kname, '--script',
867 'mkpart', 'primary', '2048s',
868 '1001471s'], capture=True)
869+
870+ @patch('curtin.util.write_file')
871+ def test_mount_handler_defaults(self, mock_write_file):
872+ """Test mount_handler has defaults to 'defaults' for mount options"""
873+ fstab = self.tmp_path('fstab')
874+ self.mock_load_env.return_value = {'fstab': fstab,
875+ 'target': self.target}
876+ disk_info = self.storage_config.get('sda')
877+ fs_info = self.storage_config.get('sda1-root')
878+ mount_info = self.storage_config.get('sda-part1-mnt-root')
879+
880+ self.mock_getpath.return_value = '/wark/xxx'
881+ self.mock_volpath_is_iscsi.return_value = False
882+ self.mock_block_get_volume_uuid.return_value = None
883+
884+ block_meta.mount_handler(mount_info, self.storage_config)
885+ options = 'defaults'
886+ expected = "%s %s %s %s 0 0\n" % (disk_info['path'],
887+ mount_info['path'],
888+ fs_info['fstype'], options)
889+
890+ mock_write_file.assert_called_with(fstab, expected, omode='a')
891+
892+ @patch('curtin.util.write_file')
893+ def test_mount_handler_uses_mount_options(self, mock_write_file):
894+ """Test mount_handler 'options' string is present in fstab entry"""
895+ fstab = self.tmp_path('fstab')
896+ self.mock_load_env.return_value = {'fstab': fstab,
897+ 'target': self.target}
898+ disk_info = self.storage_config.get('sda')
899+ fs_info = self.storage_config.get('sda1-root')
900+ mount_info = self.storage_config.get('sda-part1-mnt-root-ro')
901+
902+ self.mock_getpath.return_value = '/wark/xxx'
903+ self.mock_volpath_is_iscsi.return_value = False
904+ self.mock_block_get_volume_uuid.return_value = None
905+
906+ block_meta.mount_handler(mount_info, self.storage_config)
907+ options = 'ro'
908+ expected = "%s %s %s %s 0 0\n" % (disk_info['path'],
909+ mount_info['path'],
910+ fs_info['fstype'], options)
911+
912+ mock_write_file.assert_called_with(fstab, expected, omode='a')
913+
914+ @patch('curtin.util.write_file')
915+ def test_mount_handler_empty_options_string(self, mock_write_file):
916+ """Test mount_handler with empty 'options' string, selects defaults"""
917+ fstab = self.tmp_path('fstab')
918+ self.mock_load_env.return_value = {'fstab': fstab,
919+ 'target': self.target}
920+ disk_info = self.storage_config.get('sda')
921+ fs_info = self.storage_config.get('sda1-root')
922+ mount_info = self.storage_config.get('sda-part1-mnt-root-ro')
923+ mount_info['options'] = ''
924+
925+ self.mock_getpath.return_value = '/wark/xxx'
926+ self.mock_volpath_is_iscsi.return_value = False
927+ self.mock_block_get_volume_uuid.return_value = None
928+
929+ block_meta.mount_handler(mount_info, self.storage_config)
930+ options = 'defaults'
931+ expected = "%s %s %s %s 0 0\n" % (disk_info['path'],
932+ mount_info['path'],
933+ fs_info['fstype'], options)
934+
935+ mock_write_file.assert_called_with(fstab, expected, omode='a')
936+
937+ def test_mount_handler_appends_to_fstab(self):
938+ """Test mount_handler appends fstab lines to existing file"""
939+ fstab = self.tmp_path('fstab')
940+ with open(fstab, 'w') as fh:
941+ fh.write("#curtin-test\n")
942+
943+ self.mock_load_env.return_value = {'fstab': fstab,
944+ 'target': self.target}
945+ disk_info = self.storage_config.get('sda')
946+ fs_info = self.storage_config.get('sda1-root')
947+ mount_info = self.storage_config.get('sda-part1-mnt-root-ro')
948+ mount_info['options'] = ''
949+
950+ self.mock_getpath.return_value = '/wark/xxx'
951+ self.mock_volpath_is_iscsi.return_value = False
952+ self.mock_block_get_volume_uuid.return_value = None
953+
954+ block_meta.mount_handler(mount_info, self.storage_config)
955+ options = 'defaults'
956+ expected = "#curtin-test\n%s %s %s %s 0 0\n" % (disk_info['path'],
957+ mount_info['path'],
958+ fs_info['fstype'],
959+ options)
960+
961+ with open(fstab, 'r') as fh:
962+ rendered_fstab = fh.read()
963+
964+ print(rendered_fstab)
965+ self.assertEqual(rendered_fstab, expected)
966
967=== added file 'tests/unittests/test_commands_unmount.py'
968--- tests/unittests/test_commands_unmount.py 1970-01-01 00:00:00 +0000
969+++ tests/unittests/test_commands_unmount.py 2017-12-08 20:52:11 +0000
970@@ -0,0 +1,47 @@
971+
972+from curtin.commands import unmount
973+from .helpers import CiTestCase
974+
975+import argparse
976+import mock
977+import os
978+
979+
980+class TestUnmount(CiTestCase):
981+
982+ def setUp(self):
983+ super(TestUnmount, self).setUp()
984+ self.args = argparse.Namespace()
985+ self.args.target = os.environ.get('TEST_CURTIN_TARGET_MOUNT_POINT')
986+ self.args.disable_recursive_mounts = False
987+ self.add_patch('curtin.util.do_umount', 'm_umount')
988+
989+ def test_unmount_notarget_raises_exception(self):
990+ """Check missing target raises ValueError exception"""
991+ self.assertRaises(ValueError, unmount.unmount_main, self.args)
992+
993+ def test_unmount_target_not_found_exception(self):
994+ """Check target path not found raises FileNotFoundError exception"""
995+ self.args.target = "catch-me-if-you-can"
996+ try:
997+ FileNotFoundError
998+ except NameError:
999+ FileNotFoundError = IOError
1000+ self.assertRaises(FileNotFoundError, unmount.unmount_main, self.args)
1001+
1002+ @mock.patch('curtin.commands.unmount.os')
1003+ def test_unmount_target_with_path(self, mock_os):
1004+ """Assert do_umount is called with args.target and recursive=True"""
1005+ self.args.target = "test/path/to/my/path"
1006+ mock_os.path.exists.return_value = True
1007+ unmount.unmount_main(self.args)
1008+ self.m_umount.assert_called_with(self.args.target, recursive=True)
1009+
1010+ @mock.patch('curtin.commands.unmount.os')
1011+ def test_unmount_target_with_path_no_recursive(self, mock_os):
1012+ """Assert args.disable_recursive_mounts True sends recursive=False"""
1013+ self.args.target = "test/path/to/my/path"
1014+ self.args.disable_recursive_mounts = True
1015+ mock_os.path.exists.return_value = True
1016+ unmount.unmount_main(self.args)
1017+ self.m_umount.assert_called_with(self.args.target, recursive=False)
1018
1019=== modified file 'tests/unittests/test_curthooks.py'
1020--- tests/unittests/test_curthooks.py 2017-08-03 19:48:07 +0000
1021+++ tests/unittests/test_curthooks.py 2017-12-08 20:52:11 +0000
1022@@ -5,7 +5,7 @@
1023 from curtin import util
1024 from curtin import config
1025 from curtin.reporter import events
1026-from .helpers import CiTestCase
1027+from .helpers import CiTestCase, dir2dict
1028
1029
1030 class TestGetFlashKernelPkgs(CiTestCase):
1031@@ -761,4 +761,50 @@
1032 curthooks.detect_required_packages({'network': {'version': 3}})
1033
1034
1035+class TestCurthooksWriteFiles(CiTestCase):
1036+ def test_handle_write_files_empty(self):
1037+ """ Test curthooks.write_files returns for empty config """
1038+ tmpd = self.tmp_dir()
1039+ ret = curthooks.write_files({}, tmpd)
1040+ self.assertEqual({}, dir2dict(tmpd, prefix=tmpd))
1041+ self.assertIsNone(ret)
1042+
1043+ def test_handle_write_files(self):
1044+ """ Test curthooks.write_files works as it used to """
1045+ tmpd = self.tmp_dir()
1046+ cfg = {'file1': {'path': '/etc/default/hello.txt',
1047+ 'content': "Hello World!\n"},
1048+ 'foobar': {'path': '/sys/wark', 'content': "Engauge!\n"}}
1049+ curthooks.write_files({'write_files': cfg}, tmpd)
1050+ self.assertEqual(
1051+ dict((cfg[i]['path'], cfg[i]['content']) for i in cfg.keys()),
1052+ dir2dict(tmpd, prefix=tmpd))
1053+
1054+ @patch('curtin.commands.curthooks.futil.target_path')
1055+ @patch('curtin.commands.curthooks.futil.write_finfo')
1056+ def test_handle_write_files_finfo(self, mock_write_finfo, mock_tp):
1057+ """ Validate that futils.write_files handles target_path correctly """
1058+ cc_target = "/tmpXXXX/random/dir/used/by/maas"
1059+ cfg = {
1060+ 'file1': {
1061+ 'path': '/etc/default/hello.txt',
1062+ 'content': "Hello World!\n",
1063+ },
1064+ }
1065+ mock_tp.side_effect = [
1066+ cc_target + cfg['file1']['path'],
1067+ ]
1068+
1069+ expected_cfg = {
1070+ 'file1': {
1071+ 'path': '/etc/default/hello.txt',
1072+ 'content': cfg['file1']['content']},
1073+ }
1074+ curthooks.write_files({'write_files': cfg}, cc_target)
1075+ mock_write_finfo.assert_called_with(
1076+ content=expected_cfg['file1']['content'], owner='-1:-1',
1077+ path=cc_target + expected_cfg['file1']['path'],
1078+ perms='0644')
1079+
1080+
1081 # vi: ts=4 expandtab syntax=python
1082
1083=== modified file 'tests/vmtests/__init__.py'
1084--- tests/vmtests/__init__.py 2017-11-07 17:11:12 +0000
1085+++ tests/vmtests/__init__.py 2017-12-08 20:52:11 +0000
1086@@ -11,6 +11,7 @@
1087 import tempfile
1088 import textwrap
1089 import time
1090+import uuid
1091 import yaml
1092 import curtin.net as curtin_net
1093 import curtin.util as util
1094@@ -361,6 +362,7 @@
1095 iscsi_disks = []
1096 recorded_errors = 0
1097 recorded_failures = 0
1098+ conf_replace = {}
1099 uefi = False
1100 proxy = None
1101
1102@@ -415,6 +417,29 @@
1103 return ftypes
1104
1105 @classmethod
1106+ def load_conf_file(cls):
1107+ logger.info('Loading testcase config file: %s', cls.conf_file)
1108+ confdata = util.load_file(cls.conf_file)
1109+ # replace rootfs file system format with class value
1110+ if cls.conf_replace:
1111+ logger.debug('Rendering conf template: %s', cls.conf_replace)
1112+ for k, v in cls.conf_replace.items():
1113+ confdata = confdata.replace(k, v)
1114+ suffix = ".yaml"
1115+ prefix = (cls.td.tmpdir + '/' +
1116+ os.path.basename(cls.conf_file).replace(suffix, "-"))
1117+ temp_yaml = tempfile.NamedTemporaryFile(prefix=prefix,
1118+ suffix=suffix, mode='w+t',
1119+ delete=False)
1120+ shutil.copyfile(cls.conf_file, temp_yaml.name)
1121+ cls.conf_file = temp_yaml.name
1122+ logger.info('Updating class conf file %s', cls.conf_file)
1123+ with open(cls.conf_file, 'w+t') as fh:
1124+ fh.write(confdata)
1125+
1126+ return confdata
1127+
1128+ @classmethod
1129 def build_iscsi_disks(cls):
1130 cls._iscsi_disks = list()
1131 disks = []
1132@@ -470,10 +495,7 @@
1133 disk_iuser = ''
1134 disk_ipassword = ''
1135
1136- uuid, _ = util.subp(['uuidgen'], capture=True,
1137- decode='replace')
1138- uuid = uuid.rstrip()
1139- target = 'curtin-%s' % uuid
1140+ target = 'curtin-%s' % uuid.uuid1()
1141 cls._iscsi_disks.append(target)
1142 dpath = os.path.join(cls.td.disks, '%s.img' % (target))
1143 iscsi_disk = '{}:{}:iscsi:{}:{}:{}:{}:{}:{}'.format(
1144@@ -482,30 +504,16 @@
1145 disks.extend(['--disk', iscsi_disk])
1146
1147 # replace next __RFC4173__ placeholder in YAML
1148+ rfc4173 = get_rfc4173(cls.tgtd_ip, cls.tgtd_port, target,
1149+ user=disk_user, pword=disk_password,
1150+ iuser=disk_iuser, ipword=disk_ipassword)
1151 with tempfile.NamedTemporaryFile(mode='w+t') as temp_yaml:
1152 shutil.copyfile(cls.conf_file, temp_yaml.name)
1153 with open(cls.conf_file, 'w+t') as conf:
1154 replaced = False
1155 for line in temp_yaml:
1156 if not replaced and '__RFC4173__' in line:
1157- actual_rfc4173 = ''
1158- if len(disk_user) > 0:
1159- actual_rfc4173 += '%s:%s' % (disk_user,
1160- disk_password)
1161- if len(disk_iuser) > 0:
1162- # empty target user/password
1163- if len(actual_rfc4173) == 0:
1164- actual_rfc4173 += ':'
1165- actual_rfc4173 += ':%s:%s' % (disk_iuser,
1166- disk_ipassword)
1167- # any auth specified?
1168- if len(actual_rfc4173) > 0:
1169- actual_rfc4173 += '@'
1170- # assumes LUN 1
1171- actual_rfc4173 += '%s::%s:1:%s' % (
1172- cls.tgtd_ip,
1173- cls.tgtd_port, target)
1174- line = line.replace('__RFC4173__', actual_rfc4173)
1175+ line = line.replace('__RFC4173__', rfc4173)
1176 replaced = True
1177 conf.write(line)
1178 return disks
1179@@ -613,7 +621,7 @@
1180
1181 # build disk arguments
1182 disks = []
1183- sc = util.load_file(cls.conf_file)
1184+ sc = cls.load_conf_file()
1185 storage_config = yaml.load(sc).get('storage', {}).get('config', {})
1186 cls.disk_wwns = ["wwn=%s" % x.get('wwn') for x in storage_config
1187 if 'wwn' in x]
1188@@ -679,7 +687,7 @@
1189
1190 # always attempt to update target nvram (via grub)
1191 grub_config = os.path.join(cls.td.install, 'grub.cfg')
1192- if not os.path.exists(grub_config):
1193+ if not os.path.exists(grub_config) and not cls.td.restored:
1194 with open(grub_config, "w") as fp:
1195 fp.write(json.dumps({'grub': {'update_nvram': True}}))
1196 configs.append(grub_config)
1197@@ -1263,6 +1271,24 @@
1198 raise exc
1199
1200
1201+def get_rfc4173(ip, port, target, user=None, pword=None,
1202+ iuser=None, ipword=None, lun=1):
1203+
1204+ rfc4173 = ''
1205+ if user:
1206+ rfc4173 += '%s:%s' % (user, pword)
1207+ if iuser:
1208+ # empty target user/password
1209+ if not rfc4173:
1210+ rfc4173 += ':'
1211+ rfc4173 += ':%s:%s' % (iuser, ipword)
1212+ # any auth specified?
1213+ if rfc4173:
1214+ rfc4173 += '@'
1215+ rfc4173 += '%s::%s:%d:%s' % (ip, port, lun, target)
1216+ return rfc4173
1217+
1218+
1219 def find_error_context(err_match, contents, nrchars=200):
1220 context_start = err_match.start() - nrchars
1221 context_end = err_match.end() + nrchars
1222
1223=== modified file 'tests/vmtests/releases.py'
1224--- tests/vmtests/releases.py 2017-11-07 17:11:12 +0000
1225+++ tests/vmtests/releases.py 2017-12-08 20:52:11 +0000
1226@@ -80,6 +80,10 @@
1227 release = "artful"
1228
1229
1230+class _BionicBase(_UbuntuBase):
1231+ release = "bionic"
1232+
1233+
1234 class _Releases(object):
1235 trusty = _TrustyBase
1236 trusty_hwe_u = _TrustyHWEU
1237@@ -90,6 +94,7 @@
1238 xenial = _XenialBase
1239 zesty = _ZestyBase
1240 artful = _ArtfulBase
1241+ bionic = _BionicBase
1242
1243
1244 class _CentosReleases(object):
1245
1246=== modified file 'tests/vmtests/test_apt_config_cmd.py'
1247--- tests/vmtests/test_apt_config_cmd.py 2017-08-03 19:48:07 +0000
1248+++ tests/vmtests/test_apt_config_cmd.py 2017-12-08 20:52:11 +0000
1249@@ -61,3 +61,7 @@
1250
1251 class ArtfulTestAptConfigCMDCMD(relbase.artful, TestAptConfigCMD):
1252 __test__ = True
1253+
1254+
1255+class BionicTestAptConfigCMDCMD(relbase.bionic, TestAptConfigCMD):
1256+ __test__ = True
1257
1258=== modified file 'tests/vmtests/test_basic.py'
1259--- tests/vmtests/test_basic.py 2017-11-07 17:11:12 +0000
1260+++ tests/vmtests/test_basic.py 2017-12-08 20:52:11 +0000
1261@@ -104,6 +104,7 @@
1262 break
1263 self.assertIsNotNone(fstab_entry)
1264 self.assertEqual(fstab_entry.split(' ')[1], "/btrfs")
1265+ self.assertEqual(fstab_entry.split(' ')[3], "defaults,noatime")
1266
1267 def test_whole_disk_format(self):
1268 # confirm the whole disk format is the expected device
1269@@ -166,6 +167,10 @@
1270 __test__ = True
1271
1272
1273+class BionicTestBasic(relbase.bionic, TestBasicAbs):
1274+ __test__ = True
1275+
1276+
1277 class TestBasicScsiAbs(TestBasicAbs):
1278 conf_file = "examples/tests/basic_scsi.yaml"
1279 disk_driver = 'scsi-hd'
1280@@ -237,7 +242,7 @@
1281 self.assertIsNotNone(fstab_entry)
1282 self.assertEqual(fstab_entry.split(' ')[1], "/home")
1283
1284- # Test whole disk sdc is mounted at /btrfs
1285+ # Test whole disk sdc is mounted at /btrfs, and uses defaults,noatime
1286 uuid = self._kname_to_uuid('sdc')
1287 fstab_entry = None
1288 for line in fstab_lines:
1289@@ -246,6 +251,7 @@
1290 break
1291 self.assertIsNotNone(fstab_entry)
1292 self.assertEqual(fstab_entry.split(' ')[1], "/btrfs")
1293+ self.assertEqual(fstab_entry.split(' ')[3], "defaults,noatime")
1294
1295 def test_whole_disk_format(self):
1296 # confirm the whole disk format is the expected device
1297@@ -272,3 +278,7 @@
1298
1299 class ArtfulTestScsiBasic(relbase.artful, TestBasicScsiAbs):
1300 __test__ = True
1301+
1302+
1303+class BionicTestScsiBasic(relbase.bionic, TestBasicScsiAbs):
1304+ __test__ = True
1305
1306=== modified file 'tests/vmtests/test_bcache_basic.py'
1307--- tests/vmtests/test_bcache_basic.py 2017-11-07 17:11:12 +0000
1308+++ tests/vmtests/test_bcache_basic.py 2017-12-08 20:52:11 +0000
1309@@ -61,3 +61,7 @@
1310
1311 class ArtfulBcacheBasic(relbase.artful, TestBcacheBasic):
1312 __test__ = True
1313+
1314+
1315+class BionicBcacheBasic(relbase.bionic, TestBcacheBasic):
1316+ __test__ = True
1317
1318=== modified file 'tests/vmtests/test_bcache_bug1718699.py'
1319--- tests/vmtests/test_bcache_bug1718699.py 2017-11-07 17:11:12 +0000
1320+++ tests/vmtests/test_bcache_bug1718699.py 2017-12-08 20:52:11 +0000
1321@@ -19,3 +19,7 @@
1322
1323 class ArtfulTestBcacheBug1718699(relbase.artful, TestBcacheBug1718699):
1324 __test__ = True
1325+
1326+
1327+class BionicTestBcacheBug1718699(relbase.bionic, TestBcacheBug1718699):
1328+ __test__ = True
1329
1330=== added file 'tests/vmtests/test_install_umount.py'
1331--- tests/vmtests/test_install_umount.py 1970-01-01 00:00:00 +0000
1332+++ tests/vmtests/test_install_umount.py 2017-12-08 20:52:11 +0000
1333@@ -0,0 +1,56 @@
1334+from . import VMBaseClass
1335+from .releases import base_vm_classes as relbase
1336+
1337+import textwrap
1338+import yaml
1339+
1340+
1341+class TestInstallUnmount(VMBaseClass):
1342+ """ Test a curtin install which disabled unmonting """
1343+ conf_file = "examples/tests/install_disable_unmount.yaml"""
1344+ extra_nics = []
1345+ extra_disks = []
1346+ collect_scripts = [textwrap.dedent("""
1347+ cd OUTPUT_COLLECT_D
1348+ sfdisk --list > sfdisk_list
1349+ for d in /dev/[sv]d[a-z] /dev/xvd?; do
1350+ [ -b "$d" ] || continue
1351+ echo == $d ==
1352+ sgdisk --print $d
1353+ done > sgdisk_list
1354+ blkid > blkid
1355+ cat /proc/partitions > proc_partitions
1356+ cp /etc/network/interfaces interfaces
1357+ if [ -f /var/log/cloud-init-output.log ]; then
1358+ cp /var/log/cloud-init-output.log .
1359+ fi
1360+ cp /var/log/cloud-init.log .
1361+ find /etc/network/interfaces.d > find_interfacesd
1362+ """)]
1363+
1364+ def test_proc_mounts_before_unmount(self):
1365+ """Test TARGET_MOUNT_POINT value is in ephemeral /proc/mounts"""
1366+ self.output_files_exist([
1367+ 'root/postinst_mounts.out',
1368+ 'root/target.out'])
1369+
1370+ # read target mp and mounts
1371+ target_mp = self.load_collect_file('root/target.out').strip()
1372+ curtin_mounts = self.load_collect_file('root/postinst_mounts.out')
1373+ self.assertIn(target_mp, curtin_mounts)
1374+
1375+ def test_install_config_has_unmount_disabled(self):
1376+ """Test that install ran with unmount: disabled"""
1377+ collect_curtin_cfg = 'root/curtin-install-cfg.yaml'
1378+ self.output_files_exist([collect_curtin_cfg])
1379+ curtin_cfg = yaml.load(self.load_collect_file(collect_curtin_cfg))
1380+
1381+ # check that we have
1382+ # install:
1383+ # unmount: disabled
1384+ install_unmount = curtin_cfg.get('install', {}).get('unmount')
1385+ self.assertEqual(install_unmount, "disabled")
1386+
1387+
1388+class XenialTestInstallUnmount(relbase.xenial, TestInstallUnmount):
1389+ __test__ = True
1390
1391=== modified file 'tests/vmtests/test_iscsi.py'
1392--- tests/vmtests/test_iscsi.py 2017-11-07 17:11:12 +0000
1393+++ tests/vmtests/test_iscsi.py 2017-12-08 20:52:11 +0000
1394@@ -61,3 +61,7 @@
1395
1396 class ArtfulTestIscsiBasic(relbase.artful, TestBasicIscsiAbs):
1397 __test__ = True
1398+
1399+
1400+class BionicTestIscsiBasic(relbase.bionic, TestBasicIscsiAbs):
1401+ __test__ = True
1402
1403=== modified file 'tests/vmtests/test_journald_reporter.py'
1404--- tests/vmtests/test_journald_reporter.py 2017-10-06 02:20:08 +0000
1405+++ tests/vmtests/test_journald_reporter.py 2017-12-08 20:52:11 +0000
1406@@ -50,3 +50,7 @@
1407
1408 class ArtfulTestJournaldReporter(relbase.artful, TestJournaldReporter):
1409 __test__ = True
1410+
1411+
1412+class BionicTestJournaldReporter(relbase.bionic, TestJournaldReporter):
1413+ __test__ = True
1414
1415=== modified file 'tests/vmtests/test_lvm.py'
1416--- tests/vmtests/test_lvm.py 2017-11-07 17:11:12 +0000
1417+++ tests/vmtests/test_lvm.py 2017-12-08 20:52:11 +0000
1418@@ -66,3 +66,7 @@
1419
1420 class ArtfulTestLvm(relbase.artful, TestLvmAbs):
1421 __test__ = True
1422+
1423+
1424+class BionicTestLvm(relbase.bionic, TestLvmAbs):
1425+ __test__ = True
1426
1427=== modified file 'tests/vmtests/test_lvm_iscsi.py'
1428--- tests/vmtests/test_lvm_iscsi.py 2017-11-07 17:11:12 +0000
1429+++ tests/vmtests/test_lvm_iscsi.py 2017-12-08 20:52:11 +0000
1430@@ -61,3 +61,7 @@
1431
1432 class ArtfulTestIscsiLvm(relbase.artful, TestLvmIscsiAbs):
1433 __test__ = True
1434+
1435+
1436+class BionicTestIscsiLvm(relbase.bionic, TestLvmIscsiAbs):
1437+ __test__ = True
1438
1439=== added file 'tests/vmtests/test_lvm_root.py'
1440--- tests/vmtests/test_lvm_root.py 1970-01-01 00:00:00 +0000
1441+++ tests/vmtests/test_lvm_root.py 2017-12-08 20:52:11 +0000
1442@@ -0,0 +1,155 @@
1443+from . import VMBaseClass
1444+from .releases import base_vm_classes as relbase
1445+
1446+import json
1447+import textwrap
1448+
1449+
1450+class TestLvmRootAbs(VMBaseClass):
1451+ conf_file = "examples/tests/lvmroot.yaml"
1452+ interactive = False
1453+ rootfs_uuid = '04836770-e989-460f-8774-8e277ddcb40f'
1454+ extra_disks = []
1455+ dirty_disks = True
1456+ collect_scripts = [textwrap.dedent("""
1457+ cd OUTPUT_COLLECT_D
1458+ cat /etc/fstab > fstab
1459+ lsblk --json --fs -o KNAME,MOUNTPOINT,UUID,FSTYPE > lsblk.json
1460+ lsblk --fs -P -o KNAME,MOUNTPOINT,UUID,FSTYPE > lsblk.out
1461+ ls -al /dev/disk/by-dname > ls_al_dname
1462+ ls -al /dev/disk/by-id > ls_al_byid
1463+ ls -al /dev/disk/by-uuid > ls_al_byuuid
1464+ ls -al /dev/mapper > ls_al_dev_mapper
1465+ find /etc/network/interfaces.d > find_interfacesd
1466+ pvdisplay -C --separator = -o vg_name,pv_name --noheadings > pvs
1467+ lvdisplay -C --separator = -o lv_name,vg_name --noheadings > lvs
1468+ pvdisplay > pvdisplay
1469+ vgdisplay > vgdisplay
1470+ lvdisplay > lvdisplay
1471+ ls -al /dev/root_vg/ > dev_root_vg
1472+ """)]
1473+ fstab_expected = {
1474+ 'UUID=04836770-e989-460f-8774-8e277ddcb40f': '/',
1475+ }
1476+ conf_replace = {}
1477+
1478+ def test_output_files_exist(self):
1479+ self.output_files_exist(["fstab"])
1480+
1481+ def test_rootfs_format(self):
1482+ if self.release not in ['trusty']:
1483+ self.output_files_exist(["lsblk.json"])
1484+ lsblk_data = json.load(open(self.collect_path('lsblk.json')))
1485+ print(json.dumps(lsblk_data, indent=4))
1486+ [entry] = [entry for entry in lsblk_data.get('blockdevices')
1487+ if entry['mountpoint'] == '/']
1488+ print(entry)
1489+ self.assertEqual(self.conf_replace['__ROOTFS_FORMAT__'],
1490+ entry['fstype'])
1491+ else:
1492+ # no json output on trusty
1493+ self.output_files_exist(["lsblk.out"])
1494+ lsblk_data = open(self.collect_path('lsblk.out')).readlines()
1495+ print(lsblk_data)
1496+ [root] = [line.strip() for line in lsblk_data
1497+ if 'MOUNTPOINT="/"' in line]
1498+ print(root)
1499+ [fstype] = [val.replace('"', '').split("=")[1]
1500+ for val in root.split() if 'FSTYPE' in val]
1501+ print(fstype)
1502+ self.assertEqual(self.conf_replace['__ROOTFS_FORMAT__'], fstype)
1503+
1504+
1505+class TrustyTestLvmRootExt4(relbase.trusty, TestLvmRootAbs):
1506+ __test__ = True
1507+ conf_replace = {
1508+ '__ROOTFS_FORMAT__': 'ext4',
1509+ }
1510+
1511+
1512+class TrustyTestLvmRootXfs(relbase.trusty, TestLvmRootAbs):
1513+ __test__ = True
1514+ # xfs on trusty can't support uuid=
1515+ fstab_expected = {}
1516+ conf_replace = {
1517+ '__ROOTFS_FORMAT__': 'xfs',
1518+ }
1519+
1520+
1521+class XenialTestLvmRootExt4(relbase.xenial, TestLvmRootAbs):
1522+ __test__ = True
1523+ conf_replace = {
1524+ '__ROOTFS_FORMAT__': 'ext4',
1525+ }
1526+
1527+
1528+class XenialTestLvmRootXfs(relbase.xenial, TestLvmRootAbs):
1529+ __test__ = True
1530+ conf_replace = {
1531+ '__ROOTFS_FORMAT__': 'xfs',
1532+ }
1533+
1534+
1535+class ArtfulTestLvmRootExt4(relbase.artful, TestLvmRootAbs):
1536+ __test__ = True
1537+ conf_replace = {
1538+ '__ROOTFS_FORMAT__': 'ext4',
1539+ }
1540+
1541+
1542+class ArtfulTestLvmRootXfs(relbase.artful, TestLvmRootAbs):
1543+ __test__ = True
1544+ conf_replace = {
1545+ '__ROOTFS_FORMAT__': 'xfs',
1546+ }
1547+
1548+
1549+class TestUefiLvmRootAbs(TestLvmRootAbs):
1550+ conf_file = "examples/tests/uefi_lvmroot.yaml"
1551+ uefi = True
1552+
1553+
1554+class XenialTestUefiLvmRootExt4(relbase.xenial, TestUefiLvmRootAbs):
1555+ __test__ = True
1556+ conf_replace = {
1557+ '__BOOTFS_FORMAT__': 'ext4',
1558+ '__ROOTFS_FORMAT__': 'ext4',
1559+ }
1560+
1561+
1562+class XenialTestUefiLvmRootXfs(relbase.xenial, TestUefiLvmRootAbs):
1563+ __test__ = True
1564+ conf_replace = {
1565+ '__BOOTFS_FORMAT__': 'ext4',
1566+ '__ROOTFS_FORMAT__': 'xfs',
1567+ }
1568+
1569+
1570+class XenialTestUefiLvmRootXfsBootXfs(relbase.xenial, TestUefiLvmRootAbs):
1571+ __test__ = True
1572+ conf_replace = {
1573+ '__BOOTFS_FORMAT__': 'xfs', # Expected to fail until LP: #1652822
1574+ '__ROOTFS_FORMAT__': 'xfs',
1575+ }
1576+
1577+ @classmethod
1578+ def setUpClass(cls):
1579+ cls.skip_by_date(cls.__name__, cls.release, "1652822",
1580+ fixby=(2018, 1, 20), removeby=(2018, 2, 23))
1581+ super().setUpClass()
1582+
1583+
1584+class ArtfulTestUefiLvmRootExt4(relbase.artful, TestUefiLvmRootAbs):
1585+ __test__ = True
1586+ conf_replace = {
1587+ '__BOOTFS_FORMAT__': 'ext4',
1588+ '__ROOTFS_FORMAT__': 'ext4',
1589+ }
1590+
1591+
1592+class ArtfulTestUefiLvmRootXfs(relbase.artful, TestUefiLvmRootAbs):
1593+ __test__ = True
1594+ conf_replace = {
1595+ '__BOOTFS_FORMAT__': 'ext4',
1596+ '__ROOTFS_FORMAT__': 'xfs',
1597+ }
1598
1599=== modified file 'tests/vmtests/test_mdadm_bcache.py'
1600--- tests/vmtests/test_mdadm_bcache.py 2017-10-06 02:20:08 +0000
1601+++ tests/vmtests/test_mdadm_bcache.py 2017-12-08 20:52:11 +0000
1602@@ -144,6 +144,10 @@
1603 __test__ = True
1604
1605
1606+class BionicTestMdadmBcache(relbase.bionic, TestMdadmBcacheAbs):
1607+ __test__ = True
1608+
1609+
1610 class TestMirrorbootAbs(TestMdadmAbs):
1611 # alternative config for more complex setup
1612 conf_file = "examples/tests/mirrorboot.yaml"
1613@@ -184,6 +188,10 @@
1614 __test__ = True
1615
1616
1617+class BionicTestMirrorboot(relbase.bionic, TestMirrorbootAbs):
1618+ __test__ = True
1619+
1620+
1621 class TestMirrorbootPartitionsAbs(TestMdadmAbs):
1622 # alternative config for more complex setup
1623 conf_file = "examples/tests/mirrorboot-msdos-partition.yaml"
1624@@ -228,6 +236,11 @@
1625 __test__ = True
1626
1627
1628+class BionicTestMirrorbootPartitions(relbase.bionic,
1629+ TestMirrorbootPartitionsAbs):
1630+ __test__ = True
1631+
1632+
1633 class TestMirrorbootPartitionsUEFIAbs(TestMdadmAbs):
1634 # alternative config for more complex setup
1635 conf_file = "examples/tests/mirrorboot-uefi.yaml"
1636@@ -271,6 +284,11 @@
1637 __test__ = True
1638
1639
1640+class BionicTestMirrorbootPartitionsUEFI(relbase.bionic,
1641+ TestMirrorbootPartitionsUEFIAbs):
1642+ __test__ = True
1643+
1644+
1645 class TestRaid5bootAbs(TestMdadmAbs):
1646 # alternative config for more complex setup
1647 conf_file = "examples/tests/raid5boot.yaml"
1648@@ -312,6 +330,10 @@
1649 __test__ = True
1650
1651
1652+class BionicTestRaid5boot(relbase.bionic, TestRaid5bootAbs):
1653+ __test__ = True
1654+
1655+
1656 class TestRaid6bootAbs(TestMdadmAbs):
1657 # alternative config for more complex setup
1658 conf_file = "examples/tests/raid6boot.yaml"
1659@@ -365,6 +387,10 @@
1660 __test__ = True
1661
1662
1663+class BionicTestRaid6boot(relbase.bionic, TestRaid6bootAbs):
1664+ __test__ = True
1665+
1666+
1667 class TestRaid10bootAbs(TestMdadmAbs):
1668 # alternative config for more complex setup
1669 conf_file = "examples/tests/raid10boot.yaml"
1670@@ -406,6 +432,10 @@
1671 __test__ = True
1672
1673
1674+class BionicTestRaid10boot(relbase.bionic, TestRaid10bootAbs):
1675+ __test__ = True
1676+
1677+
1678 class TestAllindataAbs(TestMdadmAbs):
1679 # more complex, needs more time
1680 # alternative config for more complex setup
1681@@ -505,3 +535,7 @@
1682
1683 class ArtfulTestAllindata(relbase.artful, TestAllindataAbs):
1684 __test__ = True
1685+
1686+
1687+class BionicTestAllindata(relbase.bionic, TestAllindataAbs):
1688+ __test__ = True
1689
1690=== modified file 'tests/vmtests/test_mdadm_iscsi.py'
1691--- tests/vmtests/test_mdadm_iscsi.py 2017-11-07 17:11:12 +0000
1692+++ tests/vmtests/test_mdadm_iscsi.py 2017-12-08 20:52:11 +0000
1693@@ -36,3 +36,7 @@
1694
1695 class ArtfulTestIscsiMdadm(relbase.artful, TestMdadmIscsiAbs):
1696 __test__ = True
1697+
1698+
1699+class BionicTestIscsiMdadm(relbase.bionic, TestMdadmIscsiAbs):
1700+ __test__ = True
1701
1702=== modified file 'tests/vmtests/test_multipath.py'
1703--- tests/vmtests/test_multipath.py 2017-08-03 19:48:07 +0000
1704+++ tests/vmtests/test_multipath.py 2017-12-08 20:52:11 +0000
1705@@ -62,3 +62,7 @@
1706
1707 class ArtfulTestMultipathBasic(relbase.artful, TestMultipathBasicAbs):
1708 __test__ = True
1709+
1710+
1711+class BionicTestMultipathBasic(relbase.bionic, TestMultipathBasicAbs):
1712+ __test__ = True
1713
1714=== modified file 'tests/vmtests/test_network.py'
1715--- tests/vmtests/test_network.py 2017-11-07 17:11:12 +0000
1716+++ tests/vmtests/test_network.py 2017-12-08 20:52:11 +0000
1717@@ -484,6 +484,10 @@
1718 __test__ = True
1719
1720
1721+class BionicTestNetworkBasic(relbase.bionic, TestNetworkBasicAbs):
1722+ __test__ = True
1723+
1724+
1725 class Centos66TestNetworkBasic(centos_relbase.centos66fromxenial,
1726 CentosTestNetworkBasicAbs):
1727 __test__ = True
1728
1729=== modified file 'tests/vmtests/test_network_alias.py'
1730--- tests/vmtests/test_network_alias.py 2017-11-07 17:11:12 +0000
1731+++ tests/vmtests/test_network_alias.py 2017-12-08 20:52:11 +0000
1732@@ -74,3 +74,7 @@
1733
1734 class ArtfulTestNetworkAlias(relbase.artful, TestNetworkAliasAbs):
1735 __test__ = True
1736+
1737+
1738+class BionicTestNetworkAlias(relbase.bionic, TestNetworkAliasAbs):
1739+ __test__ = True
1740
1741=== modified file 'tests/vmtests/test_network_bonding.py'
1742--- tests/vmtests/test_network_bonding.py 2017-11-07 17:11:12 +0000
1743+++ tests/vmtests/test_network_bonding.py 2017-12-08 20:52:11 +0000
1744@@ -80,6 +80,20 @@
1745 self.debian_packages.get('ifenslave'))
1746
1747
1748+class BionicTestBonding(relbase.bionic, TestNetworkBondingAbs):
1749+ __test__ = True
1750+
1751+ def test_ifenslave_installed(self):
1752+ """Bionic should not have ifenslave installed."""
1753+ pass
1754+
1755+ def test_ifenslave_not_installed(self):
1756+ """Confirm that ifenslave is not installed on bionic"""
1757+ self.assertNotIn('ifenslave', self.debian_packages,
1758+ "ifenslave is not expected in bionic: %s" %
1759+ self.debian_packages.get('ifenslave'))
1760+
1761+
1762 class Centos66TestNetworkBonding(centos_relbase.centos66fromxenial,
1763 CentosTestNetworkBondingAbs):
1764 __test__ = True
1765
1766=== modified file 'tests/vmtests/test_network_bridging.py'
1767--- tests/vmtests/test_network_bridging.py 2017-10-06 02:20:08 +0000
1768+++ tests/vmtests/test_network_bridging.py 2017-12-08 20:52:11 +0000
1769@@ -226,12 +226,6 @@
1770 class ArtfulTestBridging(relbase.artful, TestBridgeNetworkAbs):
1771 __test__ = True
1772
1773- @classmethod
1774- def setUpClass(cls):
1775- cls.skip_by_date(cls.__name__, cls.release, "1721157",
1776- fixby=(2017, 10, 16), removeby=(2017, 11, 16))
1777- super().setUpClass()
1778-
1779 def test_bridge_utils_installed(self):
1780 """bridge-utils not needed in artful."""
1781 pass
1782@@ -240,3 +234,16 @@
1783 self.assertNotIn("bridge-utils", self.debian_packages,
1784 "bridge-utils is not expected in artful: %s" %
1785 self.debian_packages.get('bridge-utils'))
1786+
1787+
1788+class BionicTestBridging(relbase.bionic, TestBridgeNetworkAbs):
1789+ __test__ = True
1790+
1791+ def test_bridge_utils_installed(self):
1792+ """bridge-utils not needed in bionic."""
1793+ pass
1794+
1795+ def test_bridge_utils_not_installed(self):
1796+ self.assertNotIn("bridge-utils", self.debian_packages,
1797+ "bridge-utils is not expected in bionic: %s" %
1798+ self.debian_packages.get('bridge-utils'))
1799
1800=== modified file 'tests/vmtests/test_network_enisource.py'
1801--- tests/vmtests/test_network_enisource.py 2017-11-07 17:11:12 +0000
1802+++ tests/vmtests/test_network_enisource.py 2017-12-08 20:52:11 +0000
1803@@ -27,7 +27,8 @@
1804 extract information about what curtin wrote and compare that
1805 with what was actually configured (which we capture via ifconfig)
1806
1807- Note: This test is *not* valid for Artful as it has no ENI.
1808+ Note: This test is *not* valid for Artful and later as they do not
1809+ have ENI.
1810 """
1811
1812 conf_file = "examples/tests/network_source.yaml"
1813@@ -94,3 +95,6 @@
1814
1815 class ZestyTestNetworkENISource(relbase.zesty, TestNetworkENISource):
1816 __test__ = True
1817+
1818+
1819+# Artful and later are deliberately not present. They do not have ifupdown.
1820
1821=== modified file 'tests/vmtests/test_network_ipv6.py'
1822--- tests/vmtests/test_network_ipv6.py 2017-11-07 17:11:12 +0000
1823+++ tests/vmtests/test_network_ipv6.py 2017-12-08 20:52:11 +0000
1824@@ -71,6 +71,10 @@
1825 __test__ = True
1826
1827
1828+class BionicTestNetworkIPV6(relbase.bionic, TestNetworkIPV6Abs):
1829+ __test__ = True
1830+
1831+
1832 class Centos66TestNetworkIPV6(centos_relbase.centos66fromxenial,
1833 CentosTestNetworkIPV6Abs):
1834 __test__ = True
1835
1836=== modified file 'tests/vmtests/test_network_ipv6_enisource.py'
1837--- tests/vmtests/test_network_ipv6_enisource.py 2017-11-07 17:11:12 +0000
1838+++ tests/vmtests/test_network_ipv6_enisource.py 2017-12-08 20:52:11 +0000
1839@@ -29,6 +29,4 @@
1840 __test__ = True
1841
1842
1843-# Artful no longer has eni/ifupdown
1844-class ArtfulTestNetworkIPV6ENISource(relbase.artful, TestNetworkIPV6ENISource):
1845- __test__ = False
1846+# Artful and later are deliberately not present. They do not have ifupdown.
1847
1848=== modified file 'tests/vmtests/test_network_ipv6_static.py'
1849--- tests/vmtests/test_network_ipv6_static.py 2017-11-07 17:11:12 +0000
1850+++ tests/vmtests/test_network_ipv6_static.py 2017-12-08 20:52:11 +0000
1851@@ -52,6 +52,10 @@
1852 __test__ = True
1853
1854
1855+class BionicTestNetworkIPV6Static(relbase.bionic, TestNetworkIPV6StaticAbs):
1856+ __test__ = True
1857+
1858+
1859 class Centos66TestNetworkIPV6Static(centos_relbase.centos66fromxenial,
1860 CentosTestNetworkIPV6StaticAbs):
1861 __test__ = True
1862
1863=== modified file 'tests/vmtests/test_network_ipv6_vlan.py'
1864--- tests/vmtests/test_network_ipv6_vlan.py 2017-11-07 17:11:12 +0000
1865+++ tests/vmtests/test_network_ipv6_vlan.py 2017-12-08 20:52:11 +0000
1866@@ -33,6 +33,10 @@
1867 __test__ = True
1868
1869
1870+class BionicTestNetworkIPV6Vlan(relbase.bionic, TestNetworkIPV6VlanAbs):
1871+ __test__ = True
1872+
1873+
1874 class Centos66TestNetworkIPV6Vlan(centos_relbase.centos66fromxenial,
1875 CentosTestNetworkIPV6VlanAbs):
1876 __test__ = True
1877
1878=== modified file 'tests/vmtests/test_network_mtu.py'
1879--- tests/vmtests/test_network_mtu.py 2017-11-07 17:11:12 +0000
1880+++ tests/vmtests/test_network_mtu.py 2017-12-08 20:52:11 +0000
1881@@ -202,6 +202,16 @@
1882 super().setUpClass()
1883
1884
1885+class BionicTestNetworkMtu(relbase.bionic, TestNetworkMtuAbs):
1886+ __test__ = True
1887+
1888+ @classmethod
1889+ def setUpClass(cls):
1890+ cls.skip_by_date(cls.__name__, cls.release, "1671951",
1891+ fixby=(2018, 1, 20), removeby=(2018, 2, 23))
1892+ super().setUpClass()
1893+
1894+
1895 class Centos66TestNetworkMtu(centos_relbase.centos66fromxenial,
1896 CentosTestNetworkMtuAbs):
1897 __test__ = True
1898
1899=== modified file 'tests/vmtests/test_network_static.py'
1900--- tests/vmtests/test_network_static.py 2017-11-07 17:11:12 +0000
1901+++ tests/vmtests/test_network_static.py 2017-12-08 20:52:11 +0000
1902@@ -67,6 +67,10 @@
1903 __test__ = True
1904
1905
1906+class BionicTestNetworkStatic(relbase.bionic, TestNetworkStaticAbs):
1907+ __test__ = True
1908+
1909+
1910 class Centos66TestNetworkStatic(centos_relbase.centos66fromxenial,
1911 CentosTestNetworkStaticAbs):
1912 __test__ = True
1913
1914=== modified file 'tests/vmtests/test_network_static_routes.py'
1915--- tests/vmtests/test_network_static_routes.py 2017-11-07 17:11:12 +0000
1916+++ tests/vmtests/test_network_static_routes.py 2017-12-08 20:52:11 +0000
1917@@ -56,6 +56,11 @@
1918 __test__ = True
1919
1920
1921+class BionicTestNetworkStaticRoutes(relbase.bionic,
1922+ TestNetworkStaticRoutesAbs):
1923+ __test__ = True
1924+
1925+
1926 class Centos66TestNetworkStaticRoutes(centos_relbase.centos66fromxenial,
1927 CentosTestNetworkStaticRoutesAbs):
1928 __test__ = False
1929
1930=== modified file 'tests/vmtests/test_network_vlan.py'
1931--- tests/vmtests/test_network_vlan.py 2017-11-07 17:11:12 +0000
1932+++ tests/vmtests/test_network_vlan.py 2017-12-08 20:52:11 +0000
1933@@ -88,6 +88,10 @@
1934 __test__ = True
1935
1936
1937+class BionicTestNetworkVlan(relbase.bionic, TestNetworkVlanAbs):
1938+ __test__ = True
1939+
1940+
1941 class Centos66TestNetworkVlan(centos_relbase.centos66fromxenial,
1942 CentosTestNetworkVlanAbs):
1943 __test__ = True
1944
1945=== modified file 'tests/vmtests/test_nvme.py'
1946--- tests/vmtests/test_nvme.py 2017-11-07 17:11:12 +0000
1947+++ tests/vmtests/test_nvme.py 2017-12-08 20:52:11 +0000
1948@@ -91,6 +91,10 @@
1949 __test__ = True
1950
1951
1952+class BionicTestNvme(relbase.bionic, TestNvmeAbs):
1953+ __test__ = True
1954+
1955+
1956 class TestNvmeBcacheAbs(VMBaseClass):
1957 arch_skip = [
1958 "s390x", # nvme is a pci device, no pci on s390x
1959@@ -170,3 +174,7 @@
1960
1961 class ArtfulTestNvmeBcache(relbase.artful, TestNvmeBcacheAbs):
1962 __test__ = True
1963+
1964+
1965+class BionicTestNvmeBcache(relbase.bionic, TestNvmeBcacheAbs):
1966+ __test__ = True
1967
1968=== modified file 'tests/vmtests/test_raid5_bcache.py'
1969--- tests/vmtests/test_raid5_bcache.py 2017-11-07 17:11:12 +0000
1970+++ tests/vmtests/test_raid5_bcache.py 2017-12-08 20:52:11 +0000
1971@@ -99,3 +99,7 @@
1972
1973 class ArtfulTestRaid5Bcache(relbase.artful, TestMdadmBcacheAbs):
1974 __test__ = True
1975+
1976+
1977+class BionicTestRaid5Bcache(relbase.bionic, TestMdadmBcacheAbs):
1978+ __test__ = True
1979
1980=== modified file 'tests/vmtests/test_simple.py'
1981--- tests/vmtests/test_simple.py 2017-08-03 19:48:07 +0000
1982+++ tests/vmtests/test_simple.py 2017-12-08 20:52:11 +0000
1983@@ -46,3 +46,7 @@
1984
1985 class ArtfulTestSimple(relbase.artful, TestSimple):
1986 __test__ = True
1987+
1988+
1989+class BionicTestSimple(relbase.bionic, TestSimple):
1990+ __test__ = True
1991
1992=== modified file 'tests/vmtests/test_uefi_basic.py'
1993--- tests/vmtests/test_uefi_basic.py 2017-11-07 17:11:12 +0000
1994+++ tests/vmtests/test_uefi_basic.py 2017-12-08 20:52:11 +0000
1995@@ -105,6 +105,10 @@
1996 __test__ = True
1997
1998
1999+class BionicUefiTestBasic(relbase.bionic, TestBasicAbs):
2000+ __test__ = True
2001+
2002+
2003 class TrustyUefiTestBasic4k(TrustyUefiTestBasic):
2004 disk_block_size = 4096
2005
2006@@ -123,3 +127,7 @@
2007
2008 class ArtfulUefiTestBasic4k(ArtfulUefiTestBasic):
2009 disk_block_size = 4096
2010+
2011+
2012+class BionicUefiTestBasic4k(BionicUefiTestBasic):
2013+ disk_block_size = 4096
2014
2015=== modified file 'tools/build-deb'
2016--- tools/build-deb 2017-08-03 19:48:07 +0000
2017+++ tools/build-deb 2017-12-08 20:52:11 +0000
2018@@ -42,7 +42,22 @@
2019 clogver_upstream=${clogver_o%%-*}
2020 clogver_debian=${clogver_o#*-}
2021
2022-uver=$(echo "${clogver_upstream}" | sed "s,REVNO,$revno,")
2023+# uver_bzr gets 17.1~bzr<revno>
2024+uver_bzr=$(echo "${clogver_upstream}" | sed "s,REVNO,$revno,")
2025+# uver_rel gets '17.1'
2026+uver_rel=$(echo "${clogver_upstream}" | sed 's,~bzrREVNO,,')
2027+
2028+tag_revno=$(bzr tags | awk '$1 == v { print $2 }' v="${uver_rel}")
2029+if [ "${tag_revno}" = "$revno" ]; then
2030+ # if this is a tag, then drop ~bzrXXX from the changelog version.
2031+ echo "Building a tag of upstream version ($uver_rel -> $revno)" 1>&2
2032+ clogver_new="${uver}-${clogver_debian}"
2033+ uver="${uver_rel}"
2034+ export_string="tag:${uver_rel}"
2035+else
2036+ uver="${uver_bzr}"
2037+ export_string="$revno"
2038+fi
2039 clogver_new="${uver}-${clogver_debian}"
2040
2041 TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${bname}.XXXXXX")
2042@@ -55,11 +70,31 @@
2043 tarball="${sourcename}_$uver.orig.tar.gz"
2044
2045 myd=$(dirname "$0")
2046-"$myd/export-tarball" "${revno}" "${TEMP_D}/$tarball"
2047+"$myd/export-tarball" "${export_string}" "${TEMP_D}/$tarball"
2048 echo "created ${tarball}"
2049
2050 cd "${TEMP_D}"
2051 tar xzf "$tarball" || fail "failed extract tarball"
2052+
2053+if [ ! -d "$dir" ]; then
2054+ # export-tarball will create the directory name based on the
2055+ # contents of debian/changelog.trunk in the version provided.
2056+ # if that differs from what is here, then user has changes.
2057+ for d in ${sourcename}*; do
2058+ [ -d "$d" ] && break
2059+ done
2060+ if [ -d "$d" ]; then
2061+ {
2062+ echo "WARNING: bzr at '${export_string}' had different version"
2063+ echo " in debian/changelog.trunk than your tree. version there"
2064+ echo " is '$d' working directory had $uver"
2065+ } 1>&2
2066+ dir=$d
2067+ else
2068+ echo "did not find a directory created by export-tarball. sorry." 1>&2
2069+ exit
2070+ fi
2071+fi
2072 cd "$dir" || fail "failed cd $dir"
2073
2074 # move files ending in .trunk to name without .trunk
2075@@ -68,7 +103,10 @@
2076 mv "$f" "${f%.trunk}"
2077 done
2078
2079-sed -i -e "1s,${clogver_o},${clogver_new}," \
2080+# first line of debian/changelog looks like
2081+# curtin (<version>) UNRELEASED; urgency=low
2082+# fix the version and UNRELEASED
2083+sed -i -e "1s,([^)]*),(${clogver_new})," \
2084 -e "1s,UNRELEASED,${RELEASE}," debian/changelog ||
2085 fail "failed to write debian/changelog"
2086 debuild "$@" || fail "debuild failed"
2087
2088=== modified file 'tox.ini'
2089--- tox.ini 2017-02-28 15:26:03 +0000
2090+++ tox.ini 2017-12-08 20:52:11 +0000
2091@@ -43,7 +43,7 @@
2092 # set basepython because tox 1.6 (trusty) does not support generated environments
2093 basepython = python3
2094 deps = {[testenv]deps}
2095- pylint==1.5.4
2096+ pylint==1.7.4
2097 bzr+lp:simplestreams
2098 commands = {envpython} -m pylint --errors-only {posargs:curtin tests/vmtests}
2099
2100@@ -51,7 +51,7 @@
2101 # set basepython because tox 1.6 (trusty) does not support generated environments
2102 basepython = python2.7
2103 deps = {[testenv]deps}
2104- pylint==1.5.4
2105+ pylint==1.7.4
2106 commands = {envpython} -m pylint --errors-only {posargs:curtin}
2107
2108 [testenv:docs]

Subscribers

People subscribed via source and target branches

to all changes: