Merge lp:~gmb/maas/label-in-pxe-config into lp:~maas-committers/maas/trunk

Proposed by Graham Binns
Status: Merged
Approved by: Graham Binns
Approved revision: no longer in the source branch.
Merged at revision: 2139
Proposed branch: lp:~gmb/maas/label-in-pxe-config
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 134 lines (+96/-1)
4 files modified
src/maasserver/api.py (+16/-1)
src/maasserver/models/bootimage.py (+8/-0)
src/maasserver/models/tests/test_bootimage.py (+59/-0)
src/maasserver/tests/test_api_pxeconfig.py (+13/-0)
To merge this branch: bzr merge lp:~gmb/maas/label-in-pxe-config
Reviewer Review Type Date Requested Status
Jeroen T. Vermeulen (community) Approve
Review via email: mp+211480@code.launchpad.net

Commit message

Use the label of the latest available image for (nodegroup, arch, subarch, release, purpose) as the default label in pxeconfig(). Previously we always fell back to 'release' as the default label if one wasn't specified.

Description of the change

This was a fairly simple change but I'm not entirely happy with the if...else at line 15-18 of the diff. I put this in to avoid several pserv tests blowing up, but it now feels slightly lazy... Please tell me if I'm right about that and I'll go and fix up the tests and take that block out.

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

Thanks for filing the bug. In these hectic days it's good to know we're keeping track.

The division of work here strikes me as very sensible.

I think the indentation in the test is missing something: the list comprehension for actual_images does not indent the arguments to get_latest_image.

No test for the case where you return no-such-image?

