Merge lp:~tycho-s/maas/fix-1181334 into lp:~maas-committers/maas/trunk

Proposed by Tycho Andersen
Status: Merged
Approved by: Raphaël Badin
Approved revision: no longer in the source branch.
Merged at revision: 1740
Proposed branch: lp:~tycho-s/maas/fix-1181334
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 168 lines (+72/-14)
4 files modified
src/maasserver/api.py (+21/-14)
src/maasserver/models/bootimage.py (+10/-0)
src/maasserver/models/tests/test_bootimage.py (+30/-0)
src/maasserver/tests/test_api_pxeconfig.py (+11/-0)
To merge this branch: bzr merge lp:~tycho-s/maas/fix-1181334
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Julian Edwards (community) Approve
Review via email: mp+195132@code.launchpad.net

Commit message

Use existing image instead of defaulting to i386

Fixes bug #1181334

Description of the change

Use existing image instead of defaulting to i386

Fixes bug #1181334

To post a comment you must log in.
Revision history for this message
Julian Edwards (julian-edwards) wrote :

Thanks for the fix Tycho. It's got a bug though!

26 + images = BootImage.objects.filter(release=series)

This will not restrict images by nodegroup. Remember that each nodegroup (cluster) has its own set of images.

It would be better if you added a new, tested, BootImagesManager method so it's widely available. Something like:

    get_default_arch_image_in_nodegroup(self, nodegroup, series, purpose):
        """Return the first image available for any architecture in the nodegroup/series supplied."""
        images = BootImage.objects.filter(release=series, nodegroup=nodegroup, purpose=purpose)
        return get_one(images)

You need to test that:
 * it returns None if no images are available
 * it returns the only image if only one image is available
 * it returns the first image if multiple images are available

As an aside, pxeconfig is far too big already and needs better unit tests, but we'll leave that for another day...

Cheers.

review: Needs Fixing
Revision history for this message
Tycho Andersen (tycho-s) wrote :

On Thu, Nov 14, 2013 at 12:30:20AM -0000, Julian Edwards wrote:
> Review: Needs Fixing
>
> Thanks for the fix Tycho. It's got a bug though!
>
> 26 + images = BootImage.objects.filter(release=series)
>
> This will not restrict images by nodegroup. Remember that each nodegroup (cluster) has its own set of images.
>
> It would be better if you added a new, tested, BootImagesManager method so it's widely available. Something like:
>
> get_default_arch_image_in_nodegroup(self, nodegroup, series, purpose):
> """Return the first image available for any architecture in the nodegroup/series supplied."""
> images = BootImage.objects.filter(release=series, nodegroup=nodegroup, purpose=purpose)
> return get_one(images)
>
> You need to test that:
> * it returns None if no images are available
> * it returns the only image if only one image is available
> * it returns the first image if multiple images are available
>
> As an aside, pxeconfig is far too big already and needs better unit tests, but we'll leave that for another day...

Tried to make those changes, except now I can't get the test framework
to behave. As near as I can tell, I'm doing the same thing as I was
before. Thoughts?

\t

Revision history for this message
Gavin Panella (allenap) wrote :

I've tried getting the tests passing, and came up with: http://paste.ubuntu.com/6418301/

There needs to be some ordering in get_default_arch_image_in_nodegroup() to give a stable result.

I had to specify purpose. You were passing None. If that was meant to mean "no preference" then you'll need some more logic in get_default_arch_image_in_nodegroup() too.

Revision history for this message
Tycho Andersen (tycho-s) wrote :

On Thu, Nov 14, 2013 at 10:36:30PM -0000, Gavin Panella wrote:
> I've tried getting the tests passing, and came up with: http://paste.ubuntu.com/6418301/
>
> There needs to be some ordering in get_default_arch_image_in_nodegroup() to give a stable result.
>
> I had to specify purpose. You were passing None. If that was meant to mean "no preference" then you'll need some more logic in get_default_arch_image_in_nodegroup() too.

