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
=== modified file 'src/maasserver/api.py'
--- src/maasserver/api.py 2014-03-18 03:23:55 +0000
+++ src/maasserver/api.py 2014-03-18 14:42:32 +0000
@@ -2377,7 +2377,22 @@
23772377
2378 subarch = get_optional_param(request.GET, 'subarch', 'generic')2378 subarch = get_optional_param(request.GET, 'subarch', 'generic')
23792379
2380 label = get_optional_param(request.GET, 'label', 'release')2380 # We use as our default label the label of the most recent image for
2381 # the criteria we've assembled above. If there is no latest image
2382 # (which should never happen in reality but may happen in tests), we
2383 # fall back to using 'no-such-image' as our default.
2384 latest_image = BootImage.objects.get_latest_image(
2385 nodegroup, arch, subarch, series, purpose)
2386 if latest_image is None:
2387 # XXX 2014-03-18 gmb bug=1294131:
2388 # We really ought to raise an exception here so that client
2389 # and server can handle it according to their needs. At the
2390 # moment, though, that breaks too many tests in awkward
2391 # ways.
2392 latest_label = 'no-such-image'
2393 else:
2394 latest_label = latest_image.label
2395 label = get_optional_param(request.GET, 'label', latest_label)
23812396
2382 if node is not None:2397 if node is not None:
2383 # We don't care if the kernel opts is from the global setting or a tag,2398 # We don't care if the kernel opts is from the global setting or a tag,
23842399
=== modified file 'src/maasserver/models/bootimage.py'
--- src/maasserver/models/bootimage.py 2014-03-11 23:08:35 +0000
+++ src/maasserver/models/bootimage.py 2014-03-18 14:42:32 +0000
@@ -106,6 +106,14 @@
106 nodegroup, 'install')106 nodegroup, 'install')
107 return arches_commissioning & arches_install107 return arches_commissioning & arches_install
108108
109 def get_latest_image(self, nodegroup, architecture, subarchitecture,
110 release, purpose):
111 """Return the latest image for a set of criteria."""
112 return BootImage.objects.filter(
113 nodegroup=nodegroup, architecture=architecture,
114 subarchitecture=subarchitecture, release=release,
115 purpose=purpose).order_by('id').last()
116
109117
110class BootImage(Model):118class BootImage(Model):
111 """Available boot image (i.e. kernel and initrd).119 """Available boot image (i.e. kernel and initrd).
112120
=== modified file 'src/maasserver/models/tests/test_bootimage.py'
--- src/maasserver/models/tests/test_bootimage.py 2014-03-11 12:19:40 +0000
+++ src/maasserver/models/tests/test_bootimage.py 2014-03-18 14:42:32 +0000
@@ -162,3 +162,62 @@
162 self.assertItemsEqual(162 self.assertItemsEqual(
163 [],163 [],
164 BootImage.objects.get_usable_architectures(nodegroup))164 BootImage.objects.get_usable_architectures(nodegroup))
165
166 def test_get_latest_image_returns_latest_image_for_criteria(self):
167 arch = factory.make_name('arch')
168 subarch = factory.make_name('sub')
169 release = factory.make_name('release')
170 nodegroup = factory.make_node_group()
171 purpose = factory.make_name("purpose")
172 boot_image = factory.make_boot_image(
173 nodegroup=nodegroup, architecture=arch,
174 subarchitecture=subarch, release=release, purpose=purpose,
175 label=factory.make_name('label'))
176 self.assertEqual(
177 boot_image,
178 BootImage.objects.get_latest_image(
179 nodegroup, arch, subarch, release, purpose))
180
181 def test_get_latest_image_doesnt_return_images_for_other_purposes(self):
182 arch = factory.make_name('arch')
183 subarch = factory.make_name('sub')
184 release = factory.make_name('release')
185 nodegroup = factory.make_node_group()
186 purpose = factory.make_name("purpose")
187 relevant_image = factory.make_boot_image(
188 nodegroup=nodegroup, architecture=arch,
189 subarchitecture=subarch, release=release, purpose=purpose,
190 label=factory.make_name('label'))
191
192 # Create a bunch of more recent but irrelevant BootImages..
193 factory.make_boot_image(
194 nodegroup=factory.make_node_group(), architecture=arch,
195 subarchitecture=subarch, release=release,
196 purpose=purpose, label=factory.make_name('label'))
197 factory.make_boot_image(
198 nodegroup=nodegroup,
199 architecture=factory.make_name('arch'),
200 subarchitecture=subarch, release=release, purpose=purpose,
201 label=factory.make_name('label'))
202 factory.make_boot_image(
203 nodegroup=nodegroup, architecture=arch,
204 subarchitecture=factory.make_name('subarch'),
205 release=release, purpose=purpose,
206 label=factory.make_name('label'))
207 factory.make_boot_image(
208 nodegroup=nodegroup,
209 architecture=factory.make_name('arch'),
210 subarchitecture=subarch,
211 release=factory.make_name('release'), purpose=purpose,
212 label=factory.make_name('label'))
213 factory.make_boot_image(
214 nodegroup=nodegroup,
215 architecture=factory.make_name('arch'),
216 subarchitecture=subarch, release=release,
217 purpose=factory.make_name('purpose'),
218 label=factory.make_name('label'))
219
220 self.assertEqual(
221 relevant_image,
222 BootImage.objects.get_latest_image(
223 nodegroup, arch, subarch, release, purpose))
165224
=== modified file 'src/maasserver/tests/test_api_pxeconfig.py'
--- src/maasserver/tests/test_api_pxeconfig.py 2014-03-06 05:46:50 +0000
+++ src/maasserver/tests/test_api_pxeconfig.py 2014-03-18 14:42:32 +0000
@@ -341,3 +341,16 @@
341 params['mac'] = mac.mac_address341 params['mac'] = mac.mac_address
342 pxe_config = self.get_pxeconfig(params)342 pxe_config = self.get_pxeconfig(params)
343 self.assertEqual(None, pxe_config['extra_opts'])343 self.assertEqual(None, pxe_config['extra_opts'])
344
345 def test_pxeconfig_sets_nonsense_label_for_insane_state(self):
346 # If pxeconfig() encounters a state where there is no relevant
347 # BootImage for a given set of (nodegroup, arch, subarch,
348 # release, purpose) it sets the label to no-such-image. This is
349 # clearly nonsensical, but this state only arises during tests
350 # or an insane environment.
351 mac = factory.make_mac_address()
352 params = self.get_default_params()
353 params['mac'] = mac.mac_address
354 params['arch'] = 'iHaveNoIdea'
355 pxe_config = self.get_pxeconfig(params)
356 self.assertEqual('no-such-image', pxe_config['label'])