Merge lp:~raharper/ubuntu/xenial/curtin/pkg-sru-revno399 into lp:~smoser/ubuntu/xenial/curtin/pkg

Proposed by Ryan Harper
Status: Merged
Merged at revision: 51
Proposed branch: lp:~raharper/ubuntu/xenial/curtin/pkg-sru-revno399
Merge into: lp:~smoser/ubuntu/xenial/curtin/pkg
Diff against target: 2457 lines (+1095/-323)
35 files modified
curtin/block/__init__.py (+9/-3)
curtin/commands/block_meta.py (+7/-2)
curtin/commands/curthooks.py (+19/-3)
curtin/commands/net_meta.py (+39/-28)
curtin/net/__init__.py (+25/-5)
curtin/reporter/handlers.py (+4/-13)
curtin/util.py (+56/-0)
debian/changelog (+19/-0)
examples/tests/basic_network.yaml (+6/-0)
examples/tests/basic_scsi.yaml (+72/-0)
examples/tests/bonding_network.yaml (+2/-0)
examples/tests/multipath.yaml (+38/-0)
examples/tests/simple.yaml (+3/-0)
helpers/common (+14/-6)
tests/unittests/test_net.py (+49/-1)
tests/unittests/test_reporter.py (+23/-2)
tests/vmtests/__init__.py (+121/-58)
tests/vmtests/helpers.py (+169/-0)
tests/vmtests/image_sync.py (+16/-2)
tests/vmtests/releases.py (+5/-0)
tests/vmtests/test_basic.py (+121/-4)
tests/vmtests/test_bcache_basic.py (+4/-0)
tests/vmtests/test_bonding.py (+6/-44)
tests/vmtests/test_lvm.py (+4/-4)
tests/vmtests/test_mdadm_bcache.py (+24/-24)
tests/vmtests/test_multipath.py (+63/-0)
tests/vmtests/test_network.py (+19/-57)
tests/vmtests/test_nvme.py (+4/-4)
tests/vmtests/test_raid5_bcache.py (+8/-4)
tests/vmtests/test_simple.py (+40/-0)
tests/vmtests/test_uefi_basic.py (+8/-8)
tools/jenkins-runner (+10/-0)
tools/launch (+74/-44)
tools/vmtest-sync-images (+5/-1)
tools/xkvm (+9/-6)
To merge this branch: bzr merge lp:~raharper/ubuntu/xenial/curtin/pkg-sru-revno399
Reviewer Review Type Date Requested Status
Scott Moser Approve
Review via email: mp+299841@code.launchpad.net

Description of the change

SRU'ing revno399 into Xenial

To post a comment you must log in.
Revision history for this message
Scott Moser (smoser) wrote :

