Merge lp:~julian-edwards/maas/nodegroup-on-bootimage-schema-part2 into lp:~maas-committers/maas/trunk

Proposed by Julian Edwards
Status: Merged
Merged at revision: 1297
Proposed branch: lp:~julian-edwards/maas/nodegroup-on-bootimage-schema-part2
Merge into: lp:~maas-committers/maas/trunk
Prerequisite: lp:~julian-edwards/maas/nodegroup-on-bootimage-schema
Diff against target: 356 lines (+85/-52)
11 files modified
src/maasserver/api.py (+4/-1)
src/maasserver/models/bootimage.py (+15/-10)
src/maasserver/preseed.py (+1/-1)
src/maasserver/testing/factory.py (+4/-1)
src/maasserver/tests/test_api.py (+27/-19)
src/maasserver/tests/test_bootimage.py (+20/-9)
src/maasserver/tests/test_preseed.py (+2/-2)
src/maasserver/tests/test_start_up.py (+1/-1)
src/provisioningserver/boot_images.py (+2/-1)
src/provisioningserver/testing/boot_images.py (+2/-4)
src/provisioningserver/tests/test_boot_images.py (+7/-3)
To merge this branch: bzr merge lp:~julian-edwards/maas/nodegroup-on-bootimage-schema-part2
Reviewer Review Type Date Requested Status
Julian Edwards (community) Approve
Gavin Panella (community) Approve
Review via email: mp+131331@code.launchpad.net

Commit message

Enable handling of NodeGroup as a FK on BootImage. All boot images must now be associated with a node group.

Description of the change

Enable handling of NodeGroup as a FK on BootImage. All boot images must now be associated with a node group.

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

