Merge lp:~blake-rouse/maas/fix-1487135 into lp:~maas-committers/maas/trunk
- fix-1487135
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Blake Rouse |
Approved revision: | no longer in the source branch. |
Merged at revision: | 4471 |
Proposed branch: | lp:~blake-rouse/maas/fix-1487135 |
Merge into: | lp:~maas-committers/maas/trunk |
Diff against target: |
259 lines (+130/-12) 5 files modified
src/maasserver/api/nodes.py (+27/-0) src/maasserver/api/tests/test_doc.py (+1/-0) src/maasserver/api/tests/test_node.py (+42/-0) src/maasserver/preseed.py (+26/-12) src/maasserver/tests/test_preseed.py (+34/-0) |
To merge this branch: | bzr merge lp:~blake-rouse/maas/fix-1487135 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mike Pontillo (community) | Approve | ||
Jeffrey C Jones (community) | Approve | ||
Review via email: mp+276689@code.launchpad.net |
Commit message
Add get_curtin_config API to return the curtin configuration that was passed when the node started deployment.
Description of the change
Blake Rouse (blake-rouse) wrote : | # |
Jeffrey C Jones (trapnine) wrote : | # |
looks good, just a comment typo
Mike Pontillo (mpontillo) wrote : | # |
This is great. Thanks for adding this.
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~blake-rouse/maas/fix-1487135 into lp:maas failed. Below is the output from the failed tests.
Get:1 http://
Ign http://
Get:2 http://
Hit http://
Hit http://
Get:3 http://
Get:4 http://
Get:5 http://
Get:6 http://
Get:7 http://
Get:8 http://
Get:9 http://
Hit http://
Hit http://
Get:10 http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Ign http://
Ign http://
Fetched 2,092 kB in 4s (502 kB/s)
Reading package lists...
sudo DEBIAN_
--
Andres Rodriguez (andreserl) wrote : | # |
should we have get_storage_config and get_network_config or something along those lines?
Blake Rouse (blake-rouse) wrote : | # |
No because it is the same config. It is shown in the output.
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~blake-rouse/maas/fix-1487135 into lp:maas failed. Below is the output from the failed tests.
Get:1 http://
Ign http://
Get:2 http://
Get:3 http://
Get:4 http://
Get:5 http://
Hit http://
Hit http://
Get:6 http://
Get:7 http://
Hit http://
Hit http://
Get:8 http://
Get:9 http://
Get:10 http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Ign http://
Ign http://
Fetched 2,092 kB in 4s (493 kB/s)
Reading package lists...
sudo DEBIAN_
--
Preview Diff
1 | === modified file 'src/maasserver/api/nodes.py' | |||
2 | --- src/maasserver/api/nodes.py 2015-11-04 23:38:04 +0000 | |||
3 | +++ src/maasserver/api/nodes.py 2015-11-05 14:17:35 +0000 | |||
4 | @@ -78,6 +78,7 @@ | |||
5 | 78 | from maasserver.models.node import RELEASABLE_STATUSES | 78 | from maasserver.models.node import RELEASABLE_STATUSES |
6 | 79 | from maasserver.models.nodeprobeddetails import get_single_probed_details | 79 | from maasserver.models.nodeprobeddetails import get_single_probed_details |
7 | 80 | from maasserver.node_constraint_filter_forms import AcquireNodeForm | 80 | from maasserver.node_constraint_filter_forms import AcquireNodeForm |
8 | 81 | from maasserver.preseed import get_curtin_merged_config | ||
9 | 81 | from maasserver.rpc import getClientFor | 82 | from maasserver.rpc import getClientFor |
10 | 82 | from maasserver.storage_layouts import ( | 83 | from maasserver.storage_layouts import ( |
11 | 83 | StorageLayoutError, | 84 | StorageLayoutError, |
12 | @@ -96,6 +97,7 @@ | |||
13 | 96 | from provisioningserver.rpc.cluster import PowerQuery | 97 | from provisioningserver.rpc.cluster import PowerQuery |
14 | 97 | from provisioningserver.rpc.exceptions import NoConnectionsAvailable | 98 | from provisioningserver.rpc.exceptions import NoConnectionsAvailable |
15 | 98 | import simplejson as json | 99 | import simplejson as json |
16 | 100 | import yaml | ||
17 | 99 | 101 | ||
18 | 100 | # Node's fields exposed on the API. | 102 | # Node's fields exposed on the API. |
19 | 101 | DISPLAYED_NODE_FIELDS = ( | 103 | DISPLAYED_NODE_FIELDS = ( |
20 | @@ -930,6 +932,10 @@ | |||
21 | 930 | If the default gateways need to be specific for this node you can set | 932 | If the default gateways need to be specific for this node you can set |
22 | 931 | which interface and subnet's gateway to use when this node is deployed | 933 | which interface and subnet's gateway to use when this node is deployed |
23 | 932 | with the `node-interfaces set-default-gateway` API. | 934 | with the `node-interfaces set-default-gateway` API. |
24 | 935 | |||
25 | 936 | Returns 404 if the node could not be found. | ||
26 | 937 | Returns 403 if the user does not have permission to clear the default | ||
27 | 938 | gateways. | ||
28 | 933 | """ | 939 | """ |
29 | 934 | node = Node.nodes.get_node_or_404( | 940 | node = Node.nodes.get_node_or_404( |
30 | 935 | system_id=system_id, user=request.user, perm=NODE_PERMISSION.ADMIN) | 941 | system_id=system_id, user=request.user, perm=NODE_PERMISSION.ADMIN) |
31 | @@ -938,6 +944,27 @@ | |||
32 | 938 | node.save() | 944 | node.save() |
33 | 939 | return node | 945 | return node |
34 | 940 | 946 | ||
35 | 947 | @operation(idempotent=True) | ||
36 | 948 | def get_curtin_config(self, request, system_id): | ||
37 | 949 | """Return the rendered curtin configuration for the node. | ||
38 | 950 | |||
39 | 951 | Returns 404 if the node could not be found. | ||
40 | 952 | Returns 403 if the user does not have permission to get the curtin | ||
41 | 953 | configuration. | ||
42 | 954 | """ | ||
43 | 955 | node = Node.nodes.get_node_or_404( | ||
44 | 956 | system_id=system_id, user=request.user, perm=NODE_PERMISSION.VIEW) | ||
45 | 957 | if node.status not in [ | ||
46 | 958 | NODE_STATUS.DEPLOYING, | ||
47 | 959 | NODE_STATUS.DEPLOYED, | ||
48 | 960 | NODE_STATUS.FAILED_DEPLOYMENT]: | ||
49 | 961 | raise MAASAPIBadRequest( | ||
50 | 962 | "Node %s is not in a deployment state." % node.hostname) | ||
51 | 963 | return HttpResponse( | ||
52 | 964 | yaml.safe_dump( | ||
53 | 965 | get_curtin_merged_config(node), default_flow_style=False), | ||
54 | 966 | content_type='text/plain') | ||
55 | 967 | |||
56 | 941 | 968 | ||
57 | 942 | def create_node(request): | 969 | def create_node(request): |
58 | 943 | """Service an http request to create a node. | 970 | """Service an http request to create a node. |
59 | 944 | 971 | ||
60 | === modified file 'src/maasserver/api/tests/test_doc.py' | |||
61 | --- src/maasserver/api/tests/test_doc.py 2015-09-15 19:07:22 +0000 | |||
62 | +++ src/maasserver/api/tests/test_doc.py 2015-11-05 14:17:35 +0000 | |||
63 | @@ -323,6 +323,7 @@ | |||
64 | 323 | "POST set_storage_layout op=set_storage_layout restful=False", | 323 | "POST set_storage_layout op=set_storage_layout restful=False", |
65 | 324 | "POST clear_default_gateways op=clear_default_gateways " | 324 | "POST clear_default_gateways op=clear_default_gateways " |
66 | 325 | "restful=False", | 325 | "restful=False", |
67 | 326 | "GET get_curtin_config op=get_curtin_config restful=False", | ||
68 | 326 | } | 327 | } |
69 | 327 | observed_actions = { | 328 | observed_actions = { |
70 | 328 | "%(method)s %(name)s op=%(op)s restful=%(restful)s" % action | 329 | "%(method)s %(name)s op=%(op)s restful=%(restful)s" % action |
71 | 329 | 330 | ||
72 | === modified file 'src/maasserver/api/tests/test_node.py' | |||
73 | --- src/maasserver/api/tests/test_node.py 2015-10-27 20:53:16 +0000 | |||
74 | +++ src/maasserver/api/tests/test_node.py 2015-11-05 14:17:35 +0000 | |||
75 | @@ -24,6 +24,7 @@ | |||
76 | 24 | from django.core.urlresolvers import reverse | 24 | from django.core.urlresolvers import reverse |
77 | 25 | from django.db import transaction | 25 | from django.db import transaction |
78 | 26 | from maasserver import forms | 26 | from maasserver import forms |
79 | 27 | from maasserver.api import nodes as nodes_module | ||
80 | 27 | from maasserver.enum import ( | 28 | from maasserver.enum import ( |
81 | 28 | INTERFACE_TYPE, | 29 | INTERFACE_TYPE, |
82 | 29 | IPADDRESS_TYPE, | 30 | IPADDRESS_TYPE, |
83 | @@ -83,6 +84,7 @@ | |||
84 | 83 | HasLength, | 84 | HasLength, |
85 | 84 | Not, | 85 | Not, |
86 | 85 | ) | 86 | ) |
87 | 87 | import yaml | ||
88 | 86 | 88 | ||
89 | 87 | 89 | ||
90 | 88 | class NodeAnonAPITest(MAASServerTestCase): | 90 | class NodeAnonAPITest(MAASServerTestCase): |
91 | @@ -2186,3 +2188,43 @@ | |||
92 | 2186 | node = reload_object(node) | 2188 | node = reload_object(node) |
93 | 2187 | self.assertIsNone(node.gateway_link_ipv4) | 2189 | self.assertIsNone(node.gateway_link_ipv4) |
94 | 2188 | self.assertIsNone(node.gateway_link_ipv6) | 2190 | self.assertIsNone(node.gateway_link_ipv6) |
95 | 2191 | |||
96 | 2192 | |||
97 | 2193 | class TestGetCurtinConfig(APITestCase): | ||
98 | 2194 | |||
99 | 2195 | def get_node_uri(self, node): | ||
100 | 2196 | """Get the API URI for `node`.""" | ||
101 | 2197 | return reverse('node_handler', args=[node.system_id]) | ||
102 | 2198 | |||
103 | 2199 | def test__500_when_node_not_in_deployment_state(self): | ||
104 | 2200 | node = factory.make_Node( | ||
105 | 2201 | owner=self.logged_in_user, | ||
106 | 2202 | status=factory.pick_enum( | ||
107 | 2203 | NODE_STATUS, but_not=[ | ||
108 | 2204 | NODE_STATUS.DEPLOYING, | ||
109 | 2205 | NODE_STATUS.DEPLOYED, | ||
110 | 2206 | NODE_STATUS.FAILED_DEPLOYMENT, | ||
111 | 2207 | ])) | ||
112 | 2208 | response = self.client.get( | ||
113 | 2209 | self.get_node_uri(node), {'op': 'get_curtin_config'}) | ||
114 | 2210 | self.assertEquals( | ||
115 | 2211 | httplib.BAD_REQUEST, response.status_code, response.content) | ||
116 | 2212 | |||
117 | 2213 | def test__returns_curtin_config_in_yaml(self): | ||
118 | 2214 | node = factory.make_Node( | ||
119 | 2215 | owner=self.logged_in_user, status=NODE_STATUS.DEPLOYING) | ||
120 | 2216 | fake_config = { | ||
121 | 2217 | "config": factory.make_name("config") | ||
122 | 2218 | } | ||
123 | 2219 | mock_get_curtin_merged_config = self.patch( | ||
124 | 2220 | nodes_module, "get_curtin_merged_config") | ||
125 | 2221 | mock_get_curtin_merged_config.return_value = fake_config | ||
126 | 2222 | response = self.client.get( | ||
127 | 2223 | self.get_node_uri(node), {'op': 'get_curtin_config'}) | ||
128 | 2224 | self.assertEquals( | ||
129 | 2225 | httplib.OK, response.status_code, response.content) | ||
130 | 2226 | self.assertEquals( | ||
131 | 2227 | yaml.safe_dump(fake_config, default_flow_style=False), | ||
132 | 2228 | response.content) | ||
133 | 2229 | self.assertThat( | ||
134 | 2230 | mock_get_curtin_merged_config, MockCalledOnceWith(node)) | ||
135 | 2189 | 2231 | ||
136 | === modified file 'src/maasserver/preseed.py' | |||
137 | --- src/maasserver/preseed.py 2015-10-30 16:14:33 +0000 | |||
138 | +++ src/maasserver/preseed.py 2015-11-05 14:17:35 +0000 | |||
139 | @@ -31,6 +31,7 @@ | |||
140 | 31 | 31 | ||
141 | 32 | from crochet import TimeoutError | 32 | from crochet import TimeoutError |
142 | 33 | from curtin.commands import block_meta | 33 | from curtin.commands import block_meta |
143 | 34 | from curtin.config import merge_config | ||
144 | 34 | from curtin.pack import pack_install | 35 | from curtin.pack import pack_install |
145 | 35 | from django.conf import settings | 36 | from django.conf import settings |
146 | 36 | from maasserver import logger | 37 | from maasserver import logger |
147 | @@ -216,14 +217,8 @@ | |||
148 | 216 | return [] | 217 | return [] |
149 | 217 | 218 | ||
150 | 218 | 219 | ||
159 | 219 | def get_curtin_userdata(node): | 220 | def get_curtin_yaml_config(node): |
160 | 220 | """Return the curtin user-data. | 221 | """Return the curtin configration for the node.""" |
153 | 221 | |||
154 | 222 | :param node: The node for which to generate the user-data. | ||
155 | 223 | :return: The rendered user-data string. | ||
156 | 224 | :rtype: unicode. | ||
157 | 225 | """ | ||
158 | 226 | installer_url = get_curtin_installer_url(node) | ||
161 | 227 | main_config = get_curtin_config(node) | 222 | main_config = get_curtin_config(node) |
162 | 228 | reporter_config = compose_curtin_maas_reporter(node) | 223 | reporter_config = compose_curtin_maas_reporter(node) |
163 | 229 | swap_config = compose_curtin_swap_preseed(node) | 224 | swap_config = compose_curtin_swap_preseed(node) |
164 | @@ -253,13 +248,32 @@ | |||
165 | 253 | else: | 248 | else: |
166 | 254 | storage_config = [] | 249 | storage_config = [] |
167 | 255 | 250 | ||
168 | 251 | return ( | ||
169 | 252 | [main_config] + reporter_config + storage_config + network_config + | ||
170 | 253 | swap_config + kernel_config + verbose_config) | ||
171 | 254 | |||
172 | 255 | |||
173 | 256 | def get_curtin_merged_config(node): | ||
174 | 257 | """Return the merged curtin configuration for the node.""" | ||
175 | 258 | yaml_config = get_curtin_yaml_config(node) | ||
176 | 259 | config = {} | ||
177 | 260 | for cfg in yaml_config: | ||
178 | 261 | merge_config(config, yaml.load(cfg)) | ||
179 | 262 | return config | ||
180 | 263 | |||
181 | 264 | |||
182 | 265 | def get_curtin_userdata(node): | ||
183 | 266 | """Return the curtin user-data. | ||
184 | 267 | |||
185 | 268 | :param node: The node for which to generate the user-data. | ||
186 | 269 | :return: The rendered user-data string. | ||
187 | 270 | :rtype: unicode. | ||
188 | 271 | """ | ||
189 | 256 | # Pack the curtin and the configuration into a script to execute on the | 272 | # Pack the curtin and the configuration into a script to execute on the |
190 | 257 | # deploying node. | 273 | # deploying node. |
191 | 258 | return pack_install( | 274 | return pack_install( |
196 | 259 | configs=( | 275 | configs=get_curtin_yaml_config(node), |
197 | 260 | [main_config] + reporter_config + storage_config + network_config + | 276 | args=[get_curtin_installer_url(node)]) |
194 | 261 | swap_config + kernel_config + verbose_config), | ||
195 | 262 | args=[installer_url]) | ||
198 | 263 | 277 | ||
199 | 264 | 278 | ||
200 | 265 | def get_curtin_image(node): | 279 | def get_curtin_image(node): |
201 | 266 | 280 | ||
202 | === modified file 'src/maasserver/tests/test_preseed.py' | |||
203 | --- src/maasserver/tests/test_preseed.py 2015-10-30 16:03:19 +0000 | |||
204 | +++ src/maasserver/tests/test_preseed.py 2015-11-05 14:17:35 +0000 | |||
205 | @@ -53,6 +53,7 @@ | |||
206 | 53 | get_curtin_context, | 53 | get_curtin_context, |
207 | 54 | get_curtin_image, | 54 | get_curtin_image, |
208 | 55 | get_curtin_installer_url, | 55 | get_curtin_installer_url, |
209 | 56 | get_curtin_merged_config, | ||
210 | 56 | get_curtin_userdata, | 57 | get_curtin_userdata, |
211 | 57 | get_enlist_preseed, | 58 | get_enlist_preseed, |
212 | 58 | get_netloc_and_path, | 59 | get_netloc_and_path, |
213 | @@ -83,6 +84,7 @@ | |||
214 | 83 | MockNotCalled, | 84 | MockNotCalled, |
215 | 84 | ) | 85 | ) |
216 | 85 | from metadataserver.models import NodeKey | 86 | from metadataserver.models import NodeKey |
217 | 87 | from mock import sentinel | ||
218 | 86 | from provisioningserver.drivers.osystem.ubuntu import UbuntuOS | 88 | from provisioningserver.drivers.osystem.ubuntu import UbuntuOS |
219 | 87 | from provisioningserver.rpc.exceptions import NoConnectionsAvailable | 89 | from provisioningserver.rpc.exceptions import NoConnectionsAvailable |
220 | 88 | from provisioningserver.utils.enum import map_enum | 90 | from provisioningserver.utils.enum import map_enum |
221 | @@ -733,6 +735,38 @@ | |||
222 | 733 | }, yaml.load(preseed[0])) | 735 | }, yaml.load(preseed[0])) |
223 | 734 | 736 | ||
224 | 735 | 737 | ||
225 | 738 | class TestGetCurtinMergedConfig(MAASServerTestCase): | ||
226 | 739 | |||
227 | 740 | def test__merges_configs_together(self): | ||
228 | 741 | configs = [ | ||
229 | 742 | yaml.safe_dump({ | ||
230 | 743 | "maas": { | ||
231 | 744 | "test": "data" | ||
232 | 745 | }, | ||
233 | 746 | "override": "data", | ||
234 | 747 | }), | ||
235 | 748 | yaml.safe_dump({ | ||
236 | 749 | "maas2": { | ||
237 | 750 | "test": "data2" | ||
238 | 751 | }, | ||
239 | 752 | "override": "data2", | ||
240 | 753 | }), | ||
241 | 754 | ] | ||
242 | 755 | mock_yaml_config = self.patch_autospec( | ||
243 | 756 | preseed_module, "get_curtin_yaml_config") | ||
244 | 757 | mock_yaml_config.return_value = configs | ||
245 | 758 | self.assertEquals({ | ||
246 | 759 | "maas": { | ||
247 | 760 | "test": "data" | ||
248 | 761 | }, | ||
249 | 762 | "maas2": { | ||
250 | 763 | "test": "data2" | ||
251 | 764 | }, | ||
252 | 765 | "override": "data2", | ||
253 | 766 | }, get_curtin_merged_config(sentinel.node)) | ||
254 | 767 | self.assertThat(mock_yaml_config, MockCalledOnceWith(sentinel.node)) | ||
255 | 768 | |||
256 | 769 | |||
257 | 736 | class TestGetCurtinUserData( | 770 | class TestGetCurtinUserData( |
258 | 737 | PreseedRPCMixin, BootImageHelperMixin, MAASServerTestCase): | 771 | PreseedRPCMixin, BootImageHelperMixin, MAASServerTestCase): |
259 | 738 | """Tests for `get_curtin_userdata`.""" | 772 | """Tests for `get_curtin_userdata`.""" |
I did this because I kept needing a way to get the configuration when curtin deployments fail.