Merge ~racb/uvtool:python3-packaging into uvtool:ubuntu/devel
- Git
- lp:~racb/uvtool
- python3-packaging
- Merge into ubuntu/devel
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) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Christian Ehrhardt | Approve | ||
Canonical Server | Pending | ||
Review via email: mp+374591@code.launchpad.net |
Commit message
Description of the change
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=
I am under the impression that --buildsystem=
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/
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-
I'm confident that it isn't necessary to test further, but if you'd like
to, then please do :)
Preview Diff
1 | diff --git a/bin/uvt-kvm b/bin/uvt-kvm |
2 | index 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 |
16 | diff --git a/bin/uvt-simplestreams-libvirt b/bin/uvt-simplestreams-libvirt |
17 | index 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 |
31 | diff --git a/debian/changelog b/debian/changelog |
32 | index 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 ] |
65 | diff --git a/debian/control b/debian/control |
66 | index 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 |
117 | diff --git a/debian/rules b/debian/rules |
118 | index 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 |
138 | diff --git a/man/uvt-kvm.1 b/man/uvt-kvm.1 |
139 | index 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. |
200 | diff --git a/template.xml b/template.xml |
201 | index 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> |
217 | diff --git a/uvtool/libvirt/__init__.py b/uvtool/libvirt/__init__.py |
218 | index 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) |
236 | diff --git a/uvtool/libvirt/kvm.py b/uvtool/libvirt/kvm.py |
237 | index 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') |
663 | diff --git a/uvtool/libvirt/simplestreams.py b/uvtool/libvirt/simplestreams.py |
664 | index 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]) |
747 | diff --git a/uvtool/ssh.py b/uvtool/ssh.py |
748 | index 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) |
787 | diff --git a/uvtool/tests/test_kvm.py b/uvtool/tests/test_kvm.py |
788 | index 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 | |
831 | diff --git a/uvtool/tests/test_simplestreams.py b/uvtool/tests/test_simplestreams.py |
832 | index 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 |
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?