Merge ~racb/uvtool:python3-packaging into uvtool:ubuntu/devel

Proposed by Robie Basak
Status: Merged
Merged at revision: e358612386352367209ce3612d16ea6419ff1618
Proposed branch: ~racb/uvtool:python3-packaging
Merge into: uvtool:ubuntu/devel
Diff against target: 881 lines (+267/-125)
13 files modified
bin/uvt-kvm (+2/-2)
bin/uvt-simplestreams-libvirt (+2/-2)
debian/changelog (+26/-0)
debian/control (+13/-15)
debian/rules (+3/-3)
man/uvt-kvm.1 (+27/-3)
template.xml (+6/-1)
uvtool/libvirt/__init__.py (+2/-2)
uvtool/libvirt/kvm.py (+150/-62)
uvtool/libvirt/simplestreams.py (+16/-12)
uvtool/ssh.py (+6/-7)
uvtool/tests/test_kvm.py (+7/-8)
uvtool/tests/test_simplestreams.py (+7/-8)
Reviewer Review Type Date Requested Status
Christian Ehrhardt  Approve
Canonical Server Pending
Review via email: mp+374591@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

I didn't expect you to need an explicit --buildsystem=pybuild, but it is ok.
Content changes match what I have reviewed for the non packaging branch.

Packaging changes pull in quite some old changelogs that are correct, but not part of this merge.
But it matches what is in Eoan, so that should be fine as well.

Was that the base for the test PPA that we already tested or do you want testing again?

review: Approve
Revision history for this message
Robie Basak (racb) wrote :

On Wed, Oct 23, 2019 at 11:20:54AM -0000, Christian Ehrhardt  wrote:
> I didn't expect you to need an explicit --buildsystem=pybuild, but it is ok.

I am under the impression that --buildsystem=pybuild is necessary
currently because it is the recommended best practice but not the
debhelper default.

> Packaging changes pull in quite some old changelogs that are correct, but not part of this merge.
> But it matches what is in Eoan, so that should be fine as well.

I think that's a problem with the preview diff. I think the actual
commits are correct and don't include any unnecessary changes.

> Was that the base for the test PPA that we already tested or do you want testing again?

I updated ppa:racb/experimental2 for Eoan (only) with essentially the
same changes. The only differences are that this branch is rebased on to
my merge into master, rather than my development Python 3 branch, I
dropped uvtool-coverage, and the changelog is different.

If you'd like to see the changes, I have been tagging previous versions
of the packaging branch and pushing those tags - see
python3-packaging/v* in my repo.

