Merge lp:~dimitern/maas/1.2-node-view-shows-kernel-params into lp:~maas-committers/maas/trunk
- 1.2-node-view-shows-kernel-params
- Merge into trunk
Status: | Rejected |
---|---|
Rejected by: | Dimiter Naydenov |
Proposed branch: | lp:~dimitern/maas/1.2-node-view-shows-kernel-params |
Merge into: | lp:~maas-committers/maas/trunk |
Diff against target: |
566 lines (+305/-31) (has conflicts) 15 files modified
src/maasserver/forms.py (+20/-0) src/maasserver/static/css/components/blocks.css (+3/-0) src/maasserver/templates/maasserver/form_field.html (+1/-1) src/maasserver/templates/maasserver/node_list.html (+5/-0) src/maasserver/templates/maasserver/node_view.html (+12/-0) src/maasserver/templates/maasserver/settings.html (+15/-0) src/maasserver/templates/maasserver/tag_view.html (+6/-0) src/maasserver/tests/test_api.py (+45/-0) src/maasserver/tests/test_forms.py (+75/-30) src/maasserver/tests/test_views_nodes.py (+60/-0) src/maasserver/views/nodes.py (+8/-0) src/maasserver/views/settings.py (+9/-0) src/provisioningserver/boot_images.py (+4/-0) src/provisioningserver/tasks.py (+16/-0) src/provisioningserver/tests/test_tasks.py (+26/-0) Text conflict in src/maasserver/forms.py Text conflict in src/maasserver/templates/maasserver/node_list.html Text conflict in src/maasserver/tests/test_api.py Text conflict in src/maasserver/tests/test_forms.py Text conflict in src/maasserver/tests/test_views_nodes.py Text conflict in src/provisioningserver/boot_images.py Text conflict in src/provisioningserver/tasks.py Text conflict in src/provisioningserver/tests/test_tasks.py |
To merge this branch: | bzr merge lp:~dimitern/maas/1.2-node-view-shows-kernel-params |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Huw Wilkins | ui | Pending | |
Review via email: mp+132911@code.launchpad.net |
Commit message
Description of the change
I'd like to request a UI review for the changes I'm about to make to show kernel boot parameters in the node view, tag view and settings page.
http://
http://
http://
-http://
Edit: instead of the last link, use this (single line, as suggested):
http://
John A Meinel (jameinel) wrote : | # |
Nick Veitch (evilnick) wrote : | # |
>http://
yeah, this one takes up quite a bit of vertical space on a page which is only going to get bigger - wider and shorter is the way to go...
Nick
Martin Packman (gz) wrote : | # |
View and template changes look good, though the real branch will want proposing against lp:maas/1.2 rather than trunk.
Dimiter Naydenov (dimitern) wrote : | # |
Thanks for the review and comments. I did the changes needed and now I'm rejecting this MP and recreating it, since it targets trunk, rather than 1.2.
Preview Diff
1 | === modified file 'etc/celeryconfig_common.py' |
2 | === modified file 'src/maasserver/api.py' |
3 | === modified file 'src/maasserver/forms.py' |
4 | --- src/maasserver/forms.py 2012-11-01 15:13:10 +0000 |
5 | +++ src/maasserver/forms.py 2012-11-07 10:56:25 +0000 |
6 | @@ -394,6 +394,7 @@ |
7 | node.save() |
8 | for mac in self.cleaned_data['mac_addresses']: |
9 | node.add_mac_address(mac) |
10 | +<<<<<<< TREE |
11 | hostname = self.cleaned_data['hostname'] |
12 | stripped_hostname = strip_domain(hostname) |
13 | # Generate a hostname for this node if the provided hostname is |
14 | @@ -404,6 +405,18 @@ |
15 | IP_BASED_HOSTNAME_REGEXP.match(stripped_hostname) != None) |
16 | if generate_hostname: |
17 | node.set_random_hostname() |
18 | +======= |
19 | + hostname = self.cleaned_data['hostname'] |
20 | + stripped_hostname = strip_domain(hostname) |
21 | + # Generate a hostname for this node if the provided hostname is |
22 | + # IP-based (because this means that this name comes from a DNS |
23 | + # reverse query to the MAAS DNS) or an empty string. |
24 | + generate_hostname = ( |
25 | + hostname == "" or |
26 | + IP_BASED_HOSTNAME_REGEXP.match(stripped_hostname) != None) |
27 | + if generate_hostname: |
28 | + node.set_mac_based_hostname(self.cleaned_data['mac_addresses'][0]) |
29 | +>>>>>>> MERGE-SOURCE |
30 | return node |
31 | |
32 | |
33 | @@ -646,6 +659,13 @@ |
34 | self._load_initials() |
35 | |
36 | |
37 | +class GlobalKernelOptsForm(ConfigForm): |
38 | + """Settings page, Global Kernel Parameters section.""" |
39 | + global_kernel_opts = forms.CharField( |
40 | + label="Boot parameters to pass to the kernel by default", |
41 | + required=False) |
42 | + |
43 | + |
44 | hostname_error_msg = "Enter a valid hostname (e.g. host.example.com)." |
45 | |
46 | |
47 | |
48 | === modified file 'src/maasserver/models/node.py' |
49 | === modified file 'src/maasserver/static/css/components/blocks.css' |
50 | --- src/maasserver/static/css/components/blocks.css 2012-10-03 03:59:53 +0000 |
51 | +++ src/maasserver/static/css/components/blocks.css 2012-11-07 10:56:25 +0000 |
52 | @@ -82,3 +82,6 @@ |
53 | .size12 { |
54 | width: 720px; |
55 | } |
56 | +.size12 input { |
57 | + width: 100%; |
58 | + } |
59 | |
60 | === modified file 'src/maasserver/templates/maasserver/form_field.html' |
61 | --- src/maasserver/templates/maasserver/form_field.html 2012-08-03 16:36:26 +0000 |
62 | +++ src/maasserver/templates/maasserver/form_field.html 2012-11-07 10:56:25 +0000 |
63 | @@ -1,5 +1,5 @@ |
64 | {% load field_type %} |
65 | -<li class="{{ field.html_name }}{% if field.errors %} error{% endif %}"> |
66 | +<li class="{{ field.html_name }}{% if css_class %} {{ css_class }}{% endif %}{% if field.errors %} error{% endif %}"> |
67 | <label for="id_{{ field.html_name }}"> |
68 | {% ifequal field|field_type "CheckboxInput" %} |
69 | {{ field }} |
70 | |
71 | === modified file 'src/maasserver/templates/maasserver/node_list.html' |
72 | --- src/maasserver/templates/maasserver/node_list.html 2012-10-24 12:51:10 +0000 |
73 | +++ src/maasserver/templates/maasserver/node_list.html 2012-11-07 10:56:25 +0000 |
74 | @@ -38,8 +38,13 @@ |
75 | {% if input_query_error %} |
76 | <p class="form-errors">{{input_query_error}}</p> |
77 | {% endif %} |
78 | +<<<<<<< TREE |
79 | {% include "maasserver/nodes_listing.html" with sorting="true" %} |
80 | {% include "maasserver/pagination.html" %} |
81 | +======= |
82 | + {% include "maasserver/nodes_listing.html" %} |
83 | + {% include "maasserver/pagination.html" %} |
84 | +>>>>>>> MERGE-SOURCE |
85 | <a id="addnode" href="#" class="button right space-top">+ Add node</a> |
86 | <div class="clear"></div> |
87 | <a class="right space-top" href="{% url "enlist-preseed-view" %}">View enlistment preseed</a> |
88 | |
89 | === modified file 'src/maasserver/templates/maasserver/node_view.html' |
90 | --- src/maasserver/templates/maasserver/node_view.html 2012-10-05 17:42:12 +0000 |
91 | +++ src/maasserver/templates/maasserver/node_view.html 2012-11-07 10:56:25 +0000 |
92 | @@ -85,6 +85,18 @@ |
93 | {% endif %} |
94 | </span> |
95 | </li> |
96 | + <li class="block size10 first"> |
97 | + <h4>Kernel Parameters |
98 | + {% if kernel_opts.is_global %} |
99 | + - from: <a href="{% url 'settings' %}">Global Kernel Parameters</a> |
100 | + {% elif kernel_opts.is_tag %} |
101 | + - from tag: <span><a href="{% url 'tag-view' kernel_opts.tag.name %}">{{ kernel_opts.tag.name }}</a></span> |
102 | + {% endif %} |
103 | + </h4> |
104 | + <span id="node_kernel_opts"> |
105 | + {{ kernel_opts.value }} |
106 | + </span> |
107 | + </li> |
108 | {% if error_text %} |
109 | <li class="block first"> |
110 | <h4>Error output</h4> |
111 | |
112 | === modified file 'src/maasserver/templates/maasserver/settings.html' |
113 | --- src/maasserver/templates/maasserver/settings.html 2012-10-04 07:50:22 +0000 |
114 | +++ src/maasserver/templates/maasserver/settings.html 2012-11-07 10:56:25 +0000 |
115 | @@ -125,6 +125,21 @@ |
116 | <div class="clear"></div> |
117 | </div> |
118 | <div class="divider"></div> |
119 | + <div id="global_kernel_opts" class="block size7 first"> |
120 | + <h2>Global Kernel Parameters</h2> |
121 | + <form action="{% url "settings" %}" method="post"> |
122 | + {% csrf_token %} |
123 | + <ul> |
124 | + {% with field=kernelopts_form.global_kernel_opts %} |
125 | + {% include "maasserver/form_field.html" with css_class="size12" %} |
126 | + {% endwith %} |
127 | + </ul> |
128 | + <input type="hidden" name="kernelopts_submit" value="1" /> |
129 | + <input type="submit" class="button right" value="Save" /> |
130 | + </form> |
131 | + <div class="clear"></div> |
132 | + </div> |
133 | + <div class="divider"></div> |
134 | <div id="maas_and_network" class="block size7 first"> |
135 | <h2>Network Configuration</h2> |
136 | <form action="{% url "settings" %}" method="post"> |
137 | |
138 | === modified file 'src/maasserver/templates/maasserver/tag_view.html' |
139 | --- src/maasserver/templates/maasserver/tag_view.html 2012-10-24 12:51:10 +0000 |
140 | +++ src/maasserver/templates/maasserver/tag_view.html 2012-11-07 10:56:25 +0000 |
141 | @@ -19,6 +19,12 @@ |
142 | <h4>Definition</h4> |
143 | <span>{{ tag.definition }}</span> |
144 | </li> |
145 | + {% if tag.kernel_params %} |
146 | + <li class="block size10"> |
147 | + <h4>Kernel Parameters</h4> |
148 | + <span>{{ tag.kernel_params }}</span> |
149 | + </li> |
150 | + {% endif %} |
151 | {% if error_text %} |
152 | <li class="block first"> |
153 | <h4>Error output</h4> |
154 | |
155 | === modified file 'src/maasserver/testing/factory.py' |
156 | === modified file 'src/maasserver/tests/test_api.py' |
157 | --- src/maasserver/tests/test_api.py 2012-11-06 18:35:35 +0000 |
158 | +++ src/maasserver/tests/test_api.py 2012-11-07 10:56:25 +0000 |
159 | @@ -4267,6 +4267,7 @@ |
160 | (response.status_code, response.content)) |
161 | |
162 | def test_report_boot_images_warns_if_no_images_found(self): |
163 | +<<<<<<< TREE |
164 | nodegroup = NodeGroup.objects.ensure_master() |
165 | factory.make_node_group() # Second nodegroup with no images. |
166 | recorder = self.patch(api, 'register_persistent_error') |
167 | @@ -4310,6 +4311,50 @@ |
168 | response = self.report_images(nodegroup, [image], client=client) |
169 | self.assertEqual(httplib.OK, response.status_code) |
170 | self.assertEqual(0, recorder.call_count) |
171 | +======= |
172 | + nodegroup = NodeGroup.objects.ensure_master() |
173 | + factory.make_node_group() # Second nodegroup with no images. |
174 | + recorder = self.patch(api, 'register_persistent_error') |
175 | + client = make_worker_client(nodegroup) |
176 | + response = self.report_images(nodegroup, [], client=client) |
177 | + self.assertEqual( |
178 | + (httplib.OK, "OK"), |
179 | + (response.status_code, response.content)) |
180 | + |
181 | + self.assertIn( |
182 | + COMPONENT.IMPORT_PXE_FILES, |
183 | + [args[0][0] for args in recorder.call_args_list]) |
184 | + # Check that the persistent error message contains a link to the |
185 | + # clusters listing. |
186 | + self.assertIn( |
187 | + "/settings/#accepted-clusters", recorder.call_args_list[0][0][1]) |
188 | + |
189 | + def test_report_boot_images_warns_if_any_nodegroup_has_no_images(self): |
190 | + nodegroup = NodeGroup.objects.ensure_master() |
191 | + # Second nodegroup with no images. |
192 | + factory.make_node_group(status=NODEGROUP_STATUS.ACCEPTED) |
193 | + recorder = self.patch(api, 'register_persistent_error') |
194 | + client = make_worker_client(nodegroup) |
195 | + image = make_boot_image_params() |
196 | + response = self.report_images(nodegroup, [image], client=client) |
197 | + self.assertEqual( |
198 | + (httplib.OK, "OK"), |
199 | + (response.status_code, response.content)) |
200 | + |
201 | + self.assertIn( |
202 | + COMPONENT.IMPORT_PXE_FILES, |
203 | + [args[0][0] for args in recorder.call_args_list]) |
204 | + |
205 | + def test_report_boot_images_ignores_non_accepted_groups(self): |
206 | + nodegroup = factory.make_node_group(status=NODEGROUP_STATUS.ACCEPTED) |
207 | + factory.make_node_group(status=NODEGROUP_STATUS.PENDING) |
208 | + factory.make_node_group(status=NODEGROUP_STATUS.REJECTED) |
209 | + recorder = self.patch(api, 'register_persistent_error') |
210 | + client = make_worker_client(nodegroup) |
211 | + image = make_boot_image_params() |
212 | + response = self.report_images(nodegroup, [image], client=client) |
213 | + self.assertEqual(0, recorder.call_count) |
214 | +>>>>>>> MERGE-SOURCE |
215 | |
216 | def test_report_boot_images_removes_warning_if_images_found(self): |
217 | self.patch(api, 'register_persistent_error') |
218 | |
219 | === modified file 'src/maasserver/tests/test_dhcplease.py' |
220 | === modified file 'src/maasserver/tests/test_forms.py' |
221 | --- src/maasserver/tests/test_forms.py 2012-11-01 11:11:48 +0000 |
222 | +++ src/maasserver/tests/test_forms.py 2012-11-07 10:56:25 +0000 |
223 | @@ -350,36 +350,81 @@ |
224 | after_commissioning_action, node.after_commissioning_action) |
225 | self.assertEqual(power_type, node.power_type) |
226 | |
227 | - def test_AdminNodeForm_refuses_to_update_hostname_on_allocated_node(self): |
228 | - old_name = factory.make_name('old-hostname') |
229 | - new_name = factory.make_name('new-hostname') |
230 | - node = factory.make_node( |
231 | - hostname=old_name, status=NODE_STATUS.ALLOCATED) |
232 | - form = AdminNodeForm( |
233 | - data={ |
234 | - 'hostname': new_name, |
235 | - 'architecture': node.architecture, |
236 | - }, |
237 | - instance=node) |
238 | - self.assertFalse(form.is_valid()) |
239 | - self.assertEqual( |
240 | - ["Can't change hostname to %s: node is in use." % new_name], |
241 | - form._errors['hostname']) |
242 | - |
243 | - def test_AdminNodeForm_accepts_unchanged_hostname_on_allocated_node(self): |
244 | - old_name = factory.make_name('old-hostname') |
245 | - node = factory.make_node( |
246 | - hostname=old_name, status=NODE_STATUS.ALLOCATED) |
247 | - form = AdminNodeForm( |
248 | - data={ |
249 | - 'hostname': old_name, |
250 | - 'architecture': node.architecture, |
251 | - }, |
252 | - instance=node) |
253 | - self.assertTrue(form.is_valid(), form._errors) |
254 | - form.save() |
255 | - self.assertEqual(old_name, reload_object(node).hostname) |
256 | - |
257 | +<<<<<<< TREE |
258 | + def test_AdminNodeForm_refuses_to_update_hostname_on_allocated_node(self): |
259 | + old_name = factory.make_name('old-hostname') |
260 | + new_name = factory.make_name('new-hostname') |
261 | + node = factory.make_node( |
262 | + hostname=old_name, status=NODE_STATUS.ALLOCATED) |
263 | + form = AdminNodeForm( |
264 | + data={ |
265 | + 'hostname': new_name, |
266 | + 'architecture': node.architecture, |
267 | + }, |
268 | + instance=node) |
269 | + self.assertFalse(form.is_valid()) |
270 | + self.assertEqual( |
271 | + ["Can't change hostname to %s: node is in use." % new_name], |
272 | + form._errors['hostname']) |
273 | + |
274 | + def test_AdminNodeForm_accepts_unchanged_hostname_on_allocated_node(self): |
275 | + old_name = factory.make_name('old-hostname') |
276 | + node = factory.make_node( |
277 | + hostname=old_name, status=NODE_STATUS.ALLOCATED) |
278 | + form = AdminNodeForm( |
279 | + data={ |
280 | + 'hostname': old_name, |
281 | + 'architecture': node.architecture, |
282 | + }, |
283 | + instance=node) |
284 | + self.assertTrue(form.is_valid(), form._errors) |
285 | + form.save() |
286 | + self.assertEqual(old_name, reload_object(node).hostname) |
287 | + |
288 | +======= |
289 | + def test_AdminNodeForm_refuses_to_update_hostname_on_allocated_node(self): |
290 | + old_name = factory.make_name('old-hostname') |
291 | + new_name = factory.make_name('new-hostname') |
292 | + node = factory.make_node( |
293 | + hostname=old_name, status=NODE_STATUS.ALLOCATED) |
294 | + form = AdminNodeForm( |
295 | + data={ |
296 | + 'hostname': new_name, |
297 | + 'architecture': node.architecture, |
298 | + }, |
299 | + instance=node) |
300 | + self.assertFalse(form.is_valid()) |
301 | + self.assertEqual( |
302 | + ["Can't change hostname to %s: node is in use." % new_name], |
303 | + form._errors['hostname']) |
304 | + |
305 | + def test_AdminNodeForm_accepts_unchanged_hostname_on_allocated_node(self): |
306 | + old_name = factory.make_name('old-hostname') |
307 | + node = factory.make_node( |
308 | + hostname=old_name, status=NODE_STATUS.ALLOCATED) |
309 | + form = AdminNodeForm( |
310 | + data={ |
311 | + 'hostname': old_name, |
312 | + 'architecture': node.architecture, |
313 | + }, |
314 | + instance=node) |
315 | + self.assertTrue(form.is_valid(), form._errors) |
316 | + form.save() |
317 | + self.assertEqual(old_name, reload_object(node).hostname) |
318 | + |
319 | + def test_AdminNodeForm_accepts_omitted_hostname_on_allocated_node(self): |
320 | + node = factory.make_node(status=NODE_STATUS.ALLOCATED) |
321 | + old_name = node.hostname |
322 | + form = AdminNodeForm( |
323 | + data={ |
324 | + 'architecture': node.architecture, |
325 | + }, |
326 | + instance=node) |
327 | + self.assertTrue(form.is_valid()) |
328 | + form.save() |
329 | + self.assertEqual(old_name, reload_object(node).hostname) |
330 | + |
331 | +>>>>>>> MERGE-SOURCE |
332 | def test_remove_None_values_removes_None_values_in_dict(self): |
333 | random_input = factory.getRandomString() |
334 | self.assertEqual( |
335 | |
336 | === modified file 'src/maasserver/tests/test_node.py' |
337 | === modified file 'src/maasserver/tests/test_views_nodes.py' |
338 | --- src/maasserver/tests/test_views_nodes.py 2012-11-01 15:59:22 +0000 |
339 | +++ src/maasserver/tests/test_views_nodes.py 2012-11-07 10:56:25 +0000 |
340 | @@ -532,6 +532,7 @@ |
341 | "//div[@id='nodes']/table//td/a/@href") |
342 | self.assertEqual([node2_link], node_links) |
343 | |
344 | +<<<<<<< TREE |
345 | def test_node_list_paginates(self): |
346 | """Node listing is split across multiple pages with links""" |
347 | # Set a very small page size to save creating lots of nodes |
348 | @@ -591,6 +592,65 @@ |
349 | [(a.text.lower(), a.get("href")) |
350 | for a in document.xpath("//div[@class='pagination']//a")]) |
351 | |
352 | +======= |
353 | + def test_node_list_paginates(self): |
354 | + """Node listing is split across multiple pages with links""" |
355 | + # Set a very small page size to save creating lots of nodes |
356 | + page_size = 2 |
357 | + self.patch(nodes_views.NodeListView, 'paginate_by', page_size) |
358 | + nodes = [factory.make_node(created="2012-10-12 12:00:%02d" % i) |
359 | + for i in range(page_size * 2 + 1)] |
360 | + # Order node links with newest first as the view is expected to |
361 | + node_links = [reverse('node-view', args=[node.system_id]) |
362 | + for node in reversed(nodes)] |
363 | + expr_node_links = XPath("//div[@id='nodes']/table//a/@href") |
364 | + expr_page_anchors = XPath("//div[@class='pagination']//a") |
365 | + # Fetch first page, should link newest two nodes and page 2 |
366 | + response = self.client.get(reverse('node-list')) |
367 | + page1 = fromstring(response.content) |
368 | + self.assertEqual(node_links[:page_size], expr_node_links(page1)) |
369 | + self.assertEqual([("next", "?page=2"), ("last", "?page=3")], |
370 | + [(a.text.lower(), a.get("href")) |
371 | + for a in expr_page_anchors(page1)]) |
372 | + # Fetch second page, should link next nodes and adjacent pages |
373 | + response = self.client.get(reverse('node-list'), {"page": 2}) |
374 | + page2 = fromstring(response.content) |
375 | + self.assertEqual(node_links[page_size:page_size * 2], |
376 | + expr_node_links(page2)) |
377 | + self.assertEqual([("first", "."), ("previous", "."), |
378 | + ("next", "?page=3"), ("last", "?page=3")], |
379 | + [(a.text.lower(), a.get("href")) |
380 | + for a in expr_page_anchors(page2)]) |
381 | + # Fetch third page, should link oldest node and node list page |
382 | + response = self.client.get(reverse('node-list'), {"page": 3}) |
383 | + page3 = fromstring(response.content) |
384 | + self.assertEqual(node_links[page_size * 2:], expr_node_links(page3)) |
385 | + self.assertEqual([("first", "."), ("previous", "?page=2")], |
386 | + [(a.text.lower(), a.get("href")) |
387 | + for a in expr_page_anchors(page3)]) |
388 | + |
389 | + def test_node_list_query_paginates(self): |
390 | + """Node list query subset is split across multiple pages with links""" |
391 | + # Set a very small page size to save creating lots of nodes |
392 | + self.patch(nodes_views.NodeListView, 'paginate_by', 2) |
393 | + nodes = [factory.make_node(created="2012-10-12 12:00:%02d" % i) |
394 | + for i in range(10)] |
395 | + tag = factory.make_tag("odd") |
396 | + for node in nodes[::2]: |
397 | + node.tags = [tag] |
398 | + last_node_link = reverse('node-view', args=[nodes[0].system_id]) |
399 | + response = self.client.get(reverse('node-list'), |
400 | + {"query": "maas-tags=odd", "page": 3}) |
401 | + document = fromstring(response.content) |
402 | + self.assertIn("5 matching nodes", document.xpath("string(//h1)")) |
403 | + self.assertEqual([last_node_link], |
404 | + document.xpath("//div[@id='nodes']/table//a/@href")) |
405 | + self.assertEqual([("first", "?query=maas-tags%3Dodd"), |
406 | + ("previous", "?query=maas-tags%3Dodd&page=2")], |
407 | + [(a.text.lower(), a.get("href")) |
408 | + for a in document.xpath("//div[@class='pagination']//a")]) |
409 | + |
410 | +>>>>>>> MERGE-SOURCE |
411 | |
412 | class NodePreseedViewTest(LoggedInTestCase): |
413 | |
414 | |
415 | === modified file 'src/maasserver/views/nodes.py' |
416 | --- src/maasserver/views/nodes.py 2012-10-24 15:57:18 +0000 |
417 | +++ src/maasserver/views/nodes.py 2012-11-07 10:56:25 +0000 |
418 | @@ -56,6 +56,7 @@ |
419 | from maasserver.models import ( |
420 | MACAddress, |
421 | Node, |
422 | + Tag, |
423 | ) |
424 | from maasserver.models.node import CONSTRAINTS_JUJU_MAP |
425 | from maasserver.models.node_constraint_filter import constrain_nodes |
426 | @@ -245,6 +246,13 @@ |
427 | node.error if node.status == NODE_STATUS.FAILED_TESTS else None) |
428 | context['status_text'] = ( |
429 | node.error if node.status != NODE_STATUS.FAILED_TESTS else None) |
430 | + kernel_opts = node.get_effective_kernel_options() |
431 | + context['kernel_opts'] = { |
432 | + 'is_global': kernel_opts[0] is None, |
433 | + 'is_tag': isinstance(kernel_opts[0], Tag), |
434 | + 'tag': kernel_opts[0], |
435 | + 'value': kernel_opts[1] |
436 | + } |
437 | return context |
438 | |
439 | def dispatch(self, *args, **kwargs): |
440 | |
441 | === modified file 'src/maasserver/views/settings.py' |
442 | --- src/maasserver/views/settings.py 2012-10-03 15:48:11 +0000 |
443 | +++ src/maasserver/views/settings.py 2012-11-07 10:56:25 +0000 |
444 | @@ -46,6 +46,7 @@ |
445 | MAASAndNetworkForm, |
446 | NewUserCreationForm, |
447 | UbuntuForm, |
448 | + GlobalKernelOptsForm, |
449 | ) |
450 | from maasserver.models import ( |
451 | NodeGroup, |
452 | @@ -177,6 +178,13 @@ |
453 | if response is not None: |
454 | return response |
455 | |
456 | + # Process the Global Kernel Opts form. |
457 | + kernelopts_form, response = process_form( |
458 | + request, GlobalKernelOptsForm, reverse('settings'), 'kernelopts', |
459 | + "Configuration updated.") |
460 | + if response is not None: |
461 | + return response |
462 | + |
463 | # Process accept clusters en masse. |
464 | if 'mass_accept_submit' in request.POST: |
465 | number = NodeGroup.objects.accept_all_pending() |
466 | @@ -207,6 +215,7 @@ |
467 | 'maas_and_network_form': maas_and_network_form, |
468 | 'commissioning_form': commissioning_form, |
469 | 'ubuntu_form': ubuntu_form, |
470 | + 'kernelopts_form': kernelopts_form, |
471 | }, |
472 | context_instance=RequestContext(request)) |
473 | |
474 | |
475 | === modified file 'src/provisioningserver/boot_images.py' |
476 | --- src/provisioningserver/boot_images.py 2012-10-25 07:27:24 +0000 |
477 | +++ src/provisioningserver/boot_images.py 2012-11-07 10:56:25 +0000 |
478 | @@ -32,10 +32,14 @@ |
479 | ) |
480 | from provisioningserver.config import Config |
481 | from provisioningserver.pxe import tftppath |
482 | +<<<<<<< TREE |
483 | from provisioningserver.start_cluster_controller import get_cluster_uuid |
484 | |
485 | |
486 | task_logger = get_task_logger(name=__name__) |
487 | +======= |
488 | +from provisioningserver.start_cluster_controller import get_cluster_uuid |
489 | +>>>>>>> MERGE-SOURCE |
490 | |
491 | |
492 | def get_cached_knowledge(): |
493 | |
494 | === modified file 'src/provisioningserver/dns/config.py' |
495 | === modified file 'src/provisioningserver/dns/tests/test_config.py' |
496 | === modified file 'src/provisioningserver/tasks.py' |
497 | --- src/provisioningserver/tasks.py 2012-11-05 11:54:01 +0000 |
498 | +++ src/provisioningserver/tasks.py 2012-11-07 10:56:25 +0000 |
499 | @@ -369,6 +369,7 @@ |
500 | exc=exc, countdown=UPDATE_NODE_TAGS_RETRY_DELAY) |
501 | else: |
502 | raise |
503 | +<<<<<<< TREE |
504 | |
505 | |
506 | # ===================================================================== |
507 | @@ -382,3 +383,18 @@ |
508 | env['http_proxy'] = http_proxy |
509 | env['https_proxy'] = http_proxy |
510 | check_call(['sudo', '-n', 'maas-import-pxe-files'], env=env) |
511 | +======= |
512 | + |
513 | + |
514 | +# ===================================================================== |
515 | +# Image importing-related tasks |
516 | +# ===================================================================== |
517 | + |
518 | +@task |
519 | +def import_pxe_files(http_proxy=None): |
520 | + env = dict(os.environ) |
521 | + if http_proxy is not None: |
522 | + env['http_proxy'] = http_proxy |
523 | + env['https_proxy'] = http_proxy |
524 | + check_call(['maas-import-pxe-files'], env=env) |
525 | +>>>>>>> MERGE-SOURCE |
526 | |
527 | === modified file 'src/provisioningserver/tests/test_tasks.py' |
528 | --- src/provisioningserver/tests/test_tasks.py 2012-11-05 11:54:01 +0000 |
529 | +++ src/provisioningserver/tests/test_tasks.py 2012-11-07 10:56:25 +0000 |
530 | @@ -536,6 +536,7 @@ |
531 | self.assertRaises( |
532 | MissingCredentials, update_node_tags.delay, tag, |
533 | '//node', retry=True) |
534 | +<<<<<<< TREE |
535 | |
536 | |
537 | class TestImportPxeFiles(PservTestCase): |
538 | @@ -560,3 +561,28 @@ |
539 | expected_env = dict(os.environ, http_proxy=proxy, https_proxy=proxy) |
540 | recorder.assert_called_once_with( |
541 | ['sudo', '-n', 'maas-import-pxe-files'], env=expected_env) |
542 | +======= |
543 | + |
544 | + |
545 | +class TestImportPxeFiles(PservTestCase): |
546 | + |
547 | + def test_import_pxe_files(self): |
548 | + recorder = self.patch(tasks, 'check_call', Mock()) |
549 | + import_pxe_files() |
550 | + recorder.assert_called_once_with(['maas-import-pxe-files'], env=ANY) |
551 | + self.assertIsInstance(import_pxe_files, Task) |
552 | + |
553 | + def test_import_pxe_files_preserves_environment(self): |
554 | + recorder = self.patch(tasks, 'check_call', Mock()) |
555 | + import_pxe_files() |
556 | + recorder.assert_called_once_with( |
557 | + ['maas-import-pxe-files'], env=os.environ) |
558 | + |
559 | + def test_import_pxe_files_sets_proxy(self): |
560 | + recorder = self.patch(tasks, 'check_call', Mock()) |
561 | + proxy = factory.getRandomString() |
562 | + import_pxe_files(http_proxy=proxy) |
563 | + expected_env = dict(os.environ, http_proxy=proxy, https_proxy=proxy) |
564 | + recorder.assert_called_once_with( |
565 | + ['maas-import-pxe-files'], env=expected_env) |
566 | +>>>>>>> MERGE-SOURCE |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On 11/5/2012 7:26 PM, Dimiter Naydenov wrote: /bugs.launchpad .net/maas/ +bug/1044503 /code.launchpad .net/~dimitern/ maas/1. 2-node- view-shows- kernel- params/ +merge/ 132911 people. canonical. com/~dimitern/ maas-node- view-kernel- opts-tag. png people. canonical. com/~dimitern/ maas-node- view-kernel- opts-global. png people. canonical. com/~dimitern/ maas-tag- view-kernel- opts.png people. canonical. com/~dimitern/ maas-settings- global- kernel- opts.png
> Dimiter Naydenov has proposed merging
> lp:~dimitern/maas/1.2-node-view-shows-kernel-params into lp:maas.
>
> Requested reviews: Huw Wilkins (huwshimi): ui Related bugs: Bug
> #1044503 in MAAS: "kernel command line is not easily customizable"
> https:/
>
> For more details, see:
> https:/
>
> I'd like to request a UI review for the changes I'm about to make
> to show kernel boot parameters in the node view, tag view and
> settings page.
>
> http://
>
>
http://
> http://
>
>
http://
>
- From my PoV, the only one I would change is probably making the global
parameter entry box a single line box, just extra wide, rather than
multiline. Though I'm not set on that.
John www.enigmail. net/
=:->
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (Cygwin)
Comment: Using GnuPG with Mozilla - http://
iEYEARECAAYFAlC X4UAACgkQJdeBCY SNAAMMdQCfRNFY5 QAegoULhXQzY+ VhLyx4 FA0JuTFI6XcKMVM na8
8isAoJEnHffo8/
=lx+H
-----END PGP SIGNATURE-----