Taint bad.

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 2012-10-19 13:55:51 +0000
+++ src/maasserver/api.py 2012-10-25 07:51:20 +0000
@@ -1861,11 +1861,14 @@
1861 `purpose`, all as in the code that determines TFTP paths for1861 `purpose`, all as in the code that determines TFTP paths for
1862 these images.1862 these images.
1863 """1863 """
1864 check_nodegroup_access(request, NodeGroup.objects.ensure_master())1864 nodegroup_uuid = get_mandatory_param(request.data, "nodegroup")
1865 nodegroup = get_object_or_404(NodeGroup, uuid=nodegroup_uuid)
1866 check_nodegroup_access(request, nodegroup)
1865 images = json.loads(get_mandatory_param(request.data, 'images'))1867 images = json.loads(get_mandatory_param(request.data, 'images'))
18661868
1867 for image in images:1869 for image in images:
1868 BootImage.objects.register_image(1870 BootImage.objects.register_image(
1871 nodegroup=nodegroup,
1869 architecture=image['architecture'],1872 architecture=image['architecture'],
1870 subarchitecture=image.get('subarchitecture', 'generic'),1873 subarchitecture=image.get('subarchitecture', 'generic'),
1871 release=image['release'],1874 release=image['release'],
18721875
=== modified file 'src/maasserver/models/bootimage.py'
--- src/maasserver/models/bootimage.py 2012-10-25 07:51:20 +0000
+++ src/maasserver/models/bootimage.py 2012-10-25 07:51:20 +0000
@@ -31,25 +31,30 @@
31 Don't import or instantiate this directly; access as `BootImage.objects`.31 Don't import or instantiate this directly; access as `BootImage.objects`.
32 """32 """
3333
34 def get_by_natural_key(self, architecture, subarchitecture, release,34 def get_by_natural_key(self, nodegroup, architecture, subarchitecture,
35 purpose):35 release, purpose):
36 """Look up a specific image."""36 """Look up a specific image."""
37 return self.get(37 return self.get(
38 architecture=architecture, subarchitecture=subarchitecture,38 nodegroup=nodegroup, architecture=architecture,
39 release=release, purpose=purpose)39 subarchitecture=subarchitecture, release=release,
40 purpose=purpose)
4041
41 def register_image(self, architecture, subarchitecture, release, purpose):42 def register_image(self, nodegroup, architecture, subarchitecture,
43 release, purpose):
42 """Register an image if it wasn't already registered."""44 """Register an image if it wasn't already registered."""
43 self.get_or_create(45 self.get_or_create(
44 architecture=architecture, subarchitecture=subarchitecture,46 nodegroup=nodegroup, architecture=architecture,
45 release=release, purpose=purpose)47 subarchitecture=subarchitecture, release=release,
48 purpose=purpose)
4649
47 def have_image(self, architecture, subarchitecture, release, purpose):50 def have_image(self, nodegroup, architecture, subarchitecture, release,
51 purpose):
48 """Is an image for the given kind of boot available?"""52 """Is an image for the given kind of boot available?"""
49 try:53 try:
50 self.get_by_natural_key(54 self.get_by_natural_key(
51 architecture=architecture, subarchitecture=subarchitecture,55 nodegroup=nodegroup, architecture=architecture,
52 release=release, purpose=purpose)56 subarchitecture=subarchitecture, release=release,
57 purpose=purpose)
53 return True58 return True
54 except BootImage.DoesNotExist:59 except BootImage.DoesNotExist:
55 return False60 return False
5661
=== modified file 'src/maasserver/preseed.py'
--- src/maasserver/preseed.py 2012-10-02 21:07:00 +0000
+++ src/maasserver/preseed.py 2012-10-25 07:51:20 +0000
@@ -242,7 +242,7 @@
242 """Whether or not the SquashFS image can be used during installation."""242 """Whether or not the SquashFS image can be used during installation."""
243 arch, subarch = node.architecture.split("/")243 arch, subarch = node.architecture.split("/")
244 return BootImage.objects.have_image(244 return BootImage.objects.have_image(
245 arch, subarch, node.get_distro_series(), "filesystem")245 node.nodegroup, arch, subarch, node.get_distro_series(), "filesystem")
246246
247247
248def render_preseed(node, prefix, release=''):248def render_preseed(node, prefix, release=''):
249249
=== modified file 'src/maasserver/testing/factory.py'
--- src/maasserver/testing/factory.py 2012-10-02 22:28:07 +0000
+++ src/maasserver/testing/factory.py 2012-10-25 07:51:20 +0000
@@ -325,7 +325,7 @@
325 '%s="%s"' % (key, value) for key, value in items.items()])325 '%s="%s"' % (key, value) for key, value in items.items()])
326326
327 def make_boot_image(self, architecture=None, subarchitecture=None,327 def make_boot_image(self, architecture=None, subarchitecture=None,
328 release=None, purpose=None):328 release=None, purpose=None, nodegroup=None):
329 if architecture is None:329 if architecture is None:
330 architecture = self.make_name('architecture')330 architecture = self.make_name('architecture')
331 if subarchitecture is None:331 if subarchitecture is None:
@@ -334,7 +334,10 @@
334 release = self.make_name('release')334 release = self.make_name('release')
335 if purpose is None:335 if purpose is None:
336 purpose = self.make_name('purpose')336 purpose = self.make_name('purpose')
337 if nodegroup is None:
338 nodegroup = self.make_node_group()
337 return BootImage.objects.create(339 return BootImage.objects.create(
340 nodegroup=nodegroup,
338 architecture=architecture,341 architecture=architecture,
339 subarchitecture=subarchitecture,342 subarchitecture=subarchitecture,
340 release=release,343 release=release,
341344
=== modified file 'src/maasserver/tests/test_api.py'
--- src/maasserver/tests/test_api.py 2012-10-19 13:33:31 +0000
+++ src/maasserver/tests/test_api.py 2012-10-25 07:51:20 +0000
@@ -4026,48 +4026,55 @@
4026 ('celery', FixtureResource(CeleryFixture())),4026 ('celery', FixtureResource(CeleryFixture())),
4027 )4027 )
40284028
4029 def report_images(self, images, client=None):4029 def report_images(self, nodegroup, images, client=None):
4030 if client is None:4030 if client is None:
4031 client = self.client4031 client = self.client
4032 return client.post(4032 return client.post(
4033 reverse('boot_images_handler'),4033 reverse('boot_images_handler'), {
4034 {'op': 'report_boot_images', 'images': json.dumps(images)})4034 'images': json.dumps(images),
4035 'nodegroup': nodegroup.uuid,
4036 'op': 'report_boot_images',
4037 })
40354038
4036 def test_report_boot_images_does_not_work_for_normal_user(self):4039 def test_report_boot_images_does_not_work_for_normal_user(self):
4037 NodeGroup.objects.ensure_master()4040 nodegroup = NodeGroup.objects.ensure_master()
4038 log_in_as_normal_user(self.client)4041 log_in_as_normal_user(self.client)
4039 response = self.report_images([])4042 response = self.report_images(nodegroup, [])
4040 self.assertEqual(httplib.FORBIDDEN, response.status_code)4043 self.assertEqual(
4044 httplib.FORBIDDEN, response.status_code, response.content)
40414045
4042 def test_report_boot_images_works_for_master_worker(self):4046 def test_report_boot_images_works_for_master_worker(self):
4043 client = make_worker_client(NodeGroup.objects.ensure_master())4047 nodegroup = NodeGroup.objects.ensure_master()
4044 response = self.report_images([], client=client)4048 client = make_worker_client(nodegroup)
4049 response = self.report_images(nodegroup, [], client=client)
4045 self.assertEqual(httplib.OK, response.status_code)4050 self.assertEqual(httplib.OK, response.status_code)
40464051
4047 def test_report_boot_images_stores_images(self):4052 def test_report_boot_images_stores_images(self):
4053 nodegroup = NodeGroup.objects.ensure_master()
4048 image = make_boot_image_params()4054 image = make_boot_image_params()
4049 client = make_worker_client(NodeGroup.objects.ensure_master())4055 client = make_worker_client(nodegroup)
4050 response = self.report_images([image], client=client)4056 response = self.report_images(nodegroup, [image], client=client)
4051 self.assertEqual(4057 self.assertEqual(
4052 (httplib.OK, "OK"),4058 (httplib.OK, "OK"),
4053 (response.status_code, response.content))4059 (response.status_code, response.content))
4054 self.assertTrue(4060 self.assertTrue(
4055 BootImage.objects.have_image(**image))4061 BootImage.objects.have_image(nodegroup=nodegroup, **image))
40564062
4057 def test_report_boot_images_ignores_unknown_image_properties(self):4063 def test_report_boot_images_ignores_unknown_image_properties(self):
4064 nodegroup = NodeGroup.objects.ensure_master()
4058 image = make_boot_image_params()4065 image = make_boot_image_params()
4059 image['nonesuch'] = factory.make_name('nonesuch'),4066 image['nonesuch'] = factory.make_name('nonesuch'),
4060 client = make_worker_client(NodeGroup.objects.ensure_master())4067 client = make_worker_client(nodegroup)
4061 response = self.report_images([image], client=client)4068 response = self.report_images(nodegroup, [image], client=client)
4062 self.assertEqual(4069 self.assertEqual(
4063 (httplib.OK, "OK"),4070 (httplib.OK, "OK"),
4064 (response.status_code, response.content))4071 (response.status_code, response.content))
40654072
4066 def test_report_boot_images_warns_if_no_images_found(self):4073 def test_report_boot_images_warns_if_no_images_found(self):
4074 nodegroup = NodeGroup.objects.ensure_master()
4067 recorder = self.patch(api, 'register_persistent_error')4075 recorder = self.patch(api, 'register_persistent_error')
4068 client = make_worker_client(NodeGroup.objects.ensure_master())4076 client = make_worker_client(nodegroup)
40694077 response = self.report_images(nodegroup, [], client=client)
4070 response = self.report_images([], client=client)
4071 self.assertEqual(4078 self.assertEqual(
4072 (httplib.OK, "OK"),4079 (httplib.OK, "OK"),
4073 (response.status_code, response.content))4080 (response.status_code, response.content))
@@ -4079,10 +4086,11 @@
4079 def test_report_boot_images_removes_warning_if_images_found(self):4086 def test_report_boot_images_removes_warning_if_images_found(self):
4080 self.patch(api, 'register_persistent_error')4087 self.patch(api, 'register_persistent_error')
4081 self.patch(api, 'discard_persistent_error')4088 self.patch(api, 'discard_persistent_error')
4082 client = make_worker_client(NodeGroup.objects.ensure_master())4089 nodegroup = factory.make_node_group()
4090 image = make_boot_image_params()
4091 client = make_worker_client(nodegroup)
40834092
4084 response = self.report_images(4093 response = self.report_images(nodegroup, [image], client=client)
4085 [make_boot_image_params()], client=client)
4086 self.assertEqual(4094 self.assertEqual(
4087 (httplib.OK, "OK"),4095 (httplib.OK, "OK"),
4088 (response.status_code, response.content))4096 (response.status_code, response.content))
40894097
=== modified file 'src/maasserver/tests/test_bootimage.py'
--- src/maasserver/tests/test_bootimage.py 2012-09-14 14:16:01 +0000
+++ src/maasserver/tests/test_bootimage.py 2012-10-25 07:51:20 +0000
@@ -12,7 +12,10 @@
12__metaclass__ = type12__metaclass__ = type
13__all__ = []13__all__ = []
1414
15from maasserver.models import BootImage15from maasserver.models import (
16 BootImage,
17 NodeGroup,
18 )
16from maasserver.testing.factory import factory19from maasserver.testing.factory import factory
17from maasserver.testing.testcase import TestCase20from maasserver.testing.testcase import TestCase
18from provisioningserver.testing.boot_images import make_boot_image_params21from provisioningserver.testing.boot_images import make_boot_image_params
@@ -20,22 +23,30 @@
2023
21class TestBootImageManager(TestCase):24class TestBootImageManager(TestCase):
2225
26 def setUp(self):
27 super(TestBootImageManager, self).setUp()
28 self.nodegroup = NodeGroup.objects.ensure_master()
29
23 def test_have_image_returns_False_if_image_not_available(self):30 def test_have_image_returns_False_if_image_not_available(self):
24 self.assertFalse(31 self.assertFalse(
25 BootImage.objects.have_image(**make_boot_image_params()))32 BootImage.objects.have_image(
33 self.nodegroup, **make_boot_image_params()))
2634
27 def test_have_image_returns_True_if_image_available(self):35 def test_have_image_returns_True_if_image_available(self):
28 params = make_boot_image_params()36 params = make_boot_image_params()
29 factory.make_boot_image(**params)37 factory.make_boot_image(nodegroup=self.nodegroup, **params)
30 self.assertTrue(BootImage.objects.have_image(**params))38 self.assertTrue(
39 BootImage.objects.have_image(self.nodegroup, **params))
3140
32 def test_register_image_registers_new_image(self):41 def test_register_image_registers_new_image(self):
33 params = make_boot_image_params()42 params = make_boot_image_params()
34 BootImage.objects.register_image(**params)43 BootImage.objects.register_image(self.nodegroup, **params)
35 self.assertTrue(BootImage.objects.have_image(**params))44 self.assertTrue(
45 BootImage.objects.have_image(self.nodegroup, **params))
3646
37 def test_register_image_leaves_existing_image_intact(self):47 def test_register_image_leaves_existing_image_intact(self):
38 params = make_boot_image_params()48 params = make_boot_image_params()
39 factory.make_boot_image(**params)49 factory.make_boot_image(nodegroup=self.nodegroup, **params)
40 BootImage.objects.register_image(**params)50 BootImage.objects.register_image(self.nodegroup, **params)
41 self.assertTrue(BootImage.objects.have_image(**params))51 self.assertTrue(
52 BootImage.objects.have_image(self.nodegroup, **params))
4253
=== modified file 'src/maasserver/tests/test_preseed.py'
--- src/maasserver/tests/test_preseed.py 2012-10-05 04:21:12 +0000
+++ src/maasserver/tests/test_preseed.py 2012-10-25 07:51:20 +0000
@@ -330,10 +330,10 @@
330 )330 )
331331
332 def test_squashfs_available(self):332 def test_squashfs_available(self):
333 BootImage.objects.register_image(
334 self.arch, self.subarch, self.series, self.purpose)
335 node = factory.make_node(333 node = factory.make_node(
336 architecture="i386/generic", distro_series="quantal")334 architecture="i386/generic", distro_series="quantal")
335 BootImage.objects.register_image(
336 node.nodegroup, self.arch, self.subarch, self.series, self.purpose)
337 self.assertEqual(self.present, is_squashfs_image_present(node))337 self.assertEqual(self.present, is_squashfs_image_present(node))
338338
339339
340340
=== modified file 'src/maasserver/tests/test_start_up.py'
--- src/maasserver/tests/test_start_up.py 2012-09-28 18:42:16 +0000
+++ src/maasserver/tests/test_start_up.py 2012-10-25 07:51:20 +0000
@@ -32,9 +32,9 @@
32 NodeGroup,32 NodeGroup,
33 )33 )
34from maasserver.testing.factory import factory34from maasserver.testing.factory import factory
35from maasserver.testing.testcase import TestCase
35from maastesting.celery import CeleryFixture36from maastesting.celery import CeleryFixture
36from maastesting.fakemethod import FakeMethod37from maastesting.fakemethod import FakeMethod
37from maastesting.testcase import TestCase
38from mock import Mock38from mock import Mock
39from provisioningserver import tasks39from provisioningserver import tasks
40from testresources import FixtureResource40from testresources import FixtureResource
4141
=== modified file 'src/provisioningserver/boot_images.py'
--- src/provisioningserver/boot_images.py 2012-10-19 15:08:52 +0000
+++ src/provisioningserver/boot_images.py 2012-10-25 07:51:20 +0000
@@ -32,6 +32,7 @@
32 )32 )
33from provisioningserver.config import Config33from provisioningserver.config import Config
34from provisioningserver.pxe import tftppath34from provisioningserver.pxe import tftppath
35from provisioningserver.start_cluster_controller import get_cluster_uuid
3536
3637
37task_logger = get_task_logger(name=__name__)38task_logger = get_task_logger(name=__name__)
@@ -56,7 +57,7 @@
56 """Submit images to server."""57 """Submit images to server."""
57 MAASClient(MAASOAuth(*api_credentials), MAASDispatcher(), maas_url).post(58 MAASClient(MAASOAuth(*api_credentials), MAASDispatcher(), maas_url).post(
58 'api/1.0/boot-images/', 'report_boot_images',59 'api/1.0/boot-images/', 'report_boot_images',
59 images=json.dumps(images))60 nodegroup=get_cluster_uuid(), images=json.dumps(images))
6061
6162
62def report_to_server():63def report_to_server():
6364
=== modified file 'src/provisioningserver/testing/boot_images.py'
--- src/provisioningserver/testing/boot_images.py 2012-09-14 14:16:01 +0000
+++ src/provisioningserver/testing/boot_images.py 2012-10-25 07:51:20 +0000
@@ -17,7 +17,7 @@
17from maastesting.factory import factory17from maastesting.factory import factory
1818
1919
20def make_boot_image_params(**kwargs):20def make_boot_image_params():
21 """Create an arbitrary dict of boot-image parameters.21 """Create an arbitrary dict of boot-image parameters.
2222
23 These are the parameters that together describe a kind of boot that we23 These are the parameters that together describe a kind of boot that we
@@ -25,10 +25,8 @@
25 Ubuntu release, and boot purpose. See the `tftppath` module for how25 Ubuntu release, and boot purpose. See the `tftppath` module for how
26 these fit together.26 these fit together.
27 """27 """
28 fields = dict(28 return dict(
29 architecture=factory.make_name('architecture'),29 architecture=factory.make_name('architecture'),
30 subarchitecture=factory.make_name('subarchitecture'),30 subarchitecture=factory.make_name('subarchitecture'),
31 release=factory.make_name('release'),31 release=factory.make_name('release'),
32 purpose=factory.make_name('purpose'))32 purpose=factory.make_name('purpose'))
33 fields.update(kwargs)
34 return fields
3533
=== modified file 'src/provisioningserver/tests/test_boot_images.py'
--- src/provisioningserver/tests/test_boot_images.py 2012-10-04 05:49:09 +0000
+++ src/provisioningserver/tests/test_boot_images.py 2012-10-25 07:51:20 +0000
@@ -15,7 +15,10 @@
15import json15import json
1616
17from apiclient.maas_client import MAASClient17from apiclient.maas_client import MAASClient
18from mock import Mock18from mock import (
19 Mock,
20 sentinel,
21 )
19from provisioningserver import boot_images22from provisioningserver import boot_images
20from provisioningserver.pxe import tftppath23from provisioningserver.pxe import tftppath
21from provisioningserver.testing.boot_images import make_boot_image_params24from provisioningserver.testing.boot_images import make_boot_image_params
@@ -34,11 +37,12 @@
34 self.set_api_credentials()37 self.set_api_credentials()
35 image = make_boot_image_params()38 image = make_boot_image_params()
36 self.patch(tftppath, 'list_boot_images', Mock(return_value=[image]))39 self.patch(tftppath, 'list_boot_images', Mock(return_value=[image]))
40 get_cluster_uuid = self.patch(boot_images, "get_cluster_uuid")
41 get_cluster_uuid.return_value = sentinel.uuid
37 self.patch(MAASClient, 'post')42 self.patch(MAASClient, 'post')
38
39 boot_images.report_to_server()43 boot_images.report_to_server()
40
41 args, kwargs = MAASClient.post.call_args44 args, kwargs = MAASClient.post.call_args
45 self.assertIs(sentinel.uuid, kwargs["nodegroup"])
42 self.assertItemsEqual([image], json.loads(kwargs['images']))46 self.assertItemsEqual([image], json.loads(kwargs['images']))
4347
44 def test_does_nothing_without_maas_url(self):48 def test_does_nothing_without_maas_url(self):