Merge lp:~julian-edwards/maas/report-subarches-in-bootimage into lp:~maas-committers/maas/trunk

Proposed by Julian Edwards
Status: Merged
Approved by: Julian Edwards
Approved revision: no longer in the source branch.
Merged at revision: 2340
Proposed branch: lp:~julian-edwards/maas/report-subarches-in-bootimage
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 424 lines (+275/-9)
5 files modified
src/provisioningserver/boot/tests/test_tftppath.py (+154/-1)
src/provisioningserver/boot/tftppath.py (+71/-8)
src/provisioningserver/import_images/boot_image_mapping.py (+27/-0)
src/provisioningserver/import_images/testing/factory.py (+9/-0)
src/provisioningserver/import_images/tests/test_boot_image_mapping.py (+14/-0)
To merge this branch: bzr merge lp:~julian-edwards/maas/report-subarches-in-bootimage
Reviewer Review Type Date Requested Status
Jeroen T. Vermeulen (community) Approve
Review via email: mp+219987@code.launchpad.net

Commit message

Make list_boot_images(), which is called from the periodic Celery task, report the supported_subarches which exists in the maas.meta file as subarches in the resource data for each image. This will enable subarch fallback to the right image when looking for a boot image.

Description of the change

I am throwing this up before I've tested it on my rig so I can get some early feedback. There's probably a fair bit that needs tidying up but given I don't overlap with you lot at the moment, please forgive my indulgence :)

To post a comment you must log in.
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Comments inline. Several things need, or may need, addressing before this can land, including the docstrings.

review: Approve
Revision history for this message
Julian Edwards (julian-edwards) wrote :
Download full text (6.8 KiB)

On 20/05/14 02:49, Jeroen T. Vermeulen wrote:
> Review: Approve
>
> Comments inline. Several things need, or may need, addressing before this can land, including the docstrings.

Thanks for reviewing.

I would have liked to have split this into smaller branches for better
focused changed, but a) it evolved over time, and b) I'd be blocked on
reviews that I won't get until my night-time. So, sorry for the
larger-than-I-wanted branch. Move back to BKK and they get smaller :)

> Don't forget to run "make format" when you're done

Done!