Ah ha, I felt like ignoring purpose was probably a bad idea. Thanks!

\t

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

Looks ok to me now, unless Gavin has any more to add?

review: Approve
Revision history for this message
Gavin Panella (allenap) wrote :

Nope, looks good.

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 2013-11-15 13:24:55 +0000
3+++ src/maasserver/api.py 2013-11-15 15:29:36 +0000
4@@ -2229,6 +2229,13 @@
5 """
6 node = get_node_from_mac_string(request.GET.get('mac', None))
7
8+ if node is None or node.status == NODE_STATUS.COMMISSIONING:
9+ series = Config.objects.get_config('commissioning_distro_series')
10+ else:
11+ series = node.get_distro_series()
12+
13+ purpose = get_boot_purpose(node)
14+
15 if node:
16 arch, subarch = node.architecture.split('/')
17 preseed_url = compose_preseed_url(node)
18@@ -2238,6 +2245,11 @@
19 nodegroup = node.nodegroup
20 domain = nodegroup.name
21 else:
22+ nodegroup = find_nodegroup_for_pxeconfig_request(request)
23+ preseed_url = compose_enlistment_preseed_url(nodegroup=nodegroup)
24+ hostname = 'maas-enlist'
25+ domain = Config.objects.get_config('enlistment_domain')
26+
27 try:
28 pxelinux_arch = request.GET['arch']
29 except KeyError:
30@@ -2246,9 +2258,15 @@
31 # to pxelinux.cfg/default-<arch>-<subarch> for arch detection.
32 return HttpResponse(status=httplib.NO_CONTENT)
33 else:
34- # Request has already fallen back, so if arch is still not
35- # provided then use i386.
36- arch = ARCHITECTURE.i386.split('/')[0]
37+ # Look in BootImage for an image that actually exists for the
38+ # current series. If nothing is found, fall back to i386 like
39+ # we used to. LP #1181334
40+ image = BootImage.objects.get_default_arch_image_in_nodegroup(
41+ nodegroup, series, purpose=purpose)
42+ if image is None:
43+ arch = ARCHITECTURE.i386.split('/')[0]
44+ else:
45+ arch = image.architecture
46 else:
47 # Map from pxelinux namespace architecture names to MAAS namespace
48 # architecture names. If this gets bigger, an external lookup table
49@@ -2270,16 +2288,6 @@
50 # 1-1 mapping.
51 subarch = pxelinux_subarch
52
53- nodegroup = find_nodegroup_for_pxeconfig_request(request)
54- preseed_url = compose_enlistment_preseed_url(nodegroup=nodegroup)
55- hostname = 'maas-enlist'
56- domain = Config.objects.get_config('enlistment_domain')
57-
58- if node is None or node.status == NODE_STATUS.COMMISSIONING:
59- series = Config.objects.get_config('commissioning_distro_series')
60- else:
61- series = node.get_distro_series()
62-
63 if node is not None:
64 # We don't care if the kernel opts is from the global setting or a tag,
65 # just get the options
66@@ -2289,7 +2297,6 @@
67 # we still need to return the global kernel options.
68 extra_kernel_opts = Config.objects.get_config("kernel_opts")
69
70- purpose = get_boot_purpose(node)
71 server_address = get_maas_facing_server_address(nodegroup=nodegroup)
72 cluster_address = get_mandatory_param(request.GET, "local")
73
74
75=== modified file 'src/maasserver/models/bootimage.py'
76--- src/maasserver/models/bootimage.py 2013-10-07 09:12:40 +0000
77+++ src/maasserver/models/bootimage.py 2013-11-15 15:29:36 +0000
78@@ -25,6 +25,7 @@
79 )
80 from maasserver import DefaultMeta
81 from maasserver.models.nodegroup import NodeGroup
82+from maasserver.utils.orm import get_first
83
84
85 class BootImageManager(Manager):
86@@ -61,6 +62,15 @@
87 except BootImage.DoesNotExist:
88 return False
89
90+ def get_default_arch_image_in_nodegroup(self, nodegroup, series, purpose):
91+ """Return the first image available for any architecture in the
92+ nodegroup/series supplied.
93+ """
94+ images = BootImage.objects.filter(
95+ release=series, nodegroup=nodegroup, purpose=purpose)
96+ images = images.order_by('architecture')
97+ return get_first(images)
98+
99
100 class BootImage(Model):
101 """Available boot image (i.e. kernel and initrd).
102
103=== modified file 'src/maasserver/models/tests/test_bootimage.py'
104--- src/maasserver/models/tests/test_bootimage.py 2013-10-07 09:12:40 +0000
105+++ src/maasserver/models/tests/test_bootimage.py 2013-11-15 15:29:36 +0000
106@@ -17,6 +17,7 @@
107 from maasserver.models import (
108 BootImage,
109 NodeGroup,
110+ Config,
111 )
112 from maasserver.testing.factory import factory
113 from maasserver.testing.testcase import MAASServerTestCase
114@@ -52,3 +53,32 @@
115 BootImage.objects.register_image(self.nodegroup, **params)
116 self.assertTrue(
117 BootImage.objects.have_image(self.nodegroup, **params))
118+
119+ def test_default_arch_image_none(self):
120+ series = Config.objects.get_config('commissioning_distro_series')
121+ result = BootImage.objects.get_default_arch_image_in_nodegroup(
122+ self.nodegroup, series, None)
123+ self.assertIsNone(result)
124+
125+ def test_default_arch_image_one(self):
126+ series = Config.objects.get_config('commissioning_distro_series')
127+ purpose = factory.make_name("purpose")
128+ factory.make_boot_image(
129+ architecture="amd64", release=series, nodegroup=self.nodegroup,
130+ purpose=purpose)
131+ result = BootImage.objects.get_default_arch_image_in_nodegroup(
132+ self.nodegroup, series, purpose=purpose)
133+ self.assertEqual(result.architecture, "amd64")
134+
135+ def test_default_arch_image_many(self):
136+ series = Config.objects.get_config('commissioning_distro_series')
137+ purpose = factory.make_name("purpose")
138+ factory.make_boot_image(
139+ architecture="amd64", release=series, nodegroup=self.nodegroup,
140+ purpose=purpose)
141+ factory.make_boot_image(
142+ architecture="other", release=series, nodegroup=self.nodegroup,
143+ purpose=purpose)
144+ result = BootImage.objects.get_default_arch_image_in_nodegroup(
145+ self.nodegroup, series, purpose=purpose)
146+ self.assertEqual(result.architecture, "amd64")
147
148=== modified file 'src/maasserver/tests/test_api_pxeconfig.py'
149--- src/maasserver/tests/test_api_pxeconfig.py 2013-10-07 09:12:40 +0000
150+++ src/maasserver/tests/test_api_pxeconfig.py 2013-11-15 15:29:36 +0000
151@@ -134,6 +134,17 @@
152 response_dict = json.loads(response.content)
153 self.assertEqual(value, response_dict['extra_opts'])
154
155+ def test_pxeconfig_uses_present_boot_image(self):
156+ release = Config.objects.get_config('commissioning_distro_series')
157+ nodegroup = factory.make_node_group()
158+ factory.make_boot_image(
159+ architecture="amd64", release=release, nodegroup=nodegroup,
160+ purpose="commissioning")
161+ params = self.get_default_params()
162+ params['cluster_uuid'] = nodegroup.uuid
163+ params_out = self.get_pxeconfig(params)
164+ self.assertEqual("amd64", params_out["arch"])
165+
166 def test_pxeconfig_defaults_to_i386_for_default(self):
167 # As a lowest-common-denominator, i386 is chosen when the node is not
168 # yet known to MAAS.