I'm confident that it isn't necessary to test further, but if you'd like
to, then please do :)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/bin/uvt-kvm b/bin/uvt-kvm
2index c96c50d..282e866 100755
3--- a/bin/uvt-kvm
4+++ b/bin/uvt-kvm
5@@ -1,8 +1,8 @@
6-#!/usr/bin/python
7+#!/usr/bin/python3
8
9 # Wrapper around cloud-localds and libvirt
10
11-# Copyright (C) 2012-3 Canonical Ltd.
12+# Copyright (C) 2012-9 Canonical Ltd.
13 # Author: Robie Basak <robie.basak@canonical.com>
14 #
15 # This program is free software: you can redistribute it and/or modify
16diff --git a/bin/uvt-simplestreams-libvirt b/bin/uvt-simplestreams-libvirt
17index 7693b02..b220da2 100755
18--- a/bin/uvt-simplestreams-libvirt
19+++ b/bin/uvt-simplestreams-libvirt
20@@ -1,8 +1,8 @@
21-#!/usr/bin/python
22+#!/usr/bin/python3
23
24 # Keep Ubuntu Cloud images synced to a local libvirt storage pool.
25
26-# Copyright (C) 2013 Canonical Ltd.
27+# Copyright (C) 2013-9 Canonical Ltd.
28 # Author: Robie Basak <robie.basak@canonical.com>
29 #
30 # This program is free software: you can redistribute it and/or modify
31diff --git a/debian/changelog b/debian/changelog
32index af18485..3f77faa 100644
33--- a/debian/changelog
34+++ b/debian/changelog
35@@ -1,3 +1,29 @@
36+uvtool (0~git162-0ubuntu1) UNRELEASED; urgency=medium
37+
38+ * Port to Python 3.
39+
40+ -- Robie Basak <robie.basak@ubuntu.com> Wed, 23 Oct 2019 11:30:04 +0100
41+
42+uvtool (0~git148-0ubuntu1) eoan; urgency=medium
43+
44+ [ Christian Ehrhardt ]
45+ * kvm: add --machine-type option (LP: #1775645)
46+ * man: fix rendering page of option after --machine-type
47+ * man: describe implications and defaults of --machine-type
48+
49+ [ Robie Basak ]
50+ * Add --mac option (LP: #1781727). Thanks to adamretter.
51+
52+ [ Christian Ehrhardt ]
53+ * template (x86): switch the default video to QXL
54+ * template (x86): add spice graphics backend
55+
56+ [ Robie Basak ]
57+ * Add --network-config option (LP: #1781785). Thanks to adamretter.
58+ * Default to plain HTTP for cloud image downloads (LP: #1409400)
59+
60+ -- Robie Basak <robie.basak@ubuntu.com> Tue, 30 Apr 2019 16:55:01 +0100
61+
62 uvtool (0~git140-0ubuntu1) bionic; urgency=medium
63
64 [ Christian Ehrhardt ]
65diff --git a/debian/control b/debian/control
66index 9f509bf..8163583 100644
67--- a/debian/control
68+++ b/debian/control
69@@ -4,15 +4,13 @@ Priority: extra
70 Standards-Version: 3.9.4
71 Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
72 Build-Depends: debhelper (>= 7),
73- python-all,
74- python-setuptools,
75- python-mock,
76- python-libvirt,
77- python-lxml,
78- python-pyinotify,
79- python-simplestreams,
80- python-yaml
81-X-Python-Version: >= 2.7
82+ dh-python,
83+ python3,
84+ python3-libvirt,
85+ python3-lxml,
86+ python3-pyinotify,
87+ python3-simplestreams,
88+ python3-yaml
89
90 Package: uvtool
91 Architecture: all
92@@ -30,18 +28,18 @@ Package: uvtool-libvirt
93 Architecture: all
94 Depends: libvirt-daemon-system | libvirt-bin,
95 libvirt-clients | libvirt-bin,
96- python-libvirt,
97- python-simplestreams,
98- python-lxml,
99- python-pyinotify,
100- python-yaml,
101+ python3-libvirt,
102+ python3-simplestreams,
103+ python3-lxml,
104+ python3-pyinotify,
105+ python3-yaml,
106 distro-info,
107 cloud-image-utils (>= 0.27),
108 qemu-utils,
109 ubuntu-cloudimage-keyring,
110 socat,
111 ${misc:Depends},
112- ${python:Depends}
113+ ${python3:Depends}
114 Recommends: qemu-kvm, cpu-checker
115 Description: Library and tools for using Ubuntu Cloud Images with libvirt
116 This package provides libvirt-specific tools for consuming Ubuntu Cloud
117diff --git a/debian/rules b/debian/rules
118index 25dccc9..82c03eb 100755
119--- a/debian/rules
120+++ b/debian/rules
121@@ -1,13 +1,13 @@
122 #!/usr/bin/make -f
123
124 %:
125- dh $@ --with python2
126+ dh $@ --with python3 --buildsystem=pybuild
127
128 override_dh_auto_build:
129 $(MAKE) -C uvtool/tests/streams
130 dh_auto_build
131- PYTHONPATH=$(CURDIR) python -m unittest uvtool.tests.test_kvm
132- PYTHONPATH=$(CURDIR) python -m unittest uvtool.tests.test_simplestreams
133+ PYTHONPATH=$(CURDIR) python3 -m unittest uvtool.tests.test_kvm
134+ PYTHONPATH=$(CURDIR) python3 -m unittest uvtool.tests.test_simplestreams
135
136 override_dh_auto_clean:
137 $(MAKE) -C uvtool/tests/streams clean
138diff --git a/man/uvt-kvm.1 b/man/uvt-kvm.1
139index 7ad22e6..e626370 100644
140--- a/man/uvt-kvm.1
141+++ b/man/uvt-kvm.1
142@@ -50,9 +50,11 @@ wrapping libvirt and cloud-init.
143 is not intended to wrap all possible use cases. Where possible, it
144 provides access to some more advanced cases using options to override
145 entire sections of default operation, such as the ability to directly
146-override the backing volume image used, the libvirt domain definition
147-and cloud-init metadata and userdata. For yet more complex cases, it is
148-expected that the user will call libvirt directly (for example by using
149+override the backing volume image used, the libvirt domain definition,
150+cloud-init metadata and userdata, and directly provide a network-config
151+file.
152+For yet more complex cases, it is expected that the user will call
153+libvirt directly (for example by using
154 .BR virsh (1)),
155 and use uvt-kvm for only the simpler operations required on affected
156 VMs. See ADVANCED OVERRIDE OPTIONS and ADVANCED USAGE for details.
157@@ -364,6 +366,11 @@ Replace the first defined NIC with one that connects to the given host
158 bridge. Default: unaltered from the libvirt domain template.
159
160 .TP
161+.BI --mac\ mac
162+Use this MAC address for the first defined NIC. Can be used in
163+conjunction with --bridge. Default unspecified.
164+
165+.TP
166 .B --log-console-output
167 Log output to a disk file on the host instead of to a pty. With
168 libvirt's default configuration on Ubuntu, this log can be found in
169@@ -381,6 +388,17 @@ Instead of the default cpu model - which mostly is a compatibility focused
170 lowest denominator of cpu features - use host-passthrough which will try to
171 make all of the hosts cpu features available in the guest.
172
173+.TP
174+.BI --machine-type\ type
175+Set the machine type to the specified string before instantiating the guest.
176+See \fBkvm -M ?\fR for a list of types supported by your current system.
177+If not set this section of the template is not altered before the guest is
178+defined.
179+If set this modifies the internal temporary libvirt domain template at
180+the element \fBdomain->os->type\fR and sets the attribute \fBmachine\fR to the
181+given value before defining the guest. This implies that libvirt will add the
182+type-dependent default devices when defining the guest.
183+
184 .SH CLOUD-INIT CONFIGURATION OPTIONS
185
186 Valid for: \fBuvt-kvm\ create\fR only.
187@@ -487,6 +505,12 @@ not otherwise tunable.
188 Default: minimal file with automatically generated instance-id.
189
190 .TP
191+.BI --network-config\ network_config_file
192+Provide cloud-init network-config using the file supplied.
193+
194+Default: no network-config file.
195+
196+.TP
197 .BI --backing-image-file\ image_file
198 Specify the name of a local file that will be used to create the VM instead of
199 relying on the volume storage pool. It must point to a qcow2 formatted file.
200diff --git a/template.xml b/template.xml
201index 3c795c1..b76bcb8 100644
202--- a/template.xml
203+++ b/template.xml
204@@ -20,6 +20,11 @@
205 <graphics type='vnc' autoport='yes' listen='127.0.0.1'>
206 <listen type='address' address='127.0.0.1'/>
207 </graphics>
208- <video/>
209+ <graphics type='spice' autoport='yes' listen='127.0.0.1'>
210+ <listen type='address' address='127.0.0.1'/>
211+ </graphics>
212+ <video>
213+ <model type='qxl'/>
214+ </video>
215 </devices>
216 </domain>
217diff --git a/uvtool/libvirt/__init__.py b/uvtool/libvirt/__init__.py
218index 2cd950c..3988e24 100644
219--- a/uvtool/libvirt/__init__.py
220+++ b/uvtool/libvirt/__init__.py
221@@ -1,4 +1,4 @@
222-# Copyright (C) 2013 Canonical Ltd.
223+# Copyright (C) 2013-9 Canonical Ltd.
224 # Author: Robie Basak <robie.basak@canonical.com>
225 #
226 # This program is free software: you can redistribute it and/or modify
227@@ -91,7 +91,7 @@ def _create_volume_from_fobj_with_size(new_volume_name, fobj, fobj_size,
228 E.target(E.format(type=image_type)),
229 *extra
230 )
231- vol = pool.createXML(etree.tostring(new_vol), 0)
232+ vol = pool.createXML(etree.tostring(new_vol, encoding=str), 0)
233
234 try:
235 stream = conn.newStream(0)
236diff --git a/uvtool/libvirt/kvm.py b/uvtool/libvirt/kvm.py
237index 5e1c15e..c5b10d7 100755
238--- a/uvtool/libvirt/kvm.py
239+++ b/uvtool/libvirt/kvm.py
240@@ -1,8 +1,8 @@
241-#!/usr/bin/python
242+#!/usr/bin/python3
243
244 # Wrapper around cloud-localds and libvirt
245
246-# Copyright (C) 2012-3 Canonical Ltd.
247+# Copyright (C) 2012-9 Canonical Ltd.
248 # Author: Robie Basak <robie.basak@canonical.com>
249 #
250 # This program is free software: you can redistribute it and/or modify
251@@ -23,15 +23,16 @@ from __future__ import print_function
252 from __future__ import unicode_literals
253
254 import argparse
255+import base64
256 import errno
257 import functools
258+import io
259 import itertools
260 import os
261 import platform
262 import shutil
263 import signal
264 import string
265-import StringIO
266 import subprocess
267 import sys
268 import tempfile
269@@ -92,19 +93,19 @@ def subprocess_setup():
270 def run_script_once_arg_to_config(arg, unique_id):
271 with open(arg, 'rb') as f:
272 script = f.read()
273- encoded_script = script.encode('base64')
274+ encoded_script = base64.b64encode(script).decode('ascii')
275 return [
276- b'cloud-init-per',
277- b'once',
278- unique_id.encode('utf-8'),
279- b'sh', b'-c',
280+ 'cloud-init-per',
281+ 'once',
282+ unique_id,
283+ 'sh', '-c',
284 (
285- b'f=$(mktemp --tmpdir %s-XXXXXXXXXX) && ' +
286- b'echo "%s" | base64 -d > "$f" && ' +
287- b'chmod 700 "$f" && ' +
288- b'"$f" && ' +
289- b'rm "$f"'
290- ) % (unique_id.encode('utf-8'), encoded_script)
291+ 'f=$(mktemp --tmpdir %s-XXXXXXXXXX) && ' +
292+ 'echo "%s" | base64 -d > "$f" && ' +
293+ 'chmod 700 "$f" && ' +
294+ '"$f" && ' +
295+ 'rm "$f"'
296+ ) % (unique_id, encoded_script)
297 ]
298
299
300@@ -122,7 +123,7 @@ def get_ssh_agent_public_keys():
301 output = subprocess.check_output(
302 ['ssh-add', '-L'],
303 stderr=fpnull
304- )
305+ ).decode()
306 except subprocess.CalledProcessError:
307 return None
308
309@@ -140,7 +141,7 @@ def read_ssh_public_key_file(filename):
310 filename = os.path.join(os.environ['HOME'], '.ssh', 'id_rsa.pub')
311
312 try:
313- f = open(filename, 'rb')
314+ f = open(filename, 'r')
315 except IOError as e:
316 if e.errno != errno.ENOENT:
317 raise
318@@ -178,51 +179,58 @@ def create_default_user_data(fobj, args, ssh_host_keys=None):
319
320 """
321
322- ssh_authorized_keys = get_ssh_authorized_keys(args.ssh_public_key_file)
323+ ssh_authorized_keys = list(
324+ get_ssh_authorized_keys(args.ssh_public_key_file),
325+ )
326
327 data = {
328- b'hostname': args.hostname.encode('ascii'),
329- b'manage_etc_hosts': b'localhost',
330- b'ssh_keys': uvtool.ssh.generate_ssh_host_keys()[0],
331+ 'hostname': args.hostname,
332+ 'manage_etc_hosts': 'localhost',
333+ 'ssh_keys': uvtool.ssh.generate_ssh_host_keys()[0],
334 }
335
336 if ssh_host_keys:
337- data[b'ssh_keys'] = ssh_host_keys
338+ data['ssh_keys'] = ssh_host_keys
339
340 if ssh_authorized_keys:
341- data[b'ssh_authorized_keys'] = ssh_authorized_keys
342+ data['ssh_authorized_keys'] = ssh_authorized_keys
343
344 if args.password:
345- data[b'password'] = args.password.encode('utf-8')
346- data[b'chpasswd'] = {b'expire': False}
347- data[b'ssh_pwauth'] = True
348+ data['password'] = args.password
349+ data['chpasswd'] = {'expire': False}
350+ data['ssh_pwauth'] = True
351
352 if args.run_script_once:
353- data[b'runcmd'] = run_script_once_args_to_config(args.run_script_once)
354+ data['runcmd'] = run_script_once_args_to_config(args.run_script_once)
355
356 if args.packages:
357- data[b'packages'] = [
358- s.encode('ascii') # Debian Policy dictates a-z,0-9,+,-,.
359- for s in itertools.chain(*[p.split(',') for p in args.packages])
360- ]
361+ data['packages'] = list(
362+ itertools.chain(*[p.split(',') for p in args.packages])
363+ )
364
365- fobj.write("#cloud-config\n")
366- fobj.write(yaml.dump(data))
367+ fobj.write(b"#cloud-config\n")
368+ fobj.write(yaml.dump(data).encode())
369
370
371 def create_default_meta_data(fobj, args):
372 data = {
373- b'instance-id': str(uuid.uuid1()).encode('ascii'),
374+ 'instance-id': str(uuid.uuid1()),
375 }
376- fobj.write(yaml.dump(data))
377+ fobj.write(yaml.dump(data).encode())
378
379
380-def create_ds_image(temp_dir, hostname, user_data_fobj, meta_data_fobj):
381+def create_ds_image(
382+ temp_dir,
383+ hostname,
384+ user_data_fobj,
385+ meta_data_fobj,
386+ network_config_fobj=None
387+ ):
388 """Create a file called ds.img inside temp_dir that contains a useful
389 cloud-init data source.
390
391 Other temporary files created in temp_dir are currently metadata and
392- userdata and can be safely deleted.
393+ userdata and network-config and can be safely deleted.
394
395 """
396
397@@ -230,17 +238,35 @@ def create_ds_image(temp_dir, hostname, user_data_fobj, meta_data_fobj):
398 f.write(user_data_fobj.read())
399 with open(os.path.join(temp_dir, 'metadata'), 'wb') as f:
400 f.write(meta_data_fobj.read())
401-
402- subprocess.check_call(
403- ['cloud-localds', '--disk-format=qcow2', 'ds.img', 'userdata', 'metadata'], cwd=temp_dir)
404-
405-
406-def create_ds_volume(new_volume_name, hostname, user_data_fobj, meta_data_fobj):
407+ if network_config_fobj:
408+ with open(os.path.join(temp_dir, 'network-config'), 'wb') as f:
409+ f.write(network_config_fobj.read())
410+
411+ args = ['cloud-localds', '--disk-format=qcow2']
412+ if network_config_fobj:
413+ args.extend(['--network-config', 'network-config'])
414+ args.extend(['ds.img', 'userdata', 'metadata'])
415+ subprocess.check_call(args, cwd=temp_dir)
416+
417+
418+def create_ds_volume(
419+ new_volume_name,
420+ hostname,
421+ user_data_fobj,
422+ meta_data_fobj,
423+ network_config_fobj=None
424+ ):
425 """Create a new libvirt cloud-init datasource volume."""
426
427 temp_dir = tempfile.mkdtemp(prefix='uvt-kvm-')
428 try:
429- create_ds_image(temp_dir, hostname, user_data_fobj, meta_data_fobj)
430+ create_ds_image(
431+ temp_dir=temp_dir,
432+ hostname=hostname,
433+ user_data_fobj=user_data_fobj,
434+ meta_data_fobj=meta_data_fobj,
435+ network_config_fobj=network_config_fobj,
436+ )
437 with open(os.path.join(temp_dir, 'ds.img'), 'rb') as f:
438 return uvtool.libvirt.create_volume_from_fobj(
439 new_volume_name, f, image_type='qcow2', pool_name=POOL_NAME)
440@@ -301,12 +327,12 @@ def create_cow_volume_by_path(backing_volume_path, new_volume_name,
441 E.format(type='qcow2'),
442 )
443 )
444- return pool.createXML(etree.tostring(new_vol), 0)
445+ return pool.createXML(etree.tostring(new_vol, encoding=str), 0)
446
447
448 def compose_domain_xml(name, volumes, template_path, cpu=1, memory=512,
449 unsafe_caching=False, log_console_output=False, host_passthrough=False,
450- bridge=None, ssh_known_hosts=None):
451+ machine_type=None, bridge=None, mac=None, ssh_known_hosts=None):
452 tree = etree.parse(template_path)
453 domain = tree.getroot()
454 assert domain.tag == 'domain'
455@@ -351,11 +377,28 @@ def compose_domain_xml(name, volumes, template_path, cpu=1, memory=512,
456
457 if bridge:
458 etree.strip_elements(devices, 'interface')
459- devices.append(E.interface(
460- E.source(bridge=bridge),
461- E.model(type='virtio'),
462- type='bridge'),
463- )
464+ if mac:
465+ devices.append(E.interface(
466+ E.mac(address=mac),
467+ E.source(bridge=bridge),
468+ E.model(type='virtio'),
469+ type='bridge'),
470+ )
471+ else:
472+ devices.append(E.interface(
473+ E.source(bridge=bridge),
474+ E.model(type='virtio'),
475+ type='bridge'),
476+ )
477+ else:
478+ if mac:
479+ etree.strip_elements(devices, 'interface')
480+ devices.append(E.interface(
481+ E.mac(address=mac),
482+ E.source(network='default'),
483+ E.model(type='virtio'),
484+ type='network'),
485+ )
486
487 if log_console_output:
488 if ARCH == 's390x':
489@@ -378,6 +421,11 @@ def compose_domain_xml(name, volumes, template_path, cpu=1, memory=512,
490 else:
491 etree.SubElement(domain, 'cpu', mode='host-passthrough')
492
493+ if machine_type:
494+ domos = domain.find('os')
495+ domtype = domos.find('type')
496+ domtype.set('machine', machine_type)
497+
498 if ssh_known_hosts:
499 metadata = domain.find('metadata')
500 if metadata is None:
501@@ -389,7 +437,7 @@ def compose_domain_xml(name, volumes, template_path, cpu=1, memory=512,
502 )
503 metadata.append(EX.ssh_known_hosts(ssh_known_hosts))
504
505- return etree.tostring(tree)
506+ return etree.tostring(tree, encoding=str)
507
508
509 def get_base_image(filters):
510@@ -403,11 +451,27 @@ def get_base_image(filters):
511 return result[0]
512
513
514-def create(hostname, filters, user_data_fobj, meta_data_fobj, template_path,
515- memory=512, cpu=1, disk=2, unsafe_caching=False,
516- log_console_output=False, host_passthrough=False, bridge=None,
517- backing_image_file=None, start=True, ssh_known_hosts=None,
518- ephemeral_disks=None):
519+def create(
520+ hostname,
521+ filters,
522+ user_data_fobj,
523+ meta_data_fobj,
524+ network_config_fobj,
525+ template_path,
526+ memory=512,
527+ cpu=1,
528+ disk=2,
529+ unsafe_caching=False,
530+ log_console_output=False,
531+ host_passthrough=False,
532+ bridge=None,
533+ mac=None,
534+ backing_image_file=None,
535+ start=True,
536+ ssh_known_hosts=None,
537+ ephemeral_disks=None,
538+ machine_type=None
539+ ):
540 if backing_image_file is None:
541 base_volume_name = get_base_image(filters)
542 if ephemeral_disks is None:
543@@ -430,7 +494,12 @@ def create(hostname, filters, user_data_fobj, meta_data_fobj, template_path,
544 undo_volume_creation.append(main_vol)
545
546 ds_vol = create_ds_volume(
547- "%s-ds.qcow" % hostname, hostname, user_data_fobj, meta_data_fobj)
548+ "%s-ds.qcow" % hostname,
549+ hostname,
550+ user_data_fobj,
551+ meta_data_fobj,
552+ network_config_fobj,
553+ )
554 undo_volume_creation.append(ds_vol)
555
556 volumes = [main_vol, ds_vol]
557@@ -443,6 +512,7 @@ def create(hostname, filters, user_data_fobj, meta_data_fobj, template_path,
558 xml = compose_domain_xml(
559 hostname, volumes=volumes,
560 bridge=bridge,
561+ mac=mac,
562 cpu=cpu,
563 log_console_output=log_console_output,
564 host_passthrough=host_passthrough,
565@@ -450,6 +520,7 @@ def create(hostname, filters, user_data_fobj, meta_data_fobj, template_path,
566 template_path=template_path,
567 unsafe_caching=unsafe_caching,
568 ssh_known_hosts=ssh_known_hosts,
569+ machine_type=machine_type,
570 )
571 conn = libvirt.open('qemu:///system')
572 domain = conn.defineXML(xml)
573@@ -510,7 +581,7 @@ def destroy(hostname):
574
575 def get_lts_series():
576 output = subprocess.check_output(['distro-info', '--lts'], close_fds=True)
577- return output.strip()
578+ return output.strip().decode()
579
580
581 def apply_default_fobj(args, key, create_default_data_fn):
582@@ -528,7 +599,7 @@ def apply_default_fobj(args, key, create_default_data_fn):
583 if specified_fobj:
584 return specified_fobj
585 else:
586- default_fobj = StringIO.StringIO()
587+ default_fobj = io.BytesIO()
588 create_default_data_fn(default_fobj, args)
589 default_fobj.seek(0)
590 return default_fobj
591@@ -583,6 +654,7 @@ def ssh(name, login_name, arguments, stdin=None, checked=False, sysexit=True,
592 )
593 if ssh_known_hosts:
594 ssh_known_hosts_file = tempfile.NamedTemporaryFile(
595+ mode='w',
596 prefix='uvt-kvm.known_hoststmp')
597 objects_to_close.append(ssh_known_hosts_file)
598 ssh_known_hosts_file.write(ssh_known_hosts)
599@@ -662,9 +734,14 @@ def main_create(parser, args):
600 else:
601 abs_image_backing_file = None
602 create(
603- args.hostname, args.filters, user_data_fobj, meta_data_fobj,
604+ hostname=args.hostname,
605+ filters=args.filters,
606+ user_data_fobj=user_data_fobj,
607+ meta_data_fobj=meta_data_fobj,
608+ network_config_fobj=args.network_config,
609 backing_image_file=abs_image_backing_file,
610 bridge=args.bridge,
611+ mac=args.mac,
612 cpu=args.cpu,
613 disk=args.disk,
614 log_console_output=args.log_console_output,
615@@ -675,6 +752,7 @@ def main_create(parser, args):
616 start=not args.no_start,
617 ssh_known_hosts=ssh_known_hosts,
618 ephemeral_disks=args.ephemeral_disks,
619+ machine_type=args.machine_type,
620 )
621
622
623@@ -789,10 +867,16 @@ class DeveloperOptionAction(argparse.Action):
624 def main(args):
625 # Workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1063766
626 # (LP: #1228231)
627- libvirt.registerErrorHandler(lambda _: None, None)
628+ def noop(*args, **kwargs): pass
629+ libvirt.registerErrorHandler(noop, None)
630
631 parser = argparse.ArgumentParser()
632- subparsers = parser.add_subparsers()
633+ # Workarounds needed here: set dest and required
634+ # https://bugs.python.org/issue9253#msg181855
635+ # https://stackoverflow.com/q/22990977/478206
636+ # https://stackoverflow.com/a/23354355/478206
637+ subparsers = parser.add_subparsers(dest='subcommand')
638+ subparsers.required = True
639 create_subparser = subparsers.add_parser('create')
640 create_subparser.set_defaults(func=main_create)
641 create_subparser.add_argument(
642@@ -805,16 +889,20 @@ def main(args):
643 '--ephemeral-disk', action='append', type=int, dest='ephemeral_disks',
644 help='Add an empty disk of SIZE in GB', metavar='SIZE')
645 create_subparser.add_argument('--bridge')
646+ create_subparser.add_argument('--mac')
647 create_subparser.add_argument('--unsafe-caching', action='store_true')
648 create_subparser.add_argument(
649 '--user-data', type=argparse.FileType('rb'))
650 create_subparser.add_argument(
651 '--meta-data', type=argparse.FileType('rb'))
652+ create_subparser.add_argument(
653+ '--network-config', type=argparse.FileType('rb'))
654 create_subparser.add_argument('--password')
655 create_subparser.add_argument('--guest-arch',
656 help='guest arch to select template, default is the host architecture')
657 create_subparser.add_argument('--log-console-output', action='store_true')
658 create_subparser.add_argument('--host-passthrough', action='store_true')
659+ create_subparser.add_argument('--machine-type')
660 create_subparser.add_argument('--backing-image-file')
661 create_subparser.add_argument('--run-script-once', action='append')
662 create_subparser.add_argument('--ssh-public-key-file')
663diff --git a/uvtool/libvirt/simplestreams.py b/uvtool/libvirt/simplestreams.py
664index 93fd3fb..d4cb8bc 100755
665--- a/uvtool/libvirt/simplestreams.py
666+++ b/uvtool/libvirt/simplestreams.py
667@@ -1,8 +1,8 @@
668-#!/usr/bin/python
669+#!/usr/bin/python3
670
671 # Keep Ubuntu Cloud images synced to a local libvirt storage pool.
672
673-# Copyright (C) 2013 Canonical Ltd.
674+# Copyright (C) 2013-9 Canonical Ltd.
675 # Author: Robie Basak <robie.basak@canonical.com>
676 #
677 # This program is free software: you can redistribute it and/or modify
678@@ -107,7 +107,7 @@ BASE64_PREFIX = 'x-uvt-b64-'
679 def _encode_libvirt_pool_name(product_name, version_name):
680 return BASE64_PREFIX + base64.b64encode(
681 (' '.join([product_name, version_name])).encode(), b'-_'
682- )
683+ ).decode()
684
685
686 def _decode_libvirt_pool_name(encoded_pool_name):
687@@ -119,7 +119,7 @@ def _decode_libvirt_pool_name(encoded_pool_name):
688 return base64.b64decode(
689 encoded_pool_name[len(BASE64_PREFIX):],
690 altchars=b'-_'
691- ).split(None, 1)
692+ ).decode().split(None, 1)
693
694
695 def purge_pool(conn=None):
696@@ -164,16 +164,14 @@ def _load_products(path=None, content_id=None, clean=False):
697 def new_product():
698 return {'versions': {}}
699 products = collections.defaultdict(new_product)
700- for encoded_libvirt_name_string, metadata in pool_metadata.items():
701- encoded_libvirt_name_bytes = encoded_libvirt_name_string.encode(
702- 'utf-8')
703+ for encoded_libvirt_name_string, metadata in list(pool_metadata.items()):
704 if not uvtool.libvirt.have_volume_by_name(
705- encoded_libvirt_name_bytes, pool_name=LIBVIRT_POOL_NAME):
706+ encoded_libvirt_name_string, pool_name=LIBVIRT_POOL_NAME):
707 if clean:
708 del pool_metadata[encoded_libvirt_name_string]
709 continue
710 product, version = _decode_libvirt_pool_name(
711- encoded_libvirt_name_bytes)
712+ encoded_libvirt_name_string)
713 assert(product == metadata['product_name'])
714 assert(version == metadata['version_name'])
715 products[product]['versions'][version] = {
716@@ -292,13 +290,19 @@ def main_purge(args):
717 def main(argv=None):
718 # Workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1063766
719 # (LP: #1228231)
720- libvirt.registerErrorHandler(lambda _: None, None)
721+ def noop(*args, **kwargs): pass
722+ libvirt.registerErrorHandler(noop, None)
723
724 system_arch = subprocess.check_output(
725 ['dpkg', '--print-architecture']).decode().strip()
726 parser = argparse.ArgumentParser()
727 parser.add_argument('--verbose', '-v', action='store_true')
728- subparsers = parser.add_subparsers()
729+ # Workarounds needed here: set dest and required
730+ # https://bugs.python.org/issue9253#msg181855
731+ # https://stackoverflow.com/q/22990977/478206
732+ # https://stackoverflow.com/a/23354355/478206
733+ subparsers = parser.add_subparsers(dest='subcommand')
734+ subparsers.required = True
735
736 sync_subparser = subparsers.add_parser('sync')
737 sync_subparser.set_defaults(func=main_sync)
738@@ -312,7 +316,7 @@ def main(argv=None):
739 default='/usr/share/keyrings/ubuntu-cloudimage-keyring.gpg'
740 )
741 sync_subparser.add_argument('--source', dest='mirror_url',
742- default='https://cloud-images.ubuntu.com/releases/')
743+ default='http://cloud-images.ubuntu.com/releases/')
744 sync_subparser.add_argument('--no-authentication', action='store_true')
745 sync_subparser.add_argument('filters', nargs='*', metavar='filter',
746 default=["arch=%s" % system_arch])
747diff --git a/uvtool/ssh.py b/uvtool/ssh.py
748index bb6b6d4..0516603 100644
749--- a/uvtool/ssh.py
750+++ b/uvtool/ssh.py
751@@ -1,6 +1,6 @@
752-#!/usr/bin/python
753+#!/usr/bin/python3
754
755-# Copyright (C) 2014 Canonical Ltd.
756+# Copyright (C) 2014-9 Canonical Ltd.
757 # Author: Robie Basak <robie.basak@canonical.com>
758 #
759 # This program is free software: you can redistribute it and/or modify
760@@ -36,7 +36,7 @@ def _keygen(key_type, private_path):
761
762
763 def read_file(path):
764- with open(path, 'rb') as f:
765+ with open(path, 'r') as f:
766 return f.read()
767
768
769@@ -52,9 +52,8 @@ def generate_ssh_host_keys():
770 # ssh-keygen(1) defines that ".pub" is appended
771 public_path = private_path + ".pub"
772
773- key_type_utf8 = key_type.encode('utf-8')
774- private_ci_key = key_type_utf8 + b'_private'
775- public_ci_key = key_type_utf8 + b'_public'
776+ private_ci_key = key_type + '_private'
777+ public_ci_key = key_type + '_public'
778
779 private_key = read_file(private_path)
780 public_key = read_file(public_path)
781@@ -66,4 +65,4 @@ def generate_ssh_host_keys():
782 finally:
783 shutil.rmtree(tmp_dir)
784
785- return cloud_init_result, b''.join(known_hosts_result)
786+ return cloud_init_result, ''.join(known_hosts_result)
787diff --git a/uvtool/tests/test_kvm.py b/uvtool/tests/test_kvm.py
788index 7a96ebf..13d7da6 100644
789--- a/uvtool/tests/test_kvm.py
790+++ b/uvtool/tests/test_kvm.py
791@@ -1,4 +1,4 @@
792-# Copyright (C) 2014 Canonical Ltd.
793+# Copyright (C) 2014-9 Canonical Ltd.
794 # Author: Robie Basak <robie.basak@canonical.com>
795 #
796 # This program is free software: you can redistribute it and/or modify
797@@ -15,8 +15,7 @@
798 # along with this program. If not, see <http://www.gnu.org/licenses/>.
799
800 import unittest
801-
802-import mock
803+import unittest.mock
804
805 from uvtool.libvirt.kvm import main_ssh
806
807@@ -25,18 +24,18 @@ class TestKVM(unittest.TestCase):
808 def check_ssh(self, args_hostname, args_login_name, expected_hostname,
809 expected_login_name):
810
811- parser = mock.Mock()
812- args = mock.Mock()
813+ parser = unittest.mock.Mock()
814+ args = unittest.mock.Mock()
815 args.login_name = args_login_name
816 args.name = args_hostname
817- args.ssh_arguments = mock.sentinel.ssh_arguments
818+ args.ssh_arguments = unittest.mock.sentinel.ssh_arguments
819 args.insecure = True
820- with mock.patch('uvtool.libvirt.kvm.ssh') as ssh_mock:
821+ with unittest.mock.patch('uvtool.libvirt.kvm.ssh') as ssh_mock:
822 main_ssh(parser, args)
823 ssh_mock.assert_called_with(
824 expected_hostname,
825 expected_login_name,
826- mock.sentinel.ssh_arguments,
827+ unittest.mock.sentinel.ssh_arguments,
828 insecure=True,
829 )
830
831diff --git a/uvtool/tests/test_simplestreams.py b/uvtool/tests/test_simplestreams.py
832index 211454d..883391e 100644
833--- a/uvtool/tests/test_simplestreams.py
834+++ b/uvtool/tests/test_simplestreams.py
835@@ -1,4 +1,4 @@
836-# Copyright (C) 2014 Canonical Ltd.
837+# Copyright (C) 2014-9 Canonical Ltd.
838 # Author: Robie Basak <robie.basak@canonical.com>
839 #
840 # This program is free software: you can redistribute it and/or modify
841@@ -15,8 +15,7 @@
842 # along with this program. If not, see <http://www.gnu.org/licenses/>.
843
844 import unittest
845-
846-import mock
847+import unittest.mock
848
849 import uvtool.libvirt.simplestreams as simplestreams
850
851@@ -28,7 +27,7 @@ import uvtool.libvirt.simplestreams as simplestreams
852 # NormalizedVersion as described in PEP-0386. As this is a temporary hack
853 # anyway, this will do for now, since we know that the mock version on Ubuntu
854 # Precise will never change.
855-ON_PRECISE = mock.__version__ == '0.7.2'
856+ON_PRECISE = unittest.mock.__version__ == '0.7.2'
857
858 FAKE_VOLUME_PRODUCT_NAME = 'com.ubuntu.cloud:server:12.04:amd64'
859 FAKE_VOLUME_VERSION_0 = '20131119'
860@@ -39,9 +38,9 @@ ENCODED_FAKE_VOLUME_PRODUCT_NAME_1 = simplestreams._encode_libvirt_pool_name(
861 FAKE_VOLUME_PRODUCT_NAME, FAKE_VOLUME_VERSION_1)
862
863 @unittest.skipIf(ON_PRECISE, 'mock version is too old')
864-@mock.patch('uvtool.libvirt.simplestreams.uvtool.libvirt')
865-@mock.patch('uvtool.libvirt.simplestreams.pool_metadata', new={})
866-@mock.patch('uvtool.libvirt.simplestreams.libvirt')
867+@unittest.mock.patch('uvtool.libvirt.simplestreams.uvtool.libvirt')
868+@unittest.mock.patch('uvtool.libvirt.simplestreams.pool_metadata', new={})
869+@unittest.mock.patch('uvtool.libvirt.simplestreams.libvirt')
870 class TestSimpleStreams(unittest.TestCase):
871 def testSync(self, libvirt, uvtool_libvirt):
872 uvtool_libvirt.have_volume_by_name.return_value = False
873@@ -61,7 +60,7 @@ class TestSimpleStreams(unittest.TestCase):
874 # least once by uvtool.libvirt.simplestreams directly. This is more of
875 # an assertion about the test being correct than part of the test
876 # itself.
877- libvirt.assert_has_calls([mock.call.open(u'qemu:///system')])
878+ libvirt.assert_has_calls([unittest.mock.call.open(u'qemu:///system')])
879
880 # create_volume_from_fobj should have been called exactly once to
881 # create the volume with the name that we expect

Subscribers

People subscribed via source and target branches