Merge lp:~heber013/utah/adding-setup-for-autopkgtest into lp:utah
- adding-setup-for-autopkgtest
- Merge into dev
Proposed by
Heber Parrucci
Status: | Merged |
---|---|
Approved by: | Jean-Baptiste Lallement |
Approved revision: | 1108 |
Merged at revision: | 1108 |
Proposed branch: | lp:~heber013/utah/adding-setup-for-autopkgtest |
Merge into: | lp:utah |
Diff against target: |
454 lines (+288/-4) 10 files modified
examples/run_utah_tests.py (+1/-0) setup.py (+3/-0) templates/casper-preseed-script.jinja2 (+1/-0) templates/utah-latecommand.jinja2 (+11/-0) utah/config.py (+6/-0) utah/parser.py (+2/-0) utah/provisioning/provisioning.py (+33/-4) utah/provisioning/qemu-scripts/qemu-setup.py (+211/-0) utah/provisioning/qemu-scripts/qemu-setup.service (+9/-0) utah/provisioning/vm.py (+11/-0) |
To merge this branch: | bzr merge lp:~heber013/utah/adding-setup-for-autopkgtest |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jean-Baptiste Lallement | Approve | ||
Review via email: mp+326954@code.launchpad.net |
Commit message
* Creating a serial console on ttyS1 for autopkgtest to use it.
* Adding option to poweroff VM after tests have run.
Description of the change
Creating a serial console on ttyS1 for autopkgtest to use it.
Adding option to poweroff VM after tests have run.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'examples/run_utah_tests.py' | |||
2 | --- examples/run_utah_tests.py 2013-06-20 00:06:02 +0000 | |||
3 | +++ examples/run_utah_tests.py 2017-07-06 18:08:08 +0000 | |||
4 | @@ -54,6 +54,7 @@ | |||
5 | 54 | if image is None: | 54 | if image is None: |
6 | 55 | image = ISO(arch=args.arch, installtype=args.type, series=args.series) | 55 | image = ISO(arch=args.arch, installtype=args.type, series=args.series) |
7 | 56 | kw = {'clean': (not args.no_destroy), | 56 | kw = {'clean': (not args.no_destroy), |
8 | 57 | 'poweroff': args.poweroff, | ||
9 | 57 | 'boot': args.boot, | 58 | 'boot': args.boot, |
10 | 58 | 'debug': args.debug, | 59 | 'debug': args.debug, |
11 | 59 | 'image': image, | 60 | 'image': image, |
12 | 60 | 61 | ||
13 | === modified file 'setup.py' | |||
14 | --- setup.py 2013-06-19 20:30:45 +0000 | |||
15 | +++ setup.py 2017-07-06 18:08:08 +0000 | |||
16 | @@ -52,6 +52,9 @@ | |||
17 | 52 | 'utah.client.probe', | 52 | 'utah.client.probe', |
18 | 53 | 'utah.provisioning', | 53 | 'utah.provisioning', |
19 | 54 | 'utah.provisioning.baremetal'], | 54 | 'utah.provisioning.baremetal'], |
20 | 55 | package_data={ | ||
21 | 56 | 'utah': ['provisioning/qemu-scripts/*'] | ||
22 | 57 | }, | ||
23 | 55 | maintainer=maintainer, | 58 | maintainer=maintainer, |
24 | 56 | maintainer_email=maintainer_email, | 59 | maintainer_email=maintainer_email, |
25 | 57 | ) | 60 | ) |
26 | 58 | 61 | ||
27 | === modified file 'templates/casper-preseed-script.jinja2' | |||
28 | --- templates/casper-preseed-script.jinja2 2013-05-22 16:56:32 +0000 | |||
29 | +++ templates/casper-preseed-script.jinja2 2017-07-06 18:08:08 +0000 | |||
30 | @@ -6,3 +6,4 @@ | |||
31 | 6 | cp utah-copy-files.sh /root/ | 6 | cp utah-copy-files.sh /root/ |
32 | 7 | cp -r utah-copy-files /root/ | 7 | cp -r utah-copy-files /root/ |
33 | 8 | cp -r utah-autorun /root/ | 8 | cp -r utah-autorun /root/ |
34 | 9 | cp qemu* /root | ||
35 | 9 | 10 | ||
36 | === modified file 'templates/utah-latecommand.jinja2' | |||
37 | --- templates/utah-latecommand.jinja2 2016-06-10 17:40:15 +0000 | |||
38 | +++ templates/utah-latecommand.jinja2 2017-07-06 18:08:08 +0000 | |||
39 | @@ -98,9 +98,20 @@ | |||
40 | 98 | fi | 98 | fi |
41 | 99 | } | 99 | } |
42 | 100 | 100 | ||
43 | 101 | qemu_setup() { | ||
44 | 102 | if [ -f qemu-setup.py ] ; then | ||
45 | 103 | cp qemu-setup.py /target/usr/local/bin/qemu-setup.py | ||
46 | 104 | chmod 755 /target/usr/local/bin/qemu-setup.py | ||
47 | 105 | cp qemu-setup.service /target/lib/systemd/system/qemu-setup.service | ||
48 | 106 | chmod 644 /target/lib/systemd/system/qemu-setup.service | ||
49 | 107 | chroot /target /bin/systemctl enable qemu-setup.service | ||
50 | 108 | fi | ||
51 | 109 | } | ||
52 | 110 | |||
53 | 101 | copy_ssh_keys | 111 | copy_ssh_keys |
54 | 102 | write_utah_data | 112 | write_utah_data |
55 | 103 | autologin | 113 | autologin |
56 | 104 | autorun | 114 | autorun |
57 | 105 | copy_provisioned_files | 115 | copy_provisioned_files |
58 | 106 | copy_rsyslog_cfg # do this last to avoid getting each syslog msg 2 times | 116 | copy_rsyslog_cfg # do this last to avoid getting each syslog msg 2 times |
59 | 117 | qemu_setup | ||
60 | 107 | 118 | ||
61 | === modified file 'utah/config.py' | |||
62 | --- utah/config.py 2017-06-06 20:18:58 +0000 | |||
63 | +++ utah/config.py 2017-07-06 18:08:08 +0000 | |||
64 | @@ -81,6 +81,12 @@ | |||
65 | 81 | 'type': 'boolean', | 81 | 'type': 'boolean', |
66 | 82 | 'default': True, | 82 | 'default': True, |
67 | 83 | }, | 83 | }, |
68 | 84 | 'poweroff': { | ||
69 | 85 | 'description': ( | ||
70 | 86 | 'Setting for whether to power off VM after a test run'), | ||
71 | 87 | 'type': 'boolean', | ||
72 | 88 | 'default': False, | ||
73 | 89 | }, | ||
74 | 84 | 'client_install_timeout': { | 90 | 'client_install_timeout': { |
75 | 85 | 'description': ( | 91 | 'description': ( |
76 | 86 | 'Maximum amount of time to wait ' | 92 | 'Maximum amount of time to wait ' |
77 | 87 | 93 | ||
78 | === modified file 'utah/parser.py' | |||
79 | --- utah/parser.py 2013-05-28 11:58:47 +0000 | |||
80 | +++ utah/parser.py 2017-07-06 18:08:08 +0000 | |||
81 | @@ -110,6 +110,8 @@ | |||
82 | 110 | '(%(choices)s) (Default is %(default)s)') | 110 | '(%(choices)s) (Default is %(default)s)') |
83 | 111 | parser.add_argument('-n', '--no-destroy', action='store_true', | 111 | parser.add_argument('-n', '--no-destroy', action='store_true', |
84 | 112 | help='Preserve VM after tests have run') | 112 | help='Preserve VM after tests have run') |
85 | 113 | parser.add_argument('--poweroff', action='store_true', | ||
86 | 114 | help='Power off VM after tests have run') | ||
87 | 113 | parser.add_argument('-d', '--debug', action='store_true', | 115 | parser.add_argument('-d', '--debug', action='store_true', |
88 | 114 | help='Enable debug logging') | 116 | help='Enable debug logging') |
89 | 115 | parser.add_argument('-j', '--json', action='store_true', | 117 | parser.add_argument('-j', '--json', action='store_true', |
90 | 116 | 118 | ||
91 | === modified file 'utah/provisioning/provisioning.py' | |||
92 | --- utah/provisioning/provisioning.py 2017-03-03 14:35:49 +0000 | |||
93 | +++ utah/provisioning/provisioning.py 2017-07-06 18:08:08 +0000 | |||
94 | @@ -24,6 +24,7 @@ | |||
95 | 24 | import pipes | 24 | import pipes |
96 | 25 | import re | 25 | import re |
97 | 26 | import shutil | 26 | import shutil |
98 | 27 | import stat | ||
99 | 27 | import sys | 28 | import sys |
100 | 28 | import urllib | 29 | import urllib |
101 | 29 | import uuid | 30 | import uuid |
102 | @@ -63,8 +64,8 @@ | |||
103 | 63 | 64 | ||
104 | 64 | """ | 65 | """ |
105 | 65 | 66 | ||
108 | 66 | def __init__(self, boot=config.boot, clean=True, debug=False, | 67 | def __init__(self, boot=config.boot, clean=True, poweroff=False, |
109 | 67 | image=None, initrd=config.initrd, inventory=None, | 68 | debug=False, image=None, initrd=config.initrd, inventory=None, |
110 | 68 | kernel=config.kernel, machineid=config.machineid, | 69 | kernel=config.kernel, machineid=config.machineid, |
111 | 69 | machineuuid=config.machineuuid, name=config.name, new=False, | 70 | machineuuid=config.machineuuid, name=config.name, new=False, |
112 | 70 | preseed=config.preseed, rewrite=config.rewrite, | 71 | preseed=config.preseed, rewrite=config.rewrite, |
113 | @@ -110,6 +111,7 @@ | |||
114 | 110 | # TODO: Consider a global temp file creator, maybe as part of install. | 111 | # TODO: Consider a global temp file creator, maybe as part of install. |
115 | 111 | self.boot = boot | 112 | self.boot = boot |
116 | 112 | self.clean = clean | 113 | self.clean = clean |
117 | 114 | self.poweroff = poweroff | ||
118 | 113 | self.debug = debug | 115 | self.debug = debug |
119 | 114 | self.image = image | 116 | self.image = image |
120 | 115 | self.inventory = inventory | 117 | self.inventory = inventory |
121 | @@ -522,11 +524,13 @@ | |||
122 | 522 | if self.clean: | 524 | if self.clean: |
123 | 523 | cleanup.add_path(path) | 525 | cleanup.add_path(path) |
124 | 524 | 526 | ||
126 | 525 | def cleanfunction(self, function, *args, **kw): | 527 | def cleanfunction(self, function, force=False, *args, **kw): |
127 | 526 | """Register a function to be run on cleanup. | 528 | """Register a function to be run on cleanup. |
128 | 527 | 529 | ||
129 | 528 | :param function: A callable that will do some cleanup. | 530 | :param function: A callable that will do some cleanup. |
130 | 529 | :type function: callable | 531 | :type function: callable |
131 | 532 | :param force: whether to force adding the cleanup function. | ||
132 | 533 | :type force: boolean | ||
133 | 530 | :param args: Positional arguments to the function. | 534 | :param args: Positional arguments to the function. |
134 | 531 | :type args: Tuple | 535 | :type args: Tuple |
135 | 532 | :param kw: Keyword arguments to the function. | 536 | :param kw: Keyword arguments to the function. |
136 | @@ -536,7 +540,7 @@ | |||
137 | 536 | 540 | ||
138 | 537 | """ | 541 | """ |
139 | 538 | # TODO: consider doing this directly instead of through Machine | 542 | # TODO: consider doing this directly instead of through Machine |
141 | 539 | if self.clean: | 543 | if self.clean or force: |
142 | 540 | cleanup.add_function(60, function, *args, **kw) | 544 | cleanup.add_function(60, function, *args, **kw) |
143 | 541 | 545 | ||
144 | 542 | def cleancommand(self, cmd): | 546 | def cleancommand(self, cmd): |
145 | @@ -703,6 +707,13 @@ | |||
146 | 703 | filename, | 707 | filename, |
147 | 704 | log_file='/var/log/utah-install') | 708 | log_file='/var/log/utah-install') |
148 | 705 | 709 | ||
149 | 710 | def _copy_qemu_scripts(self, tmpdir=None): | ||
150 | 711 | initrd_work_dir = os.path.join(tmpdir, 'initrd') | ||
151 | 712 | if not os.path.exists(initrd_work_dir): | ||
152 | 713 | os.makedirs(initrd_work_dir) | ||
153 | 714 | # copy the qemu scripts into initrd | ||
154 | 715 | self.copy_qemu_scripts_to_path(initrd_work_dir) | ||
155 | 716 | |||
156 | 706 | def _setuppreseed(self, tmpdir=None): | 717 | def _setuppreseed(self, tmpdir=None): |
157 | 707 | """Rewrite the preseed to automate installation and access.""" | 718 | """Rewrite the preseed to automate installation and access.""" |
158 | 708 | # TODO: document this better | 719 | # TODO: document this better |
159 | @@ -978,3 +989,21 @@ | |||
160 | 978 | """ | 989 | """ |
161 | 979 | iface = netifaces.ifaddresses(ifname) | 990 | iface = netifaces.ifaddresses(ifname) |
162 | 980 | return iface[netifaces.AF_INET][0]['addr'] | 991 | return iface[netifaces.AF_INET][0]['addr'] |
163 | 992 | |||
164 | 993 | def get_qemu_scripts_dir(self): | ||
165 | 994 | """Return path of the source directory containing qemu setup scripts.""" | ||
166 | 995 | src_dir = os.path.dirname(os.path.realpath(__file__)) | ||
167 | 996 | return os.path.join(src_dir, 'qemu-scripts') | ||
168 | 997 | |||
169 | 998 | def copy_qemu_scripts_to_path(self, path): | ||
170 | 999 | """Copy all qemu scripts to required path and ensure correct permissions. | ||
171 | 1000 | :param path: Target path to copy scripts to. | ||
172 | 1001 | """ | ||
173 | 1002 | src_dir = self.get_qemu_scripts_dir() | ||
174 | 1003 | mask = (stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | | ||
175 | 1004 | stat.S_IXOTH) | ||
176 | 1005 | for src_name in os.listdir(src_dir): | ||
177 | 1006 | src = os.path.join(src_dir, src_name) | ||
178 | 1007 | dst = os.path.join(path, src_name) | ||
179 | 1008 | shutil.copy2(src, dst) | ||
180 | 1009 | os.chmod(dst, mask) | ||
181 | 981 | 1010 | ||
182 | === added directory 'utah/provisioning/qemu-scripts' | |||
183 | === added file 'utah/provisioning/qemu-scripts/qemu-setup.py' | |||
184 | --- utah/provisioning/qemu-scripts/qemu-setup.py 1970-01-01 00:00:00 +0000 | |||
185 | +++ utah/provisioning/qemu-scripts/qemu-setup.py 2017-07-06 18:08:08 +0000 | |||
186 | @@ -0,0 +1,211 @@ | |||
187 | 1 | # Ubuntu Testing Automation Harness | ||
188 | 2 | # Copyright 2017 Canonical Ltd. | ||
189 | 3 | |||
190 | 4 | # This program is free software: you can redistribute it and/or modify it | ||
191 | 5 | # under the terms of the GNU General Public License version 3, as published | ||
192 | 6 | # by the Free Software Foundation. | ||
193 | 7 | |||
194 | 8 | # This program is distributed in the hope that it will be useful, but | ||
195 | 9 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
196 | 10 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
197 | 11 | # PURPOSE. See the GNU General Public License for more details. | ||
198 | 12 | |||
199 | 13 | # You should have received a copy of the GNU General Public License along | ||
200 | 14 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
201 | 15 | |||
202 | 16 | import os | ||
203 | 17 | import subprocess | ||
204 | 18 | import time | ||
205 | 19 | |||
206 | 20 | AUTOPKGTEST_NAME = 'autopkgtest.service' | ||
207 | 21 | AUTOPKGTEST_SERVICE = os.path.join('/lib/systemd/system', AUTOPKGTEST_NAME) | ||
208 | 22 | AUTOPKGTEST_CFG = '/etc/default/grub.d/90-autopkgtest.cfg' | ||
209 | 23 | |||
210 | 24 | """ This script is run on first boot of the qemu after installation, by the | ||
211 | 25 | qemu-setup.service. It will prepare the system by: | ||
212 | 26 | - Creating a serial console on ttyS1 that will be used in future by | ||
213 | 27 | autopkgtest. The service will be started automatically on all subsequent | ||
214 | 28 | boots by autopkgtest.service. | ||
215 | 29 | - Disable qemu-setup.service to stop this script from running on subsequent | ||
216 | 30 | boots. | ||
217 | 31 | - Power down the system to complete the setup process. | ||
218 | 32 | """ | ||
219 | 33 | |||
220 | 34 | |||
221 | 35 | def wait_for_network(): | ||
222 | 36 | """Wait until network connection is active.""" | ||
223 | 37 | max_count = 60 | ||
224 | 38 | for count in range(max_count): | ||
225 | 39 | status = subprocess.call(['wget', '-q', '--spider', 'launchpad.net']) | ||
226 | 40 | if status == 0: | ||
227 | 41 | break | ||
228 | 42 | elif count >= max_count - 1: | ||
229 | 43 | raise Exception('No network connection available') | ||
230 | 44 | else: | ||
231 | 45 | print('wget attempt {} failed, trying again...'.format(count + 1)) | ||
232 | 46 | time.sleep(1) | ||
233 | 47 | |||
234 | 48 | |||
235 | 49 | def wait_for_locks(): | ||
236 | 50 | """Wait for file locks to be released for doing install operations.""" | ||
237 | 51 | max_count = 60 | ||
238 | 52 | lock_files = [ | ||
239 | 53 | '/var/lib/dpkg/lock', | ||
240 | 54 | '/var/cache/apt/archives/lock', | ||
241 | 55 | '/var/lib/apt/lists/lock', | ||
242 | 56 | ] | ||
243 | 57 | for count in range(max_count): | ||
244 | 58 | status = subprocess.call(['fuser'] + lock_files) | ||
245 | 59 | if status == 1: | ||
246 | 60 | break | ||
247 | 61 | elif count >= max_count - 1: | ||
248 | 62 | raise RuntimeError('File locks not released') | ||
249 | 63 | else: | ||
250 | 64 | print('file locks active, trying again...'.format(count + 1)) | ||
251 | 65 | time.sleep(1) | ||
252 | 66 | |||
253 | 67 | |||
254 | 68 | def get_architecture(): | ||
255 | 69 | """Return architecture of running system.""" | ||
256 | 70 | return subprocess.check_output( | ||
257 | 71 | ['dpkg', '--print-architecture']).decode().strip() | ||
258 | 72 | |||
259 | 73 | |||
260 | 74 | def install_packages(packages): | ||
261 | 75 | """Install specfied list of packages.""" | ||
262 | 76 | wait_for_locks() | ||
263 | 77 | subprocess.check_call(['dpkg', '--configure', '-a']) | ||
264 | 78 | subprocess.check_call(['apt-get', 'install', '-y'] + packages) | ||
265 | 79 | |||
266 | 80 | |||
267 | 81 | def create_file(path, content): | ||
268 | 82 | """Create a file with specified path and content.""" | ||
269 | 83 | with open(path, 'w') as f: | ||
270 | 84 | f.write(content) | ||
271 | 85 | |||
272 | 86 | |||
273 | 87 | def setup_root_shell_service(): | ||
274 | 88 | """Setup root shell service on ttyS1 for use by autopkgtest.""" | ||
275 | 89 | create_file( | ||
276 | 90 | AUTOPKGTEST_SERVICE, | ||
277 | 91 | '[Unit]\n' | ||
278 | 92 | 'Description=autopkgtest root shell on ttyS1\n' | ||
279 | 93 | 'ConditionPathExists=/dev/ttyS1\n\n' | ||
280 | 94 | '[Service]\n' | ||
281 | 95 | 'ExecStart=/bin/sh\n' | ||
282 | 96 | 'StandardInput=tty-fail\n' | ||
283 | 97 | 'StandardOutput=tty\n' | ||
284 | 98 | 'StandardError=tty\n' | ||
285 | 99 | 'TTYPath=/dev/ttyS1\n' | ||
286 | 100 | 'SendSIGHUP=yes\n' | ||
287 | 101 | 'SuccessExitStatus=0 208 SIGHUP SIGINT SIGTERM SIGPIPE\n\n' | ||
288 | 102 | '[Install]\n' | ||
289 | 103 | 'WantedBy=multi-user.target\n' | ||
290 | 104 | ) | ||
291 | 105 | os.chmod(AUTOPKGTEST_SERVICE, 0o644) | ||
292 | 106 | subprocess.check_call(['systemctl', 'enable', AUTOPKGTEST_NAME]) | ||
293 | 107 | |||
294 | 108 | |||
295 | 109 | def _grubd_autopkgtest_setup(architecture): | ||
296 | 110 | """Add autopkgtest config to grub.d directory. | ||
297 | 111 | :param architecture: Required architecture. | ||
298 | 112 | :return: True if changes made, False otherwise. | ||
299 | 113 | """ | ||
300 | 114 | if architecture == 'i386': | ||
301 | 115 | create_file( | ||
302 | 116 | AUTOPKGTEST_CFG, | ||
303 | 117 | 'GRUB_CMDLINE_LINUX_DEFAULT="console=ttyS0 vmalloc=512M"' | ||
304 | 118 | ) | ||
305 | 119 | return True | ||
306 | 120 | elif architecture == 'amd64': | ||
307 | 121 | create_file( | ||
308 | 122 | AUTOPKGTEST_CFG, | ||
309 | 123 | 'GRUB_CMDLINE_LINUX_DEFAULT="console=ttyS0"' | ||
310 | 124 | ) | ||
311 | 125 | return True | ||
312 | 126 | return False | ||
313 | 127 | |||
314 | 128 | |||
315 | 129 | def _grub_autopkgtest_setup(architecture): | ||
316 | 130 | """Add autopkgtest config to grub directory. | ||
317 | 131 | :param architecture: Required architecture. | ||
318 | 132 | :return: True if changes made, False otherwise. | ||
319 | 133 | """ | ||
320 | 134 | changed = False | ||
321 | 135 | if architecture == 'i386': | ||
322 | 136 | subprocess.check_call( | ||
323 | 137 | ['sed', '-i', | ||
324 | 138 | '/CMDLINE_LINUX_DEFAULT/ s/"$/ console=ttyS0 ' | ||
325 | 139 | 'vmalloc=512M"/', '/etc/default/grub'] | ||
326 | 140 | ) | ||
327 | 141 | changed = True | ||
328 | 142 | elif architecture == 'amd64': | ||
329 | 143 | subprocess.check_call( | ||
330 | 144 | ['sed', '-i', | ||
331 | 145 | '/CMDLINE_LINUX_DEFAULT/ s/"$/ console=ttyS0"/', | ||
332 | 146 | '/etc/default/grub'] | ||
333 | 147 | ) | ||
334 | 148 | changed = True | ||
335 | 149 | |||
336 | 150 | zero_timeout = subprocess.check_output( | ||
337 | 151 | ['grep', '-q', 'GRUB_HIDDEN_TIMEOUT=0', '/etc/default/grub'] | ||
338 | 152 | ) == 0 | ||
339 | 153 | |||
340 | 154 | if zero_timeout: | ||
341 | 155 | subprocess.check_output( | ||
342 | 156 | ['sed', '-i', '/^GRUB_TIMEOUT=/ s/=.*$/=1/', | ||
343 | 157 | '/etc/default/grub'] | ||
344 | 158 | ) | ||
345 | 159 | changed = True | ||
346 | 160 | |||
347 | 161 | return changed | ||
348 | 162 | |||
349 | 163 | |||
350 | 164 | def setup_serial_console(): | ||
351 | 165 | """Setup a serial console for autopkgtest to use.""" | ||
352 | 166 | # This method is derived from: | ||
353 | 167 | # /usr/share/autopkgtest/setup-commands/setup-testbed | ||
354 | 168 | setup_root_shell_service() | ||
355 | 169 | grub = subprocess.call(['which', 'update-grub']) == 0 | ||
356 | 170 | if not os.path.isfile(AUTOPKGTEST_CFG) and grub: | ||
357 | 171 | arch = get_architecture() | ||
358 | 172 | if os.path.isdir('/etc/default/grub.d'): | ||
359 | 173 | changed = _grubd_autopkgtest_setup(arch) | ||
360 | 174 | else: | ||
361 | 175 | changed = _grub_autopkgtest_setup(arch) | ||
362 | 176 | if changed: | ||
363 | 177 | subprocess.check_call(['update-grub']) | ||
364 | 178 | |||
365 | 179 | |||
366 | 180 | def disable_qemu_setup_on_boot(): | ||
367 | 181 | """Disable this setup script from running again on future boot up.""" | ||
368 | 182 | subprocess.call(['systemctl', 'disable', 'qemu-setup.service']) | ||
369 | 183 | |||
370 | 184 | |||
371 | 185 | def system_shutdown(): | ||
372 | 186 | """Shutdown the system to complete first boot setup.""" | ||
373 | 187 | subprocess.call(['shutdown', '--poweroff', '0']) | ||
374 | 188 | |||
375 | 189 | |||
376 | 190 | def do_setup(): | ||
377 | 191 | """Complete first boot setup actions.""" | ||
378 | 192 | wait_for_network() | ||
379 | 193 | setup_serial_console() | ||
380 | 194 | install_packages(['openssh-server']) | ||
381 | 195 | |||
382 | 196 | |||
383 | 197 | def do_teardown(): | ||
384 | 198 | """Cleanup actions to disable this from running after first boot.""" | ||
385 | 199 | disable_qemu_setup_on_boot() | ||
386 | 200 | system_shutdown() | ||
387 | 201 | |||
388 | 202 | |||
389 | 203 | def main(): | ||
390 | 204 | try: | ||
391 | 205 | do_setup() | ||
392 | 206 | finally: | ||
393 | 207 | do_teardown() | ||
394 | 208 | |||
395 | 209 | |||
396 | 210 | if __name__ == '__main__': | ||
397 | 211 | main() | ||
398 | 0 | 212 | ||
399 | === added file 'utah/provisioning/qemu-scripts/qemu-setup.service' | |||
400 | --- utah/provisioning/qemu-scripts/qemu-setup.service 1970-01-01 00:00:00 +0000 | |||
401 | +++ utah/provisioning/qemu-scripts/qemu-setup.service 2017-07-06 18:08:08 +0000 | |||
402 | @@ -0,0 +1,9 @@ | |||
403 | 1 | [Unit] | ||
404 | 2 | Description=Ubuntu System Tests QEMU setup autostart script | ||
405 | 3 | |||
406 | 4 | [Service] | ||
407 | 5 | Type=oneshot | ||
408 | 6 | ExecStart=/bin/sh -c "/usr/bin/python3 /usr/local/bin/qemu-setup.py > /dev/ttyS0 2>&1" | ||
409 | 7 | |||
410 | 8 | [Install] | ||
411 | 9 | WantedBy=multi-user.target | ||
412 | 0 | 10 | ||
413 | === modified file 'utah/provisioning/vm.py' | |||
414 | --- utah/provisioning/vm.py 2016-12-23 15:01:36 +0000 | |||
415 | +++ utah/provisioning/vm.py 2017-07-06 18:08:08 +0000 | |||
416 | @@ -27,6 +27,7 @@ | |||
417 | 27 | 27 | ||
418 | 28 | from xml.etree import ElementTree | 28 | from xml.etree import ElementTree |
419 | 29 | 29 | ||
420 | 30 | from utah.cleanup import cleanup | ||
421 | 30 | from utah.config import config | 31 | from utah.config import config |
422 | 31 | from utah.process import ProcessChecker, ProcessRunner | 32 | from utah.process import ProcessChecker, ProcessRunner |
423 | 32 | from utah.provisioning.exceptions import UTAHProvisioningException | 33 | from utah.provisioning.exceptions import UTAHProvisioningException |
424 | @@ -518,6 +519,8 @@ | |||
425 | 518 | initrd_dir = os.path.join(tmpdir, 'initrd.d') | 519 | initrd_dir = os.path.join(tmpdir, 'initrd.d') |
426 | 519 | provision_data.update_initrd(initrd_dir) | 520 | provision_data.update_initrd(initrd_dir) |
427 | 520 | 521 | ||
428 | 522 | self._copy_qemu_scripts(tmpdir=tmpdir) | ||
429 | 523 | |||
430 | 521 | self._setuplatecommand(tmpdir=tmpdir) | 524 | self._setuplatecommand(tmpdir=tmpdir) |
431 | 522 | 525 | ||
432 | 523 | self._setuppreseed(tmpdir=tmpdir) | 526 | self._setuppreseed(tmpdir=tmpdir) |
433 | @@ -546,6 +549,8 @@ | |||
434 | 546 | self.vm = self.lv.defineXML(ElementTree.tostring(xml.getroot())) | 549 | self.vm = self.lv.defineXML(ElementTree.tostring(xml.getroot())) |
435 | 547 | self.cleanfunction(self.vm.destroy) | 550 | self.cleanfunction(self.vm.destroy) |
436 | 548 | self.cleanfunction(self.vm.undefine) | 551 | self.cleanfunction(self.vm.undefine) |
437 | 552 | if self.poweroff: | ||
438 | 553 | self.cleanfunction(self.vm.shutdown, force=True) | ||
439 | 549 | 554 | ||
440 | 550 | def _start(self): | 555 | def _start(self): |
441 | 551 | """Start the VM.""" | 556 | """Start the VM.""" |
442 | @@ -620,6 +625,12 @@ | |||
443 | 620 | "UPDATE machines SET state='destroyed' ""WHERE machineid=?", | 625 | "UPDATE machines SET state='destroyed' ""WHERE machineid=?", |
444 | 621 | [machineid]) | 626 | [machineid]) |
445 | 622 | 627 | ||
446 | 628 | def shut_off(self, machineid): | ||
447 | 629 | """Update the database to indicate a machine is shut off.""" | ||
448 | 630 | self.execute( | ||
449 | 631 | "UPDATE machines SET state='shut off' ""WHERE machineid=?", | ||
450 | 632 | [machineid]) | ||
451 | 633 | |||
452 | 623 | 634 | ||
453 | 624 | def get_vm(**kw): | 635 | def get_vm(**kw): |
454 | 625 | """Return a Machine object for a VM with the passed in arguments. | 636 | """Return a Machine object for a VM with the passed in arguments. |
Approved. Thanks.