i also added the debian/new-upstream-snapshot fix.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'curtin/block/__init__.py'
2--- curtin/block/__init__.py 2016-05-10 16:13:29 +0000
3+++ curtin/block/__init__.py 2016-07-12 16:34:37 +0000
4@@ -356,6 +356,7 @@
5 cmd.append('--replace-whitespace')
6 try:
7 (out, err) = util.subp(cmd, capture=True)
8+ LOG.debug("scsi_id output raw:\n%s\nerror:\n%s", out, err)
9 scsi_wwid = out.rstrip('\n')
10 return scsi_wwid
11 except util.ProcessExecutionError as e:
12@@ -474,9 +475,14 @@
13 """
14 # Get all volumes in /dev/disk/by-id/ containing the serial string. The
15 # string specified can be either in the short or long serial format
16- disks = list(filter(lambda x: serial in x, os.listdir("/dev/disk/by-id/")))
17+ # hack, some serials have spaces, udev usually converts ' ' -> '_'
18+ serial_udev = serial.replace(' ', '_')
19+ LOG.info('Processing serial %s via udev to %s', serial, serial_udev)
20+
21+ disks = list(filter(lambda x: serial_udev in x,
22+ os.listdir("/dev/disk/by-id/")))
23 if not disks or len(disks) < 1:
24- raise ValueError("no disk with serial '%s' found" % serial)
25+ raise ValueError("no disk with serial '%s' found" % serial_udev)
26
27 # Sort by length and take the shortest path name, as the longer path names
28 # will be the partitions on the disk. Then use os.path.realpath to
29@@ -486,7 +492,7 @@
30
31 if not os.path.exists(path):
32 raise ValueError("path '%s' to block device for disk with serial '%s' \
33- does not exist" % (path, serial))
34+ does not exist" % (path, serial_udev))
35 return path
36
37
38
39=== modified file 'curtin/commands/block_meta.py'
40--- curtin/commands/block_meta.py 2016-05-10 16:13:29 +0000
41+++ curtin/commands/block_meta.py 2016-07-12 16:34:37 +0000
42@@ -379,9 +379,14 @@
43 if vol.get('serial'):
44 volume_path = block.lookup_disk(vol.get('serial'))
45 elif vol.get('path'):
46- volume_path = vol.get('path')
47+ # resolve any symlinks to the dev_kname so sys/class/block access
48+ # is valid. ie, there are no udev generated values in sysfs
49+ volume_path = os.path.realpath(vol.get('path'))
50+ elif vol.get('wwn'):
51+ by_wwn = '/dev/disk/by-id/wwn-%s' % vol.get('wwn')
52+ volume_path = os.path.realpath(by_wwn)
53 else:
54- raise ValueError("serial number or path to block dev must be \
55+ raise ValueError("serial, wwn or path to block dev must be \
56 specified to identify disk")
57
58 elif vol.get('type') == "lvm_partition":
59
60=== modified file 'curtin/commands/curthooks.py'
61--- curtin/commands/curthooks.py 2016-06-03 13:50:09 +0000
62+++ curtin/commands/curthooks.py 2016-07-12 16:34:37 +0000
63@@ -238,7 +238,7 @@
64
65 def install_kernel(cfg, target):
66 kernel_cfg = cfg.get('kernel', {'package': None,
67- 'fallback-package': None,
68+ 'fallback-package': "linux-generic",
69 'mapping': {}})
70 if kernel_cfg is not None:
71 kernel_package = kernel_cfg.get('package')
72@@ -270,7 +270,7 @@
73 LOG.warn("Couldn't detect kernel package to install for %s."
74 % kernel)
75 if kernel_fallback is not None:
76- util.install_packages([kernel_fallback])
77+ util.install_packages([kernel_fallback], target=target)
78 return
79
80 package = "linux-{flavor}{map_suffix}".format(
81@@ -638,6 +638,21 @@
82 LOG.info("Detected multipath devices. Installing support via %s", mppkgs)
83
84 util.install_packages(mppkgs, target=target)
85+ replace_spaces = True
86+ try:
87+ # check in-target version
88+ pkg_ver = util.get_package_version('multipath-tools', target=target)
89+ LOG.debug("get_package_version:\n%s", pkg_ver)
90+ LOG.debug("multipath version is %s (major=%s minor=%s micro=%s)",
91+ pkg_ver['semantic_version'], pkg_ver['major'],
92+ pkg_ver['minor'], pkg_ver['micro'])
93+ # multipath-tools versions < 0.5.0 do _NOT_ want whitespace replaced
94+ # i.e. 0.4.X in Trusty.
95+ if pkg_ver['semantic_version'] < 500:
96+ replace_spaces = False
97+ except Exception as e:
98+ LOG.warn("failed reading multipath-tools version, "
99+ "assuming it wants no spaces in wwids: %s", e)
100
101 multipath_cfg_path = os.path.sep.join([target, '/etc/multipath.conf'])
102 multipath_bind_path = os.path.sep.join([target, '/etc/multipath/bindings'])
103@@ -658,7 +673,8 @@
104 if mpbindings or not os.path.isfile(multipath_bind_path):
105 # we do assume that get_devices_for_mp()[0] is /
106 target_dev = block.get_devices_for_mp(target)[0]
107- wwid = block.get_scsi_wwid(target_dev)
108+ wwid = block.get_scsi_wwid(target_dev,
109+ replace_whitespace=replace_spaces)
110 blockdev, partno = block.get_blockdev_for_partition(target_dev)
111
112 mpname = "mpath0"
113
114=== modified file 'curtin/commands/net_meta.py'
115--- curtin/commands/net_meta.py 2016-02-12 21:54:46 +0000
116+++ curtin/commands/net_meta.py 2016-07-12 16:34:37 +0000
117@@ -20,6 +20,7 @@
118 import sys
119
120 from curtin import net
121+from curtin.log import LOG
122 import curtin.util as util
123 import curtin.config as config
124
125@@ -53,23 +54,20 @@
126 raise ValueError("'%s' is not an alias: %s", alias, DEVNAME_ALIASES)
127
128
129-def interfaces_basic_dhcp(devices):
130- content = '\n'.join(
131- [("# This file describes the network interfaces available on "
132- "your system"),
133- "# and how to activate them. For more information see interfaces(5).",
134- "",
135- "# The loopback network interface",
136- "auto lo",
137- "iface lo inet loopback",
138- ])
139-
140- for d in devices:
141- content += '\n'.join(("", "", "auto %s" % d,
142- "iface %s inet dhcp" % d,))
143- content += "\n"
144-
145- return content
146+def interfaces_basic_dhcp(devices, macs=None):
147+ # return network configuration that says to dhcp on provided devices
148+ if macs is None:
149+ macs = {}
150+ for dev in devices:
151+ macs[dev] = net.get_interface_mac(dev)
152+
153+ config = []
154+ for dev in devices:
155+ config.append({
156+ 'type': 'physical', 'name': dev, 'mac_address': macs.get(dev),
157+ 'subnets': [{'type': 'dhcp4'}]})
158+
159+ return {'network': {'version': 1, 'config': config}}
160
161
162 def interfaces_custom(args):
163@@ -81,7 +79,7 @@
164 raise Exception("network configuration is required by mode '%s' "
165 "but not provided in the config file" % 'custom')
166
167- return config.dump_config({'network': network_config})
168+ return {'network': network_config}
169
170
171 def net_meta(args):
172@@ -124,6 +122,9 @@
173 else:
174 devices.append(dev)
175
176+ LOG.debug("net-meta mode is '%s'. devices=%s", args.mode, devices)
177+
178+ output_network_config = os.environ.get("OUTPUT_NETWORK_CONFIG", "")
179 if args.mode == "copy":
180 if not args.target:
181 raise argparse.ArgumentTypeError("mode 'copy' requires --target")
182@@ -131,21 +132,31 @@
183 t_eni = os.path.sep.join((args.target, "etc/network/interfaces",))
184 with open(t_eni, "r") as fp:
185 content = fp.read()
186+ LOG.warn("net-meta mode is 'copy', static network interfaces files"
187+ "can be brittle. Copied interfaces: %s", content)
188+ target = args.output
189
190 elif args.mode == "dhcp":
191- content = interfaces_basic_dhcp(devices)
192+ target = output_network_config
193+ content = config.dump_config(interfaces_basic_dhcp(devices))
194+
195 elif args.mode == 'custom':
196- content = interfaces_custom(args)
197- # if we have a config, write it out to OUTPUT_NETWORK_CONFIG
198- output_network_config = os.environ.get("OUTPUT_NETWORK_CONFIG", "")
199- if output_network_config:
200- with open(output_network_config, "w") as fp:
201- fp.write(content)
202-
203- if args.output == "-":
204+ target = output_network_config
205+ content = config.dump_config(interfaces_custom(args))
206+
207+ else:
208+ raise Exception("Unexpected network config mode '%s'." % args.mode)
209+
210+ if not target:
211+ raise Exception(
212+ "No target given for mode = '%s'. No where to write content: %s" %
213+ (args.mode, content))
214+
215+ LOG.debug("writing to file %s with network config: %s", target, content)
216+ if target == "-":
217 sys.stdout.write(content)
218 else:
219- with open(args.output, "w") as fp:
220+ with open(target, "w") as fp:
221 fp.write(content)
222
223 sys.exit(0)
224
225=== modified file 'curtin/net/__init__.py'
226--- curtin/net/__init__.py 2016-05-10 16:13:29 +0000
227+++ curtin/net/__init__.py 2016-07-12 16:34:37 +0000
228@@ -331,7 +331,14 @@
229
230
231 # TODO: switch to valid_map for attrs
232-def iface_add_attrs(iface):
233+def iface_add_attrs(iface, index):
234+ # If the index is non-zero, this is an alias interface. Alias interfaces
235+ # represent additional interface addresses, and should not have additional
236+ # attributes. (extra attributes here are almost always either incorrect,
237+ # or are applied to the parent interface.) So if this is an alias, stop
238+ # right here.
239+ if index != 0:
240+ return ""
241 content = ""
242 ignore_map = [
243 'control',
244@@ -423,22 +430,30 @@
245 iface['index'] = index
246 iface['mode'] = subnet['type']
247 iface['control'] = subnet.get('control', 'auto')
248+ subnet_inet = 'inet'
249 if iface['mode'].endswith('6'):
250- iface['inet'] += '6'
251+ # This is a request for DHCPv6.
252+ subnet_inet += '6'
253 elif iface['mode'] == 'static' and ":" in subnet['address']:
254- iface['inet'] += '6'
255+ # This is a static IPv6 address.
256+ subnet_inet += '6'
257+ iface['inet'] = subnet_inet
258 if iface['mode'].startswith('dhcp'):
259 iface['mode'] = 'dhcp'
260
261 content += iface_start_entry(iface, index)
262 content += iface_add_subnet(iface, subnet)
263- content += iface_add_attrs(iface)
264+ content += iface_add_attrs(iface, index)
265+ if len(subnets) > 1 and index == 0:
266+ for i in range(1, len(subnets)):
267+ content += " post-up ifup %s:%s\n" % (iface['name'],
268+ i)
269 else:
270 # ifenslave docs say to auto the slave devices
271 if 'bond-master' in iface:
272 content += "auto {name}\n".format(**iface)
273 content += "iface {name} {inet} {mode}\n".format(**iface)
274- content += iface_add_attrs(iface)
275+ content += iface_add_attrs(iface, index)
276
277 for route in network_state.get('routes'):
278 content += render_route(route)
279@@ -469,4 +484,9 @@
280 LOG.info('Writing ' + cc_disable)
281 util.write_file(cc_disable, content='network: {config: disabled}\n')
282
283+
284+def get_interface_mac(ifname):
285+ """Returns the string value of an interface's MAC Address"""
286+ return read_sys_net(ifname, "address", enoent=False)
287+
288 # vi: ts=4 expandtab syntax=python
289
290=== modified file 'curtin/reporter/handlers.py'
291--- curtin/reporter/handlers.py 2016-05-10 16:13:29 +0000
292+++ curtin/reporter/handlers.py 2016-07-12 16:34:37 +0000
293@@ -23,7 +23,7 @@
294
295
296 class LogHandler(ReportingHandler):
297- """Publishes events to the cloud-init log at the ``INFO`` log level."""
298+ """Publishes events to the curtin log at the ``DEBUG`` log level."""
299
300 def __init__(self, level="DEBUG"):
301 super(LogHandler, self).__init__()
302@@ -39,9 +39,9 @@
303 self.level = level
304
305 def publish_event(self, event):
306- """Publish an event to the ``INFO`` log level."""
307+ """Publish an event to the ``DEBUG`` log level."""
308 logger = logging.getLogger(
309- '.'.join(['cloudinit', 'reporting', event.event_type, event.name]))
310+ '.'.join(['curtin', 'reporting', event.event_type, event.name]))
311 logger.log(self.level, event.as_string())
312
313
314@@ -55,7 +55,7 @@
315 class WebHookHandler(ReportingHandler):
316 def __init__(self, endpoint, consumer_key=None, token_key=None,
317 token_secret=None, consumer_secret=None, timeout=None,
318- retries=None, level="INFO"):
319+ retries=None, level="DEBUG"):
320 super(WebHookHandler, self).__init__()
321
322 self.oauth_helper = url_helper.OauthUrlHelper(
323@@ -72,15 +72,6 @@
324 self.headers = {'Content-Type': 'application/json'}
325
326 def publish_event(self, event):
327- if isinstance(event.level, int):
328- ev_level = event.level
329- else:
330- try:
331- ev_level = getattr(logging, event.level.upper())
332- except:
333- ev_level = logging.INFO
334- if ev_level < self.level:
335- return
336 try:
337 return self.oauth_helper.geturl(
338 url=self.endpoint, data=event.as_dict(),
339
340=== modified file 'curtin/util.py'
341--- curtin/util.py 2016-06-03 13:50:09 +0000
342+++ curtin/util.py 2016-07-12 16:34:37 +0000
343@@ -499,6 +499,62 @@
344 return False
345
346
347+def parse_dpkg_version(raw, name=None, semx=None):
348+ """Parse a dpkg version string into various parts and calcualate a
349+ numerical value of the version for use in comparing package versions
350+
351+ returns a dictionary with the results
352+ """
353+ if semx is None:
354+ semx = (10000, 100, 1)
355+
356+ upstream = raw.split('-')[0]
357+ toks = upstream.split(".", 2)
358+ if len(toks) == 3:
359+ major, minor, micro = toks
360+ elif len(toks) == 2:
361+ major, minor, micro = (toks[0], toks[1], 0)
362+ elif len(toks) == 1:
363+ major, minor, micro = (toks[0], 0, 0)
364+
365+ version = {
366+ 'major': major,
367+ 'minor': minor,
368+ 'micro': micro,
369+ 'raw': raw,
370+ 'upstream': upstream,
371+ }
372+ if name:
373+ version['name'] = name
374+
375+ if semx:
376+ try:
377+ version['semantic_version'] = int(
378+ int(major) * semx[0] + int(minor) * semx[1] +
379+ int(micro) * semx[2])
380+ except (ValueError, IndexError):
381+ version['semantic_version'] = None
382+
383+ return version
384+
385+
386+def get_package_version(pkg, target=None, semx=None):
387+ """Use dpkg-query to extract package pkg's version string
388+ and parse the version string into a dictionary
389+ """
390+ chroot = []
391+ if target is not None:
392+ chroot = ['chroot', target]
393+ try:
394+ out, _ = subp(chroot + ['dpkg-query', '--show', '--showformat',
395+ '${Version}', pkg],
396+ capture=True)
397+ raw = out.rstrip()
398+ return parse_dpkg_version(raw, name=pkg, semx=semx)
399+ except ProcessExecutionError:
400+ return None
401+
402+
403 def find_newer(src, files):
404 mtime = os.stat(src).st_mtime
405 return [f for f in files if
406
407=== modified file 'debian/changelog'
408--- debian/changelog 2016-06-03 14:35:22 +0000
409+++ debian/changelog 2016-07-12 16:34:37 +0000
410@@ -1,3 +1,22 @@
411+curtin (0.1.0~bzr399-0ubuntu1~16.04.1) xenial; urgency=medium
412+
413+ * sru current curtin
414+ - curtin/net: fix inet value for subnets, don't add interface attributes
415+ to alias (LP: #1588547)
416+ - improve net-meta network configuration (LP: #1592149)
417+ - reporting: set webhook handler level to DEBUG, no filtering
418+ (LP: #1590846)
419+ - tests/vmtests: add yakkety, remove vivid
420+ - curtin/net: use post-up for interface alias, resolve 120 second time out
421+ on Trusty when using interface aliases
422+ - vmtest: provide info on images used
423+ - fix multipath configuration and add multipath tests (LP: #1551937)
424+ - tools/launch and tools/xkvm: whitespace cleanup and bash -x
425+ - tools/launch: boot by root=LABEL=cloudimg-rootfs
426+ - Initial vmtest power8 support and TestSimple test.
427+
428+ -- Ryan Harper <ryan.harper@canonical.com> Tue, 12 Jul 2016 11:29:30 -0500
429+
430 curtin (0.1.0~bzr389-0ubuntu1~16.04.1) xenial-proposed; urgency=medium
431
432 * New upstream snapshot.
433
434=== modified file 'examples/tests/basic_network.yaml'
435--- examples/tests/basic_network.yaml 2016-06-03 13:50:09 +0000
436+++ examples/tests/basic_network.yaml 2016-07-12 16:34:37 +0000
437@@ -16,6 +16,12 @@
438 - type: static
439 address: 10.0.2.100/24
440 - type: static
441+ address: 10.0.3.100/24
442+ - type: static
443+ address: 10.0.4.100/24
444+ - type: static
445+ address: 10.0.5.100/24
446+ - type: static
447 address: 10.0.2.200/24
448 dns_nameservers:
449 - 8.8.8.8
450
451=== added file 'examples/tests/basic_scsi.yaml'
452--- examples/tests/basic_scsi.yaml 1970-01-01 00:00:00 +0000
453+++ examples/tests/basic_scsi.yaml 2016-07-12 16:34:37 +0000
454@@ -0,0 +1,72 @@
455+showtrace: true
456+storage:
457+ version: 1
458+ config:
459+ - id: sda
460+ type: disk
461+ ptable: msdos
462+ wwn: '0x39cc071e72c64cc4'
463+ name: main_disk
464+ wipe: superblock
465+ grub_device: true
466+ - id: sda1
467+ type: partition
468+ number: 1
469+ size: 3GB
470+ device: sda
471+ flag: boot
472+ - id: sda2
473+ type: partition
474+ number: 2
475+ size: 1GB
476+ device: sda
477+ - id: sda1_root
478+ type: format
479+ fstype: ext4
480+ volume: sda1
481+ - id: sda2_home
482+ type: format
483+ fstype: ext4
484+ volume: sda2
485+ - id: sda1_mount
486+ type: mount
487+ path: /
488+ device: sda1_root
489+ - id: sda2_mount
490+ type: mount
491+ path: /home
492+ device: sda2_home
493+ - id: sparedisk_id
494+ type: disk
495+ wwn: '0x080258d13ea95ae5'
496+ name: sparedisk
497+ wipe: superblock
498+ - id: btrfs_disk_id
499+ type: disk
500+ wwn: '0x22dc58dc023c7008'
501+ name: btrfs_volume
502+ wipe: superblock
503+ - id: btrfs_disk_fmt_id
504+ type: format
505+ fstype: btrfs
506+ volume: btrfs_disk_id
507+ - id: btrfs_disk_mnt_id
508+ type: mount
509+ path: /btrfs
510+ device: btrfs_disk_fmt_id
511+ - id: pnum_disk
512+ type: disk
513+ wwn: '0x550a270c3a5811c5'
514+ name: pnum_disk
515+ wipe: superblock
516+ ptable: gpt
517+ - id: pnum_disk_p1
518+ type: partition
519+ number: 1
520+ size: 1GB
521+ device: pnum_disk
522+ - id: pnum_disk_p2
523+ type: partition
524+ number: 10
525+ size: 1GB
526+ device: pnum_disk
527
528=== modified file 'examples/tests/bonding_network.yaml'
529--- examples/tests/bonding_network.yaml 2016-06-03 13:50:09 +0000
530+++ examples/tests/bonding_network.yaml 2016-07-12 16:34:37 +0000
531@@ -26,6 +26,8 @@
532 subnets:
533 - type: static
534 address: 10.23.23.2/24
535+ - type: static
536+ address: 10.23.24.2/24
537
538 curthooks_commands:
539 # use curtin to disable open-iscsi ifupdown hooks for precise; they're
540
541=== added file 'examples/tests/multipath.yaml'
542--- examples/tests/multipath.yaml 1970-01-01 00:00:00 +0000
543+++ examples/tests/multipath.yaml 2016-07-12 16:34:37 +0000
544@@ -0,0 +1,38 @@
545+showtrace: true
546+storage:
547+ version: 1
548+ config:
549+ - id: sda
550+ type: disk
551+ ptable: msdos
552+ serial: 'IPR-0 1234567890'
553+ name: mpath_a
554+ wipe: superblock
555+ grub_device: true
556+ - id: sda1
557+ type: partition
558+ number: 1
559+ size: 3GB
560+ device: sda
561+ flag: boot
562+ - id: sda2
563+ type: partition
564+ number: 2
565+ size: 1GB
566+ device: sda
567+ - id: sda1_root
568+ type: format
569+ fstype: ext4
570+ volume: sda1
571+ - id: sda2_home
572+ type: format
573+ fstype: ext4
574+ volume: sda2
575+ - id: sda1_mount
576+ type: mount
577+ path: /
578+ device: sda1_root
579+ - id: sda2_mount
580+ type: mount
581+ path: /home
582+ device: sda2_home
583
584=== added file 'examples/tests/simple.yaml'
585--- examples/tests/simple.yaml 1970-01-01 00:00:00 +0000
586+++ examples/tests/simple.yaml 2016-07-12 16:34:37 +0000
587@@ -0,0 +1,3 @@
588+# This pushes curtin through a automatic installation
589+# where no storage configuration is necessary.
590+placeholder_simple_install: unused
591
592=== modified file 'helpers/common'
593--- helpers/common 2016-05-10 16:13:29 +0000
594+++ helpers/common 2016-07-12 16:34:37 +0000
595@@ -356,15 +356,17 @@
596 local cmd="" isblk=false
597 [ -b "$target" ] && isblk=true
598
599+ local pprep="1" proot="2"
600 wipedev "$target" ||
601 { error "failed to clear $target"; return 1; }
602
603 cmd=(
604 sgdisk
605- --new "1::+8M" --typecode=1:4100
606- --new "2::$end" --typecode=2:8300
607+ --new "${pprep}::+8M" "--typecode=${pprep}:4100"
608+ --new "${proot}::$end" "--typecode=${proot}:8300"
609 "$target"
610 )
611+ debug 1 "partitioning '$target' with ${cmd[*]}"
612 "${cmd[@]}" ||
613 fail "Failed to create GPT partitions (${cmd[*]})"
614
615@@ -374,13 +376,13 @@
616 if $isblk; then
617 blockdev --rereadpt "$target"
618 udevadm settle
619- assert_partitions "$target" 1 2 ||
620+ assert_partitions "$target" "${proot}" "${pprep}" ||
621 { error "$target missing partitions: $_RET"; return 1; }
622 # wipe the full prep partition
623- wipe_partitions --full "$target" 1 ||
624+ wipe_partitions --full "$target" "${pprep}" ||
625 { error "$target: failed to wipe full PReP partition"; return 1;}
626- wipe_partitions "$target" 2 ||
627- { error "$target: failed to wipe partition 2"; return 1;}
628+ wipe_partitions "$target" "${proot}" ||
629+ { error "$target: failed to wipe partition ${proot}"; return 1;}
630 fi
631
632 return 0
633@@ -695,6 +697,12 @@
634 "${grub_name}" "${grub_target}" "$nvram" </dev/null ||
635 { error "failed to install grub!"; return 1; }
636 else
637+ # Note: dpkg-reconfigure calls grub-install on ppc64
638+ # this means that using '--no-nvram' below ends up
639+ # failing very oddly. This is because grub's post-inst
640+ # runs grub-install with no target. That ends up
641+ # updating nvram badly, and then the grub-install would
642+ # not fix it because of the no-nvram there.
643 debug 1 "installing ${grub_name} to: ${grubdevs[*]}"
644 chroot "$mp" env DEBIAN_FRONTEND=noninteractive sh -ec '
645 pkg=$1; shift;
646
647=== modified file 'tests/unittests/test_net.py'
648--- tests/unittests/test_net.py 2016-06-03 13:50:09 +0000
649+++ tests/unittests/test_net.py 2016-07-12 16:34:37 +0000
650@@ -473,6 +473,7 @@
651
652 auto eth0
653 iface eth0 inet dhcp
654+ post-up ifup eth0:1
655
656 auto eth0:1
657 iface eth0:1 inet static
658@@ -516,6 +517,11 @@
659 bond-mode active-backup
660 hwaddress 52:54:00:12:34:06
661 bond-slaves none
662+ post-up ifup bond0:1
663+
664+ auto bond0:1
665+ iface bond0:1 inet static
666+ address 10.23.24.2/24
667
668 source /etc/network/interfaces.d/*.cfg
669 """)
670@@ -545,11 +551,11 @@
671 address 192.168.14.2/24
672 gateway 192.168.14.1
673 mtu 1492
674+ post-up ifup interface1:1
675
676 auto interface1:1
677 iface interface1:1 inet static
678 address 192.168.14.4/24
679- mtu 1492
680
681 allow-hotplug interface2
682 iface interface2 inet static
683@@ -566,4 +572,46 @@
684 self.assertEqual(sorted(ifaces.split('\n')),
685 sorted(net_ifaces.split('\n')))
686
687+ def test_render_interfaces_ipv6_aliases(self):
688+ ipv6_aliases_config = '''
689+# YAML example of a simple network config
690+network:
691+ version: 1
692+ config:
693+ # Physical interfaces.
694+ - type: physical
695+ name: eth0
696+ mac_address: "c0:d6:9f:2c:e8:80"
697+ subnets:
698+ - type: static
699+ address: fde9:8f83:4a81:1:0:1:0:6/64
700+ - type: static
701+ address: 192.168.0.1/24
702+'''
703+
704+ ns = self.get_net_state(ipv6_aliases_config)
705+ ifaces = dedent("""\
706+ auto lo
707+ iface lo inet loopback
708+
709+ auto eth0
710+ iface eth0 inet6 static
711+ address fde9:8f83:4a81:1:0:1:0:6/64
712+ post-up ifup eth0:1
713+
714+ auto eth0:1
715+ iface eth0:1 inet static
716+ address 192.168.0.1/24
717+
718+ source /etc/network/interfaces.d/*.cfg
719+ """)
720+ net_ifaces = net.render_interfaces(ns.network_state)
721+ print("\n".join(sorted(ifaces.split('\n'))))
722+ print("\n^^ LOCAL -- RENDER vv")
723+ print("\n".join(sorted(net_ifaces.split('\n'))))
724+ print(ns.network_state.get('interfaces'))
725+ self.assertEqual(sorted(ifaces.split('\n')),
726+ sorted(net_ifaces.split('\n')))
727+
728+
729 # vi: ts=4 expandtab syntax=python
730
731=== modified file 'tests/unittests/test_reporter.py'
732--- tests/unittests/test_reporter.py 2016-05-10 16:13:29 +0000
733+++ tests/unittests/test_reporter.py 2016-07-12 16:34:37 +0000
734@@ -128,7 +128,8 @@
735 def test_webhook_handler(self, mock_url_helper):
736 event = events.ReportingEvent(events.START_EVENT_TYPE, 'test_event',
737 'test event', level='INFO')
738- webhook_handler = handlers.WebHookHandler('127.0.0.1:8000')
739+ webhook_handler = handlers.WebHookHandler('127.0.0.1:8000',
740+ level='INFO')
741 webhook_handler.publish_event(event)
742 webhook_handler.oauth_helper.geturl.assert_called_with(
743 url='127.0.0.1:8000', data=event.as_dict(),
744@@ -136,7 +137,6 @@
745 event.level = 'DEBUG'
746 webhook_handler.oauth_helper.geturl.called = False
747 webhook_handler.publish_event(event)
748- self.assertFalse(webhook_handler.oauth_helper.geturl.called)
749 webhook_handler = handlers.WebHookHandler('127.0.0.1:8000',
750 level="INVALID")
751 self.assertEquals(webhook_handler.level, 30)
752@@ -194,3 +194,24 @@
753 base64.b64encode(test_data).decode())
754 finally:
755 os.remove(tmp[1])
756+
757+ @patch('curtin.url_helper.OauthUrlHelper')
758+ def test_webhook_handler_post_files(self, mock_url_helper):
759+ test_data = b'abcdefg'
760+ tmp = tempfile.mkstemp()
761+ tmpfname = tmp[1]
762+ try:
763+ with open(tmpfname, 'wb') as fp:
764+ fp.write(test_data)
765+ event = events.FinishReportingEvent('test_event_name',
766+ 'test event description',
767+ post_files=[tmpfname],
768+ level='INFO')
769+ webhook_handler = handlers.WebHookHandler('127.0.0.1:8000',
770+ level='INFO')
771+ webhook_handler.publish_event(event)
772+ webhook_handler.oauth_helper.geturl.assert_called_with(
773+ url='127.0.0.1:8000', data=event.as_dict(),
774+ headers=webhook_handler.headers, retries=None)
775+ finally:
776+ os.remove(tmpfname)
777
778=== modified file 'tests/vmtests/__init__.py'
779--- tests/vmtests/__init__.py 2016-06-03 13:50:09 +0000
780+++ tests/vmtests/__init__.py 2016-07-12 16:34:37 +0000
781@@ -48,6 +48,7 @@
782
783
784 DEFAULT_BRIDGE = os.environ.get("CURTIN_VMTEST_BRIDGE", "user")
785+OUTPUT_DISK_NAME = 'output_disk.img'
786
787 _TOPDIR = None
788
789@@ -223,6 +224,7 @@
790 "Expected=%s" % (found, expected))
791 for item in results:
792 ftypes[item['ftype']] = item['item_url']
793+ last_item = item
794
795 missing = [(ftype, path) for ftype, path in ftypes.items()
796 if not os.path.exists(path)]
797@@ -230,7 +232,11 @@
798 if len(missing):
799 raise ValueError("missing files for ftypes: %s" % missing)
800
801- return ftypes
802+ # trusty amd64/hwe-p 20150101
803+ version_info = ('%(release)s %(arch)s/%(subarch)s %(version_name)s' %
804+ last_item)
805+
806+ return version_info, ftypes
807
808
809 class ImageStore:
810@@ -256,16 +262,17 @@
811 self.url = pathlib.Path(self.base_dir).as_uri()
812
813 def get_image(self, release, arch, krel=None):
814- """Return local path for root image, kernel and initrd, tarball."""
815+ """Return tuple of version info, and paths for root image,
816+ kernel, initrd, tarball."""
817 if krel is None:
818 krel = release
819- ftypes = get_images(
820+ ver_info, ftypes = get_images(
821 self.source_url, self.base_dir, release, arch, krel, self.sync)
822 root_image_path = ftypes['vmtest.root-image']
823 kernel_path = ftypes['boot-kernel']
824 initrd_path = ftypes['boot-initrd']
825 tarball = ftypes['vmtest.root-tgz']
826- return (root_image_path, kernel_path, initrd_path, tarball)
827+ return ver_info, (root_image_path, kernel_path, initrd_path, tarball)
828
829
830 class TempDir(object):
831@@ -327,7 +334,7 @@
832
833 # create output disk, mount ro
834 logger.debug('Creating output disk')
835- self.output_disk = os.path.join(self.boot, "output_disk.img")
836+ self.output_disk = os.path.join(self.boot, OUTPUT_DISK_NAME)
837 subprocess.check_call(["qemu-img", "create", "-f", TARGET_IMAGE_FORMAT,
838 self.output_disk, "10M"],
839 stdout=DEVNULL, stderr=subprocess.STDOUT)
840@@ -344,20 +351,23 @@
841 class VMBaseClass(TestCase):
842 __test__ = False
843 arch_skip = []
844+ boot_timeout = 300
845+ collect_scripts = []
846+ conf_file = "examples/tests/basic.yaml"
847 disk_block_size = 512
848+ disk_driver = 'virtio-blk'
849 disk_to_check = {}
850+ extra_disks = []
851+ extra_kern_args = None
852 fstab_expected = {}
853- extra_kern_args = None
854- recorded_errors = 0
855- recorded_failures = 0
856 image_store_class = ImageStore
857- collect_scripts = []
858+ install_timeout = 3000
859 interactive = False
860- conf_file = "examples/tests/basic.yaml"
861- extra_disks = []
862+ multipath = False
863+ multipath_num_paths = 2
864 nvme_disks = []
865- boot_timeout = 300
866- install_timeout = 3000
867+ recorded_errors = 0
868+ recorded_failures = 0
869 uefi = False
870
871 # these get set from base_vm_classes
872@@ -380,15 +390,17 @@
873 # Disable sync if env var is set.
874 image_store.sync = get_env_var_bool('CURTIN_VMTEST_IMAGE_SYNC', False)
875 logger.debug("Image sync = %s", image_store.sync)
876- (boot_img, boot_kernel, boot_initrd, tarball) = image_store.get_image(
877- cls.release, cls.arch, cls.krel)
878-
879+ img_verstr, (boot_img, boot_kernel, boot_initrd, tarball) = (
880+ image_store.get_image(cls.release, cls.arch, cls.krel))
881+ logger.debug("Image %s\n boot=%s\n kernel=%s\n initrd=%s\n"
882+ " tarball=%s\n", img_verstr, boot_img, boot_kernel,
883+ boot_initrd, tarball)
884 # set up tempdir
885 cls.td = TempDir(
886 name=cls.__name__,
887 user_data=generate_user_data(collect_scripts=cls.collect_scripts))
888- logger.info('Using tempdir: {}'.format(cls.td.tmpdir))
889-
890+ logger.info('Using tempdir: %s , Image: %s', cls.td.tmpdir,
891+ img_verstr)
892 cls.install_log = os.path.join(cls.td.logs, 'install-serial.log')
893 cls.boot_log = os.path.join(cls.td.logs, 'boot-serial.log')
894 logger.debug('Install console log: {}'.format(cls.install_log))
895@@ -438,21 +450,51 @@
896 netdevs.extend(["--netdev=" + DEFAULT_BRIDGE])
897
898 # build disk arguments
899- # --disk source:size:driver:block_size
900- extra_disks = []
901+ disks = []
902+ sc = util.load_file(cls.conf_file)
903+ storage_config = yaml.load(sc).get('storage', {}).get('config', {})
904+ cls.disk_wwns = ["wwn=%s" % x.get('wwn') for x in storage_config
905+ if 'wwn' in x]
906+ cls.disk_serials = ["serial=%s" % x.get('serial')
907+ for x in storage_config if 'serial' in x]
908+
909+ target_disk = "{}:{}:{}:{}:".format(cls.td.target_disk,
910+ "",
911+ cls.disk_driver,
912+ cls.disk_block_size)
913+ if len(cls.disk_wwns):
914+ target_disk += cls.disk_wwns[0]
915+
916+ if len(cls.disk_serials):
917+ target_disk += cls.disk_serials[0]
918+
919+ disks.extend(['--disk', target_disk])
920+
921+ # --disk source:size:driver:block_size:devopts
922 for (disk_no, disk_sz) in enumerate(cls.extra_disks):
923 dpath = os.path.join(cls.td.disks, 'extra_disk_%d.img' % disk_no)
924- extra_disks.extend(
925- ['--disk', '{}:{}:{}:{}'.format(dpath, disk_sz, "",
926- cls.disk_block_size)])
927+ extra_disk = '{}:{}:{}:{}:'.format(dpath, disk_sz,
928+ cls.disk_driver,
929+ cls.disk_block_size)
930+ if len(cls.disk_wwns):
931+ w_index = disk_no + 1
932+ if w_index < len(cls.disk_wwns):
933+ extra_disk += cls.disk_wwns[w_index]
934+
935+ if len(cls.disk_serials):
936+ w_index = disk_no + 1
937+ if w_index < len(cls.disk_serials):
938+ extra_disk += cls.disk_serials[w_index]
939+
940+ disks.extend(['--disk', extra_disk])
941
942 # build nvme disk args if needed
943- nvme_disks = []
944 for (disk_no, disk_sz) in enumerate(cls.nvme_disks):
945 dpath = os.path.join(cls.td.disks, 'nvme_disk_%d.img' % disk_no)
946- nvme_disks.extend(
947- ['--disk', '{}:{}:nvme:{}'.format(dpath, disk_sz,
948- cls.disk_block_size)])
949+ nvme_disk = '{}:{}:nvme:{}:{}'.format(dpath, disk_sz,
950+ cls.disk_block_size,
951+ "serial=nvme-%d" % disk_no)
952+ disks.extend(['--disk', nvme_disk])
953
954 # proxy config
955 configs = [cls.conf_file]
956@@ -477,11 +519,10 @@
957 shutil.copy(OVMF_VARS, nvram)
958 cmd.extend(["--uefi", nvram])
959
960- # --disk source:size:driver:block_size
961- target_disk = "{}:{}:{}:{}".format(cls.td.target_disk, "", "",
962- cls.disk_block_size)
963- cmd.extend(netdevs + ["--disk", target_disk] +
964- extra_disks + nvme_disks +
965+ if cls.multipath:
966+ disks = disks * cls.multipath_num_paths
967+
968+ cmd.extend(netdevs + disks +
969 [boot_img, "--kernel=%s" % boot_kernel, "--initrd=%s" %
970 boot_initrd, "--", "curtin", "-vv", "install"] +
971 ["--config=%s" % f for f in configs] +
972@@ -510,8 +551,8 @@
973 logger.debug('')
974 try:
975 if os.path.exists(cls.install_log):
976- with open(cls.install_log) as l:
977- install_log = l.read()
978+ with open(cls.install_log, 'rb') as l:
979+ install_log = l.read().decode('utf-8', errors='replace')
980 errmsg, errors = check_install_log(install_log)
981 if errmsg:
982 for e in errors:
983@@ -527,39 +568,58 @@
984 cls.tearDownClass()
985 raise
986
987- # drop the size parameter if present in extra_disks
988- extra_disks = [x if ":" not in x else x.split(':')[0]
989- for x in extra_disks]
990 # create --disk params for nvme disks
991 bsize_args = "logical_block_size={}".format(cls.disk_block_size)
992 bsize_args += ",physical_block_size={}".format(cls.disk_block_size)
993 bsize_args += ",min_io_size={}".format(cls.disk_block_size)
994- disk_driver = "virtio-blk"
995
996 target_disks = []
997- for (disk_no, disk) in enumerate([cls.td.target_disk,
998- cls.td.output_disk]):
999- d = '--disk={},driver={},format={},{}'.format(disk, disk_driver,
1000- TARGET_IMAGE_FORMAT,
1001- bsize_args)
1002- target_disks.extend([d])
1003+ for (disk_no, disk) in enumerate([cls.td.target_disk]):
1004+ disk = '--disk={},driver={},format={},{}'.format(
1005+ disk, cls.disk_driver, TARGET_IMAGE_FORMAT, bsize_args)
1006+ if len(cls.disk_wwns):
1007+ disk += ",%s" % cls.disk_wwns[0]
1008+ if len(cls.disk_serials):
1009+ disk += ",%s" % cls.disk_serials[0]
1010+
1011+ target_disks.extend([disk])
1012
1013 extra_disks = []
1014 for (disk_no, disk_sz) in enumerate(cls.extra_disks):
1015 dpath = os.path.join(cls.td.disks, 'extra_disk_%d.img' % disk_no)
1016- d = '--disk={},driver={},format={},{}'.format(dpath, disk_driver,
1017- TARGET_IMAGE_FORMAT,
1018- bsize_args)
1019- extra_disks.extend([d])
1020+ disk = '--disk={},driver={},format={},{}'.format(
1021+ dpath, cls.disk_driver, TARGET_IMAGE_FORMAT, bsize_args)
1022+ if len(cls.disk_wwns):
1023+ w_index = disk_no + 1
1024+ if w_index < len(cls.disk_wwns):
1025+ disk += ",%s" % cls.disk_wwns[w_index]
1026+
1027+ if len(cls.disk_serials):
1028+ w_index = disk_no + 1
1029+ if w_index < len(cls.disk_serials):
1030+ disk += ",%s" % cls.disk_serials[w_index]
1031+
1032+ extra_disks.extend([disk])
1033
1034 nvme_disks = []
1035 disk_driver = 'nvme'
1036 for (disk_no, disk_sz) in enumerate(cls.nvme_disks):
1037 dpath = os.path.join(cls.td.disks, 'nvme_disk_%d.img' % disk_no)
1038- d = '--disk={},driver={},format={},{}'.format(dpath, disk_driver,
1039- TARGET_IMAGE_FORMAT,
1040- bsize_args)
1041- nvme_disks.extend([d])
1042+ disk = '--disk={},driver={},format={},{}'.format(
1043+ dpath, disk_driver, TARGET_IMAGE_FORMAT, bsize_args)
1044+ nvme_disks.extend([disk])
1045+
1046+ if cls.multipath:
1047+ target_disks = target_disks * cls.multipath_num_paths
1048+ extra_disks = extra_disks * cls.multipath_num_paths
1049+ nvme_disks = nvme_disks * cls.multipath_num_paths
1050+
1051+ # output disk is always virtio-blk, with serial of output_disk.img
1052+ output_disk = '--disk={},driver={},format={},{},{}'.format(
1053+ cls.td.output_disk, 'virtio-blk',
1054+ TARGET_IMAGE_FORMAT, bsize_args,
1055+ 'serial=%s' % os.path.basename(cls.td.output_disk))
1056+ target_disks.extend([output_disk])
1057
1058 # create xkvm cmd
1059 cmd = (["tools/xkvm", "-v", dowait] + netdevs +
1060@@ -788,10 +848,13 @@
1061 self.base_dir = base_dir
1062
1063 def get_image(self, release, arch, krel=None):
1064- """Return local path for root image, kernel and initrd, tarball."""
1065+ """Return tuple of version info, and paths for root image,
1066+ kernel, initrd, tarball."""
1067 names = ['psuedo-root-image', 'psuedo-kernel', 'psuedo-initrd',
1068 'psuedo-tarball']
1069- return [os.path.join(self.base_dir, release, arch, f) for f in names]
1070+ return (
1071+ "psuedo-%s %s/hwe-P 20160101" % (release, arch),
1072+ [os.path.join(self.base_dir, release, arch, f) for f in names])
1073
1074
1075 class PsuedoVMBaseClass(VMBaseClass):
1076@@ -940,9 +1003,9 @@
1077 'content': yaml.dump(base_cloudconfig, indent=1)},
1078 {'type': 'text/cloud-config', 'content': ssh_keys}]
1079
1080+ output_dir = '/mnt/output'
1081 output_dir_macro = 'OUTPUT_COLLECT_D'
1082- output_dir = '/mnt/output'
1083- output_device = '/dev/vdb'
1084+ output_device = '/dev/disk/by-id/virtio-%s' % OUTPUT_DISK_NAME
1085
1086 collect_prep = textwrap.dedent("mkdir -p " + output_dir)
1087 collect_post = textwrap.dedent(
1088@@ -950,7 +1013,7 @@
1089
1090 # failsafe poweroff runs on precise only, where power_state does
1091 # not exist.
1092- precise_poweroff = textwrap.dedent("""#!/bin/sh
1093+ precise_poweroff = textwrap.dedent("""#!/bin/sh -x
1094 [ "$(lsb_release -sc)" = "precise" ] || exit 0;
1095 shutdown -P now "Shutting down on precise"
1096 """)
1097@@ -960,7 +1023,7 @@
1098
1099 for part in scripts:
1100 if not part.startswith("#!"):
1101- part = "#!/bin/sh\n" + part
1102+ part = "#!/bin/sh -x\n" + part
1103 part = part.replace(output_dir_macro, output_dir)
1104 logger.debug('Cloud config archive content (pre-json):' + part)
1105 parts.append({'content': part, 'type': 'text/x-shellscript'})
1106
1107=== modified file 'tests/vmtests/helpers.py'
1108--- tests/vmtests/helpers.py 2016-02-12 21:54:46 +0000
1109+++ tests/vmtests/helpers.py 2016-07-12 16:34:37 +0000
1110@@ -117,3 +117,172 @@
1111 if getattr(test_case, 'release', ''):
1112 releases.add(getattr(test_case, 'release'))
1113 return sorted(releases)
1114+
1115+
1116+def _parse_ifconfig_xenial(ifconfig_out):
1117+ """Parse ifconfig output from xenial or earlier and return a dictionary.
1118+ given content like below, return:
1119+ {'eth0': {'address': '10.8.1.78', 'broadcast': '10.8.1.255',
1120+ 'inet6': [{'address': 'fe80::216:3eff:fe63:c05d',
1121+ 'prefixlen': '64', 'scope': 'Link'},
1122+ {'address': 'fdec:2922:2f07:0:216:3eff:fe63:c05d',
1123+ 'prefixlen': '64', 'scope': 'Global'}],
1124+ 'interface': 'eth0', 'link_encap': 'Ethernet',
1125+ 'mac_address': '00:16:3e:63:c0:5d', 'mtu': 1500,
1126+ 'multicast': True, 'netmask': '255.255.255.0',
1127+ 'running': True, 'up': True}}
1128+
1129+ eth0 Link encap:Ethernet HWaddr 00:16:3e:63:c0:5d
1130+ inet addr:10.8.1.78 Bcast:10.8.1.255 Mask:255.255.255.0
1131+ inet6 addr: fe80::216:3eff:fe63:c05d/64 Scope:Link
1132+ inet6 addr: fdec:2922:2f07:0:216:3eff:fe63:c05d/64 Scope:Global
1133+ UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
1134+ RX packets:21503 errors:0 dropped:0 overruns:0 frame:0
1135+ TX packets:11346 errors:0 dropped:0 overruns:0 carrier:0
1136+ collisions:0 txqueuelen:1000
1137+ RX bytes:31556357 (31.5 MB) TX bytes:870943 (870.9 KB)
1138+ """
1139+ ifaces = {}
1140+ combined_fields = {'addr': 'address', 'Bcast': 'broadcast',
1141+ 'Mask': 'netmask', 'MTU': 'mtu',
1142+ 'encap': 'link_encap'}
1143+ boolmap = {'RUNNING': 'running', 'UP': 'up', 'MULTICAST': 'multicast'}
1144+
1145+ for line in ifconfig_out.splitlines():
1146+ if not line:
1147+ continue
1148+ if not line.startswith(" "):
1149+ cur_iface = line.split()[0].rstrip(":")
1150+ cur_data = {'inet6': [], 'interface': cur_iface}
1151+ for t in boolmap.values():
1152+ cur_data[t] = False
1153+ ifaces[cur_iface] = cur_data
1154+
1155+ toks = line.split()
1156+
1157+ if toks[0] == "inet6":
1158+ cidr = toks[2]
1159+ address, prefixlen = cidr.split("/")
1160+ scope = toks[3].split(":")[1]
1161+ cur_ipv6 = {'address': address, 'scope': scope,
1162+ 'prefixlen': prefixlen}
1163+ cur_data['inet6'].append(cur_ipv6)
1164+ continue
1165+
1166+ for i in range(0, len(toks)):
1167+ cur_tok = toks[i]
1168+ try:
1169+ next_tok = toks[i+1]
1170+ except IndexError:
1171+ next_tok = None
1172+
1173+ if cur_tok == "HWaddr":
1174+ cur_data['mac_address'] = next_tok
1175+ elif ":" in cur_tok:
1176+ key, _colon, val = cur_tok.partition(":")
1177+ if key in combined_fields:
1178+ cur_data[combined_fields[key]] = val
1179+ elif cur_tok in boolmap:
1180+ cur_data[boolmap[cur_tok]] = True
1181+
1182+ if 'mtu' in cur_data:
1183+ cur_data['mtu'] = int(cur_data['mtu'])
1184+
1185+ return ifaces
1186+
1187+
1188+def _parse_ifconfig_yakkety(ifconfig_out):
1189+ """Parse ifconfig output from yakkety or later(?) and return a dictionary.
1190+
1191+ given ifconfig output like below, return:
1192+ {'ens2': {'address': '10.5.0.78',
1193+ 'broadcast': '10.5.255.255',
1194+ 'broadcast_flag': True,
1195+ 'inet6': [{'address': 'fe80::f816:3eff:fe05:9673',
1196+ 'prefixlen': '64', 'scopeid': '0x20<link>'},
1197+ {'address': 'fe80::f816:3eff:fe05:9673',
1198+ 'prefixlen': '64', 'scopeid': '0x20<link>'}],
1199+ 'interface': 'ens2', 'link_encap': 'Ethernet',
1200+ 'mac_address': 'fa:16:3e:05:96:73', 'mtu': 1500,
1201+ 'multicast': True, 'netmask': '255.255.0.0',
1202+ 'running': True, 'up': True}}
1203+
1204+ ens2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
1205+ inet 10.5.0.78 netmask 255.255.0.0 broadcast 10.5.255.255
1206+ inet6 fe80::f816:3eff:fe05:9673 prefixlen 64 scopeid 0x20<link>
1207+ inet6 fe80::f816:3eff:fe05:9673 prefixlen 64 scopeid 0x20<link>
1208+ ether fa:16:3e:05:96:73 txqueuelen 1000 (Ethernet)
1209+ RX packets 33196 bytes 48916947 (48.9 MB)
1210+ RX errors 0 dropped 0 overruns 0 frame 0
1211+ TX packets 5458 bytes 411486 (411.4 KB)
1212+ TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
1213+ """
1214+ fmap = {'mtu': 'mtu', 'inet': 'address',
1215+ 'netmask': 'netmask', 'broadcast': 'broadcast',
1216+ 'ether': 'mac_address'}
1217+ boolmap = {'RUNNING': 'running', 'UP': 'up', 'MULTICAST': 'multicast',
1218+ 'BROADCAST': 'broadcast_flag'}
1219+
1220+ ifaces = {}
1221+ for line in ifconfig_out.splitlines():
1222+ if not line:
1223+ continue
1224+ if not line.startswith(" "):
1225+ cur_iface = line.split()[0].rstrip(":")
1226+ cur_data = {'inet6': [], 'interface': cur_iface}
1227+ for t in boolmap.values():
1228+ cur_data[t] = False
1229+ ifaces[cur_iface] = cur_data
1230+
1231+ toks = line.split()
1232+ if toks[0] == "inet6":
1233+ cur_ipv6 = {'address': toks[1]}
1234+ cur_data['inet6'].append(cur_ipv6)
1235+
1236+ for i in range(0, len(toks)):
1237+ cur_tok = toks[i]
1238+ try:
1239+ next_tok = toks[i+1]
1240+ except IndexError:
1241+ next_tok = None
1242+ if cur_tok in fmap:
1243+ cur_data[fmap[cur_tok]] = next_tok
1244+ elif cur_tok in ('prefixlen', 'scopeid'):
1245+ cur_ipv6[cur_tok] = next_tok
1246+ cur_data['inet6'].append
1247+ elif cur_tok.startswith("flags="):
1248+ # flags=4163<UP,BROADCAST,RUNNING,MULTICAST>
1249+ flags = cur_tok[cur_tok.find("<") + 1:
1250+ cur_tok.rfind(">")].split(",")
1251+ for flag in flags:
1252+ if flag in boolmap:
1253+ cur_data[boolmap[flag]] = True
1254+ elif cur_tok == "(Ethernet)":
1255+ cur_data['link_encap'] = 'Ethernet'
1256+
1257+ if 'mtu' in cur_data:
1258+ cur_data['mtu'] = int(cur_data['mtu'])
1259+
1260+ return ifaces
1261+
1262+
1263+def ifconfig_to_dict(ifconfig_a):
1264+ # if the first token of the first line ends in a ':' then assume yakkety
1265+ # parse ifconfig output and return a dictionary.
1266+ #
1267+ # return a dictionary of network information like:
1268+ # {'ens2': {'address': '10.5.0.78', 'broadcast': '10.5.255.255',
1269+ # 'broadcast_flag': True,
1270+ # 'inet6': [{'address': 'fe80::f816:3eff:fe05:9673',
1271+ # 'prefixlen': '64', 'scopeid': '0x20<link>'},
1272+ # {'address': 'fe80::f816:3eff:fe05:9673',
1273+ # 'prefixlen': '64', 'scopeid': '0x20<link>'}],
1274+ # 'interface': 'ens2', 'link_encap': 'Ethernet',
1275+ # 'mac_address': 'fa:16:3e:05:96:73', 'mtu': 1500,
1276+ # 'multicast': True, 'netmask': '255.255.0.0',
1277+ # 'running': True, 'up': True}}
1278+ line = ifconfig_a.lstrip().splitlines()[0]
1279+ if line.split()[0].endswith(":"):
1280+ return _parse_ifconfig_yakkety(ifconfig_a)
1281+ else:
1282+ return _parse_ifconfig_xenial(ifconfig_a)
1283
1284=== modified file 'tests/vmtests/image_sync.py'
1285--- tests/vmtests/image_sync.py 2016-05-10 16:13:29 +0000
1286+++ tests/vmtests/image_sync.py 2016-07-12 16:34:37 +0000
1287@@ -29,6 +29,8 @@
1288 VMTEST_CONTENT_ID = 'com.ubuntu.maas:daily:v2:download'
1289 VMTEST_JSON_PATH = "streams/v1/vmtest.json"
1290
1291+DEFAULT_OUTPUT_FORMAT = (
1292+ "%(release)-7s %(arch)s/%(subarch)s %(version_name)-10s %(item_name)s")
1293
1294 DEFAULT_ARCHES = {
1295 'i386': ['i386'],
1296@@ -397,7 +399,19 @@
1297 results = query(args.mirror_url, args.max_items, args.filters,
1298 verbosity=vlevel)
1299 try:
1300- print(util.json_dumps(results).decode())
1301+ if args.output_format == FORMAT_JSON:
1302+ print(util.json_dumps(results).decode())
1303+ else:
1304+ output = []
1305+ for item in results:
1306+ try:
1307+ output.append(args.output_format % item)
1308+ except KeyError as e:
1309+ sys.stderr.write("output format failed (%s) for: %s\n" %
1310+ (e, item))
1311+ sys.exit(1)
1312+ for line in sorted(output):
1313+ print(line)
1314 except IOError as e:
1315 if e.errno == errno.EPIPE:
1316 sys.exit(0x80 | signal.SIGPIPE)
1317@@ -440,7 +454,7 @@
1318
1319 fmt_group = query_p.add_mutually_exclusive_group()
1320 fmt_group.add_argument('--output-format', '-o', action='store',
1321- dest='output_format', default=None,
1322+ dest='output_format', default=DEFAULT_OUTPUT_FORMAT,
1323 help="specify output format per python str.format")
1324 fmt_group.add_argument('--json', action='store_const',
1325 const=FORMAT_JSON, dest='output_format',
1326
1327=== modified file 'tests/vmtests/releases.py'
1328--- tests/vmtests/releases.py 2016-05-10 16:13:29 +0000
1329+++ tests/vmtests/releases.py 2016-07-12 16:34:37 +0000
1330@@ -46,6 +46,10 @@
1331 release = "xenial"
1332
1333
1334+class _YakketyBase(_ReleaseBase):
1335+ release = "yakkety"
1336+
1337+
1338 class _Releases(object):
1339 precise = _PreciseBase
1340 precise_hwe_t = _PreciseHWET
1341@@ -56,6 +60,7 @@
1342 vivid = _VividBase
1343 wily = _WilyBase
1344 xenial = _XenialBase
1345+ yakkety = _YakketyBase
1346
1347 base_vm_classes = _Releases
1348
1349
1350=== modified file 'tests/vmtests/test_basic.py'
1351--- tests/vmtests/test_basic.py 2016-06-03 13:50:09 +0000
1352+++ tests/vmtests/test_basic.py 2016-07-12 16:34:37 +0000
1353@@ -223,13 +223,130 @@
1354 __test__ = False
1355
1356
1357-class VividTestBasic(relbase.vivid, TestBasicAbs):
1358- __test__ = True
1359-
1360-
1361 class WilyTestBasic(relbase.wily, TestBasicAbs):
1362 __test__ = True
1363
1364
1365 class XenialTestBasic(relbase.xenial, TestBasicAbs):
1366 __test__ = True
1367+
1368+
1369+class YakketyTestBasic(relbase.yakkety, TestBasicAbs):
1370+ __test__ = True
1371+
1372+
1373+class TestBasicScsiAbs(TestBasicAbs):
1374+ conf_file = "examples/tests/basic_scsi.yaml"
1375+ disk_driver = 'scsi-hd'
1376+ extra_disks = ['128G', '128G', '4G']
1377+ nvme_disks = ['4G']
1378+ collect_scripts = [textwrap.dedent("""
1379+ cd OUTPUT_COLLECT_D
1380+ blkid -o export /dev/sda > blkid_output_sda
1381+ blkid -o export /dev/sda1 > blkid_output_sda1
1382+ blkid -o export /dev/sda2 > blkid_output_sda2
1383+ btrfs-show-super /dev/sdc > btrfs_show_super_sdc
1384+ cat /proc/partitions > proc_partitions
1385+ ls -al /dev/disk/by-uuid/ > ls_uuid
1386+ ls -al /dev/disk/by-id/ > ls_disk_id
1387+ cat /etc/fstab > fstab
1388+ mkdir -p /dev/disk/by-dname
1389+ ls /dev/disk/by-dname/ > ls_dname
1390+ find /etc/network/interfaces.d > find_interfacesd
1391+
1392+ v=""
1393+ out=$(apt-config shell v Acquire::HTTP::Proxy)
1394+ eval "$out"
1395+ echo "$v" > apt-proxy
1396+ """)]
1397+
1398+ def test_output_files_exist(self):
1399+ self.output_files_exist(
1400+ ["blkid_output_sda", "blkid_output_sda1", "blkid_output_sda2",
1401+ "btrfs_show_super_sdc", "fstab", "ls_dname", "ls_uuid",
1402+ "ls_disk_id", "proc_partitions"])
1403+
1404+ def test_ptable(self):
1405+ blkid_info = self.get_blkid_data("blkid_output_sda")
1406+ self.assertEquals(blkid_info["PTTYPE"], "dos")
1407+
1408+ def test_partition_numbers(self):
1409+ # vde should have partitions 1 and 10
1410+ disk = "sdd"
1411+ proc_partitions_path = os.path.join(self.td.collect,
1412+ 'proc_partitions')
1413+ self.assertTrue(os.path.exists(proc_partitions_path))
1414+ found = []
1415+ with open(proc_partitions_path, 'r') as fp:
1416+ for line in fp.readlines():
1417+ if disk in line:
1418+ found.append(line.split()[3])
1419+ # /proc/partitions should have 3 lines with 'vde' in them.
1420+ expected = [disk + s for s in ["", "1", "10"]]
1421+ self.assertEqual(found, expected)
1422+
1423+ def test_partitions(self):
1424+ with open(os.path.join(self.td.collect, "fstab")) as fp:
1425+ fstab_lines = fp.readlines()
1426+ print("\n".join(fstab_lines))
1427+ # Test that vda1 is on /
1428+ blkid_info = self.get_blkid_data("blkid_output_sda1")
1429+ fstab_entry = None
1430+ for line in fstab_lines:
1431+ if blkid_info['UUID'] in line:
1432+ fstab_entry = line
1433+ break
1434+ self.assertIsNotNone(fstab_entry)
1435+ self.assertEqual(fstab_entry.split(' ')[1], "/")
1436+
1437+ # Test that vda2 is on /home
1438+ blkid_info = self.get_blkid_data("blkid_output_sda2")
1439+ fstab_entry = None
1440+ for line in fstab_lines:
1441+ if blkid_info['UUID'] in line:
1442+ fstab_entry = line
1443+ break
1444+ self.assertIsNotNone(fstab_entry)
1445+ self.assertEqual(fstab_entry.split(' ')[1], "/home")
1446+
1447+ # Test whole disk sdc is mounted at /btrfs
1448+ fstab_entry = None
1449+ for line in fstab_lines:
1450+ if "/dev/sdc" in line:
1451+ fstab_entry = line
1452+ break
1453+ self.assertIsNotNone(fstab_entry)
1454+ self.assertEqual(fstab_entry.split(' ')[1], "/btrfs")
1455+
1456+ def test_whole_disk_format(self):
1457+ # confirm the whole disk format is the expected device
1458+ with open(os.path.join(self.td.collect,
1459+ "btrfs_show_super_sdc"), "r") as fp:
1460+ btrfs_show_super = fp.read()
1461+
1462+ with open(os.path.join(self.td.collect, "ls_uuid"), "r") as fp:
1463+ ls_uuid = fp.read()
1464+
1465+ # extract uuid from btrfs superblock
1466+ btrfs_fsid = [line for line in btrfs_show_super.split('\n')
1467+ if line.startswith('fsid\t\t')]
1468+ self.assertEqual(len(btrfs_fsid), 1)
1469+ btrfs_uuid = btrfs_fsid[0].split()[1]
1470+ self.assertTrue(btrfs_uuid is not None)
1471+
1472+ # extract uuid from /dev/disk/by-uuid on /dev/sdc
1473+ # parsing ls -al output on /dev/disk/by-uuid:
1474+ # lrwxrwxrwx 1 root root 9 Dec 4 20:02
1475+ # d591e9e9-825a-4f0a-b280-3bfaf470b83c -> ../../vdg
1476+ uuid = [line.split()[8] for line in ls_uuid.split('\n')
1477+ if 'sdc' in line]
1478+ self.assertEqual(len(uuid), 1)
1479+ uuid = uuid.pop()
1480+ self.assertTrue(uuid is not None)
1481+
1482+ # compare them
1483+ self.assertEqual(uuid, btrfs_uuid)
1484+
1485+
1486+class XenialTestScsiBasic(relbase.xenial, TestBasicScsiAbs):
1487+ __test__ = True
1488
1489=== modified file 'tests/vmtests/test_bcache_basic.py'
1490--- tests/vmtests/test_bcache_basic.py 2016-06-03 13:50:09 +0000
1491+++ tests/vmtests/test_bcache_basic.py 2016-07-12 16:34:37 +0000
1492@@ -50,3 +50,7 @@
1493
1494 class XenialBcacheBasic(relbase.xenial, TestBcacheBasic):
1495 __test__ = True
1496+
1497+
1498+class YakketyBcacheBasic(relbase.yakkety, TestBcacheBasic):
1499+ __test__ = True
1500
1501=== modified file 'tests/vmtests/test_bonding.py'
1502--- tests/vmtests/test_bonding.py 2016-06-03 13:50:09 +0000
1503+++ tests/vmtests/test_bonding.py 2016-07-12 16:34:37 +0000
1504@@ -1,4 +1,4 @@
1505-from . import VMBaseClass, logger
1506+from . import VMBaseClass, logger, helpers
1507 from .releases import base_vm_classes as relbase
1508
1509 import ipaddress
1510@@ -8,44 +8,6 @@
1511 import yaml
1512
1513
1514-def iface_extract(input):
1515- mo = re.search(r'^(?P<interface>\w+|\w+:\d+)\s+' +
1516- r'Link encap:(?P<link_encap>\S+)\s+' +
1517- r'(HWaddr\s+(?P<mac_address>\S+))?' +
1518- r'(\s+inet addr:(?P<address>\S+))?' +
1519- r'(\s+Bcast:(?P<broadcast>\S+)\s+)?' +
1520- r'(Mask:(?P<netmask>\S+)\s+)?',
1521- input, re.MULTILINE)
1522-
1523- mtu = re.search(r'(\s+MTU:(?P<mtu>\d+)\s+)\s+', input, re.MULTILINE)
1524- mtu_info = mtu.groupdict('')
1525- mtu_info['mtu'] = int(mtu_info['mtu'])
1526-
1527- if mo:
1528- info = mo.groupdict('')
1529- info['running'] = False
1530- info['up'] = False
1531- info['multicast'] = False
1532- if 'RUNNING' in input:
1533- info['running'] = True
1534- if 'UP' in input:
1535- info['up'] = True
1536- if 'MULTICAST' in input:
1537- info['multicast'] = True
1538- info.update(mtu_info)
1539- return info
1540- return {}
1541-
1542-
1543-def ifconfig_to_dict(ifconfig):
1544- interfaces = {}
1545- for iface in [iface_extract(iface) for iface in ifconfig.split('\n\n')
1546- if iface.strip()]:
1547- interfaces[iface['interface']] = iface
1548-
1549- return interfaces
1550-
1551-
1552 class TestNetworkAbs(VMBaseClass):
1553 interactive = False
1554 conf_file = "examples/tests/bonding_network.yaml"
1555@@ -96,7 +58,7 @@
1556 ifconfig_a = fp.read()
1557 logger.debug('ifconfig -a:\n{}'.format(ifconfig_a))
1558
1559- ifconfig_dict = ifconfig_to_dict(ifconfig_a)
1560+ ifconfig_dict = helpers.ifconfig_to_dict(ifconfig_a)
1561 logger.debug('parsed ifcfg dict:\n{}'.format(
1562 yaml.dump(ifconfig_dict, default_flow_style=False, indent=4)))
1563
1564@@ -230,13 +192,13 @@
1565 __test__ = True
1566
1567
1568-class VividTestBonding(relbase.vivid, TestNetworkAbs):
1569- __test__ = True
1570-
1571-
1572 class WilyTestBonding(relbase.wily, TestNetworkAbs):
1573 __test__ = True
1574
1575
1576 class XenialTestBonding(relbase.xenial, TestNetworkAbs):
1577 __test__ = True
1578+
1579+
1580+class YakketyTestBonding(relbase.yakkety, TestNetworkAbs):
1581+ __test__ = True
1582
1583=== modified file 'tests/vmtests/test_lvm.py'
1584--- tests/vmtests/test_lvm.py 2016-06-03 13:50:09 +0000
1585+++ tests/vmtests/test_lvm.py 2016-07-12 16:34:37 +0000
1586@@ -63,13 +63,13 @@
1587 print("test_dname does not work for Trusty")
1588
1589
1590-class VividTestLvm(relbase.vivid, TestLvmAbs):
1591- __test__ = True
1592-
1593-
1594 class WilyTestLvm(relbase.wily, TestLvmAbs):
1595 __test__ = True
1596
1597
1598 class XenialTestLvm(relbase.xenial, TestLvmAbs):
1599 __test__ = True
1600+
1601+
1602+class YakketyTestLvm(relbase.yakkety, TestLvmAbs):
1603+ __test__ = True
1604
1605=== modified file 'tests/vmtests/test_mdadm_bcache.py'
1606--- tests/vmtests/test_mdadm_bcache.py 2016-06-03 13:50:09 +0000
1607+++ tests/vmtests/test_mdadm_bcache.py 2016-07-12 16:34:37 +0000
1608@@ -130,10 +130,6 @@
1609 __test__ = True
1610
1611
1612-class VividTestMdadmBcache(relbase.vivid, TestMdadmBcacheAbs):
1613- __test__ = True
1614-
1615-
1616 class WilyTestMdadmBcache(relbase.wily, TestMdadmBcacheAbs):
1617 __test__ = True
1618
1619@@ -142,6 +138,10 @@
1620 __test__ = True
1621
1622
1623+class YakketyTestMdadmBcache(relbase.yakkety, TestMdadmBcacheAbs):
1624+ __test__ = True
1625+
1626+
1627 class TestMirrorbootAbs(TestMdadmAbs):
1628 # alternative config for more complex setup
1629 conf_file = "examples/tests/mirrorboot.yaml"
1630@@ -170,10 +170,6 @@
1631 __test__ = True
1632
1633
1634-class VividTestMirrorboot(relbase.vivid, TestMirrorbootAbs):
1635- __test__ = True
1636-
1637-
1638 class WilyTestMirrorboot(relbase.wily, TestMirrorbootAbs):
1639 __test__ = True
1640
1641@@ -182,6 +178,10 @@
1642 __test__ = True
1643
1644
1645+class YakketyTestMirrorboot(relbase.yakkety, TestMirrorbootAbs):
1646+ __test__ = True
1647+
1648+
1649 class TestRaid5bootAbs(TestMdadmAbs):
1650 # alternative config for more complex setup
1651 conf_file = "examples/tests/raid5boot.yaml"
1652@@ -211,10 +211,6 @@
1653 __test__ = True
1654
1655
1656-class VividTestRaid5boot(relbase.vivid, TestRaid5bootAbs):
1657- __test__ = True
1658-
1659-
1660 class WilyTestRaid5boot(relbase.wily, TestRaid5bootAbs):
1661 __test__ = True
1662
1663@@ -223,6 +219,10 @@
1664 __test__ = True
1665
1666
1667+class YakketyTestRaid5boot(relbase.yakkety, TestRaid5bootAbs):
1668+ __test__ = True
1669+
1670+
1671 class TestRaid6bootAbs(TestMdadmAbs):
1672 # alternative config for more complex setup
1673 conf_file = "examples/tests/raid6boot.yaml"
1674@@ -264,10 +264,6 @@
1675 __test__ = True
1676
1677
1678-class VividTestRaid6boot(relbase.vivid, TestRaid6bootAbs):
1679- __test__ = True
1680-
1681-
1682 class WilyTestRaid6boot(relbase.wily, TestRaid6bootAbs):
1683 __test__ = True
1684
1685@@ -276,6 +272,10 @@
1686 __test__ = True
1687
1688
1689+class YakketyTestRaid6boot(relbase.yakkety, TestRaid6bootAbs):
1690+ __test__ = True
1691+
1692+
1693 class TestRaid10bootAbs(TestMdadmAbs):
1694 # alternative config for more complex setup
1695 conf_file = "examples/tests/raid10boot.yaml"
1696@@ -305,10 +305,6 @@
1697 __test__ = True
1698
1699
1700-class VividTestRaid10boot(relbase.vivid, TestRaid10bootAbs):
1701- __test__ = True
1702-
1703-
1704 class WilyTestRaid10boot(relbase.wily, TestRaid10bootAbs):
1705 __test__ = True
1706
1707@@ -317,6 +313,10 @@
1708 __test__ = True
1709
1710
1711+class YakketyTestRaid10boot(relbase.yakkety, TestRaid10bootAbs):
1712+ __test__ = True
1713+
1714+
1715 class TestAllindataAbs(TestMdadmAbs):
1716 # more complex, needs more time
1717 # alternative config for more complex setup
1718@@ -403,13 +403,13 @@
1719 __test__ = False # lukes=no does not disable mounting of device
1720
1721
1722-class VividTestAllindata(relbase.vivid, TestAllindataAbs):
1723- __test__ = True
1724-
1725-
1726 class WilyTestAllindata(relbase.wily, TestAllindataAbs):
1727 __test__ = True
1728
1729
1730 class XenialTestAllindata(relbase.xenial, TestAllindataAbs):
1731 __test__ = True
1732+
1733+
1734+class YakketyTestAllindata(relbase.yakkety, TestAllindataAbs):
1735+ __test__ = True
1736
1737=== added file 'tests/vmtests/test_multipath.py'
1738--- tests/vmtests/test_multipath.py 1970-01-01 00:00:00 +0000
1739+++ tests/vmtests/test_multipath.py 2016-07-12 16:34:37 +0000
1740@@ -0,0 +1,63 @@
1741+from . import VMBaseClass
1742+from .releases import base_vm_classes as relbase
1743+
1744+import os
1745+import textwrap
1746+
1747+
1748+class TestMultipathBasicAbs(VMBaseClass):
1749+ conf_file = "examples/tests/multipath.yaml"
1750+ multipath = True
1751+ disk_driver = 'scsi-hd'
1752+ extra_disks = []
1753+ nvme_disks = []
1754+ collect_scripts = [textwrap.dedent("""
1755+ cd OUTPUT_COLLECT_D
1756+ blkid -o export /dev/sda > blkid_output_sda
1757+ blkid -o export /dev/sda1 > blkid_output_sda1
1758+ blkid -o export /dev/sda2 > blkid_output_sda2
1759+ blkid -o export /dev/sdb > blkid_output_sdb
1760+ blkid -o export /dev/sdb1 > blkid_output_sdb1
1761+ blkid -o export /dev/sdb2 > blkid_output_sdb2
1762+ dmsetup ls > dmsetup_ls
1763+ dmsetup info > dmsetup_info
1764+ cat /proc/partitions > proc_partitions
1765+ multipath -ll > multipath_ll
1766+ multipath -v3 -ll > multipath_v3_ll
1767+ multipath -r > multipath_r
1768+ cp -a /etc/multipath* .
1769+ ls -al /dev/disk/by-uuid/ > ls_uuid
1770+ ls -al /dev/disk/by-id/ > ls_disk_id
1771+ readlink -f /sys/class/block/sda/holders/dm-0 > holders_sda
1772+ readlink /sys/class/block/sdb/holders/dm-0 > holders_sdb
1773+ cat /etc/fstab > fstab
1774+ mkdir -p /dev/disk/by-dname
1775+ ls /dev/disk/by-dname/ > ls_dname
1776+ find /etc/network/interfaces.d > find_interfacesd
1777+ """)]
1778+
1779+ def test_multipath_disks_match(self):
1780+ sda = os.path.join(self.td.collect, 'holders_sda')
1781+ sdb = os.path.join(self.td.collect, 'holders_sdb')
1782+ self.assertTrue(os.path.exists(sda))
1783+ self.assertTrue(os.path.exists(sdb))
1784+ with open(sda, 'r') as fp:
1785+ sda_data = fp.read()
1786+ print('sda holders:\n%s' % sda_data)
1787+ with open(sda, 'r') as fp:
1788+ sdb_data = fp.read()
1789+ print('sdb holders:\n%s' % sda_data)
1790+
1791+ self.assertEqual(sda_data, sdb_data)
1792+
1793+
1794+class TrustyTestMultipathBasic(relbase.trusty, TestMultipathBasicAbs):
1795+ __test__ = True
1796+
1797+
1798+class XenialTestMultipathBasic(relbase.xenial, TestMultipathBasicAbs):
1799+ __test__ = True
1800+
1801+
1802+class YakketyTestMultipathBasic(relbase.yakkety, TestMultipathBasicAbs):
1803+ __test__ = True
1804
1805=== modified file 'tests/vmtests/test_network.py'
1806--- tests/vmtests/test_network.py 2016-06-03 13:50:09 +0000
1807+++ tests/vmtests/test_network.py 2016-07-12 16:34:37 +0000
1808@@ -1,4 +1,4 @@
1809-from . import VMBaseClass, logger
1810+from . import VMBaseClass, logger, helpers
1811 from .releases import base_vm_classes as relbase
1812
1813 import ipaddress
1814@@ -9,44 +9,6 @@
1815 import yaml
1816
1817
1818-def iface_extract(input):
1819- mo = re.search(r'^(?P<interface>\w+|\w+:\d+|\w+\.\d+)\s+' +
1820- r'Link encap:(?P<link_encap>\S+)\s+' +
1821- r'(HWaddr\s+(?P<mac_address>\S+))?' +
1822- r'(\s+inet addr:(?P<address>\S+))?' +
1823- r'(\s+Bcast:(?P<broadcast>\S+)\s+)?' +
1824- r'(Mask:(?P<netmask>\S+)\s+)?',
1825- input, re.MULTILINE)
1826-
1827- mtu = re.search(r'(\s+MTU:(?P<mtu>\d+)\s+)\s+', input, re.MULTILINE)
1828- mtu_info = mtu.groupdict('')
1829- mtu_info['mtu'] = int(mtu_info['mtu'])
1830-
1831- if mo:
1832- info = mo.groupdict('')
1833- info['running'] = False
1834- info['up'] = False
1835- info['multicast'] = False
1836- if 'RUNNING' in input:
1837- info['running'] = True
1838- if 'UP' in input:
1839- info['up'] = True
1840- if 'MULTICAST' in input:
1841- info['multicast'] = True
1842- info.update(mtu_info)
1843- return info
1844- return {}
1845-
1846-
1847-def ifconfig_to_dict(ifconfig):
1848- interfaces = {}
1849- for iface in [iface_extract(iface) for iface in ifconfig.split('\n\n')
1850- if iface.strip()]:
1851- interfaces[iface['interface']] = iface
1852-
1853- return interfaces
1854-
1855-
1856 class TestNetworkAbs(VMBaseClass):
1857 interactive = False
1858 conf_file = "examples/tests/basic_network.yaml"
1859@@ -132,7 +94,7 @@
1860 ifconfig_a = fp.read()
1861 logger.debug('ifconfig -a:\n{}'.format(ifconfig_a))
1862
1863- ifconfig_dict = ifconfig_to_dict(ifconfig_a)
1864+ ifconfig_dict = helpers.ifconfig_to_dict(ifconfig_a)
1865 logger.debug('parsed ifcfg dict:\n{}'.format(
1866 yaml.dump(ifconfig_dict, default_flow_style=False, indent=4)))
1867
1868@@ -340,7 +302,7 @@
1869 ifconfig_a = fp.read()
1870 logger.debug('ifconfig -a:\n{}'.format(ifconfig_a))
1871
1872- ifconfig_dict = ifconfig_to_dict(ifconfig_a)
1873+ ifconfig_dict = helpers.ifconfig_to_dict(ifconfig_a)
1874 logger.debug('parsed ifconfig dict:\n{}'.format(
1875 yaml.dump(ifconfig_dict, default_flow_style=False, indent=4)))
1876 print('parsed ifconfig dict:\n{}'.format(
1877@@ -411,14 +373,6 @@
1878 __test__ = False
1879
1880
1881-class VividTestNetwork(relbase.vivid, TestNetworkAbs):
1882- __test__ = True
1883-
1884-
1885-class VividTestNetworkStatic(relbase.vivid, TestNetworkStaticAbs):
1886- __test__ = True
1887-
1888-
1889 class WilyTestNetwork(relbase.wily, TestNetworkAbs):
1890 __test__ = True
1891
1892@@ -435,6 +389,14 @@
1893 __test__ = True
1894
1895
1896+class YakketyTestNetwork(relbase.yakkety, TestNetworkAbs):
1897+ __test__ = True
1898+
1899+
1900+class YakketyTestNetworkStatic(relbase.yakkety, TestNetworkStaticAbs):
1901+ __test__ = True
1902+
1903+
1904 class PreciseTestNetworkVlan(relbase.precise, TestNetworkVlanAbs):
1905 __test__ = True
1906
1907@@ -455,10 +417,6 @@
1908 __test__ = True
1909
1910
1911-class VividTestNetworkVlan(relbase.vivid, TestNetworkVlanAbs):
1912- __test__ = True
1913-
1914-
1915 class WilyTestNetworkVlan(relbase.wily, TestNetworkVlanAbs):
1916 __test__ = True
1917
1918@@ -467,6 +425,10 @@
1919 __test__ = True
1920
1921
1922+class YakketyTestNetworkVlan(relbase.yakkety, TestNetworkVlanAbs):
1923+ __test__ = True
1924+
1925+
1926 class PreciseTestNetworkENISource(relbase.precise, TestNetworkENISource):
1927 __test__ = False
1928 # not working, still debugging though; possible older ifupdown doesn't
1929@@ -477,13 +439,13 @@
1930 __test__ = True
1931
1932
1933-class VividTestNetworkENISource(relbase.vivid, TestNetworkENISource):
1934- __test__ = True
1935-
1936-
1937 class WilyTestNetworkENISource(relbase.wily, TestNetworkENISource):
1938 __test__ = True
1939
1940
1941 class XenialTestNetworkENISource(relbase.xenial, TestNetworkENISource):
1942 __test__ = True
1943+
1944+
1945+class YakketyTestNetworkENISource(relbase.yakkety, TestNetworkENISource):
1946+ __test__ = True
1947
1948=== modified file 'tests/vmtests/test_nvme.py'
1949--- tests/vmtests/test_nvme.py 2016-06-03 13:50:09 +0000
1950+++ tests/vmtests/test_nvme.py 2016-07-12 16:34:37 +0000
1951@@ -74,13 +74,13 @@
1952 print("test_ptable does not work for Trusty")
1953
1954
1955-class VividTestNvme(relbase.vivid, TestNvmeAbs):
1956- __test__ = True
1957-
1958-
1959 class WilyTestNvme(relbase.wily, TestNvmeAbs):
1960 __test__ = True
1961
1962
1963 class XenialTestNvme(relbase.xenial, TestNvmeAbs):
1964 __test__ = True
1965+
1966+
1967+class YakketyTestNvme(relbase.yakkety, TestNvmeAbs):
1968+ __test__ = True
1969
1970=== modified file 'tests/vmtests/test_raid5_bcache.py'
1971--- tests/vmtests/test_raid5_bcache.py 2016-06-03 13:50:09 +0000
1972+++ tests/vmtests/test_raid5_bcache.py 2016-07-12 16:34:37 +0000
1973@@ -93,9 +93,13 @@
1974 __test__ = False
1975
1976
1977-class VividTestRaid5Bcache(relbase.vivid, TestMdadmBcacheAbs):
1978- __test__ = True
1979-
1980-
1981 class WilyTestRaid5Bcache(relbase.wily, TestMdadmBcacheAbs):
1982 __test__ = True
1983+
1984+
1985+class XenialTestRaid5Bcache(relbase.xenial, TestMdadmBcacheAbs):
1986+ __test__ = True
1987+
1988+
1989+class YakketyTestRaid5Bcache(relbase.yakkety, TestMdadmBcacheAbs):
1990+ __test__ = True
1991
1992=== added file 'tests/vmtests/test_simple.py'
1993--- tests/vmtests/test_simple.py 1970-01-01 00:00:00 +0000
1994+++ tests/vmtests/test_simple.py 2016-07-12 16:34:37 +0000
1995@@ -0,0 +1,40 @@
1996+from . import VMBaseClass
1997+from .releases import base_vm_classes as relbase
1998+
1999+import textwrap
2000+
2001+
2002+class TestSimple(VMBaseClass):
2003+ # Test that curtin with no config does the right thing
2004+ conf_file = "examples/tests/simple.yaml"
2005+ extra_disks = []
2006+ extra_nics = []
2007+ collect_scripts = [textwrap.dedent("""
2008+ cd OUTPUT_COLLECT_D
2009+ sfdisk --list > sfdisk_list
2010+ for d in /dev/[sv]d[a-z] /dev/xvd?; do
2011+ [ -b "$d" ] || continue
2012+ echo == $d ==
2013+ sgdisk --print $d
2014+ done > sgdisk_list
2015+ blkid > blkid
2016+ cat /proc/partitions > proc_partitions
2017+ cp /etc/network/interfaces interfaces
2018+ if [ -f /var/log/cloud-init-output.log ]; then
2019+ cp /var/log/cloud-init-output.log .
2020+ fi
2021+ cp /var/log/cloud-init.log .
2022+ find /etc/network/interfaces.d > find_interfacesd
2023+ """)]
2024+
2025+ def test_output_files_exist(self):
2026+ self.output_files_exist(["sfdisk_list", "blkid",
2027+ "proc_partitions", "interfaces"])
2028+
2029+
2030+class TrustyTestSimple(relbase.trusty, TestSimple):
2031+ __test__ = True
2032+
2033+
2034+class XenialTestSimple(relbase.xenial, TestSimple):
2035+ __test__ = True
2036
2037=== modified file 'tests/vmtests/test_uefi_basic.py'
2038--- tests/vmtests/test_uefi_basic.py 2016-06-03 13:50:09 +0000
2039+++ tests/vmtests/test_uefi_basic.py 2016-07-12 16:34:37 +0000
2040@@ -108,14 +108,14 @@
2041 __test__ = True
2042
2043
2044-class VividUefiTestBasic(relbase.vivid, TestBasicAbs):
2045- __test__ = True
2046-
2047-
2048 class XenialUefiTestBasic(relbase.xenial, TestBasicAbs):
2049 __test__ = True
2050
2051
2052+class YakketyUefiTestBasic(relbase.yakkety, TestBasicAbs):
2053+ __test__ = True
2054+
2055+
2056 class PreciseUefiTestBasic4k(PreciseUefiTestBasic):
2057 disk_block_size = 4096
2058
2059@@ -124,13 +124,13 @@
2060 disk_block_size = 4096
2061
2062
2063-class VividUefiTestBasic4k(VividUefiTestBasic):
2064- disk_block_size = 4096
2065-
2066-
2067 class WilyUefiTestBasic4k(WilyUefiTestBasic):
2068 disk_block_size = 4096
2069
2070
2071 class XenialUefiTestBasic4k(XenialUefiTestBasic):
2072 disk_block_size = 4096
2073+
2074+
2075+class YakketyUefiTestBasic4k(YakketyUefiTestBasic):
2076+ disk_block_size = 4096
2077
2078=== modified file 'tools/jenkins-runner'
2079--- tools/jenkins-runner 2016-02-12 21:54:46 +0000
2080+++ tools/jenkins-runner 2016-07-12 16:34:37 +0000
2081@@ -7,6 +7,7 @@
2082 export CURTIN_VMTEST_KEEP_DATA_FAIL=$fkeep
2083 export CURTIN_VMTEST_TOPDIR="$topdir"
2084 export CURTIN_VMTEST_LOG=${CURTIN_VMTEST_LOG:-"$topdir/debug.log"}
2085+export IMAGE_DIR=${IMAGE_DIR:-/srv/images}
2086
2087 fail() { echo "$@" 1>&2; exit 1; }
2088
2089@@ -24,6 +25,15 @@
2090 for v in ${!CURTIN_*}; do
2091 echo "$v=${!v}"
2092 done
2093+
2094+# avoid LOG info by running python3 tests/vmtests/image_sync.py
2095+# rather than python3 -m tests.vmtests.image_sync (LP: #1594465)
2096+echo "Working with images in $IMAGE_DIR"
2097+fmt=" %(release)-7s %(arch)s/%(subarch)s %(version_name)-10s"
2098+PYTHONPATH="$PWD" python3 tests/vmtests/image_sync.py query \
2099+ --output-format="$fmt" "$IMAGE_DIR" ftype=root-image.gz ||
2100+ { echo "WARNING: error querying images in $IMAGE_DIR" 1>&2; }
2101+
2102 echo "$(date -R): vmtest start: nosetests3 $*"
2103 nosetests3 "$@"
2104 ret=$?
2105
2106=== modified file 'tools/launch'
2107--- tools/launch 2016-05-10 16:13:29 +0000
2108+++ tools/launch 2016-07-12 16:34:37 +0000
2109@@ -8,6 +8,7 @@
2110 HTTP_PORT_MIN=${HTTP_PORT_MIN:-12000}
2111 HTTP_PORT_MAX=${HTTP_PORT_MAX:-65500}
2112 MY_D=$(dirname "$0")
2113+DEFAULT_ROOT_ARG="root=LABEL=cloudimg-rootfs"
2114
2115 error() { echo "$@" 1>&2; }
2116
2117@@ -15,19 +16,24 @@
2118 cat <<EOF
2119 Usage: ${0##*/} [ options ] boot-image curtin install [args]
2120
2121- boot the image 'boot-image', so that it will run
2122+ boot the image 'boot-image', so that it will run
2123 curtin install [args]
2124 after booting. 'curtin install [args]' can be any command'
2125-
2126+
2127 options:
2128 --add F[:T] add file 'F' to the curtin archive at T
2129 -a | --append append args to kernel cmdline (--kernel)
2130 -A | --arch A assume guest kernel architecture A
2131- -d | --disk D add a disk 'D' format (path[:size][:driver][:bsize])
2132+ -d | --disk D add a disk 'D' format
2133+ (path[:size][:driver][:bsize][:devopts])
2134 driver can be specified without size using path::driver
2135 driver defaults to virtio-blk
2136 bsize <logical>[,<physical>][,<min_io_size>]
2137 bsize defaults to 512b sector size
2138+ opts is a comma delimitted list of property=value
2139+ elements. Examine qemu-kvm -device scsi-hd,? for
2140+ details.
2141+ --vnc D use -vnc D (mutually exclusive with --silent)
2142 --uefi N enable uefi boot method, store nvram at N
2143 -h | --help show this message
2144 -i | --initrd F use initramfs F
2145@@ -40,6 +46,8 @@
2146 directly through to qemu-system.
2147 Note, qemu adds 5900 to port numbers. (:0 = port 5900)
2148 --serial-log F : log to F (default 'serial.log')
2149+ --root-arg X pass 'X' through as the root= param when booting a
2150+ kernel. default: $DEFAULT_ROOT_PARAM
2151 -v | --verbose be more verbose
2152 --no-install-deps do not install insert '--install-deps'
2153 on curtin command invocations
2154@@ -188,7 +196,7 @@
2155 # starts a web service at 'port' that serves files in 'pubdir'
2156 # waits until it is verified to be lisenting at ip
2157 # if port is not provided, '$tries' random ports are tried.
2158- #
2159+ #
2160 # sets HTTP_PID and returns in _RET the port selected.
2161 local pubdir="$1" ip="$2" port="$3" tries="${4:-5}" i=""
2162 [ -z "$ip" ] && ip="localhost"
2163@@ -212,7 +220,7 @@
2164 [ "$port" = "random" ] && port=$(($pmin+($RANDOM%($pmax+1-$pmin))))
2165 debug 2 "trying http server $ip:$port"
2166 _start_http "$pubdir" "$ip" "$port" &&
2167- HTTP_PID="$_RET" && _RET="$port" &&
2168+ HTTP_PID="$_RET" && _RET="$port" &&
2169 debug 1 "serving $pubdir at http://$ip:$port/ in pid $HTTP_PID" &&
2170 return 0
2171 ret=$?
2172@@ -241,7 +249,7 @@
2173
2174 main() {
2175 local short_opts="a:A:d:h:i:k:n:p:v"
2176- local long_opts="add:,append:,arch:,bios:,disk:,dowait,help,initrd:,kernel:,mem:,netdev:,no-dowait,power:,publish:,silent,serial-log:,uefi:,verbose,vnc:"
2177+ local long_opts="add:,append:,arch:,bios:,disk:,dowait,help,initrd:,kernel:,mem:,netdev:,no-dowait,power:,publish:,root-arg:,silent,serial-log:,uefi:,verbose,vnc:"
2178 local getopt_out=""
2179 getopt_out=$(getopt --name "${0##*/}" \
2180 --options "${short_opts}" --long "${long_opts}" -- "$@") &&
2181@@ -258,6 +266,7 @@
2182 local netdevs="" install_deps="--install-deps"
2183 local arch_hint=""
2184 local video="-curses -vga std" serial_log="serial.log"
2185+ local root_arg="$DEFAULT_ROOT_ARG"
2186 # dowait: run xkvm with a '&' and then 'wait' on the pid.
2187 # the reason to do this or not do this has to do with interactivity
2188 # if detached with &, then user input will not go to xkvm.
2189@@ -280,13 +289,17 @@
2190 case "$cur" in
2191 --add) addfiles[${#addfiles[@]}]="$next"; shift;;
2192 -a|--append) uappend="$next"; shift;;
2193- -A|--arch) arch_hint="$next"; shift;;
2194+ -A|--arch) arch_hint="$next"; shift;;
2195+ --bios) bios="$2"; shift;;
2196 -d|--disk) disks[${#disks[@]}]="$next"; shift;;
2197+ --dowait) pt[${#pt[@]}]="$cur"; dowait=true;;
2198 -h|--help) Usage ; exit 0;;
2199 -i|--initrd) initrd="$next"; shift;;
2200- --no-install-deps) install_deps="";;
2201 -k|--kernel) kernel="$next"; shift;;
2202 --mem) mem="$next"; shift;;
2203+ -n|--netdev) netdevs[${#netdevs[@]}]="$next"; shift;;
2204+ --no-dowait) pt[${#pt[@]}]="$cur"; dowait=false;;
2205+ --no-install-deps) install_deps="";;
2206 --power)
2207 case "$next" in
2208 off) pstate="poweroff";;
2209@@ -295,19 +308,16 @@
2210 *) error "Invalid power state, must be: off, on, reboot";;
2211 esac
2212 shift;;
2213- -n|--netdev) netdevs[${#netdevs[@]}]="$next"; shift;;
2214 -p|--publish) pubs[${#pub[@]}]="$next"; shift;;
2215+ --root-arg) root_arg="$next";;
2216+ --serial-log) serial_log="$next"; shift;;
2217+ --silent) video="-nographic";;
2218 --uefi) uefi="$2"; shift;;
2219- --bios) bios="$2"; shift;;
2220- --silent) video="-nographic";;
2221+ -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
2222 --vnc)
2223 video="-vnc $next"
2224 debug 1 "VNC requested - $next"
2225 shift;;
2226- --serial-log) serial_log="$next"; shift;;
2227- -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
2228- --dowait) pt[${#pt[@]}]="$cur"; dowait=true;;
2229- --no-dowait) pt[${#pt[@]}]="$cur"; dowait=false;;
2230 --) shift; break;;
2231 esac
2232 shift;
2233@@ -336,10 +346,10 @@
2234 if [ -n "$bios" ]; then
2235 bios_opts=( -drive "if=pflash,format=raw,file=$bios" )
2236 elif [ -n "$uefi" ]; then
2237- case `lsb_release -sc` in
2238+ case `lsb_release -sc` in
2239 precise|trusty|vivid)
2240 # for non-split UEFI firmware, the code and
2241- # var space are in the same file. We must
2242+ # var space are in the same file. We must
2243 # make a copy so we can retain modifications.
2244 local ovmf_code="/usr/share/ovmf/OVMF.fd"
2245 local ovmf_var=$ovmf_code
2246@@ -388,21 +398,22 @@
2247 { error "failed to get dir for $0"; return 1; }
2248
2249 local disk="" src="" size="" fmt="" out="" id="" driver="" if=""
2250- local split_input=""
2251+ local split_input="" serial=""
2252 disk_args=( )
2253 id=1
2254 for disk in "${disks[@]}"; do
2255 ((id++))
2256-
2257 # 1=src
2258 # 2=src:size
2259 # 3=src:size:driver
2260 # 4=src:size:driver:bsize
2261+ # 5=src:size:driver:bsize:devopts
2262 src=$(echo $disk | awk -F: '{print $1}')
2263 size=$(echo $disk | awk -F: '{print $2}')
2264 driver=$(echo $disk | awk -F: '{print $3}')
2265 bsize=$(echo $disk | awk -F: '{print $4}')
2266-
2267+ devopts=$(echo $disk | awk -F: '{print $5}')
2268+
2269 if [ -z "${src}" ]; then
2270 error "Failed to provide disk source"
2271 exit 1
2272@@ -415,7 +426,7 @@
2273 if [ -z "${driver}" ]; then
2274 driver="virtio-blk"
2275 fi
2276-
2277+
2278 if [ -z "${bsize}" ]; then
2279 bsize="512"
2280 fi
2281@@ -430,6 +441,11 @@
2282 { error "failed to determine format of $src"; return 1; }
2283 fi
2284
2285+ # prepend comma if passing devopts
2286+ if [ -n "${devopts}" ]; then
2287+ devopts=",${devopts}"
2288+ fi
2289+
2290 # set logical/physical size blocksz is logical:phys
2291 local logbs=$(round_up ${bsize%%:*})
2292 local phybs=$(round_up ${bsize##*:})
2293@@ -441,8 +457,8 @@
2294 "file=${src},if=none,cache=unsafe,format=$fmt,id=drv${id},index=$id" )
2295
2296 disk_args=( "${disk_args[@]}" "-device"
2297- "${driver},drive=drv${id},serial=dev${id},${bs_args}" )
2298-
2299+ "${driver},drive=drv${id},${bs_args}${devopts}" )
2300+
2301 done
2302
2303 get_my_ip || { error "failed to get your ip. set IP_ADDR"; return 1; }
2304@@ -554,17 +570,24 @@
2305 seedargs=()
2306 if [ -n "$kernel" ]; then
2307 local append="" root=""
2308- # if this is a partition image, root=/dev/vda. else root=/dev/vda1
2309- # this hack is necessary because LABEL even UUID might be the same
2310- # in the boot image and the target (if re-using target)
2311- if tmp=$(blkid "$bootimg_dist" -ovalue -s UUID) && [ -n "$tmp" ]; then
2312- root="/dev/vda"
2313- else
2314- root="/dev/vda1"
2315+ # Note: root_arg is by default done by LABEL. This assumes
2316+ # a.) our root device is not multipath
2317+ # b.) no other disks attached will have this LABEL
2318+ # c.) the LABEL is in fact correct.
2319+ # all of these assumptions are true under vmtest.
2320+ if [ -z "$root_arg" ]; then
2321+ debug 1 "WARN: root_arg is empty with kernel."
2322 fi
2323- append="root=$root ds=nocloud-net;seedfrom=$burl"
2324- if [ "${arch_hint}" != "s390x" ]; then
2325- append="${append} console=ttyS0"
2326+ append="${root_arg:+${root_arg} }ds=nocloud-net;seedfrom=$burl"
2327+
2328+ local console_name=""
2329+ case "${arch_hint}" in
2330+ s390x) console_name="";;
2331+ ppc64*) console_name="hvc0";;
2332+ *) console_name="ttyS0";;
2333+ esac
2334+ if [ -n "$console_name" ]; then
2335+ append="${append} console=${console_name}"
2336 fi
2337 append="${append} $uappend"
2338 seedargs=( "${seedargs[@]}" -kernel "$kernel" )
2339@@ -586,18 +609,20 @@
2340 local cmd serial_args="" chardev_arg=""
2341 [ "${serial_log}" = "none" ] && serial_log=""
2342 if [ -n "${serial_log}" ]; then
2343- if [ "${arch_hint}" = "s390x" ]; then
2344- if [ "${serial_log}" = "stdio" ]; then
2345- chardev_arg="stdio"
2346- else
2347- chardev_arg="file,path=${serial_log}"
2348- fi
2349- serial_args="-nodefaults -chardev ${chardev_arg},id=charconsole0 -device sclpconsole,chardev=charconsole0,id=console0"
2350- else
2351- serial_args="-serial file:${serial_log}"
2352- fi
2353+ if [ "${arch_hint}" = "s390x" ]; then
2354+ if [ "${serial_log}" = "stdio" ]; then
2355+ chardev_arg="stdio"
2356+ else
2357+ chardev_arg="file,path=${serial_log}"
2358+ fi
2359+ serial_args="-nodefaults -chardev ${chardev_arg},id=charconsole0 -device sclpconsole,chardev=charconsole0,id=console0"
2360+ else
2361+ serial_args="-serial file:${serial_log}"
2362+ #debug mode serial_args="-serial ${serial_log} -monitor stdio"
2363+ fi
2364 fi
2365- cmd=(
2366+ # -monitor stdio
2367+ cmd=(
2368 xkvm "${pt[@]}" "${netargs[@]}" --
2369 "${bios_opts[@]}"
2370 -m ${mem} ${serial_args} ${video}
2371@@ -625,6 +650,11 @@
2372 return $ret
2373 }
2374
2375+random_wwn() {
2376+ # wwn must be a int64, less than (1 << 63) - 1
2377+ # we achieve this by combining 4 (1 << 15) ints
2378+ printf "0x%04x%04x%04x%04x" $RANDOM $RANDOM $RANDOM $RANDOM
2379+}
2380
2381 round_up() {
2382 local size="${1}"
2383
2384=== modified file 'tools/vmtest-sync-images'
2385--- tools/vmtest-sync-images 2016-05-10 16:13:29 +0000
2386+++ tools/vmtest-sync-images 2016-07-12 16:34:37 +0000
2387@@ -34,7 +34,11 @@
2388 print(" removing vmtest file %s" % fpath)
2389 os.unlink(fpath)
2390
2391- releases = find_releases()
2392+ arg_releases = [r for r in sys.argv[1:] if r != "--clean"]
2393+ if len(arg_releases):
2394+ releases = arg_releases
2395+ else:
2396+ releases = find_releases()
2397 release_filter = 'release~{}'.format('|'.join(releases))
2398 my_filters = ['arch=' + DEFAULT_ARCH, release_filter] + ITEM_NAME_FILTERS
2399 # Sync images.
2400
2401=== modified file 'tools/xkvm'
2402--- tools/xkvm 2016-05-10 16:13:29 +0000
2403+++ tools/xkvm 2016-07-12 16:34:37 +0000
2404@@ -77,14 +77,14 @@
2405
2406 NETDEV:
2407 Above, 'NETDEV' is a comma delimited string
2408- The first field must be
2409+ The first field must be
2410 * bridge name: (br0 or virbr0): attach a device to this bridge
2411 * literal 'user': use qemu user networking
2412-
2413+
2414 Additional fields are optional, and can be anything that is acceptable
2415 to kvm either as:
2416 * '-device virtio-net-pci' option (see 'kvm -device virtio-net-pci,?')
2417- * '-net [user|tap]' option
2418+ * '-net [user|tap]' option
2419
2420 Example:
2421 * xkvm --netdev br0,macaddr=:05 -- -drive file=disk.img,if=virtio -curses
2422@@ -345,7 +345,7 @@
2423 if=sd|if=mtd|floppy) fail "do not know what to do with $tok on $cur";;
2424 id=*) id=$val;;
2425 file=*) file=$val;;
2426- format=*) fmt=$val;;
2427+ fmt=*|format=*) fmt=$val;;
2428 serial=*) serial=$val;;
2429 bus=*) bus=$val;;
2430 unit=*) unit=$val;;
2431@@ -357,6 +357,8 @@
2432 out=$(LANG=C qemu-img info "$file") &&
2433 fmt=$(echo "$out" | awk '$0 ~ /^file format:/ { print $3 }') ||
2434 { error "failed to determine format of $file"; return 1; }
2435+ else
2436+ fmt=raw
2437 fi
2438 if [ -z "$driver" ]; then
2439 driver="$def_disk_driver"
2440@@ -379,7 +381,8 @@
2441 devopts="$driver,drive=$id${serial:+,serial=${serial}}"
2442 for tok in "$@"; do
2443 case "$tok" in
2444- id=*|if=*|driver=*|$file) continue;;
2445+ id=*|if=*|driver=*|$file|file=*) continue;;
2446+ fmt=*|format=*) continue;;
2447 serial=*|bus=*|unit=*|index=*) continue;;
2448 esac
2449 isdevopt "$driver" "$tok" && devopts="${devopts},$tok" ||
2450@@ -529,7 +532,7 @@
2451
2452 netargs=()
2453 for((i=0;i<${#device_args[@]};i++)); do
2454- netargs=( "${netargs[@]}" -device "${device_args[$i]}"
2455+ netargs=( "${netargs[@]}" -device "${device_args[$i]}"
2456 -netdev "${netdev_args[$i]}")
2457 done
2458

Subscribers

People subscribed via source and target branches

to all changes: