Merge ~newell-jensen/maas:ui-pod-zones into maas:master
- Git
- lp:~newell-jensen/maas
- ui-pod-zones
- Merge into master
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Newell Jensen | ||||
Approved revision: | a86c1048c906d518b102959ae95f8f8d4aacd0c5 | ||||
Merge reported by: | MAAS Lander | ||||
Merged at revision: | not available | ||||
Proposed branch: | ~newell-jensen/maas:ui-pod-zones | ||||
Merge into: | maas:master | ||||
Diff against target: |
693 lines (+221/-35) 10 files modified
src/maasserver/api/tests/test_pods.py (+3/-0) src/maasserver/forms/pods.py (+10/-3) src/maasserver/forms/tests/test_pods.py (+49/-7) src/maasserver/static/js/angular/controllers/pod_details.js (+35/-7) src/maasserver/static/js/angular/controllers/pods_list.js (+7/-5) src/maasserver/static/js/angular/controllers/tests/test_pod_details.js (+77/-4) src/maasserver/static/js/angular/controllers/tests/test_pods_list.js (+9/-5) src/maasserver/static/partials/pod-details.html (+25/-4) src/maasserver/static/partials/pods-list.html (+2/-0) src/maasserver/websockets/handlers/tests/test_pod.py (+4/-0) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andres Rodriguez (community) | Needs Fixing | ||
MAAS Lander | Needs Fixing | ||
Lee Trager (community) | Approve | ||
Review via email: mp+340073@code.launchpad.net |
Commit message
Add Pod zones to the UI for both adding and updating the configuration of a Pod.
Description of the change
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b ui-pod-zones lp:~newell-jensen/maas/+git/maas into -b master lp:~maas-committers/maas
STATUS: FAILED BUILD
LOG: http://
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b ui-pod-zones lp:~newell-jensen/maas/+git/maas into -b master lp:~maas-committers/maas
STATUS: FAILED BUILD
LOG: http://
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b ui-pod-zones lp:~newell-jensen/maas/+git/maas into -b master lp:~maas-committers/maas
STATUS: FAILED
LOG: http://
COMMIT: 428f294ea39d538
Andres Rodriguez (andreserl) wrote : | # |
=======
ERROR: maasserver.
-------
Traceback (most recent call last):
testtools.
Twisted logs
Traceback (most recent call last):
File "/run/build/
result = function(*args, **kwargs)
File "/home/
return self._get_
File "/usr/lib/
return eventual_
File "/usr/lib/
result.
File "/usr/lib/
raise self.value.
maasserver.
=======
ERROR: maasserver.
-------
Traceback (most recent call last):
testtools.
Twisted logs
Traceback (most recent call last):
File "/run/build/
result = function(*args, **kwargs)
File "/home/
return self._get_
File "/usr/lib/
return eventual_
File "/usr/lib/
result.
File "/usr/lib/
raise self.value.
maasserver.
- a86c104... by Newell Jensen
-
Fix broken tests.
Preview Diff
1 | diff --git a/src/maasserver/api/tests/test_pods.py b/src/maasserver/api/tests/test_pods.py |
2 | index 87a61d3..ada7a53 100644 |
3 | --- a/src/maasserver/api/tests/test_pods.py |
4 | +++ b/src/maasserver/api/tests/test_pods.py |
5 | @@ -38,11 +38,13 @@ class PodMixin: |
6 | pod_ip_adddress = factory.make_ipv4_address() |
7 | pod_power_address = 'qemu+ssh://user@%s/system' % pod_ip_adddress |
8 | pod_password = factory.make_name('password') |
9 | + zone = factory.make_Zone() |
10 | return { |
11 | 'type': pod_type, |
12 | 'power_address': pod_power_address, |
13 | 'power_pass': pod_password, |
14 | 'ip_address': pod_ip_adddress, |
15 | + 'zone': zone.id |
16 | } |
17 | |
18 | def fake_pod_discovery(self): |
19 | @@ -234,6 +236,7 @@ class TestPodAPI(APITestCase.ForUser, PodMixin): |
20 | 'name': new_name, |
21 | 'power_address': pod_info['power_address'], |
22 | 'power_pass': pod_info['power_pass'], |
23 | + 'zone': pod_info['zone'], |
24 | }) |
25 | self.assertEqual( |
26 | http.client.OK, response.status_code, response.content) |
27 | diff --git a/src/maasserver/forms/pods.py b/src/maasserver/forms/pods.py |
28 | index c523e85..4ad1695 100644 |
29 | --- a/src/maasserver/forms/pods.py |
30 | +++ b/src/maasserver/forms/pods.py |
31 | @@ -113,6 +113,9 @@ class PodForm(MAASModelForm): |
32 | if not self.is_new: |
33 | if self.instance.power_type != '': |
34 | self.initial['type'] = self.instance.power_type |
35 | + self.fields['zone'] = ModelChoiceField( |
36 | + required=False, queryset=Zone.objects.all()) |
37 | + self.initial['zone'] = Zone.objects.get_default_zone() |
38 | |
39 | def _clean_fields(self): |
40 | """Override to dynamically add fields based on the value of `type` |
41 | @@ -145,7 +148,7 @@ class PodForm(MAASModelForm): |
42 | |
43 | def save(self, *args, **kwargs): |
44 | """Persist the pod into the database.""" |
45 | - def check_for_duplicate(power_type, power_parameters): |
46 | + def check_for_duplicate(zone, power_type, power_parameters): |
47 | # When the Pod is new try to get a BMC of the same type and |
48 | # parameters to convert the BMC to a new Pod. When the Pod is not |
49 | # new the form will use the already existing pod instance to update |
50 | @@ -153,6 +156,7 @@ class PodForm(MAASModelForm): |
51 | # a validation erorr will be raised from the model level. |
52 | if self.is_new: |
53 | bmc = BMC.objects.filter( |
54 | + zone=zone, |
55 | power_type=power_type, |
56 | power_parameters=power_parameters).first() |
57 | if bmc is not None: |
58 | @@ -174,10 +178,12 @@ class PodForm(MAASModelForm): |
59 | if existing_obj is not None: |
60 | self.instance = existing_obj |
61 | self.instance = super(PodForm, self).save(commit=False) |
62 | + self.instance.zone = zone |
63 | self.instance.power_type = power_type |
64 | self.instance.power_parameters = power_parameters |
65 | return self.instance |
66 | |
67 | + zone = self.cleaned_data['zone'] |
68 | power_type = self.cleaned_data['type'] |
69 | # Set power_parameters to the generated param_fields. |
70 | power_parameters = { |
71 | @@ -190,13 +196,14 @@ class PodForm(MAASModelForm): |
72 | # Running in twisted reactor, do the work inside the reactor. |
73 | d = deferToDatabase( |
74 | transactional(check_for_duplicate), |
75 | - power_type, power_parameters) |
76 | + zone, power_type, power_parameters) |
77 | d.addCallback(update_obj) |
78 | d.addCallback(lambda _: self.discover_and_sync_pod()) |
79 | return d |
80 | else: |
81 | # Perform the actions inside the executing thread. |
82 | - existing_obj = check_for_duplicate(power_type, power_parameters) |
83 | + existing_obj = check_for_duplicate( |
84 | + zone, power_type, power_parameters) |
85 | if existing_obj is not None: |
86 | self.instance = existing_obj |
87 | self.instance = update_obj(self.instance) |
88 | diff --git a/src/maasserver/forms/tests/test_pods.py b/src/maasserver/forms/tests/test_pods.py |
89 | index 9c3b871..f20f2a7 100644 |
90 | --- a/src/maasserver/forms/tests/test_pods.py |
91 | +++ b/src/maasserver/forms/tests/test_pods.py |
92 | @@ -131,12 +131,15 @@ class TestPodForm(MAASTransactionServerTestCase): |
93 | [ |
94 | 'name', |
95 | 'type', |
96 | + 'zone' |
97 | ], list(form.fields)) |
98 | |
99 | def test_creates_pod_with_discovered_information(self): |
100 | discovered_pod, discovered_racks, failed_racks = ( |
101 | self.fake_pod_discovery()) |
102 | + zone = factory.make_Zone() |
103 | pod_info = self.make_pod_info() |
104 | + pod_info['zone'] = zone.id |
105 | request = MagicMock() |
106 | request.user = factory.make_User() |
107 | form = PodForm(data=pod_info, request=request) |
108 | @@ -148,6 +151,7 @@ class TestPodForm(MAASTransactionServerTestCase): |
109 | cores=Equals(discovered_pod.cores), |
110 | memory=Equals(discovered_pod.memory), |
111 | cpu_speed=Equals(discovered_pod.cpu_speed), |
112 | + zone=Equals(zone), |
113 | power_type=Equals(pod_info['type']), |
114 | power_parameters=Equals({ |
115 | 'power_address': pod_info['power_address'], |
116 | @@ -171,7 +175,9 @@ class TestPodForm(MAASTransactionServerTestCase): |
117 | def test_creates_pod_with_name(self): |
118 | discovered_pod, discovered_racks, failed_racks = ( |
119 | self.fake_pod_discovery()) |
120 | + zone = factory.make_Zone() |
121 | pod_info = self.make_pod_info() |
122 | + pod_info['zone'] = zone.id |
123 | request = MagicMock() |
124 | request.user = factory.make_User() |
125 | pod_name = factory.make_name('pod') |
126 | @@ -185,6 +191,7 @@ class TestPodForm(MAASTransactionServerTestCase): |
127 | cores=Equals(discovered_pod.cores), |
128 | memory=Equals(discovered_pod.memory), |
129 | cpu_speed=Equals(discovered_pod.cpu_speed), |
130 | + zone=Equals(zone), |
131 | power_type=Equals(pod_info['type']), |
132 | power_parameters=Equals({ |
133 | 'power_address': pod_info['power_address'], |
134 | @@ -200,7 +207,9 @@ class TestPodForm(MAASTransactionServerTestCase): |
135 | self.fake_pod_discovery) |
136 | pods_module.discover_pod.return_value = succeed( |
137 | pods_module.discover_pod.return_value) |
138 | + zone = yield deferToDatabase(factory.make_Zone) |
139 | pod_info = self.make_pod_info() |
140 | + pod_info['zone'] = zone.id |
141 | request = MagicMock() |
142 | request.user = yield deferToDatabase(factory.make_User) |
143 | form = yield deferToDatabase(PodForm, data=pod_info, request=request) |
144 | @@ -213,6 +222,7 @@ class TestPodForm(MAASTransactionServerTestCase): |
145 | cores=Equals(discovered_pod.cores), |
146 | memory=Equals(discovered_pod.memory), |
147 | cpu_speed=Equals(discovered_pod.cpu_speed), |
148 | + zone=Equals(zone), |
149 | power_type=Equals(pod_info['type']), |
150 | power_parameters=Equals({ |
151 | 'power_address': pod_info['power_address'], |
152 | @@ -244,7 +254,9 @@ class TestPodForm(MAASTransactionServerTestCase): |
153 | self.fake_pod_discovery) |
154 | pods_module.discover_pod.return_value = succeed( |
155 | pods_module.discover_pod.return_value) |
156 | + zone = yield deferToDatabase(factory.make_Zone) |
157 | pod_info = self.make_pod_info() |
158 | + pod_info['zone'] = zone.id |
159 | pod_name = factory.make_name('pod') |
160 | pod_info['name'] = pod_name |
161 | request = MagicMock() |
162 | @@ -259,6 +271,7 @@ class TestPodForm(MAASTransactionServerTestCase): |
163 | cores=Equals(discovered_pod.cores), |
164 | memory=Equals(discovered_pod.memory), |
165 | cpu_speed=Equals(discovered_pod.cpu_speed), |
166 | + zone=Equals(zone), |
167 | power_type=Equals(pod_info['type']), |
168 | power_parameters=Equals({ |
169 | 'power_address': pod_info['power_address'], |
170 | @@ -273,7 +286,9 @@ class TestPodForm(MAASTransactionServerTestCase): |
171 | discovered_pod, discovered_racks, failed_racks = yield deferToDatabase( |
172 | self.fake_pod_discovery) |
173 | pods_module.discover_pod.return_value = fail(factory.make_exception()) |
174 | + zone = yield deferToDatabase(factory.make_Zone) |
175 | pod_info = self.make_pod_info() |
176 | + pod_info['zone'] = zone.id |
177 | request = MagicMock() |
178 | request.user = yield deferToDatabase(factory.make_User) |
179 | form = yield deferToDatabase(PodForm, data=pod_info, request=request) |
180 | @@ -289,7 +304,9 @@ class TestPodForm(MAASTransactionServerTestCase): |
181 | |
182 | def test_prevents_duplicate_pod(self): |
183 | discovered_pod, _, _ = self.fake_pod_discovery() |
184 | + zone = factory.make_Zone() |
185 | pod_info = self.make_pod_info() |
186 | + pod_info['zone'] = zone.id |
187 | request = MagicMock() |
188 | request.user = factory.make_User() |
189 | form = PodForm(data=pod_info, request=request) |
190 | @@ -301,9 +318,12 @@ class TestPodForm(MAASTransactionServerTestCase): |
191 | |
192 | def test_takes_over_bmc_with_pod(self): |
193 | discovered_pod, _, _ = self.fake_pod_discovery() |
194 | + zone = factory.make_Zone() |
195 | pod_info = self.make_pod_info() |
196 | + pod_info['zone'] = zone.id |
197 | bmc = factory.make_BMC( |
198 | - power_type=pod_info['type'], power_parameters={ |
199 | + zone=zone, power_type=pod_info['type'], |
200 | + power_parameters={ |
201 | 'power_address': pod_info['power_address'], |
202 | 'power_pass': pod_info['power_pass'], |
203 | }) |
204 | @@ -318,7 +338,9 @@ class TestPodForm(MAASTransactionServerTestCase): |
205 | def test_updates_existing_pod(self): |
206 | discovered_pod, discovered_racks, failed_racks = ( |
207 | self.fake_pod_discovery()) |
208 | + zone = factory.make_Zone() |
209 | pod_info = self.make_pod_info() |
210 | + pod_info['zone'] = zone.id |
211 | orig_pod = factory.make_Pod(pod_type=pod_info['type']) |
212 | new_name = factory.make_name("pod") |
213 | pod_info['name'] = new_name |
214 | @@ -335,6 +357,7 @@ class TestPodForm(MAASTransactionServerTestCase): |
215 | cores=Equals(discovered_pod.cores), |
216 | memory=Equals(discovered_pod.memory), |
217 | cpu_speed=Equals(discovered_pod.cpu_speed), |
218 | + zone=Equals(zone), |
219 | power_type=Equals(pod_info['type']), |
220 | power_parameters=Equals({ |
221 | 'power_address': pod_info['power_address'], |
222 | @@ -362,7 +385,9 @@ class TestPodForm(MAASTransactionServerTestCase): |
223 | self.fake_pod_discovery) |
224 | pods_module.discover_pod.return_value = succeed( |
225 | pods_module.discover_pod.return_value) |
226 | + zone = yield deferToDatabase(factory.make_Zone) |
227 | pod_info = self.make_pod_info() |
228 | + pod_info['zone'] = zone.id |
229 | orig_pod = yield deferToDatabase( |
230 | factory.make_Pod, pod_type=pod_info['type']) |
231 | new_name = factory.make_name("pod") |
232 | @@ -382,6 +407,7 @@ class TestPodForm(MAASTransactionServerTestCase): |
233 | cores=Equals(discovered_pod.cores), |
234 | memory=Equals(discovered_pod.memory), |
235 | cpu_speed=Equals(discovered_pod.cpu_speed), |
236 | + zone=Equals(zone), |
237 | power_type=Equals(pod_info['type']), |
238 | power_parameters=Equals({ |
239 | 'power_address': pod_info['power_address'], |
240 | @@ -409,8 +435,9 @@ class TestPodForm(MAASTransactionServerTestCase): |
241 | def test_discover_and_sync_existing_pod(self): |
242 | discovered_pod, discovered_racks, failed_racks = ( |
243 | self.fake_pod_discovery()) |
244 | + zone = factory.make_Zone() |
245 | pod_info = self.make_pod_info() |
246 | - orig_pod = factory.make_Pod(pod_type=pod_info['type']) |
247 | + orig_pod = factory.make_Pod(zone=zone, pod_type=pod_info['type']) |
248 | request = MagicMock() |
249 | request.user = factory.make_User() |
250 | form = PodForm(data=pod_info, request=request, instance=orig_pod) |
251 | @@ -423,6 +450,7 @@ class TestPodForm(MAASTransactionServerTestCase): |
252 | cores=Equals(discovered_pod.cores), |
253 | memory=Equals(discovered_pod.memory), |
254 | cpu_speed=Equals(discovered_pod.cpu_speed), |
255 | + zone=Equals(zone), |
256 | power_type=Equals(pod_info['type']), |
257 | power_parameters=Equals({}), |
258 | ip_address=Is(None), |
259 | @@ -447,9 +475,10 @@ class TestPodForm(MAASTransactionServerTestCase): |
260 | self.fake_pod_discovery) |
261 | pods_module.discover_pod.return_value = succeed( |
262 | pods_module.discover_pod.return_value) |
263 | + zone = yield deferToDatabase(factory.make_Zone) |
264 | pod_info = self.make_pod_info() |
265 | orig_pod = yield deferToDatabase( |
266 | - factory.make_Pod, pod_type=pod_info['type']) |
267 | + factory.make_Pod, zone=zone, pod_type=pod_info['type']) |
268 | request = MagicMock() |
269 | request.user = yield deferToDatabase(factory.make_User) |
270 | form = yield deferToDatabase( |
271 | @@ -463,6 +492,7 @@ class TestPodForm(MAASTransactionServerTestCase): |
272 | cores=Equals(discovered_pod.cores), |
273 | memory=Equals(discovered_pod.memory), |
274 | cpu_speed=Equals(discovered_pod.cpu_speed), |
275 | + zone=Equals(zone), |
276 | power_type=Equals(pod_info['type']), |
277 | power_parameters=Equals({}), |
278 | ip_address=Is(None), |
279 | @@ -486,7 +516,10 @@ class TestPodForm(MAASTransactionServerTestCase): |
280 | |
281 | def test_raises_unable_to_discover_because_no_racks(self): |
282 | self.patch(pods_module, "discover_pod").return_value = ({}, {}) |
283 | - form = PodForm(data=self.make_pod_info()) |
284 | + zone = factory.make_Zone() |
285 | + pod_info = self.make_pod_info() |
286 | + pod_info['zone'] = zone.id |
287 | + form = PodForm(data=pod_info) |
288 | self.assertTrue(form.is_valid(), form._errors) |
289 | error = self.assertRaises(PodProblem, form.save) |
290 | self.assertEquals( |
291 | @@ -498,7 +531,10 @@ class TestPodForm(MAASTransactionServerTestCase): |
292 | def test_raises_unable_to_discover_because_no_racks_in_twisted(self): |
293 | self.patch(pods_module, "discover_pod").return_value = succeed( |
294 | ({}, {})) |
295 | - form = yield deferToDatabase(PodForm, data=self.make_pod_info()) |
296 | + zone = yield deferToDatabase(factory.make_Zone) |
297 | + pod_info = self.make_pod_info() |
298 | + pod_info['zone'] = zone.id |
299 | + form = yield deferToDatabase(PodForm, data=pod_info) |
300 | is_valid = yield deferToDatabase(form.is_valid) |
301 | self.assertTrue(is_valid, form._errors) |
302 | |
303 | @@ -519,7 +555,10 @@ class TestPodForm(MAASTransactionServerTestCase): |
304 | self.patch(pods_module, "discover_pod").return_value = ({}, { |
305 | failed_rack.system_id: exc, |
306 | }) |
307 | - form = PodForm(data=self.make_pod_info()) |
308 | + zone = factory.make_Zone() |
309 | + pod_info = self.make_pod_info() |
310 | + pod_info['zone'] = zone.id |
311 | + form = PodForm(data=pod_info) |
312 | self.assertTrue(form.is_valid(), form._errors) |
313 | error = self.assertRaises(PodProblem, form.save) |
314 | self.assertEquals(str(exc), str(error)) |
315 | @@ -532,7 +571,10 @@ class TestPodForm(MAASTransactionServerTestCase): |
316 | self.patch(pods_module, "discover_pod").return_value = succeed(({}, { |
317 | failed_rack.system_id: exc, |
318 | })) |
319 | - form = yield deferToDatabase(PodForm, data=self.make_pod_info()) |
320 | + zone = yield deferToDatabase(factory.make_Zone) |
321 | + pod_info = self.make_pod_info() |
322 | + pod_info['zone'] = zone.id |
323 | + form = yield deferToDatabase(PodForm, data=pod_info) |
324 | is_valid = yield deferToDatabase(form.is_valid) |
325 | self.assertTrue(is_valid, form._errors) |
326 | |
327 | diff --git a/src/maasserver/static/js/angular/controllers/pod_details.js b/src/maasserver/static/js/angular/controllers/pod_details.js |
328 | index cd47586..27d620a 100644 |
329 | --- a/src/maasserver/static/js/angular/controllers/pod_details.js |
330 | +++ b/src/maasserver/static/js/angular/controllers/pod_details.js |
331 | @@ -1,7 +1,7 @@ |
332 | -/* Copyright 2017 Canonical Ltd. This software is licensed under the |
333 | +/* Copyright 2017-2018 Canonical Ltd. This software is licensed under the |
334 | * GNU Affero General Public License version 3 (see the file LICENSE). |
335 | * |
336 | - * MAAS Node Details Controller |
337 | + * MAAS Pod Details Controller |
338 | */ |
339 | |
340 | angular.module('MAAS').controller('PodDetailsController', [ |
341 | @@ -54,19 +54,47 @@ angular.module('MAAS').controller('PodDetailsController', [ |
342 | }] |
343 | } |
344 | }; |
345 | - $scope.powerTypes = GeneralManager.getData("power_types"); |
346 | + $scope.power_types = GeneralManager.getData("power_types"); |
347 | $scope.domains = DomainsManager.getItems(); |
348 | $scope.zones = ZonesManager.getItems(); |
349 | $scope.section = { |
350 | area: 'summary' |
351 | }; |
352 | $scope.machinesSearch = 'pod-id:=invalid'; |
353 | + $scope.editing = false; |
354 | |
355 | // Return true if the authenticated user is super user. |
356 | $scope.isSuperUser = function() { |
357 | return UsersManager.isSuperUser(); |
358 | }; |
359 | |
360 | + // Return true if at least a rack controller is connected to the |
361 | + // region controller. |
362 | + $scope.isRackControllerConnected = function() { |
363 | + // If power_types exist then a rack controller is connected. |
364 | + return $scope.power_types.length > 0; |
365 | + }; |
366 | + |
367 | + // Return true when the edit buttons can be clicked. |
368 | + $scope.canEdit = function() { |
369 | + return ( |
370 | + $scope.isRackControllerConnected() && |
371 | + $scope.isSuperUser()); |
372 | + }; |
373 | + |
374 | + // Called to edit the pod configuration. |
375 | + $scope.editPodConfiguration = function() { |
376 | + if(!$scope.canEdit()) { |
377 | + return; |
378 | + } |
379 | + $scope.editing = true; |
380 | + }; |
381 | + |
382 | + // Called when the cancel or save button is pressed. |
383 | + $scope.exitEditPodConfiguration = function() { |
384 | + $scope.editing = false; |
385 | + }; |
386 | + |
387 | // Return true if there is an action error. |
388 | $scope.isActionError = function() { |
389 | return $scope.action.error !== null; |
390 | @@ -104,10 +132,10 @@ angular.module('MAAS').controller('PodDetailsController', [ |
391 | // Return the title of the pod type. |
392 | $scope.getPodTypeTitle = function() { |
393 | var i; |
394 | - for(i = 0; i < $scope.powerTypes.length; i++) { |
395 | - var powerType = $scope.powerTypes[i]; |
396 | - if(powerType.name === $scope.pod.type) { |
397 | - return powerType.description; |
398 | + for(i = 0; i < $scope.power_types.length; i++) { |
399 | + var power_type = $scope.power_types[i]; |
400 | + if(power_type.name === $scope.pod.type) { |
401 | + return power_type.description; |
402 | } |
403 | } |
404 | return $scope.pod.type; |
405 | diff --git a/src/maasserver/static/js/angular/controllers/pods_list.js b/src/maasserver/static/js/angular/controllers/pods_list.js |
406 | index 7de9734..c47adf7 100644 |
407 | --- a/src/maasserver/static/js/angular/controllers/pods_list.js |
408 | +++ b/src/maasserver/static/js/angular/controllers/pods_list.js |
409 | @@ -1,4 +1,4 @@ |
410 | -/* Copyright 2017 Canonical Ltd. This software is licensed under the |
411 | +/* Copyright 2017-2018 Canonical Ltd. This software is licensed under the |
412 | * GNU Affero General Public License version 3 (see the file LICENSE). |
413 | * |
414 | * MAAS Pods List Controller |
415 | @@ -6,9 +6,10 @@ |
416 | |
417 | angular.module('MAAS').controller('PodsListController', [ |
418 | '$scope', '$rootScope', |
419 | - 'PodsManager', 'UsersManager', 'GeneralManager', 'ManagerHelperService', |
420 | - function($scope, $rootScope, |
421 | - PodsManager, UsersManager, GeneralManager, ManagerHelperService) { |
422 | + 'PodsManager', 'UsersManager', 'GeneralManager', 'ZonesManager', |
423 | + 'ManagerHelperService', function( |
424 | + $scope, $rootScope, PodsManager, UsersManager, GeneralManager, |
425 | + ZonesManager, ManagerHelperService) { |
426 | |
427 | // Set title and page. |
428 | $rootScope.title = "Pods"; |
429 | @@ -50,6 +51,7 @@ angular.module('MAAS').controller('PodsListController', [ |
430 | obj: {} |
431 | }; |
432 | $scope.powerTypes = GeneralManager.getData("power_types"); |
433 | + $scope.zones = ZonesManager.getItems(); |
434 | |
435 | // Called to update `allViewableChecked`. |
436 | function updateAllViewableChecked() { |
437 | @@ -223,7 +225,7 @@ angular.module('MAAS').controller('PodsListController', [ |
438 | |
439 | // Load the required managers for this controller. |
440 | ManagerHelperService.loadManagers($scope, [ |
441 | - PodsManager, UsersManager, GeneralManager]).then( |
442 | + PodsManager, UsersManager, GeneralManager, ZonesManager]).then( |
443 | function() { |
444 | $scope.loading = false; |
445 | }); |
446 | diff --git a/src/maasserver/static/js/angular/controllers/tests/test_pod_details.js b/src/maasserver/static/js/angular/controllers/tests/test_pod_details.js |
447 | index e050b8f..269f6ed 100644 |
448 | --- a/src/maasserver/static/js/angular/controllers/tests/test_pod_details.js |
449 | +++ b/src/maasserver/static/js/angular/controllers/tests/test_pod_details.js |
450 | @@ -1,4 +1,4 @@ |
451 | -/* Copyright 2017 Canonical Ltd. This software is licensed under the |
452 | +/* Copyright 2017-2018 Canonical Ltd. This software is licensed under the |
453 | * GNU Affero General Public License version 3 (see the file LICENSE). |
454 | * |
455 | * Unit tests for NodesListController. |
456 | @@ -151,13 +151,14 @@ describe("PodDetailsController", function() { |
457 | }] |
458 | } |
459 | }); |
460 | - expect($scope.powerTypes).toBe(GeneralManager.getData('power_types')); |
461 | + expect($scope.power_types).toBe(GeneralManager.getData('power_types')); |
462 | expect($scope.domains).toBe(DomainsManager.getItems()); |
463 | expect($scope.zones).toBe(ZonesManager.getItems()); |
464 | + expect($scope.editing).toBe(false); |
465 | }); |
466 | |
467 | - it("calls loadManagers with PodsManager, UsersManager, GeneralManager", |
468 | - function() { |
469 | + it("calls loadManagers with PodsManager, UsersManager, GeneralManager, \ |
470 | + DomainsManager, ZonesManager", function() { |
471 | var controller = makeController(); |
472 | expect(ManagerHelperService.loadManagers).toHaveBeenCalledWith( |
473 | $scope, |
474 | @@ -192,6 +193,78 @@ describe("PodDetailsController", function() { |
475 | }); |
476 | }); |
477 | |
478 | + describe("isRackControllerConnected", function() { |
479 | + it("returns false no power_types", function() { |
480 | + var controller = makeController(); |
481 | + $scope.power_types = []; |
482 | + expect($scope.isRackControllerConnected()).toBe(false); |
483 | + }); |
484 | + |
485 | + it("returns true if power_types", function() { |
486 | + var controller = makeController(); |
487 | + $scope.power_types = [{}]; |
488 | + expect($scope.isRackControllerConnected()).toBe(true); |
489 | + }); |
490 | + }); |
491 | + |
492 | + describe("canEdit", function() { |
493 | + it("returns false if not super user", function() { |
494 | + var controller = makeController(); |
495 | + spyOn($scope, "isSuperUser").and.returnValue(false); |
496 | + spyOn( |
497 | + $scope, |
498 | + "isRackControllerConnected").and.returnValue(true); |
499 | + expect($scope.canEdit()).toBe(false); |
500 | + }); |
501 | + |
502 | + it("returns false if rack disconnected", function() { |
503 | + var controller = makeController(); |
504 | + spyOn( |
505 | + $scope, |
506 | + "isRackControllerConnected").and.returnValue(false); |
507 | + expect($scope.canEdit()).toBe(false); |
508 | + }); |
509 | + |
510 | + it("returns true if super user, rack connected", function() { |
511 | + var controller = makeController(); |
512 | + spyOn($scope, "isSuperUser").and.returnValue(true); |
513 | + spyOn( |
514 | + $scope, |
515 | + "isRackControllerConnected").and.returnValue(true); |
516 | + expect($scope.canEdit()).toBe(true); |
517 | + }); |
518 | + }); |
519 | + |
520 | + describe("editPodConfiguration", function() { |
521 | + it("sets editing to true if can edit", |
522 | + function() { |
523 | + var controller = makeController(); |
524 | + spyOn($scope, "canEdit").and.returnValue(true); |
525 | + $scope.editing = false; |
526 | + $scope.editPodConfiguration(); |
527 | + expect($scope.editing).toBe(true); |
528 | + }); |
529 | + |
530 | + it("doesnt set editing to true if cannot", |
531 | + function() { |
532 | + var controller = makeController(); |
533 | + spyOn($scope, "canEdit").and.returnValue(false); |
534 | + $scope.editing = false; |
535 | + $scope.editPodConfiguration(); |
536 | + expect($scope.editing).toBe(false); |
537 | + }); |
538 | + }); |
539 | + |
540 | + describe("exitEditPodConfiguration", function() { |
541 | + it("sets editing to false on exiting pod configuration", |
542 | + function() { |
543 | + var controller = makeController(); |
544 | + $scope.editing = true; |
545 | + $scope.exitEditPodConfiguration(); |
546 | + expect($scope.editing).toBe(false); |
547 | + }); |
548 | + }); |
549 | + |
550 | describe("isActionError", function() { |
551 | |
552 | it("returns false if not action error", function() { |
553 | diff --git a/src/maasserver/static/js/angular/controllers/tests/test_pods_list.js b/src/maasserver/static/js/angular/controllers/tests/test_pods_list.js |
554 | index 213b731..a474945 100644 |
555 | --- a/src/maasserver/static/js/angular/controllers/tests/test_pods_list.js |
556 | +++ b/src/maasserver/static/js/angular/controllers/tests/test_pods_list.js |
557 | @@ -1,4 +1,4 @@ |
558 | -/* Copyright 2017 Canonical Ltd. This software is licensed under the |
559 | +/* Copyright 2017-2018 Canonical Ltd. This software is licensed under the |
560 | * GNU Affero General Public License version 3 (see the file LICENSE). |
561 | * |
562 | * Unit tests for NodesListController. |
563 | @@ -33,11 +33,13 @@ describe("PodsListController", function() { |
564 | })); |
565 | |
566 | // Load the required managers. |
567 | - var PodsManager, UsersManager, GeneralManager, ManagerHelperService; |
568 | + var PodsManager, UsersManager, GeneralManager; |
569 | + var ZonesManager, ManagerHelperService; |
570 | beforeEach(inject(function($injector) { |
571 | PodsManager = $injector.get("PodsManager"); |
572 | UsersManager = $injector.get("UsersManager"); |
573 | GeneralManager = $injector.get("GeneralManager"); |
574 | + ZonesManager = $injector.get("ZonesManager"); |
575 | ManagerHelperService = $injector.get("ManagerHelperService"); |
576 | })); |
577 | |
578 | @@ -104,13 +106,15 @@ describe("PodsListController", function() { |
579 | expect($scope.action.option).toBeNull(); |
580 | expect($scope.add.open).toBe(false); |
581 | expect($scope.powerTypes).toBe(GeneralManager.getData('power_types')); |
582 | + expect($scope.zones).toBe(ZonesManager.getItems()); |
583 | }); |
584 | |
585 | - it("calls loadManagers with PodsManager, UsersManager, GeneralManager", |
586 | - function() { |
587 | + it("calls loadManagers with PodsManager, UsersManager, \ |
588 | + GeneralManager, ZonesManager", function() { |
589 | var controller = makeController(); |
590 | expect(ManagerHelperService.loadManagers).toHaveBeenCalledWith( |
591 | - $scope, [PodsManager, UsersManager, GeneralManager]); |
592 | + $scope, [ |
593 | + PodsManager, UsersManager, GeneralManager, ZonesManager]); |
594 | }); |
595 | |
596 | it("sets loading to false with loadManagers resolves", function() { |
597 | diff --git a/src/maasserver/static/partials/pod-details.html b/src/maasserver/static/partials/pod-details.html |
598 | index 5185ca6..eb0347a 100644 |
599 | --- a/src/maasserver/static/partials/pod-details.html |
600 | +++ b/src/maasserver/static/partials/pod-details.html |
601 | @@ -144,7 +144,7 @@ |
602 | <a href="" role="tab" class="p-tabs__link" data-ng-class="{ 'is-active': section.area === 'summary'}" data-ng-click="section.area = 'summary'">{$ pod.composed_machines_count $} composed machines</a> |
603 | </li> |
604 | <li class="p-tabs__item" role="presentation"> |
605 | - <a href="" role="tab" class="p-tabs__link" data-ng-if="isSuperUser()" data-ng-class="{ 'is-active': section.area === 'power'}" data-ng-click="section.area= 'power'">Power</a> |
606 | + <a href="" role="tab" class="p-tabs__link" data-ng-if="isSuperUser()" data-ng-class="{ 'is-active': section.area === 'configuration'}" data-ng-click="section.area= 'configuration'">Configuration</a> |
607 | </li> |
608 | </nav> |
609 | </nav> |
610 | @@ -201,22 +201,43 @@ |
611 | </div> |
612 | </div> |
613 | </section> |
614 | - <section class="p-strip" data-ng-if="pod && section.area === 'power'"> |
615 | - <div class="row"> |
616 | + <section class="p-strip" data-ng-if="pod && section.area === 'configuration'"> |
617 | + <div class="row"> |
618 | + <div class="col-10"> |
619 | + <h2>Pod configuration</h2> |
620 | + </div> |
621 | + <div class="col-2"> |
622 | + <button class="p-button--neutral u-float--right" |
623 | + data-ng-if="canEdit()" |
624 | + data-ng-click="editPodConfiguration()">Edit</button> |
625 | + </div> |
626 | + </div> |
627 | + <div class="row"> |
628 | <maas-obj-form obj="pod" manager="podManager" manager-method="updateItem" |
629 | - table-form="true" save-on-blur="true"> |
630 | + table-form="true" save-on-blur="false" data-ng-disabled="!editing"> |
631 | <div class="col-6"> |
632 | <div class="col-6"> |
633 | <dl> |
634 | <dt>Type</dt> |
635 | <dd>{$ getPodTypeTitle() $}</dd> |
636 | </dl> |
637 | + <maas-obj-field type="options" key="zone" label="Zone" subtle="false" placeholder="Choose a zone" |
638 | + options="zone.id as zone.name for zone in zones"></maas-obj-field> |
639 | </div> |
640 | </div> |
641 | <div class="col-6"> |
642 | <maas-pod-parameters hide-type="true"></maas-pod-parameters> |
643 | <maas-obj-errors></maas-obj-errors> |
644 | </div> |
645 | + <div class="row"> |
646 | + <div class="col-12 u-align--right" data-ng-if="editing"> |
647 | + <p maas-obj-hide-saving><maas-obj-errors></maas-obj-errors></p> |
648 | + <p maas-obj-show-saving><maas-obj-saving>Trying to connect and discover pod</maas-obj-saving></p> |
649 | + <button class="p-button--base" |
650 | + data-ng-click="exitEditPodConfiguration()">Cancel</button> |
651 | + <button class="p-button--positive" data-ng-click="exitEditPodConfiguration()" maas-obj-save>Save changes</button> |
652 | + </div> |
653 | + </div> |
654 | </maas-obj-form> |
655 | </div> |
656 | </section> |
657 | diff --git a/src/maasserver/static/partials/pods-list.html b/src/maasserver/static/partials/pods-list.html |
658 | index bd966ee..99b3f39 100644 |
659 | --- a/src/maasserver/static/partials/pods-list.html |
660 | +++ b/src/maasserver/static/partials/pods-list.html |
661 | @@ -59,6 +59,8 @@ |
662 | <div class="row"> |
663 | <div class="col-5"> |
664 | <maas-obj-field type="text" key="name" label="Name" label-width="two" input-width="three" placeholder="Name (optional)"></maas-obj-field> |
665 | + <maas-obj-field type="options" key="zone" label="Zone" subtle="false" placeholder="Choose a zone" |
666 | + options="zone.id as zone.name for zone in zones"></maas-obj-field> |
667 | </div> |
668 | <div class="col-6 prefix-1"> |
669 | <maas-pod-parameters></maas-pod-parameters> |
670 | diff --git a/src/maasserver/websockets/handlers/tests/test_pod.py b/src/maasserver/websockets/handlers/tests/test_pod.py |
671 | index 76eb0a2..9653bea 100644 |
672 | --- a/src/maasserver/websockets/handlers/tests/test_pod.py |
673 | +++ b/src/maasserver/websockets/handlers/tests/test_pod.py |
674 | @@ -222,7 +222,9 @@ class TestPodHandler(MAASTransactionServerTestCase): |
675 | def test_create(self): |
676 | user = yield deferToDatabase(factory.make_admin) |
677 | handler = PodHandler(user, {}) |
678 | + zone = yield deferToDatabase(factory.make_Zone) |
679 | pod_info = self.make_pod_info() |
680 | + pod_info['zone'] = zone.id |
681 | yield deferToDatabase(self.fake_pod_discovery) |
682 | created_pod = yield handler.create(pod_info) |
683 | self.assertIsNotNone(created_pod['id']) |
684 | @@ -232,7 +234,9 @@ class TestPodHandler(MAASTransactionServerTestCase): |
685 | def test_update(self): |
686 | user = yield deferToDatabase(factory.make_admin) |
687 | handler = PodHandler(user, {}) |
688 | + zone = yield deferToDatabase(factory.make_Zone) |
689 | pod_info = self.make_pod_info() |
690 | + pod_info['zone'] = zone.id |
691 | pod = yield deferToDatabase( |
692 | factory.make_Pod, pod_type=pod_info['type']) |
693 | pod_info['id'] = pod.id |
UNIT TESTS
-b ui-pod-zones lp:~newell-jensen/maas/+git/maas into -b master lp:~maas-committers/maas
STATUS: FAILED maas-ci- jenkins. internal: 8080/job/ maas/job/ branch- tester/ 1767/console 054b59ae3644b4b 5f657c5331
LOG: http://
COMMIT: d4d8c41c574a7fb