Merge lp:~jtv/maas/bug-1084507 into lp:~maas-committers/maas/trunk

Proposed by Jeroen T. Vermeulen
Status: Merged
Approved by: Jeroen T. Vermeulen
Approved revision: no longer in the source branch.
Merged at revision: 1391
Proposed branch: lp:~jtv/maas/bug-1084507
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 143 lines (+41/-3)
4 files modified
src/maasserver/api.py (+15/-1)
src/maasserver/tests/test_api.py (+16/-2)
src/provisioningserver/tests/test_tftp.py (+5/-0)
src/provisioningserver/tftp.py (+5/-0)
To merge this branch: bzr merge lp:~jtv/maas/bug-1084507
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Needs Information
Julian Edwards (community) Approve
Review via email: mp+137755@code.launchpad.net

Commit message

Pass cluster uuid to pxeconfig API call, so it knows which cluster a request comes from.

Description of the change

The pxeconfig call used to figure out which cluster it should write a config for by inspecting the requesting IP address. That won't work in the general case because the request is (now) made by the cluster controller, from an IP address that isn't on any cluster-managed network interface.

Discussed this with Julian. We really want to avoid dragging celery config into the tftpd process, but that's where we keep a cluster's uuid at the moment. The plan is to move that into maas_cluster.conf, but in a separate branch. It's nontrivial yak-shaving. This does mean that this branch by itself won't solve the problem for cluster controllers that aren't running on the region controller: by default, the process will still read the default celery config rather than the one we manage for cluster controllers. Setting the right environment variable in the pserv startup job might fix that, but we're planning to go with the "nicer" fix.

Jeroen

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

This wasn't *quite* what I had in mind in our pre-imp, but it's a reasonable start. I was hoping that you'd fix the pserv so it gets the UUID value from a new (duplicate) location in the /etc/maas/maas_cluster.conf file, and then clean up the *duplication* later.

8 +def find_cluster_for_pxeconfig_request(request):
...
15 + return find_nodegroup(request)

We really need to be more consistent with naming; "cluster" externally and "nodegroup" internally. In fact, cluster really refers to the whole set of processes running on that edifice, whereas nodegroup is the name of a model.

Therefore I'd change that function to:
def find_nodegroup_for_pxeconfig_request(request)

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

I changed the name of find_cluster_for_pxeconfig_request. I'm sorry about the misunderstanding about when to duplicate the CLUSTER_UUID setting: the work to make it happen is not entirely trivial because an installation may already have a setting in the celeryconfig but not in the cluster config, or it may have both, or neither. All in untestable shell scripts, of course. I really wanted to keep that to a separate branch.

Revision history for this message
Raphaël Badin (rvb) wrote :

'find_nodegroup_for_pxeconfig_request_uses_cluster_uuid' is not a test because it does not start with 'test' and the method it tests, 'find_nodegroup_for_pxeconfig_request' does not seem to be looking for the param 'cluster_uuid' inside the request… What am I missing?

review: Needs Information
Revision history for this message
Raphaël Badin (rvb) wrote :

2 more things about 'RequestFactory.get(reverse('pxeconfig'), **params)':