>> +
>> + def test_extract_metadata(self):
>> + resource = dict(
>> + subarches=factory.make_name("subarch"),
>> + other_item=factory.make_name("other"),
>
> Shouldn't that be a list of subarches, i.e. [factory.make_name("subarch")] (with the added brackets)?

No, there is only one "subarches" per resource, it's just a string.

>> + def _make_path(self):
>> + osystem = factory.make_name("os")
>
> The underscore doesn't do much for us in a test case... If I were you I'd just leave it out as unnecessary reading.

I honestly disagree here. I don't know if this is an unwritten rule or
not, but things starting with underscore tell me that they are exclusive
to this class (or at least *should be*). Without it, I would wonder if
make_path came from the test case base class, especially given its
factory-like nature.

>> + params = extract_image_params(path, "")
>> +
>> + self.assertEqual(
>> + [
>
> Is ordering explicitly guaranteed? If not, best just use assertItemsEqual.

Ah, good spot!

>> -def extract_image_params(path):
>> +def extract_metadata(metadata, params):
>> + """Examine the maas.meta file for any required metadata.
>> +
>> + :param metadata: contents of the maas.meta file
>> + :param params: A dict of path components for the image
>> + (see extract_image_params).
>
> I am confused. This talks about a dict of path components; the documentation for extract_image_params talks about a list of boot-image dicts; and the :return: documentation for extract_image_params talks about a dict of metadata. How do these things fit together?

I was referring to the return value of extract_image_params, but as it
confused you I've removed the reference entirely and directly explained
in the docstring what is required.

>> +def extract_image_params(path, maas_meta):
>> """Represent a list of TFTP path elements as a list of boot-image dicts.
>>
>> - The path must consist of a full [osystem, architecture, subarchitecture,
>> - release] that identify a kind of boot that we may need an image for.
>> + :param path: Must consist of a full [osystem, architecture,
>> + subarchitecture, release] that identify a kind of boot for which we
>
> No need to say "Must consist of" any more.

I'm not sure what you are driving at, but I've re-worded it anyway.

>
>> + may need an image.
>> + :param maas_meta: Contents of the maas.meta file. This may be an
>> + empty string.
>> +
>> + :return: A dict, which may also include additional items of meta-data
>> + that are not elements in the path, such...

Read more...

Revision history for this message
Julian Edwards (julian-edwards) wrote :

This tested out on my rig, so landing it. There is one more problem to fix in a subsequent branch: the pxeconfig has the path to the node's subarch instead of the image's.

Revision history for this message
MAAS Lander (maas-lander) wrote :
Download full text (16.2 KiB)

The attempt to merge lp:~julian-edwards/maas/report-subarches-in-bootimage into lp:maas failed. Below is the output from the failed tests.

Ign http://security.ubuntu.com trusty-security InRelease
Get:1 http://security.ubuntu.com trusty-security Release.gpg [933 B]
Get:2 http://security.ubuntu.com trusty-security Release [58.5 kB]
Ign http://nova.clouds.archive.ubuntu.com trusty InRelease
Ign http://nova.clouds.archive.ubuntu.com trusty-updates InRelease
Hit http://nova.clouds.archive.ubuntu.com trusty Release.gpg
Get:3 http://nova.clouds.archive.ubuntu.com trusty-updates Release.gpg [933 B]
Hit http://nova.clouds.archive.ubuntu.com trusty Release
Get:4 http://nova.clouds.archive.ubuntu.com trusty-updates Release [58.5 kB]
Get:5 http://security.ubuntu.com trusty-security/main Sources [14.7 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty/main Sources
Get:6 http://security.ubuntu.com trusty-security/universe Sources [4,212 B]
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Sources
Get:7 http://security.ubuntu.com trusty-security/main amd64 Packages [47.0 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty/main amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/universe amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/main Translation-en
Get:8 http://security.ubuntu.com trusty-security/universe amd64 Packages [17.7 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en
Hit http://security.ubuntu.com trusty-security/main Translation-en
Hit http://security.ubuntu.com trusty-security/universe Translation-en
Ign http://security.ubuntu.com trusty-security/main Translation-en_US
Ign http://security.ubuntu.com trusty-security/universe Translation-en_US
Ign http://nova.clouds.archive.ubuntu.com trusty/main Translation-en_US
Ign http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en_US
Get:9 http://nova.clouds.archive.ubuntu.com trusty-updates/main Sources [35.9 kB]
Get:10 http://nova.clouds.archive.ubuntu.com trusty-updates/universe Sources [23.8 kB]
Get:11 http://nova.clouds.archive.ubuntu.com trusty-updates/main amd64 Packages [83.4 kB]
Get:12 http://nova.clouds.archive.ubuntu.com trusty-updates/universe amd64 Packages [64.3 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/universe Translation-en
Ign http://nova.clouds.archive.ubuntu.com trusty-updates/main Translation-en_US
Ign http://nova.clouds.archive.ubuntu.com trusty-updates/universe Translation-en_US
Fetched 410 kB in 0s (1,347 kB/s)
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
     --no-install-recommends install apache2 bind9 bind9utils build-essential bzr-builddeb curl daemontools debhelper dh-apport distro-info dnsutils firefox freeipmi-tools ipython isc-dhcp-common libjs-raphael libjs-yui3-full libjs-yui3-min libpq-dev make postgresql python-amqplib python-bzrlib python-celery python-convoy python-crochet python-cssselect python-curtin python-dev python-distro-info python-django python-django-piston python-django-south python-djorm-ext-pgarray python-docutils python-formencode python-httplib2 py...

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Yes, I would feel better with the line breaks and a mention of NOQA, thanks.

Revision history for this message
Julian Edwards (julian-edwards) wrote :

On 20/05/14 18:09, Jeroen T. Vermeulen wrote:
> Yes, I would feel better with the line breaks and a mention of NOQA, thanks.
>

Ok I'll do that in a followup branch.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/provisioningserver/boot/tests/test_tftppath.py'
--- src/provisioningserver/boot/tests/test_tftppath.py 2014-05-02 15:13:14 +0000
+++ src/provisioningserver/boot/tests/test_tftppath.py 2014-05-20 00:47:19 +0000
@@ -25,6 +25,8 @@
25 compose_image_path,25 compose_image_path,
26 drill_down,26 drill_down,
27 extend_path,27 extend_path,
28 extract_image_params,
29 extract_metadata,
28 is_visible_subdir,30 is_visible_subdir,
29 list_boot_images,31 list_boot_images,
30 list_subdirs,32 list_subdirs,
@@ -34,6 +36,14 @@
34 OperatingSystem,36 OperatingSystem,
35 OperatingSystemRegistry,37 OperatingSystemRegistry,
36 )38 )
39from provisioningserver.import_images.boot_image_mapping import (
40 BootImageMapping,
41 )
42from provisioningserver.import_images.helpers import ImageSpec
43from provisioningserver.import_images.testing.factory import (
44 make_image_spec,
45 set_resource,
46 )
37from provisioningserver.testing.boot_images import (47from provisioningserver.testing.boot_images import (
38 make_boot_image_storage_params,48 make_boot_image_storage_params,
39 )49 )
@@ -79,13 +89,15 @@
79 ]89 ]
8090
8191
82def make_image(params, purpose):92def make_image(params, purpose, metadata=None):
83 """Describe an image as a dict similar to what `list_boot_images` returns.93 """Describe an image as a dict similar to what `list_boot_images` returns.
8494
85 The `params` are as returned from `make_boot_image_storage_params`.95 The `params` are as returned from `make_boot_image_storage_params`.
86 """96 """
87 image = params.copy()97 image = params.copy()
88 image['purpose'] = purpose98 image['purpose'] = purpose
99 if metadata is not None:
100 image.update(metadata)
89 return image101 return image
90102
91103
@@ -133,6 +145,17 @@
133 factory.make_file(image_dir, 'linux')145 factory.make_file(image_dir, 'linux')
134 factory.make_file(image_dir, 'initrd.gz')146 factory.make_file(image_dir, 'initrd.gz')
135147
148 def make_meta_file(self, image_params, image_resource, tftproot):
149 image = ImageSpec(
150 arch=image_params["architecture"],
151 subarch=image_params["subarchitecture"],
152 release=image_params["release"], label=image_params["label"])
153 mapping = BootImageMapping()
154 mapping.setdefault(image, image_resource)
155 maas_meta = mapping.dump_json()
156 with open(os.path.join(tftproot, "maas.meta"), "wb") as f:
157 f.write(maas_meta)
158
136 def test_compose_image_path_follows_storage_directory_layout(self):159 def test_compose_image_path_follows_storage_directory_layout(self):
137 osystem = factory.make_name('osystem')160 osystem = factory.make_name('osystem')
138 arch = factory.make_name('arch')161 arch = factory.make_name('arch')
@@ -203,6 +226,21 @@
203 ],226 ],
204 list_boot_images(self.tftproot))227 list_boot_images(self.tftproot))
205228
229 def test_list_boot_images_merges_maas_meta_data(self):
230 params = make_boot_image_storage_params()
231 self.make_image_dir(params, self.tftproot)
232 # The required metadata is called "subarches" in maas.meta
233 metadata = dict(subarches=factory.make_name("subarches"))
234 self.make_meta_file(params, metadata, self.tftproot)
235 purposes = ['install', 'commissioning', 'xinstall']
236 make_osystem(self, params['osystem'], purposes)
237 # The API requires "supported_subarches".
238 expected_metadata = dict(supported_subarches=metadata["subarches"])
239 self.assertItemsEqual(
240 [make_image(params, purpose, expected_metadata)
241 for purpose in purposes],
242 list_boot_images(self.tftproot))
243
206 def test_list_boot_images_empty_on_missing_osystems(self):244 def test_list_boot_images_empty_on_missing_osystems(self):
207 params = [make_boot_image_storage_params() for counter in range(3)]245 params = [make_boot_image_storage_params() for counter in range(3)]
208 for param in params:246 for param in params:
@@ -296,3 +334,118 @@
296 self.assertEqual(334 self.assertEqual(
297 [[deep_dir, subdir]],335 [[deep_dir, subdir]],
298 drill_down(base_dir, [[shallow_dir], [deep_dir]]))336 drill_down(base_dir, [[shallow_dir], [deep_dir]]))
337
338 def test_extract_metadata(self):
339 resource = dict(
340 subarches=factory.make_name("subarch"),
341 other_item=factory.make_name("other"),
342 )
343 image = make_image_spec()
344 mapping = set_resource(image_spec=image, resource=resource)
345 metadata = mapping.dump_json()
346
347 # Lack of consistency across maas in naming arch vs architecture
348 # and subarch vs subarchitecture means I can't just do a simple
349 # dict parameter expansion here.
350 params = {
351 "architecture": image.arch,
352 "subarchitecture": image.subarch,
353 "release": image.release,
354 "label": image.label,
355 }
356 extracted_data = extract_metadata(metadata, params)
357
358 # We only expect the supported_subarches key from the resource data.
359 expected = dict(supported_subarches=resource["subarches"])
360 self.assertEqual(expected, extracted_data)
361
362 def _make_path(self):
363 osystem = factory.make_name("os")
364 arch = factory.make_name("arch")
365 subarch = factory.make_name("subarch")
366 release = factory.make_name("release")
367 label = factory.make_name("label")
368 path = (osystem, arch, subarch, release, label)
369 return path, osystem, arch, subarch, release, label
370
371 def _patch_osystem_registry(self, values):
372 get_item = self.patch(OperatingSystemRegistry, "get_item")
373 item_mock = Mock()
374 item_mock.get_boot_image_purposes.return_value = values
375 get_item.return_value = item_mock
376
377 def test_extract_image_params_with_no_metadata(self):
378 path, osystem, arch, subarch, release, label = self._make_path()
379
380 # Patch OperatingSystemRegistry to return a fixed list of
381 # values.
382 purpose1 = factory.make_name("purpose")
383 purpose2 = factory.make_name("purpose")
384 purposes = [purpose1, purpose2]
385 self._patch_osystem_registry(purposes)
386
387 params = extract_image_params(path, "")
388
389 self.assertItemsEqual(
390 [
391 {
392 "osystem": osystem,
393 "architecture": arch,
394 "subarchitecture": subarch,
395 "release": release,
396 "label": label,
397 "purpose": purpose1,
398 },
399 {
400 "osystem": osystem,
401 "architecture": arch,
402 "subarchitecture": subarch,
403 "release": release,
404 "label": label,
405 "purpose": purpose2,
406 },
407 ],
408 params)
409
410 def test_extract_image_params_with_metadata(self):
411 path, osystem, arch, subarch, release, label = self._make_path()
412
413 # Patch OperatingSystemRegistry to return a fixed list of
414 # values.
415 purpose1 = factory.make_name("purpose")
416 purpose2 = factory.make_name("purpose")
417 purposes = [purpose1, purpose2]
418 self._patch_osystem_registry(purposes)
419
420 # Create some maas.meta content.
421 image = ImageSpec(
422 arch=arch, subarch=subarch, release=release, label=label)
423 image_resource = dict(subarches=factory.make_name("subarches"))
424 mapping = BootImageMapping()
425 mapping.setdefault(image, image_resource)
426 maas_meta = mapping.dump_json()
427
428 params = extract_image_params(path, maas_meta)
429
430 self.assertItemsEqual(
431 [
432 {
433 "osystem": osystem,
434 "architecture": arch,
435 "subarchitecture": subarch,
436 "release": release,
437 "label": label,
438 "purpose": purpose1,
439 "supported_subarches": image_resource["subarches"],
440 },
441 {
442 "osystem": osystem,
443 "architecture": arch,
444 "subarchitecture": subarch,
445 "release": release,
446 "label": label,
447 "purpose": purpose2,
448 "supported_subarches": image_resource["subarches"],
449 },
450 ],
451 params)
299452
=== modified file 'src/provisioningserver/boot/tftppath.py'
--- src/provisioningserver/boot/tftppath.py 2014-05-19 13:16:53 +0000
+++ src/provisioningserver/boot/tftppath.py 2014-05-20 00:47:19 +0000
@@ -25,6 +25,10 @@
25import os.path25import os.path
2626
27from provisioningserver.driver import OperatingSystemRegistry27from provisioningserver.driver import OperatingSystemRegistry
28from provisioningserver.import_images.boot_image_mapping import (
29 BootImageMapping,
30 )
31from provisioningserver.import_images.helpers import ImageSpec
2832
2933
30logger = getLogger(__name__)34logger = getLogger(__name__)
@@ -112,11 +116,43 @@
112 extend_path(directory, path) for path in paths))116 extend_path(directory, path) for path in paths))
113117
114118
115def extract_image_params(path):119def extract_metadata(metadata, params):
120 """Examine the maas.meta file for any required metadata.
121
122 :param metadata: contents of the maas.meta file
123 :param params: A dict of path components for the image
124 (architecture, subarchitecture, release and label).
125 :return: a dict of name/value metadata pairs. Currently, only
126 "subarches" is extracted.
127 """
128 mapping = BootImageMapping.load_json(metadata)
129
130 image = ImageSpec(
131 arch=params["architecture"],
132 subarch=params["subarchitecture"],
133 release=params["release"],
134 label=params["label"],
135 )
136 try:
137 resource = mapping.mapping[image]
138 except KeyError:
139 return {}
140
141 return dict(supported_subarches=resource["subarches"])
142
143
144def extract_image_params(path, maas_meta):
116 """Represent a list of TFTP path elements as a list of boot-image dicts.145 """Represent a list of TFTP path elements as a list of boot-image dicts.
117146
118 The path must consist of a full [osystem, architecture, subarchitecture,147 :param path: Tuple or list that consists of a full [osystem, architecture,
119 release] that identify a kind of boot that we may need an image for.148 subarchitecture, release] that identify a kind of boot for which we
149 may need an image.
150 :param maas_meta: Contents of the maas.meta file. This may be an
151 empty string.
152
153 :return: A list of dicts, each of which may also include additional
154 items of meta-data that are not elements in the path, such as
155 "subarches".
120 """156 """
121 osystem, arch, subarch, release, label = path157 osystem, arch, subarch, release, label = path
122 osystem_obj = OperatingSystemRegistry.get_item(osystem, default=None)158 osystem_obj = OperatingSystemRegistry.get_item(osystem, default=None)
@@ -125,13 +161,27 @@
125161
126 purposes = osystem_obj.get_boot_image_purposes(162 purposes = osystem_obj.get_boot_image_purposes(
127 arch, subarch, release, label)163 arch, subarch, release, label)
128 return [164
165 # Expand the path into a list of dicts, one for each boot purpose.
166 params = [
129 dict(167 dict(
130 osystem=osystem, architecture=arch, subarchitecture=subarch,168 osystem=osystem, architecture=arch, subarchitecture=subarch,
131 release=release, label=label, purpose=purpose)169 release=release, label=label, purpose=purpose)
132 for purpose in purposes170 for purpose in purposes
133 ]171 ]
134172
173 # Merge in the meta-data.
174 for image_dict in params:
175 metadata = extract_metadata(maas_meta, image_dict)
176 image_dict.update(metadata)
177
178 return params
179
180
181def maas_meta_file_path(tftproot):
182 """Return a string containing the full path to maas.meta."""
183 return os.path.join(tftproot, 'maas.meta')
184
135185
136def list_boot_images(tftproot):186def list_boot_images(tftproot):
137 """List the available boot images.187 """List the available boot images.
@@ -149,9 +199,9 @@
149 # Directory does not exist, so return empty list.199 # Directory does not exist, so return empty list.
150 logger.warning("No boot images have been imported yet.")200 logger.warning("No boot images have been imported yet.")
151 return []201 return []
152 else:202
153 # Other error. Propagate.203 # Other error. Propagate.
154 raise204 raise
155205
156 # Starting point for iteration: paths that contain only the206 # Starting point for iteration: paths that contain only the
157 # top-level subdirectory of tftproot, i.e. the architecture name.207 # top-level subdirectory of tftproot, i.e. the architecture name.
@@ -163,8 +213,21 @@
163 for level in ['arch', 'subarch', 'release', 'label']:213 for level in ['arch', 'subarch', 'release', 'label']:
164 paths = drill_down(tftproot, paths)214 paths = drill_down(tftproot, paths)
165215
216 # Get hold of image meta-data stored in the maas.meta file.
217 meta_file_path = maas_meta_file_path(tftproot)
218 try:
219 with open(meta_file_path, "rb") as f:
220 metadata = f.read()
221 except IOError as e:
222 if e.errno != errno.ENOENT:
223 # Unexpected error, propagate.
224 raise
225 # No meta file (yet), it means no import has run so just skip
226 # it.
227 metadata = ""
228
166 # Each path we find this way should be a boot image.229 # Each path we find this way should be a boot image.
167 # This gets serialised to JSON, so we really have to return a list, not230 # This gets serialised to JSON, so we really have to return a list, not
168 # just any iterable.231 # just any iterable.
169 return list(chain.from_iterable(232 return list(chain.from_iterable(
170 extract_image_params(path) for path in paths))233 extract_image_params(path, metadata) for path in paths))
171234
=== modified file 'src/provisioningserver/import_images/boot_image_mapping.py'
--- src/provisioningserver/import_images/boot_image_mapping.py 2014-04-11 02:14:42 +0000
+++ src/provisioningserver/import_images/boot_image_mapping.py 2014-05-20 00:47:19 +0000
@@ -63,3 +63,30 @@
63 data[arch][subarch].setdefault(release, {})63 data[arch][subarch].setdefault(release, {})
64 data[arch][subarch][release][label] = resource64 data[arch][subarch][release][label] = resource
65 return json.dumps(data, sort_keys=True)65 return json.dumps(data, sort_keys=True)
66
67 @staticmethod
68 def load_json(json_data):
69 """Take a JSON representation and deserialize into an object.
70
71 :param json_data: string produced by dump_json(), above.
72 :return: A BootImageMapping
73
74 If the json data is invalid, an empty BootImageMapping is returned.
75 """
76 mapping = BootImageMapping()
77 try:
78 data = json.loads(json_data)
79 except ValueError:
80 return mapping
81
82 for arch in data:
83 for subarch in data[arch]:
84 for release in data[arch][subarch]:
85 for label in data[arch][subarch][release]:
86 image = ImageSpec(
87 arch=arch, subarch=subarch, release=release,
88 label=label)
89 resource = data[arch][subarch][release][label]
90 mapping.setdefault(image, resource)
91
92 return mapping
6693
=== modified file 'src/provisioningserver/import_images/testing/factory.py'
--- src/provisioningserver/import_images/testing/factory.py 2014-04-08 06:53:34 +0000
+++ src/provisioningserver/import_images/testing/factory.py 2014-05-20 00:47:19 +0000
@@ -15,9 +15,12 @@
15__all__ = [15__all__ = [
16 'make_boot_resource',16 'make_boot_resource',
17 'make_image_spec',17 'make_image_spec',
18 'make_maas_meta',
18 'set_resource',19 'set_resource',
19 ]20 ]
2021
22from textwrap import dedent
23
21from maastesting.factory import factory24from maastesting.factory import factory
22from provisioningserver.import_images.boot_image_mapping import (25from provisioningserver.import_images.boot_image_mapping import (
23 BootImageMapping,26 BootImageMapping,
@@ -25,6 +28,12 @@
25from provisioningserver.import_images.helpers import ImageSpec28from provisioningserver.import_images.helpers import ImageSpec
2629
2730
31def make_maas_meta():
32 """Return fake maas.meta data."""
33 return dedent("""\
34 {"amd64": {"generic": {"precise": {"release": {"content_id": "com.ubuntu.maas:v2:download", "path": "precise/amd64/20140410/raring/generic/boot-kernel", "product_name": "com.ubuntu.maas:v2:boot:12.04:amd64:hwe-r", "subarches": "generic,hwe-p,hwe-q,hwe-r", "version_name": "20140410"}}, "trusty": {"release": {"content_id": "com.ubuntu.maas:v2:download", "path": "trusty/amd64/20140416.1/root-image.gz", "product_name": "com.ubuntu.maas:v2:boot:14.04:amd64:hwe-t", "subarches": "generic,hwe-p,hwe-q,hwe-r,hwe-s,hwe-t", "version_name": "20140416.1"}}}, "hwe-s": {"precise": {"release": {"content_id": "com.ubuntu.maas:v2:download", "path": "precise/amd64/20140410/saucy/generic/boot-kernel", "product_name": "com.ubuntu.maas:v2:boot:12.04:amd64:hwe-s", "subarches": "generic,hwe-p,hwe-q,hwe-r,hwe-s", "version_name": "20140410"}}}}}""") # NOQA
35
36
28def make_boot_resource():37def make_boot_resource():
29 """Create a fake resource dict."""38 """Create a fake resource dict."""
30 return {39 return {
3140
=== modified file 'src/provisioningserver/import_images/tests/test_boot_image_mapping.py'
--- src/provisioningserver/import_images/tests/test_boot_image_mapping.py 2014-04-11 02:13:59 +0000
+++ src/provisioningserver/import_images/tests/test_boot_image_mapping.py 2014-05-20 00:47:19 +0000
@@ -23,6 +23,7 @@
23 )23 )
24from provisioningserver.import_images.testing.factory import (24from provisioningserver.import_images.testing.factory import (
25 make_image_spec,25 make_image_spec,
26 make_maas_meta,
26 set_resource,27 set_resource,
27 )28 )
2829
@@ -104,3 +105,16 @@
104 },105 },
105 },106 },
106 json.loads(image_dict.dump_json()))107 json.loads(image_dict.dump_json()))
108
109 def test_load_json_result_matches_dump_of_own_data(self):
110 # Loading the test data and dumping it again should result in
111 # identical test data.
112 test_meta_file_content = make_maas_meta()
113 mapping = BootImageMapping.load_json(test_meta_file_content)
114 dumped = mapping.dump_json()
115 self.assertEqual(test_meta_file_content, dumped)
116
117 def test_load_json_returns_empty_mapping_for_invalid_json(self):
118 bad_json = ""
119 mapping = BootImageMapping.load_json(bad_json)
120 self.assertEqual({}, mapping.mapping)