Merge lp:~blake-rouse/maas/fix-1445941 into lp:~maas-committers/maas/trunk

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: no longer in the source branch.
Merged at revision: 5445
Proposed branch: lp:~blake-rouse/maas/fix-1445941
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 387 lines (+136/-47)
12 files modified
src/maasserver/models/node.py (+8/-4)
src/maasserver/models/tests/test_node.py (+10/-0)
src/maasserver/static/js/angular/controllers/nodes_list.js (+14/-9)
src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js (+29/-29)
src/maasserver/static/js/angular/factories/machines.js (+8/-1)
src/maasserver/static/js/angular/factories/tests/test_machines.js (+1/-1)
src/maasserver/static/js/angular/filters/nodes.js (+7/-0)
src/maasserver/static/js/angular/filters/tests/test_nodes.js (+27/-0)
src/maasserver/static/partials/nodes-list.html (+10/-0)
src/maasserver/websockets/handlers/machine.py (+12/-0)
src/maasserver/websockets/handlers/node.py (+7/-2)
src/maasserver/websockets/handlers/tests/test_machine.py (+3/-1)
To merge this branch: bzr merge lp:~blake-rouse/maas/fix-1445941
Reviewer Review Type Date Requested Status
Mike Pontillo (community) Approve
Review via email: mp+307631@code.launchpad.net

Commit message

Add the ability to filter the machines listing based on the deploying or deployed os/release.

To post a comment you must log in.
Revision history for this message
Mike Pontillo (mpontillo) wrote :

Looks good to me!

One comment below about the usage of hard-coded integers rather than an easier-to-read constant or enum. I won't block you on it, but is there a reason you didn't use what is defined in src/maasserver/static/js/enums.js?

review: Approve
Revision history for this message
Blake Rouse (blake-rouse) wrote :

