Merge lp:~jtv/maas/bug-1295479 into lp:~maas-committers/maas/trunk
- bug-1295479
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Jeroen T. Vermeulen | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 2213 | ||||
Proposed branch: | lp:~jtv/maas/bug-1295479 | ||||
Merge into: | lp:~maas-committers/maas/trunk | ||||
Diff against target: |
398 lines (+0/-372) 3 files modified
src/provisioningserver/tasks.py (+0/-6) src/provisioningserver/tests/test_maas_import_pxe_files.py (+0/-347) src/provisioningserver/tests/test_tasks.py (+0/-19) |
||||
To merge this branch: | bzr merge lp:~jtv/maas/bug-1295479 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gavin Panella (community) | Approve | ||
Review via email: mp+213595@code.launchpad.net |
Commit message
Remove remains of the old import script: tests, and the passing of archive URLs (not used by Simplestreams) through environment variables.
Description of the change
In a future change, I think it would also make sense to stop running maas-import-
Jeroen
Julian Edwards (julian-edwards) wrote : | # |
On Tuesday 01 Apr 2014 10:58:00 you wrote:
> Review: Approve
>
> This is so good.
>
> > In a future change, I think it would also make sense to stop running
> > maas-import-
> > the task instead.
>
> Yes, please.
Yes yes yes! This was always the intention.
We need to make sure that the task is bulletproof and meets all the same use
cases. Also I'd like to do that work at the same time as we push all the settings
out of the yaml file and into the UI.
Preview Diff
1 | === modified file 'src/provisioningserver/tasks.py' |
2 | --- src/provisioningserver/tasks.py 2014-03-26 13:26:23 +0000 |
3 | +++ src/provisioningserver/tasks.py 2014-04-01 06:39:20 +0000 |
4 | @@ -452,12 +452,6 @@ |
5 | if http_proxy is not None: |
6 | env['http_proxy'] = http_proxy |
7 | env['https_proxy'] = http_proxy |
8 | - if main_archive is not None: |
9 | - env['MAIN_ARCHIVE'] = main_archive |
10 | - if ports_archive is not None: |
11 | - env['PORTS_ARCHIVE'] = ports_archive |
12 | - if cloud_images_archive is not None: |
13 | - env['CLOUD_IMAGES_ARCHIVE'] = cloud_images_archive |
14 | call_and_check(['sudo', '-n', '-E', 'maas-import-pxe-files'], env=env) |
15 | if callback is not None: |
16 | callback.delay() |
17 | |
18 | === removed file 'src/provisioningserver/tests/test_maas_import_pxe_files.py' |
19 | --- src/provisioningserver/tests/test_maas_import_pxe_files.py 2014-03-28 05:48:14 +0000 |
20 | +++ src/provisioningserver/tests/test_maas_import_pxe_files.py 1970-01-01 00:00:00 +0000 |
21 | @@ -1,347 +0,0 @@ |
22 | -# Copyright 2012-2014 Canonical Ltd. This software is licensed under the |
23 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
24 | - |
25 | -"""Tests for the maas-import-pxe-files script.""" |
26 | - |
27 | -from __future__ import ( |
28 | - absolute_import, |
29 | - print_function, |
30 | - unicode_literals, |
31 | - ) |
32 | - |
33 | -str = None |
34 | - |
35 | -__metaclass__ = type |
36 | -__all__ = [] |
37 | - |
38 | -import os |
39 | -from subprocess import check_call |
40 | -import unittest |
41 | - |
42 | -from maastesting import root |
43 | -from maastesting.factory import factory |
44 | -from maastesting.testcase import MAASTestCase |
45 | -from maastesting.utils import ( |
46 | - age_file, |
47 | - get_write_time, |
48 | - ) |
49 | -from provisioningserver.boot import tftppath |
50 | -from provisioningserver.boot.pxe import PXEBootMethod |
51 | -from provisioningserver.boot.uefi import UEFIBootMethod |
52 | -from provisioningserver.testing.config import set_tftp_root |
53 | -from testtools.content import text_content |
54 | -from testtools.matchers import ( |
55 | - DirExists, |
56 | - FileContains, |
57 | - Not, |
58 | - ) |
59 | - |
60 | - |
61 | -def read_file(path, name): |
62 | - """Return the contents of the file at `path/name`.""" |
63 | - with open(os.path.join(path, name)) as infile: |
64 | - return infile.read() |
65 | - |
66 | - |
67 | -def backdate(path): |
68 | - """Set the last modification time for the file at `path` to the past.""" |
69 | - age_file(path, 9999999) |
70 | - |
71 | - |
72 | -def compose_download_dir(archive, arch, release): |
73 | - """Locate a bootloader, initrd, and kernel in an archive. |
74 | - |
75 | - :param archive: Archive directory (corresponding to the script's ARCHIVE |
76 | - setting, except here it's a filesystem path not a URL). |
77 | - :param arch: Architecture. |
78 | - :param release: Ubuntu release name. |
79 | - :return: A 2-tuple of: |
80 | - * Base path of the images/ dir. |
81 | - * Full absolute path to the directory holding the requisite files |
82 | - for this archive, arch, and release. |
83 | - """ |
84 | - if arch[0] in {'amd64', 'i386'}: |
85 | - basepath = os.path.join( |
86 | - archive, 'dists', release, 'main', 'installer-%s' % arch[0], |
87 | - 'current', 'images') |
88 | - fullpath = os.path.join( |
89 | - basepath, 'netboot', 'ubuntu-installer', arch[0]) |
90 | - return basepath, fullpath |
91 | - elif arch[0] == 'armhf': |
92 | - basepath = os.path.join( |
93 | - archive, 'dists', '%s-updates' % release, 'main', |
94 | - 'installer-%s' % arch[0], 'current', 'images') |
95 | - fullpath = os.path.join(basepath, arch[1], 'netboot') |
96 | - return basepath, fullpath |
97 | - else: |
98 | - raise NotImplementedError('Unknown architecture: %r' % (arch, )) |
99 | - |
100 | - |
101 | -def compose_download_kernel_name(arch, release): |
102 | - """Name the kernel file expected to be found in a netboot archive. |
103 | - |
104 | - :param archive: Archive directory (corresponding to the script's ARCHIVE |
105 | - setting, except here it's a filesystem path not a URL). |
106 | - :param arch: Architecture. |
107 | - :return: the kernel name string, eg. "vmlinuz" or "linux" as appropriate |
108 | - """ |
109 | - if arch[0] in {'amd64', 'i386'}: |
110 | - return 'linux' |
111 | - elif arch[0] == 'armhf': |
112 | - return 'vmlinuz' |
113 | - else: |
114 | - raise NotImplementedError('Unknown architecture: %r' % arch) |
115 | - |
116 | - |
117 | -def compose_download_filenames(arch, release): |
118 | - """Name files expected to be found in the directory of a netboot archive. |
119 | - |
120 | - :param archive: Archive directory (corresponding to the script's ARCHIVE |
121 | - setting, except here it's a filesystem path not a URL). |
122 | - :param arch: Architecture. |
123 | - :return: list of names to be found in the netboot directory for this |
124 | - particular arch and release. Eg: ['vmlinuz', 'initrd.gz'] or |
125 | - ['linux', 'initrd.gz'] as appropriate. |
126 | - """ |
127 | - return [compose_download_kernel_name(arch, release), 'initrd.gz'] |
128 | - |
129 | - |
130 | -def compose_uefi_dir(archive, release): |
131 | - """File locations expected to be found in the archive for UEFI netboot. |
132 | - |
133 | - :param archive: Archive directory (corresponding to the script's ARCHIVE |
134 | - setting, except here it's a filesystem path not a URL). |
135 | - :param release: distro release |
136 | - :return: A 2-tuple of: |
137 | - * Base path of the current/ dir. |
138 | - * Requisite files for this archive. |
139 | - """ |
140 | - basepath = os.path.join( |
141 | - archive, 'dists', release, 'main', 'uefi', 'grub2-amd64', 'current') |
142 | - return basepath, ['grubnetx64.efi.signed'] |
143 | - |
144 | - |
145 | -def generate_md5sums(basepath): |
146 | - """Write MD5SUMS file at basepath.""" |
147 | - script = [ |
148 | - "bash", "-c", |
149 | - "cd %s;find . -type f | xargs md5sum > MD5SUMS" % basepath] |
150 | - |
151 | - with open(os.devnull, 'wb') as dev_null: |
152 | - check_call(script, stdout=dev_null) |
153 | - |
154 | - |
155 | -def compose_tftp_bootloader_path(tftproot): |
156 | - """Compose path for MAAS TFTP bootloader.""" |
157 | - pxe_method = PXEBootMethod() |
158 | - return tftppath.locate_tftp_path( |
159 | - pxe_method.bootloader_path, tftproot) |
160 | - |
161 | - |
162 | -def compose_tftp_uefi_bootloader_path(tftproot): |
163 | - """Compose path for MAAS UEFI TFTP bootloader.""" |
164 | - uefi_method = UEFIBootMethod() |
165 | - return tftppath.locate_tftp_path( |
166 | - uefi_method.bootloader_path, tftproot) |
167 | - |
168 | - |
169 | -def compose_tftp_path(tftproot, arch, release, label, purpose, *path): |
170 | - """Compose path for MAAS TFTP files for given architecture. |
171 | - |
172 | - After the TFTP root directory and the architecture, just append any path |
173 | - components you want to drill deeper, e.g. the release name to get to the |
174 | - files for a specific release. |
175 | - """ |
176 | - return os.path.join( |
177 | - tftppath.locate_tftp_path( |
178 | - tftppath.compose_image_path( |
179 | - arch[0], arch[1], release, label, purpose), |
180 | - tftproot), |
181 | - *path) |
182 | - |
183 | - |
184 | -class TestImportPXEFiles(MAASTestCase): |
185 | - |
186 | - # There are some differences between architectures, so run these tests for |
187 | - # all supported architectures. |
188 | - scenarios = ( |
189 | - ('i386/generic', {'arch': ('i386', 'generic')}), |
190 | - ('amd64/generic', {'arch': ('amd64', 'generic')}), |
191 | - ('armhf/highbank', {'arch': ('armhf', 'highbank')}), |
192 | - ) |
193 | - |
194 | - def setUp(self): |
195 | - super(TestImportPXEFiles, self).setUp() |
196 | - raise unittest.SkipTest( |
197 | - "XXX rvb 2014-03-21 bug=1295479: Disabled. The " |
198 | - "maas-import-pxe-files script has been replaced with a " |
199 | - "new version to use simplestreams v2's data. These tests need " |
200 | - "to be completely refactored.") |
201 | - self.tftproot = self.make_dir() |
202 | - self.config_fixture = self.useFixture(set_tftp_root(self.tftproot)) |
203 | - |
204 | - def get_arch(self): |
205 | - """Return an existing, supported architecture/subarchitecture pair.""" |
206 | - # Use the architecture which the current test scenario targets. |
207 | - return self.arch |
208 | - |
209 | - def make_downloads(self, release=None, arch=None): |
210 | - """Set up a directory with an image for "download" by the script. |
211 | - |
212 | - Returns an "ARCHIVE" URL for the download. |
213 | - """ |
214 | - if release is None: |
215 | - release = factory.make_name('release') |
216 | - archive = self.make_dir() |
217 | - basepath, download = compose_download_dir(archive, arch, release) |
218 | - os.makedirs(download) |
219 | - for filename in compose_download_filenames(arch, release): |
220 | - factory.make_file(download, filename) |
221 | - generate_md5sums(basepath) |
222 | - uefi_archive, uefi_filenames = compose_uefi_dir(archive, release) |
223 | - os.makedirs(uefi_archive) |
224 | - for uefi in uefi_filenames: |
225 | - factory.make_file(uefi_archive, uefi) |
226 | - return archive |
227 | - |
228 | - def call_script(self, archive_dir, tftproot, arch=None, release=None): |
229 | - """Call the maas-import-pxe-files script with given settings. |
230 | - |
231 | - The ARCHIVE URL and TFTPROOT path must be set, or the script will try |
232 | - to download from the Ubuntu server and store into the system's real |
233 | - TFTP root directory, respectively. Both bad ideas. |
234 | - """ |
235 | - # TODO: Use path.py <http://pypi.python.org/pypi/path.py> instead, or |
236 | - # something similar; this is tedious stuff. |
237 | - script = os.path.join(root, "scripts", "maas-import-pxe-files") |
238 | - path = [os.path.join(root, "bin"), os.path.join(root, "scripts")] |
239 | - path.extend(os.environ.get("PATH", "").split(os.pathsep)) |
240 | - env = { |
241 | - 'MAIN_ARCHIVE': 'file://%s' % archive_dir, |
242 | - 'PORTS_ARCHIVE': 'file://%s' % archive_dir, |
243 | - 'STABLE_RELEASE': release, |
244 | - # Substitute curl for wget; it accepts file:// URLs. |
245 | - 'DOWNLOAD': 'curl -O --silent', |
246 | - 'PATH': os.pathsep.join(path), |
247 | - # Suppress running of maas-import-ephemerals. It gets too |
248 | - # intimate with the system to test here. |
249 | - 'IMPORT_EPHEMERALS': '0', |
250 | - # Suppress GPG checks as we can't sign the file from |
251 | - # here. |
252 | - 'IGNORE_GPG': '1', |
253 | - # Don't check for broken efinet, so arch will be |
254 | - # correct for the download. |
255 | - 'CHECK_BROKEN_EFINET': '0', |
256 | - # Skip shim.efi.sifned, as we don't want it to really |
257 | - # download the package |
258 | - 'SKIP_SHIM_SIGNED': '1', |
259 | - } |
260 | - env.update(self.config_fixture.environ) |
261 | - if arch is not None: |
262 | - env['ARCHES'] = '/'.join(arch) |
263 | - if release is not None: |
264 | - env['RELEASES'] = release |
265 | - errfile = self.make_file() |
266 | - |
267 | - try: |
268 | - with open(os.devnull, 'wb') as out, open(errfile, 'wb') as err: |
269 | - check_call(script, env=env, stdout=out, stderr=err) |
270 | - finally: |
271 | - with open(errfile, 'r') as error_output: |
272 | - self.addDetail('stderr', text_content(error_output.read())) |
273 | - |
274 | - def test_procures_pre_boot_loader(self): |
275 | - arch = self.get_arch() |
276 | - release = 'precise' |
277 | - archive = self.make_downloads(arch=arch, release=release) |
278 | - self.call_script(archive, self.tftproot, arch=arch, release=release) |
279 | - tftp_path = compose_tftp_bootloader_path(self.tftproot) |
280 | - expected_contents = read_file('/usr/lib/syslinux', 'pxelinux.0') |
281 | - self.assertThat(tftp_path, FileContains(expected_contents)) |
282 | - |
283 | - def test_updates_pre_boot_loader(self): |
284 | - arch = self.get_arch() |
285 | - release = 'precise' |
286 | - tftp_path = compose_tftp_bootloader_path(self.tftproot) |
287 | - with open(tftp_path, 'w') as existing_file: |
288 | - existing_file.write(factory.getRandomString()) |
289 | - archive = self.make_downloads(arch=arch, release=release) |
290 | - self.call_script(archive, self.tftproot, arch=arch, release=release) |
291 | - expected_contents = read_file('/usr/lib/syslinux', 'pxelinux.0') |
292 | - self.assertThat(tftp_path, FileContains(expected_contents)) |
293 | - |
294 | - def test_procures_uefi_grubnet(self): |
295 | - arch = self.get_arch() |
296 | - release = 'precise' |
297 | - archive = self.make_downloads(arch=arch, release=release) |
298 | - self.call_script(archive, self.tftproot, arch=arch, release=release) |
299 | - tftp_path = os.path.join(self.tftproot, 'grubx64.efi') |
300 | - uefi_archive, _ = compose_uefi_dir(archive, release) |
301 | - expected_contents = read_file(uefi_archive, 'grubnetx64.efi.signed') |
302 | - self.assertThat(tftp_path, FileContains(expected_contents)) |
303 | - |
304 | - def test_updates_uefi_grubnet(self): |
305 | - arch = self.get_arch() |
306 | - release = 'precise' |
307 | - archive = self.make_downloads(arch=arch, release=release) |
308 | - tftp_path = os.path.join(self.tftproot, 'grubx64.efi') |
309 | - with open(tftp_path, 'w') as existing_file: |
310 | - existing_file.write(factory.getRandomString()) |
311 | - self.call_script(archive, self.tftproot, arch=arch, release=release) |
312 | - uefi_archive, _ = compose_uefi_dir(archive, release) |
313 | - expected_contents = read_file(uefi_archive, 'grubnetx64.efi.signed') |
314 | - self.assertThat(tftp_path, FileContains(expected_contents)) |
315 | - |
316 | - def test_procures_install_image(self): |
317 | - arch = self.get_arch() |
318 | - release = 'precise' |
319 | - archive = self.make_downloads(arch=arch, release=release) |
320 | - self.call_script(archive, self.tftproot, arch=arch, release=release) |
321 | - tftp_path = compose_tftp_path( |
322 | - self.tftproot, arch, release, 'release', 'install', 'linux') |
323 | - _, download_path = compose_download_dir(archive, arch, release) |
324 | - expected_contents = read_file( |
325 | - download_path, compose_download_kernel_name(arch, release)) |
326 | - self.assertThat(tftp_path, FileContains(expected_contents)) |
327 | - |
328 | - def test_updates_install_image(self): |
329 | - arch = self.get_arch() |
330 | - release = 'precise' |
331 | - tftp_path = compose_tftp_path( |
332 | - self.tftproot, arch, release, 'release', 'install', 'linux') |
333 | - os.makedirs(os.path.dirname(tftp_path)) |
334 | - with open(tftp_path, 'w') as existing_file: |
335 | - existing_file.write(factory.getRandomString()) |
336 | - archive = self.make_downloads(arch=arch, release=release) |
337 | - self.call_script(archive, self.tftproot, arch=arch, release=release) |
338 | - _, download_path = compose_download_dir(archive, arch, release) |
339 | - expected_contents = read_file( |
340 | - download_path, compose_download_kernel_name(arch, release)) |
341 | - self.assertThat(tftp_path, FileContains(expected_contents)) |
342 | - |
343 | - def test_leaves_install_image_untouched_if_unchanged(self): |
344 | - arch = self.get_arch() |
345 | - release = 'precise' |
346 | - archive = self.make_downloads(arch=arch, release=release) |
347 | - self.call_script(archive, self.tftproot, arch=arch, release=release) |
348 | - tftp_path = compose_tftp_path( |
349 | - self.tftproot, arch, release, 'release', 'install', 'linux') |
350 | - backdate(tftp_path) |
351 | - original_timestamp = get_write_time(tftp_path) |
352 | - self.call_script(archive, self.tftproot, arch=arch, release=release) |
353 | - self.assertEqual(original_timestamp, get_write_time(tftp_path)) |
354 | - |
355 | - def test_survives_failed_download(self): |
356 | - arch = self.get_arch() |
357 | - release = 'precise' |
358 | - archive = self.make_downloads(arch=arch, release=release) |
359 | - # Initrd can't be found. This breaks the download. |
360 | - _, download = compose_download_dir(archive, arch, release) |
361 | - os.remove(os.path.join(download, 'initrd.gz')) |
362 | - |
363 | - self.call_script(archive, self.tftproot, arch=arch, release=release) |
364 | - |
365 | - # The script does not install the broken image, and does not fail. |
366 | - installed_kernel = compose_tftp_path( |
367 | - self.tftproot, arch, release, 'release', 'initrd.gz') |
368 | - self.assertThat(installed_kernel, Not(DirExists())) |
369 | |
370 | === modified file 'src/provisioningserver/tests/test_tasks.py' |
371 | --- src/provisioningserver/tests/test_tasks.py 2014-03-28 04:31:32 +0000 |
372 | +++ src/provisioningserver/tests/test_tasks.py 2014-04-01 06:39:20 +0000 |
373 | @@ -641,25 +641,6 @@ |
374 | self.assertThat(recorder, MockCalledOnceWith( |
375 | ['sudo', '-n', '-E', 'maas-import-pxe-files'], env=expected_env)) |
376 | |
377 | - def test_import_boot_images_sets_archive_locations(self): |
378 | - self.patch(tasks, 'call_and_check') |
379 | - archives = { |
380 | - 'main_archive': self.make_archive_url('main'), |
381 | - 'ports_archive': self.make_archive_url('ports'), |
382 | - 'cloud_images_archive': self.make_archive_url('cloud-images'), |
383 | - } |
384 | - expected_settings = { |
385 | - parameter.upper(): value |
386 | - for parameter, value in archives.items()} |
387 | - import_boot_images(**archives) |
388 | - env = tasks.call_and_check.call_args[1]['env'] |
389 | - archive_settings = { |
390 | - variable: value |
391 | - for variable, value in env.items() |
392 | - if variable.endswith('_ARCHIVE') |
393 | - } |
394 | - self.assertEqual(expected_settings, archive_settings) |
395 | - |
396 | def test_import_boot_images_calls_callback(self): |
397 | self.patch(tasks, 'call_and_check') |
398 | mock_callback = Mock() |
This is so good.
> In a future change, I think it would also make sense to stop running pxe-files as a script, and call its Python function from
> maas-import-
> the task instead.
Yes, please.