- RequestFactory is, well, a factory and is supposed to be instantiated before being used (see https://docs.djangoproject.com/en/dev/topics/testing/#django.test.client.RequestFactory)

- RequestFactory().get('path', **params) will put the params in request.META; I don't think that's what you want here.

Revision history for this message
Raphaël Badin (rvb) wrote :

Happy to be told otherwise, but I think we need this: http://paste.ubuntu.com/1427119/

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

Thanks Raphaël. Reported (and fixed) that as bug 1089464.

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 2012-11-29 11:30:37 +0000
3+++ src/maasserver/api.py 2012-12-04 07:37:22 +0000
4@@ -1598,6 +1598,16 @@
5 return macaddress.node if macaddress else None
6
7
8+def find_nodegroup_for_pxeconfig_request(request):
9+ """Find the nodegroup responsible for a `pxeconfig` request.
10+
11+ Looks for the `cluster_uuid` parameter in the request. If there is
12+ none, figures it out based on the requesting IP as a compatibility
13+ measure. In that case, the result may be incorrect.
14+ """
15+ return find_nodegroup(request)
16+
17+
18 def pxeconfig(request):
19 """Get the PXE configuration given a node's details.
20
21@@ -1621,6 +1631,10 @@
22 :param subarch: Subarchitecture name (in the pxelinux namespace).
23 :param local: The IP address of the cluster controller.
24 :param remote: The IP address of the booting node.
25+ :param cluster_uuid: UUID of the cluster responsible for this node.
26+ If omitted, the call will attempt to figure it out based on the
27+ requesting IP address, for compatibility. Passing `cluster_uuid`
28+ is preferred.
29 """
30 node = get_node_from_mac_string(request.GET.get('mac', None))
31
32@@ -1665,7 +1679,7 @@
33 # 1-1 mapping.
34 subarch = pxelinux_subarch
35
36- nodegroup = find_nodegroup(request)
37+ nodegroup = find_nodegroup_for_pxeconfig_request(request)
38 preseed_url = compose_enlistment_preseed_url(nodegroup=nodegroup)
39 hostname = 'maas-enlist'
40 domain = Config.objects.get_config('enlistment_domain')
41
42=== modified file 'src/maasserver/tests/test_api.py'
43--- src/maasserver/tests/test_api.py 2012-11-28 15:37:34 +0000
44+++ src/maasserver/tests/test_api.py 2012-12-04 07:37:22 +0000
45@@ -56,6 +56,7 @@
46 describe,
47 DISPLAYED_NODEGROUP_FIELDS,
48 extract_constraints,
49+ find_nodegroup_for_pxeconfig_request,
50 store_node_power_parameters,
51 )
52 from maasserver.enum import (
53@@ -3361,7 +3362,7 @@
54 self.get_pxeconfig(),
55 ContainsAll(KernelParameters._fields))
56
57- def test_pxeconfig_returns_data_for_known_node(self):
58+ def test_pxeconfig_returns_success_for_known_node(self):
59 params = self.get_mac_params()
60 response = self.client.get(reverse('pxeconfig'), params)
61 self.assertEqual(httplib.OK, response.status_code)
62@@ -3371,7 +3372,7 @@
63 response = self.client.get(reverse('pxeconfig'), params)
64 self.assertEqual(httplib.NO_CONTENT, response.status_code)
65
66- def test_pxeconfig_returns_data_for_detailed_but_unknown_node(self):
67+ def test_pxeconfig_returns_success_for_detailed_but_unknown_node(self):
68 architecture = factory.getRandomEnum(ARCHITECTURE)
69 arch, subarch = architecture.split('/')
70 params = dict(
71@@ -3484,6 +3485,19 @@
72 compose_preseed_url(node),
73 json.loads(response.content)["preseed_url"])
74
75+ def find_nodegroup_for_pxeconfig_request_uses_cluster_uuid(self):
76+ # find_nodegroup_for_pxeconfig_request returns the nodegroup
77+ # identified by the cluster_uuid parameter, if given. It
78+ # completely ignores the other node or request details, as shown
79+ # here by passing a uuid for a different cluster.
80+ params = self.get_mac_params()
81+ nodegroup = factory.make_node_group()
82+ params['cluster_uuid'] = nodegroup.uuid
83+ request = RequestFactory.get(reverse('pxeconfig'), **params)
84+ self.assertEqual(
85+ nodegroup,
86+ find_nodegroup_for_pxeconfig_request(request))
87+
88 def test_preseed_url_for_known_node_uses_nodegroup_maas_url(self):
89 ng_url = 'http://%s' % factory.make_name('host')
90 network = IPNetwork("10.1.1/24")
91
92=== modified file 'src/provisioningserver/tests/test_tftp.py'
93--- src/provisioningserver/tests/test_tftp.py 2012-11-08 06:34:48 +0000
94+++ src/provisioningserver/tests/test_tftp.py 2012-12-04 07:37:22 +0000
95@@ -23,6 +23,7 @@
96
97 from maastesting.factory import factory
98 from maastesting.testcase import TestCase
99+from provisioningserver import tftp as tftp_module
100 from provisioningserver.pxe.tftppath import compose_config_path
101 from provisioningserver.tests.test_kernel_opts import make_kernel_parameters
102 from provisioningserver.tftp import (
103@@ -211,6 +212,9 @@
104 def test_get_reader_config_file(self):
105 # For paths matching re_config_file, TFTPBackend.get_reader() returns
106 # a Deferred that will yield a BytesReader.
107+ cluster_uuid = factory.getRandomUUID()
108+ self.patch(tftp_module, 'get_cluster_uuid').return_value = (
109+ cluster_uuid)
110 mac = factory.getRandomMACAddress(b"-")
111 config_path = compose_config_path(mac)
112 backend = TFTPBackend(self.make_dir(), b"http://example.com/")
113@@ -240,6 +244,7 @@
114 "mac": mac,
115 "local": call_context["local"][0], # address only.
116 "remote": call_context["remote"][0], # address only.
117+ "cluster_uuid": cluster_uuid,
118 }
119 observed_params = json.loads(output)
120 self.assertEqual(expected_params, observed_params)
121
122=== modified file 'src/provisioningserver/tftp.py'
123--- src/provisioningserver/tftp.py 2012-11-08 06:34:48 +0000
124+++ src/provisioningserver/tftp.py 2012-12-04 07:37:22 +0000
125@@ -28,6 +28,7 @@
126 from provisioningserver.enum import ARP_HTYPE
127 from provisioningserver.kernel_opts import KernelParameters
128 from provisioningserver.pxe.config import render_pxe_config
129+from provisioningserver.start_cluster_controller import get_cluster_uuid
130 from provisioningserver.utils import deferred
131 from tftp.backend import (
132 FilesystemSynchronousBackend,
133@@ -217,6 +218,10 @@
134 params["local"] = local_host
135 remote_host, remote_port = get("remote", (None, None))
136 params["remote"] = remote_host
137+ # XXX 2012-12-04 JeroenVermeulen bug=1086239: move
138+ # get_cluster_uuid to a properly reusable location, and
139+ # implement it without the celery import (and side effects).
140+ params["cluster_uuid"] = get_cluster_uuid()
141 d = self.get_config_reader(params)
142 d.addErrback(self.get_page_errback, file_name)
143 return d