The structure of the test there is seems like it tries to do too many things, and the result is a bit muddled. I had to read it a few times to figure out your intention. Why not do a bit more to pry apart the behaviours you're trying to establish? “Returns the most recent matching image” is an easier test than what you have here — one purpose, one result. “Does not return image with different purpose” can also be simpler and clearer: create matching image, create newer image that matches except it's for a different purpose, show that the older (but matching) image is returned. And of course it'd be easy to throw in other newer images that differ from the matching one only in arch, subarch, or release — so that the test actually covers selection by the criteria other than purpose.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/api.py'
2--- src/maasserver/api.py 2014-03-18 03:23:55 +0000
3+++ src/maasserver/api.py 2014-03-18 14:42:32 +0000
4@@ -2377,7 +2377,22 @@
5
6 subarch = get_optional_param(request.GET, 'subarch', 'generic')
7
8- label = get_optional_param(request.GET, 'label', 'release')
9+ # We use as our default label the label of the most recent image for
10+ # the criteria we've assembled above. If there is no latest image
11+ # (which should never happen in reality but may happen in tests), we
12+ # fall back to using 'no-such-image' as our default.
13+ latest_image = BootImage.objects.get_latest_image(
14+ nodegroup, arch, subarch, series, purpose)
15+ if latest_image is None:
16+ # XXX 2014-03-18 gmb bug=1294131:
17+ # We really ought to raise an exception here so that client
18+ # and server can handle it according to their needs. At the
19+ # moment, though, that breaks too many tests in awkward
20+ # ways.
21+ latest_label = 'no-such-image'
22+ else:
23+ latest_label = latest_image.label
24+ label = get_optional_param(request.GET, 'label', latest_label)
25
26 if node is not None:
27 # We don't care if the kernel opts is from the global setting or a tag,
28
29=== modified file 'src/maasserver/models/bootimage.py'
30--- src/maasserver/models/bootimage.py 2014-03-11 23:08:35 +0000
31+++ src/maasserver/models/bootimage.py 2014-03-18 14:42:32 +0000
32@@ -106,6 +106,14 @@
33 nodegroup, 'install')
34 return arches_commissioning & arches_install
35
36+ def get_latest_image(self, nodegroup, architecture, subarchitecture,
37+ release, purpose):
38+ """Return the latest image for a set of criteria."""
39+ return BootImage.objects.filter(
40+ nodegroup=nodegroup, architecture=architecture,
41+ subarchitecture=subarchitecture, release=release,
42+ purpose=purpose).order_by('id').last()
43+
44
45 class BootImage(Model):
46 """Available boot image (i.e. kernel and initrd).
47
48=== modified file 'src/maasserver/models/tests/test_bootimage.py'
49--- src/maasserver/models/tests/test_bootimage.py 2014-03-11 12:19:40 +0000
50+++ src/maasserver/models/tests/test_bootimage.py 2014-03-18 14:42:32 +0000
51@@ -162,3 +162,62 @@
52 self.assertItemsEqual(
53 [],
54 BootImage.objects.get_usable_architectures(nodegroup))
55+
56+ def test_get_latest_image_returns_latest_image_for_criteria(self):
57+ arch = factory.make_name('arch')
58+ subarch = factory.make_name('sub')
59+ release = factory.make_name('release')
60+ nodegroup = factory.make_node_group()
61+ purpose = factory.make_name("purpose")
62+ boot_image = factory.make_boot_image(
63+ nodegroup=nodegroup, architecture=arch,
64+ subarchitecture=subarch, release=release, purpose=purpose,
65+ label=factory.make_name('label'))
66+ self.assertEqual(
67+ boot_image,
68+ BootImage.objects.get_latest_image(
69+ nodegroup, arch, subarch, release, purpose))
70+
71+ def test_get_latest_image_doesnt_return_images_for_other_purposes(self):
72+ arch = factory.make_name('arch')
73+ subarch = factory.make_name('sub')
74+ release = factory.make_name('release')
75+ nodegroup = factory.make_node_group()
76+ purpose = factory.make_name("purpose")
77+ relevant_image = factory.make_boot_image(
78+ nodegroup=nodegroup, architecture=arch,
79+ subarchitecture=subarch, release=release, purpose=purpose,
80+ label=factory.make_name('label'))
81+
82+ # Create a bunch of more recent but irrelevant BootImages..
83+ factory.make_boot_image(
84+ nodegroup=factory.make_node_group(), architecture=arch,
85+ subarchitecture=subarch, release=release,
86+ purpose=purpose, label=factory.make_name('label'))
87+ factory.make_boot_image(
88+ nodegroup=nodegroup,
89+ architecture=factory.make_name('arch'),
90+ subarchitecture=subarch, release=release, purpose=purpose,
91+ label=factory.make_name('label'))
92+ factory.make_boot_image(
93+ nodegroup=nodegroup, architecture=arch,
94+ subarchitecture=factory.make_name('subarch'),
95+ release=release, purpose=purpose,
96+ label=factory.make_name('label'))
97+ factory.make_boot_image(
98+ nodegroup=nodegroup,
99+ architecture=factory.make_name('arch'),
100+ subarchitecture=subarch,
101+ release=factory.make_name('release'), purpose=purpose,
102+ label=factory.make_name('label'))
103+ factory.make_boot_image(
104+ nodegroup=nodegroup,
105+ architecture=factory.make_name('arch'),
106+ subarchitecture=subarch, release=release,
107+ purpose=factory.make_name('purpose'),
108+ label=factory.make_name('label'))
109+
110+ self.assertEqual(
111+ relevant_image,
112+ BootImage.objects.get_latest_image(
113+ nodegroup, arch, subarch, release, purpose))
114
115=== modified file 'src/maasserver/tests/test_api_pxeconfig.py'
116--- src/maasserver/tests/test_api_pxeconfig.py 2014-03-06 05:46:50 +0000
117+++ src/maasserver/tests/test_api_pxeconfig.py 2014-03-18 14:42:32 +0000
118@@ -341,3 +341,16 @@
119 params['mac'] = mac.mac_address
120 pxe_config = self.get_pxeconfig(params)
121 self.assertEqual(None, pxe_config['extra_opts'])
122+
123+ def test_pxeconfig_sets_nonsense_label_for_insane_state(self):
124+ # If pxeconfig() encounters a state where there is no relevant
125+ # BootImage for a given set of (nodegroup, arch, subarch,
126+ # release, purpose) it sets the label to no-such-image. This is
127+ # clearly nonsensical, but this state only arises during tests
128+ # or an insane environment.
129+ mac = factory.make_mac_address()
130+ params = self.get_default_params()
131+ params['mac'] = mac.mac_address
132+ params['arch'] = 'iHaveNoIdea'
133+ pxe_config = self.get_pxeconfig(params)
134+ self.assertEqual('no-such-image', pxe_config['label'])