enums.js is for YUI only. That should be converted to an Angular service at some point, once YUI is removed.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/models/node.py'
2--- src/maasserver/models/node.py 2016-09-28 13:20:00 +0000
3+++ src/maasserver/models/node.py 2016-10-04 21:01:43 +0000
4@@ -1943,15 +1943,17 @@
5 global_value = Config.objects.get_config('kernel_opts')
6 return None, global_value
7
8- def get_osystem(self):
9+ def get_osystem(self, default=None):
10 """Return the operating system to install that node."""
11 use_default_osystem = (self.osystem is None or self.osystem == '')
12 if use_default_osystem:
13- return Config.objects.get_config('default_osystem')
14+ if default is None:
15+ default = Config.objects.get_config('default_osystem')
16+ return default
17 else:
18 return self.osystem
19
20- def get_distro_series(self):
21+ def get_distro_series(self, default=None):
22 """Return the distro series to install that node."""
23 use_default_osystem = (
24 self.osystem is None or
25@@ -1960,7 +1962,9 @@
26 self.distro_series is None or
27 self.distro_series == '')
28 if use_default_osystem and use_default_distro_series:
29- return Config.objects.get_config('default_distro_series')
30+ if default is None:
31+ default = Config.objects.get_config('default_distro_series')
32+ return default
33 else:
34 return self.distro_series
35
36
37=== modified file 'src/maasserver/models/tests/test_node.py'
38--- src/maasserver/models/tests/test_node.py 2016-09-28 13:20:00 +0000
39+++ src/maasserver/models/tests/test_node.py 2016-10-04 21:01:43 +0000
40@@ -975,11 +975,21 @@
41 osystem = Config.objects.get_config('default_osystem')
42 self.assertEqual(osystem, node.get_osystem())
43
44+ def test_get_osystem_returns_passed_default(self):
45+ node = factory.make_Node(osystem='')
46+ default = factory.make_name("default")
47+ self.assertEqual(default, node.get_osystem(default=default))
48+
49 def test_get_distro_series_returns_default_series(self):
50 node = factory.make_Node(distro_series='')
51 series = Config.objects.get_config('default_distro_series')
52 self.assertEqual(series, node.get_distro_series())
53
54+ def test_get_distro_series_returns_passed_default(self):
55+ node = factory.make_Node(osystem='')
56+ default = factory.make_name("default")
57+ self.assertEqual(default, node.get_distro_series(default=default))
58+
59 def test_get_effective_license_key_returns_node_value(self):
60 license_key = factory.make_name('license_key')
61 node = factory.make_Node(license_key=license_key)
62
63=== modified file 'src/maasserver/static/js/angular/controllers/nodes_list.js'
64--- src/maasserver/static/js/angular/controllers/nodes_list.js 2016-09-30 22:45:36 +0000
65+++ src/maasserver/static/js/angular/controllers/nodes_list.js 2016-10-04 21:01:43 +0000
66@@ -433,14 +433,6 @@
67 updateActionErrorCount(tab);
68 enterViewSelected(tab);
69
70- var actionOption = $scope.tabs[tab].actionOption;
71- if(angular.isObject(actionOption) &&
72- actionOption.name === "deploy") {
73- GeneralManager.startPolling("osinfo");
74- } else {
75- GeneralManager.stopPolling("osinfo");
76- }
77-
78 // Hide the add hardware/device section.
79 if (tab === 'nodes') {
80 if(angular.isObject($scope.addHardwareScope)) {
81@@ -496,7 +488,6 @@
82 resetActionProgress(tab);
83 leaveViewSelected(tab);
84 $scope.tabs[tab].actionOption = null;
85- GeneralManager.stopPolling("osinfo");
86 };
87
88 // Perform the action on all nodes.
89@@ -606,6 +597,17 @@
90 return UsersManager.isSuperUser();
91 };
92
93+ // Returns the release title from osinfo.
94+ $scope.getReleaseTitle = function(os_release) {
95+ for(i = 0; i < $scope.osinfo.releases.length; i++) {
96+ var release = $scope.osinfo.releases[i];
97+ if(release[0] === os_release) {
98+ return release[1];
99+ }
100+ }
101+ return os_release;
102+ };
103+
104 // Load the required managers for this controller. The ServicesManager
105 // is required by the maasControllerStatus directive that is used
106 // in the partial for this controller.
107@@ -616,6 +618,9 @@
108 $scope.loading = false;
109 });
110
111+ // Start polling for the os information.
112+ GeneralManager.startPolling("osinfo");
113+
114 // Stop polling and save the current filter when the scope is destroyed.
115 $scope.$on("$destroy", function() {
116 $interval.cancel($scope.statusPoll);
117
118=== modified file 'src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js'
119--- src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js 2016-09-29 18:31:21 +0000
120+++ src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js 2016-10-04 21:01:43 +0000
121@@ -152,6 +152,28 @@
122 });
123 });
124
125+ describe("getReleaseTitle", function() {
126+ it("returns release title from osinfo", function() {
127+ var controller = makeController();
128+ $scope.osinfo = {
129+ releases: [
130+ ['ubuntu/xenial', 'Ubuntu Xenial']
131+ ]
132+ };
133+ expect($scope.getReleaseTitle('ubuntu/xenial')).toBe(
134+ 'Ubuntu Xenial');
135+ });
136+
137+ it("returns release name when not in osinfo", function() {
138+ var controller = makeController();
139+ $scope.osinfo = {
140+ releases: []
141+ };
142+ expect($scope.getReleaseTitle('ubuntu/xenial')).toBe(
143+ 'ubuntu/xenial');
144+ });
145+ });
146+
147 it("sets title and page on $rootScope", function() {
148 var controller = makeController();
149 expect($rootScope.title).toBe("Machines");
150@@ -180,6 +202,13 @@
151 expect($scope.loading).toBe(true);
152 });
153
154+ it("calls startPolling when scope created", function() {
155+ spyOn(GeneralManager, "startPolling");
156+ var controller = makeController();
157+ expect(GeneralManager.startPolling).toHaveBeenCalledWith(
158+ "osinfo");
159+ });
160+
161 it("calls stopPolling when scope destroyed", function() {
162 var controller = makeController();
163 spyOn(GeneralManager, "stopPolling");
164@@ -827,35 +856,6 @@
165 expect($scope.tabs[tab].previous_search).toBe(search);
166 });
167
168- it("action deploy calls startPolling for osinfo", function() {
169- var controller = makeController();
170- $scope.tabs[tab].actionOption = {
171- "name": "deploy"
172- };
173- spyOn(GeneralManager, "startPolling");
174- $scope.actionOptionSelected(tab);
175- expect(GeneralManager.startPolling).toHaveBeenCalledWith(
176- "osinfo");
177- });
178-
179- it("changing away from deploy calls startPolling for osinfo",
180- function() {
181- var controller = makeController();
182- $scope.tabs[tab].actionOption = {
183- "name": "deploy"
184- };
185- spyOn(GeneralManager, "startPolling");
186- spyOn(GeneralManager, "stopPolling");
187- $scope.actionOptionSelected(tab);
188-
189- $scope.tabs[tab].actionOption = {
190- "name": "acquire"
191- };
192- $scope.actionOptionSelected(tab);
193- var expected = expect(GeneralManager.stopPolling);
194- expected.toHaveBeenCalledWith("osinfo");
195- });
196-
197 it("calls hide on addHardwareScope", function() {
198 var controller;
199 if (tab === 'nodes') {
200
201=== modified file 'src/maasserver/static/js/angular/factories/machines.js'
202--- src/maasserver/static/js/angular/factories/machines.js 2016-03-28 13:54:47 +0000
203+++ src/maasserver/static/js/angular/factories/machines.js 2016-10-04 21:01:43 +0000
204@@ -28,7 +28,14 @@
205 "subnets": null,
206 "fabrics": null,
207 "spaces": null,
208- "storage_tags": null
209+ "storage_tags": null,
210+ "release": function(machine) {
211+ if(machine.status_code === 6 || machine.status_code === 9) {
212+ return machine.osystem + "/" + machine.distro_series;
213+ } else {
214+ return '';
215+ }
216+ }
217 };
218
219 // Listen for notify events for the machine object.
220
221=== modified file 'src/maasserver/static/js/angular/factories/tests/test_machines.js'
222--- src/maasserver/static/js/angular/factories/tests/test_machines.js 2016-03-28 13:54:47 +0000
223+++ src/maasserver/static/js/angular/factories/tests/test_machines.js 2016-10-04 21:01:43 +0000
224@@ -35,7 +35,7 @@
225 it("set requires attributes", function() {
226 expect(Object.keys(MachinesManager._metadataAttributes)).toEqual(
227 ["status", "owner", "tags", "zone", "subnets", "fabrics",
228- "spaces", "storage_tags"]);
229+ "spaces", "storage_tags", "release"]);
230 });
231
232 describe("mountSpecialFilesystem", function() {
233
234=== modified file 'src/maasserver/static/js/angular/filters/nodes.js'
235--- src/maasserver/static/js/angular/filters/nodes.js 2015-06-04 14:58:39 +0000
236+++ src/maasserver/static/js/angular/filters/nodes.js 2016-10-04 21:01:43 +0000
237@@ -34,6 +34,13 @@
238 },
239 power: function(node) {
240 return node.power_state;
241+ },
242+ release: function(node) {
243+ if(node.status_code === 6 || node.status_code === 9) {
244+ return node.osystem + "/" + node.distro_series;
245+ } else {
246+ return '';
247+ }
248 }
249 };
250
251
252=== modified file 'src/maasserver/static/js/angular/filters/tests/test_nodes.js'
253--- src/maasserver/static/js/angular/filters/tests/test_nodes.js 2015-06-03 21:09:38 +0000
254+++ src/maasserver/static/js/angular/filters/tests/test_nodes.js 2016-10-04 21:01:43 +0000
255@@ -340,4 +340,31 @@
256 [matchingNode]);
257 });
258
259+ it("matches using release mapping function", function() {
260+ var deployingNode = {
261+ status_code: 9,
262+ osystem: 'ubuntu',
263+ distro_series: 'xenial'
264+ };
265+ var deployedNode = {
266+ status_code: 6,
267+ osystem: 'ubuntu',
268+ distro_series: 'xenial'
269+ };
270+ var allocatedNode = {
271+ status_code: 5,
272+ osystem: 'ubuntu',
273+ distro_series: 'xenial'
274+ };
275+ var deployedOtherNode = {
276+ status_code: 6,
277+ osystem: 'ubuntu',
278+ distro_series: 'trusty'
279+ };
280+ var nodes = [
281+ deployingNode, deployedNode, allocatedNode, deployedOtherNode];
282+ expect(nodesFilter(nodes, "release:ubuntu/xenial")).toEqual(
283+ [deployingNode, deployedNode]);
284+ });
285+
286 });
287
288=== modified file 'src/maasserver/static/partials/nodes-list.html'
289--- src/maasserver/static/partials/nodes-list.html 2016-09-30 14:59:49 +0000
290+++ src/maasserver/static/partials/nodes-list.html 2016-10-04 21:01:43 +0000
291@@ -569,6 +569,16 @@
292 </ul>
293 </div>
294 </div>
295+ <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.release.length">
296+ <h4 class="accordion__tab-title maas-accordion-tab">OS/Release</h4>
297+ <div class="accordion__tab-content">
298+ <ul class="accordion__tab-list">
299+ <li class="accordion__tab-item" data-ng-repeat="release in tabs.nodes.metadata.release | orderBy:['name', '-count']" data-ng-class="{ 'is-active': isFilterActive('release', release.name, 'nodes') }">
300+ <a class="accordion__tab-link" href="" data-ng-click="toggleFilter('release', release.name, 'nodes')">{$ getReleaseTitle(release.name) $} ({$ release.count $})</a>
301+ </li>
302+ </ul>
303+ </div>
304+ </div>
305 <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.tags.length">
306 <h4 class="accordion__tab-title maas-accordion-tab">Tags</h4>
307 <div class="accordion__tab-content">
308
309=== modified file 'src/maasserver/websockets/handlers/machine.py'
310--- src/maasserver/websockets/handlers/machine.py 2016-09-09 21:49:49 +0000
311+++ src/maasserver/websockets/handlers/machine.py 2016-10-04 21:01:43 +0000
312@@ -52,6 +52,7 @@
313 from maasserver.forms_interface_link import InterfaceLinkForm
314 from maasserver.models.blockdevice import BlockDevice
315 from maasserver.models.cacheset import CacheSet
316+from maasserver.models.config import Config
317 from maasserver.models.filesystemgroup import VolumeGroup
318 from maasserver.models.interface import Interface
319 from maasserver.models.node import (
320@@ -164,6 +165,17 @@
321 return Machine.objects.get_nodes(
322 self.user, NODE_PERMISSION.VIEW, from_nodes=self._meta.queryset)
323
324+ def list(self, params):
325+ """List objects.
326+
327+ Caches default_osystem and default_distro_series so only 2 queries are
328+ made for the whole list of nodes.
329+ """
330+ self.default_osystem = Config.objects.get_config('default_osystem')
331+ self.default_distro_series = Config.objects.get_config(
332+ 'default_distro_series')
333+ return super(MachineHandler, self).list(params)
334+
335 def dehydrate(self, obj, data, for_list=False):
336 """Add extra fields to `data`."""
337 data = super(MachineHandler, self).dehydrate(
338
339=== modified file 'src/maasserver/websockets/handlers/node.py'
340--- src/maasserver/websockets/handlers/node.py 2016-09-23 20:13:07 +0000
341+++ src/maasserver/websockets/handlers/node.py 2016-10-04 21:01:43 +0000
342@@ -61,6 +61,9 @@
343
344 class NodeHandler(TimestampedModelHandler):
345
346+ default_osystem = None
347+ default_distro_series = None
348+
349 class Meta:
350 abstract = True
351 pk = 'system_id'
352@@ -138,9 +141,11 @@
353 tag.name
354 for tag in obj.tags.all()
355 ]
356+ data["osystem"] = obj.get_osystem(
357+ default=self.default_osystem)
358+ data["distro_series"] = obj.get_distro_series(
359+ default=self.default_distro_series)
360 if not for_list:
361- data["osystem"] = obj.get_osystem()
362- data["distro_series"] = obj.get_distro_series()
363 data["hwe_kernel"] = make_hwe_kernel_ui_text(obj.hwe_kernel)
364
365 data["power_type"] = obj.power_type
366
367=== modified file 'src/maasserver/websockets/handlers/tests/test_machine.py'
368--- src/maasserver/websockets/handlers/tests/test_machine.py 2016-09-27 23:03:56 +0000
369+++ src/maasserver/websockets/handlers/tests/test_machine.py 2016-10-04 21:01:43 +0000
370@@ -250,6 +250,8 @@
371 "storage",
372 "storage_tags",
373 "node_type_display",
374+ "osystem",
375+ "distro_series",
376 ]
377 for key in list(data):
378 if key not in allowed_fields:
379@@ -973,7 +975,7 @@
380 # number means regiond has to do more work slowing down its process
381 # and slowing down the client waiting for the response.
382 self.assertEqual(
383- query_10_count, 10,
384+ query_10_count, 12,
385 "Number of queries has changed; make sure this is expected.")
386 self.assertEqual(
387 query_10_count, query_20_count,