Merge lp:~blake-rouse/maas/fix-1592137 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: 5127
Proposed branch: lp:~blake-rouse/maas/fix-1592137
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 725 lines (+450/-97)
7 files modified
src/maasserver/enum.py (+7/-3)
src/maasserver/static/js/angular/controllers/subnet_details.js (+91/-5)
src/maasserver/static/js/angular/controllers/tests/test_subnet_details.js (+154/-0)
src/maasserver/static/js/angular/services/converter.js (+55/-0)
src/maasserver/static/js/angular/services/tests/test_converter.js (+104/-0)
src/maasserver/static/js/angular/services/validation.js (+15/-55)
src/maasserver/static/partials/subnet-details.html (+24/-34)
To merge this branch: bzr merge lp:~blake-rouse/maas/fix-1592137
Reviewer Review Type Date Requested Status
Mike Pontillo (community) Approve
Jeffrey C Jones (community) Approve
Review via email: mp+297468@code.launchpad.net

Commit message

Add the ability to sort the IP address table on the subnet details page.

To post a comment you must log in.
Revision history for this message
Jeffrey C Jones (trapnine) wrote :

LGTM, might want to move some display strings to a central location, comments below.

review: Approve
Revision history for this message
Mike Pontillo (mpontillo) wrote :

Looks really good!

I made a number of comments below; nothing really blocking, but some things to consider and some minor cleanup.

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

Thanks for the reviews. Addressed all comments.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/enum.py'
2--- src/maasserver/enum.py 2016-04-11 16:23:26 +0000
3+++ src/maasserver/enum.py 2016-06-16 14:50:25 +0000
4@@ -134,6 +134,8 @@
5 REGION_AND_RACK_CONTROLLER = 4
6
7
8+# This is copied in static/js/angular/controllers/subnet_details.js. If you
9+# update any choices you also need to update the controller.
10 NODE_TYPE_CHOICES = (
11 (NODE_TYPE.MACHINE, "Machine"),
12 (NODE_TYPE.DEVICE, "Device"),
13@@ -226,12 +228,14 @@
14 DISCOVERED = 6
15
16
17+# This is copied in static/js/angular/controllers/subnet_details.js. If you
18+# update any choices you also need to update the controller.
19 IPADDRESS_TYPE_CHOICES = (
20- (IPADDRESS_TYPE.AUTO, "Auto"),
21- (IPADDRESS_TYPE.STICKY, "Sticky"),
22+ (IPADDRESS_TYPE.AUTO, "Automatic"),
23+ (IPADDRESS_TYPE.STICKY, "Static"),
24 (IPADDRESS_TYPE.USER_RESERVED, "User reserved"),
25 (IPADDRESS_TYPE.DHCP, "DHCP"),
26- (IPADDRESS_TYPE.DISCOVERED, "Discovered"),
27+ (IPADDRESS_TYPE.DISCOVERED, "Observed"),
28 )
29
30
31
32=== modified file 'src/maasserver/static/js/angular/controllers/subnet_details.js'
33--- src/maasserver/static/js/angular/controllers/subnet_details.js 2016-06-09 17:18:10 +0000
34+++ src/maasserver/static/js/angular/controllers/subnet_details.js 2016-06-16 14:50:25 +0000
35@@ -8,10 +8,11 @@
36 '$scope', '$rootScope', '$routeParams', '$filter', '$location',
37 'SubnetsManager', 'IPRangesManager', 'SpacesManager', 'VLANsManager',
38 'UsersManager', 'FabricsManager', 'ManagerHelperService', 'ErrorService',
39+ 'ConverterService',
40 function(
41 $scope, $rootScope, $routeParams, $filter, $location, SubnetsManager,
42 IPRangesManager, SpacesManager, VLANsManager, UsersManager,
43- FabricsManager, ManagerHelperService, ErrorService) {
44+ FabricsManager, ManagerHelperService, ErrorService, ConverterService) {
45
46 // Set title and page.
47 $rootScope.title = "Loading...";
48@@ -29,10 +30,29 @@
49 $scope.spaces = SpacesManager.getItems();
50 $scope.vlans = VLANsManager.getItems();
51 $scope.fabrics = FabricsManager.getItems();
52+ $scope.reverse = false;
53 $scope.newRange = null;
54 $scope.editIPRange = null;
55 $scope.deleteIPRange = null;
56
57+ // Alloc type mapping.
58+ var ALLOC_TYPES = {
59+ 0: 'Automatic',
60+ 1: 'Static',
61+ 4: 'User reserved',
62+ 5: 'DHCP',
63+ 6: 'Observed'
64+ };
65+
66+ // Node type mapping.
67+ var NODE_TYPES = {
68+ 0: 'Machine',
69+ 1: 'Device',
70+ 2: 'Rack controller',
71+ 3: 'Region controller',
72+ 4: 'Rack and region controller'
73+ };
74+
75 // Updates the page title.
76 function updateTitle() {
77 subnet = $scope.subnet;
78@@ -44,10 +64,75 @@
79 }
80 }
81
82- $scope.isSuperUser = function() {
83- return UsersManager.isSuperUser();
84- };
85-
86+ // Update the IP version of the CIDR.
87+ function updateIPVersion() {
88+ var ip = $scope.subnet.cidr.split('/')[0];
89+ if(ip.indexOf(':') === -1) {
90+ $scope.ipVersion = 4;
91+ } else {
92+ $scope.ipVersion = 6;
93+ }
94+ }
95+
96+ // Sort for IP address.
97+ $scope.ipSort = function(ipAddress) {
98+ if($scope.ipVersion === 4) {
99+ return ConverterService.ipv4ToInteger(ipAddress.ip);
100+ } else {
101+ return ConverterService.ipv6Expand(ipAddress.ip);
102+ }
103+ };
104+
105+ // Set default predicate to the ipSort function.
106+ $scope.predicate = $scope.ipSort;
107+
108+ // Return the name of the allocation type.
109+ $scope.getAllocType = function(allocType) {
110+ var str = ALLOC_TYPES[allocType];
111+ if(angular.isString(str)) {
112+ return str;
113+ } else {
114+ return "Unknown";
115+ }
116+ };
117+
118+ // Sort based on the name of the allocation type.
119+ $scope.allocTypeSort = function(ipAddress) {
120+ return $scope.getAllocType(ipAddress.alloc_type);
121+ };
122+
123+ // Return the name of the node type.
124+ $scope.getNodeType = function(nodeType) {
125+ var str = NODE_TYPES[nodeType];
126+ if(angular.isString(str)) {
127+ return str;
128+ } else {
129+ return "Unknown";
130+ }
131+ };
132+
133+ // Sort based on the node type string.
134+ $scope.nodeTypeSort = function(ipAddress) {
135+ return $scope.getNodeType(ipAddress.node_summary.node_type);
136+ };
137+
138+ // Sort based on the owner name.
139+ $scope.ownerSort = function(ipAddress) {
140+ var owner = ipAddress.user;
141+ if(angular.isString(owner) && owner.length > 0) {
142+ return owner;
143+ } else {
144+ return "MAAS";
145+ }
146+ };
147+
148+ // Called to change the sort order of the IP table.
149+ $scope.sortIPTable = function(predicate) {
150+ $scope.predicate = predicate;
151+ $scope.reverse = !$scope.reverse;
152+ };
153+
154+ // Return the name of the VLAN.
155 $scope.getVLANName = function(vlan) {
156 return VLANsManager.getName(vlan);
157 };
158@@ -179,6 +264,7 @@
159 };
160 $scope.$watch("subnet.fabric", updateFabric);
161 $scope.$watch("subnet.vlan", updateFabric);
162+ $scope.$watch("subnet.cidr", updateIPVersion);
163 }
164
165 // Load all the required managers.
166
167=== modified file 'src/maasserver/static/js/angular/controllers/tests/test_subnet_details.js'
168--- src/maasserver/static/js/angular/controllers/tests/test_subnet_details.js 2016-06-13 14:21:20 +0000
169+++ src/maasserver/static/js/angular/controllers/tests/test_subnet_details.js 2016-06-16 14:50:25 +0000
170@@ -72,6 +72,7 @@
171 // Load any injected managers and services.
172 var SubnetsManager, IPRangesManager, SpacesManager, VLANsManager;
173 var FabricsManager, UsersManager, HelperService, ErrorService;
174+ var ConverterService;
175 beforeEach(inject(function($injector) {
176 SubnetsManager = $injector.get("SubnetsManager");
177 IPRangesManager = $injector.get("IPRangesManager");
178@@ -81,6 +82,7 @@
179 UsersManager = $injector.get("UsersManager");
180 ManagerHelperService = $injector.get("ManagerHelperService");
181 ErrorService = $injector.get("ErrorService");
182+ ConverterService = $injector.get("ConverterService");
183 }));
184
185 var fabric, vlan, space, subnet;
186@@ -209,6 +211,158 @@
187 expect($rootScope.title).toBe(subnet.cidr + " (" + subnet.name + ")");
188 });
189
190+ describe("ipSort", function() {
191+
192+ it("calls ipv4ToInteger when ipVersion == 4", function() {
193+ var controller = makeControllerResolveSetActiveItem();
194+ $scope.ipVersion = 4;
195+ var expected = {};
196+ spyOn(ConverterService, "ipv4ToInteger").and.returnValue(expected);
197+ var ipAddress = {
198+ ip: {}
199+ };
200+ var observed = $scope.ipSort(ipAddress);
201+ expect(ConverterService.ipv4ToInteger).toHaveBeenCalledWith(
202+ ipAddress.ip);
203+ expect(observed).toBe(expected);
204+ });
205+
206+ it("calls ipv6Expand when ipVersion == 6", function() {
207+ var controller = makeControllerResolveSetActiveItem();
208+ $scope.ipVersion = 6;
209+ var expected = {};
210+ spyOn(ConverterService, "ipv6Expand").and.returnValue(expected);
211+ var ipAddress = {
212+ ip: {}
213+ };
214+ var observed = $scope.ipSort(ipAddress);
215+ expect(ConverterService.ipv6Expand).toHaveBeenCalledWith(
216+ ipAddress.ip);
217+ expect(observed).toBe(expected);
218+ });
219+
220+ it("is predicate default", function() {
221+ var controller = makeControllerResolveSetActiveItem();
222+ expect($scope.predicate).toBe($scope.ipSort);
223+ });
224+ });
225+
226+ describe("getAllocType", function() {
227+
228+ var scenarios = {
229+ 0: 'Automatic',
230+ 1: 'Static',
231+ 4: 'User reserved',
232+ 5: 'DHCP',
233+ 6: 'Observed',
234+ 7: 'Unknown'
235+ };
236+
237+ angular.forEach(scenarios, function(expected, allocType) {
238+ it("allocType( " + allocType + ") = " + expected, function() {
239+ var controller = makeControllerResolveSetActiveItem();
240+ expect($scope.getAllocType(allocType)).toBe(expected);
241+ });
242+ });
243+ });
244+
245+ describe("allocTypeSort", function() {
246+
247+ it("calls getAllocType", function() {
248+ var controller = makeControllerResolveSetActiveItem();
249+ var expected = {};
250+ spyOn($scope, "getAllocType").and.returnValue(expected);
251+ var ipAddress = {
252+ alloc_type: {}
253+ };
254+ var observed = $scope.allocTypeSort(ipAddress);
255+ expect($scope.getAllocType).toHaveBeenCalledWith(
256+ ipAddress.alloc_type);
257+ expect(observed).toBe(expected);
258+ });
259+ });
260+
261+ describe("getNodeType", function() {
262+
263+ var scenarios = {
264+ 0: 'Machine',
265+ 1: 'Device',
266+ 2: 'Rack controller',
267+ 3: 'Region controller',
268+ 4: 'Rack and region controller',
269+ 5: 'Unknown'
270+ };
271+
272+ angular.forEach(scenarios, function(expected, nodeType) {
273+ it("nodeType( " + nodeType + ") = " + expected, function() {
274+ var controller = makeControllerResolveSetActiveItem();
275+ expect($scope.getNodeType(nodeType)).toBe(expected);
276+ });
277+ });
278+ });
279+
280+ describe("nodeTypeSort", function() {
281+
282+ it("calls getNodeType", function() {
283+ var controller = makeControllerResolveSetActiveItem();
284+ var expected = {};
285+ spyOn($scope, "getNodeType").and.returnValue(expected);
286+ var ipAddress = {
287+ node_summary: {
288+ node_type: {}
289+ }
290+ };
291+ var observed = $scope.nodeTypeSort(ipAddress);
292+ expect($scope.getNodeType).toHaveBeenCalledWith(
293+ ipAddress.node_summary.node_type);
294+ expect(observed).toBe(expected);
295+ });
296+ });
297+
298+ describe("ownerSort", function() {
299+
300+ it("returns owner", function() {
301+ var controller = makeControllerResolveSetActiveItem();
302+ var ipAddress = {
303+ user: makeName("owner")
304+ };
305+ var observed = $scope.ownerSort(ipAddress);
306+ expect(observed).toBe(ipAddress.user);
307+ });
308+
309+ it("returns MAAS for empty string", function() {
310+ var controller = makeControllerResolveSetActiveItem();
311+ var ipAddress = {
312+ user: ""
313+ };
314+ var observed = $scope.ownerSort(ipAddress);
315+ expect(observed).toBe("MAAS");
316+ });
317+
318+ it("returns MAAS for null", function() {
319+ var controller = makeControllerResolveSetActiveItem();
320+ var ipAddress = {
321+ user: null
322+ };
323+ var observed = $scope.ownerSort(ipAddress);
324+ expect(observed).toBe("MAAS");
325+ });
326+ });
327+
328+ describe("sortIPTable", function() {
329+
330+ it("sets predicate and inverts reverse", function() {
331+ var controller = makeControllerResolveSetActiveItem();
332+ $scope.reverse = true;
333+ var predicate = {};
334+ $scope.sortIPTable(predicate);
335+ expect($scope.predicate).toBe(predicate);
336+ expect($scope.reverse).toBe(false);
337+ $scope.sortIPTable(predicate);
338+ expect($scope.reverse).toBe(true);
339+ });
340+ });
341+
342 describe("deleteButton", function() {
343
344 it("confirms delete", function() {
345
346=== modified file 'src/maasserver/static/js/angular/services/converter.js'
347--- src/maasserver/static/js/angular/services/converter.js 2016-03-28 13:54:47 +0000
348+++ src/maasserver/static/js/angular/services/converter.js 2016-06-16 14:50:25 +0000
349@@ -86,4 +86,59 @@
350 this.roundByBlockSize = function(bytes, block_size) {
351 return block_size * Math.floor(bytes / block_size);
352 };
353+
354+ // Convert string ipv4 address into octets array.
355+ this.ipv4ToOctets = function(ipAddress) {
356+ var parts = ipAddress.split('.');
357+ var octets = [];
358+ angular.forEach(parts, function(part) {
359+ octets.push(parseInt(part, 10));
360+ });
361+ return octets;
362+ };
363+
364+ // Convert string ipv4 address into integer.
365+ this.ipv4ToInteger = function(ipAddress) {
366+ var octets = this.ipv4ToOctets(ipAddress);
367+ return (
368+ (octets[0] * Math.pow(256,3)) +
369+ (octets[1] * Math.pow(256,2)) +
370+ (octets[2] * 256) + octets[3]);
371+ };
372+
373+ // Convert ipv6 address to a full ipv6 address, removing the
374+ // '::' shortcut and padding each group with zeros.
375+ this.ipv6Expand = function(ipAddress) {
376+ var i, expandedAddress = ipAddress;
377+ if(expandedAddress.indexOf("::") !== -1) {
378+ // '::' is present so replace it with the required
379+ // number of '0000:' based on its location in the string.
380+ var split = ipAddress.split("::");
381+ var groups = 0;
382+ for(i = 0; i < split.length; i++) {
383+ groups += split[i].split(":").length;
384+ }
385+ expandedAddress = split[0] + ":";
386+ for(i = 0; i < 8 - groups; i++) {
387+ expandedAddress += "0000:";
388+ }
389+ expandedAddress += split[1];
390+ }
391+ // Pad the output of each part with zeros.
392+ var output = [], parts = expandedAddress.split(":");
393+ angular.forEach(parts, function(part) {
394+ output.push("0000".substr(part.length) + part);
395+ });
396+ return output.join(":");
397+ };
398+
399+ // Convert string ipv6 into groups array.
400+ this.ipv6ToGroups = function(ipAddress) {
401+ var groups = [];
402+ var parts = this.ipv6Expand(ipAddress).split(":");
403+ angular.forEach(parts, function(part) {
404+ groups.push(parseInt(part, 16));
405+ });
406+ return groups;
407+ };
408 });
409
410=== modified file 'src/maasserver/static/js/angular/services/tests/test_converter.js'
411--- src/maasserver/static/js/angular/services/tests/test_converter.js 2016-03-28 13:54:47 +0000
412+++ src/maasserver/static/js/angular/services/tests/test_converter.js 2016-06-16 14:50:25 +0000
413@@ -201,4 +201,108 @@
414 1024 * 1024 * 1024);
415 });
416 });
417+
418+ describe("ipv4ToOctets", function() {
419+
420+ var scenarios = [
421+ {
422+ input: "192.168.1.1",
423+ output: [192,168,1,1]
424+ },
425+ {
426+ input: "172.16.1.1",
427+ output: [172,16,1,1]
428+ },
429+ {
430+ input: "10.1.1.1",
431+ output: [10,1,1,1]
432+ }
433+ ];
434+
435+ angular.forEach(scenarios, function(scenario) {
436+ it("converts: " + scenario.input, function() {
437+ var result = ConverterService.ipv4ToOctets(scenario.input);
438+ expect(result).toEqual(scenario.output);
439+ });
440+ });
441+ });
442+
443+ describe("ipv4ToInteger", function() {
444+
445+ var scenarios = [
446+ {
447+ input: "192.168.1.1",
448+ output: 3232235777
449+ },
450+ {
451+ input: "172.16.1.1",
452+ output: 2886729985
453+ },
454+ {
455+ input: "10.1.1.1",
456+ output: 167837953
457+ }
458+ ];
459+
460+ angular.forEach(scenarios, function(scenario) {
461+ it("converts: " + scenario.input, function() {
462+ var result = ConverterService.ipv4ToInteger(scenario.input);
463+ expect(result).toEqual(scenario.output);
464+ });
465+ });
466+ });
467+
468+ describe("ipv6Expand", function() {
469+
470+ var scenarios = [
471+ {
472+ input: "::1",
473+ output: "0000:0000:0000:0000:0000:0000:0000:0001"
474+ },
475+ {
476+ input: "2001:db8::1",
477+ output: "2001:0db8:0000:0000:0000:0000:0000:0001"
478+ },
479+ {
480+ input: "2001:db8:1::1",
481+ output: "2001:0db8:0001:0000:0000:0000:0000:0001"
482+ },
483+ {
484+ input: "2001:db8::",
485+ output: "2001:0db8:0000:0000:0000:0000:0000:0000"
486+ }
487+ ];
488+
489+ angular.forEach(scenarios, function(scenario) {
490+ it("expands: " + scenario.input, function() {
491+ var result = ConverterService.ipv6Expand(scenario.input);
492+ expect(result).toBe(scenario.output);
493+ });
494+ });
495+ });
496+
497+ describe("ipv6ToGroups", function() {
498+
499+ var scenarios = [
500+ {
501+ input: "::1",
502+ output: [0, 0, 0, 0, 0, 0, 0, 1]
503+ },
504+ {
505+ input: "2001:db8::1",
506+ output: [8193, 3512, 0, 0, 0, 0, 0, 1]
507+ },
508+ {
509+ input: "2001:db8:1::1",
510+ output: [8193, 3512, 1, 0, 0, 0, 0, 1]
511+ }
512+ ];
513+
514+ angular.forEach(scenarios, function(scenario) {
515+ it("converts: " + scenario.input, function() {
516+ var result = ConverterService.ipv6ToGroups(scenario.input);
517+ expect(result).toEqual(scenario.output);
518+ });
519+ });
520+ });
521 });
522
523=== modified file 'src/maasserver/static/js/angular/services/validation.js'
524--- src/maasserver/static/js/angular/services/validation.js 2016-03-28 13:54:47 +0000
525+++ src/maasserver/static/js/angular/services/validation.js 2016-06-16 14:50:25 +0000
526@@ -6,7 +6,8 @@
527 * Used by controllers to validate user inputs.
528 */
529
530-angular.module('MAAS').service('ValidationService', function() {
531+angular.module('MAAS').service('ValidationService', [
532+ 'ConverterService', function(ConverterService) {
533
534 // Pattern that matches a domainname.
535 // XXX 2016-02-24 lamont: This also matches "example.com.",
536@@ -50,47 +51,6 @@
537 return true;
538 }
539
540- // Convert string ipv4 address into octets array.
541- function ipv4ToOctets(ipAddress) {
542- var parts = ipAddress.split('.');
543- var octets = [];
544- angular.forEach(parts, function(part) {
545- octets.push(parseInt(part, 10));
546- });
547- return octets;
548- }
549-
550- // Convert ipv6 address to a full ipv6 address, removing the
551- // '::' shortcut.
552- function ipv6Expand(ipAddress) {
553- var i, expandedAddress = ipAddress;
554- if(expandedAddress.indexOf("::") !== -1) {
555- // '::' is present so replace it with the required
556- // number of '0000:' based on its location in the string.
557- var split = ipAddress.split("::");
558- var groups = 0;
559- for(i = 0; i < split.length; i++) {
560- groups += split[i].split(":").length;
561- }
562- expandedAddress = split[0] + ":";
563- for(i = 0; i < 8 - groups; i++) {
564- expandedAddress += "0000:";
565- }
566- expandedAddress += split[1];
567- }
568- return expandedAddress;
569- }
570-
571- // Convert string ipv6 into octets array.
572- function ipv6ToOctets(ipAddress) {
573- var octets = [];
574- var parts = ipv6Expand(ipAddress).split(":");
575- angular.forEach(parts, function(part) {
576- octets.push(parseInt(part, 16));
577- });
578- return octets;
579- }
580-
581 // Return true if the domainname is valid, false otherwise.
582 this.validateDomainName = function(domainname) {
583 // Invalid if the domain is not a string, empty, or more than
584@@ -140,8 +100,8 @@
585 ipAddress.indexOf(':') === -1) {
586 return false;
587 }
588- var expandedAddress = ipv6Expand(ipAddress);
589- var octets = ipv6ToOctets(expandedAddress);
590+ var expandedAddress = ConverterService.ipv6Expand(ipAddress);
591+ var octets = ConverterService.ipv6ToGroups(expandedAddress);
592 if(octets.length !== 8) {
593 return false;
594 }
595@@ -180,14 +140,14 @@
596 if(this.validateIPv4(ipAddress) &&
597 this.validateIPv4(networkAddress)) {
598 return cidrMatcher(
599- ipv4ToOctets(ipAddress),
600- ipv4ToOctets(networkAddress),
601+ ConverterService.ipv4ToOctets(ipAddress),
602+ ConverterService.ipv4ToOctets(networkAddress),
603 8, cidrBits);
604 } else if(this.validateIPv6(ipAddress) &&
605 this.validateIPv6(networkAddress)) {
606 return cidrMatcher(
607- ipv6ToOctets(ipAddress),
608- ipv6ToOctets(networkAddress),
609+ ConverterService.ipv6ToGroups(ipAddress),
610+ ConverterService.ipv6ToGroups(networkAddress),
611 16, cidrBits);
612 }
613 return false;
614@@ -210,9 +170,9 @@
615
616 // Check that each octet is of the ip address is more or equal
617 // to the low address and less or equal to the high address.
618- ipOctets = ipv4ToOctets(ipAddress);
619- lowOctets = ipv4ToOctets(lowAddress);
620- highOctets = ipv4ToOctets(highAddress);
621+ ipOctets = ConverterService.ipv4ToOctets(ipAddress);
622+ lowOctets = ConverterService.ipv4ToOctets(lowAddress);
623+ highOctets = ConverterService.ipv4ToOctets(highAddress);
624 for(i = 0; i < 4; i++) {
625 if(ipOctets[i] > highOctets[i] ||
626 ipOctets[i] < lowOctets[i]) {
627@@ -226,9 +186,9 @@
628
629 // Check that each octet is of the ip address is more or equal
630 // to the low address and less or equal to the high address.
631- ipOctets = ipv6ToOctets(ipAddress);
632- lowOctets = ipv6ToOctets(lowAddress);
633- highOctets = ipv6ToOctets(highAddress);
634+ ipOctets = ConverterService.ipv6ToGroups(ipAddress);
635+ lowOctets = ConverterService.ipv6ToGroups(lowAddress);
636+ highOctets = ConverterService.ipv6ToGroups(highAddress);
637 for(i = 0; i < 8; i++) {
638 if(ipOctets[i] > highOctets[i] ||
639 ipOctets[i] < lowOctets[i]) {
640@@ -239,4 +199,4 @@
641 }
642 return false;
643 };
644- });
645+ }]);
646
647=== modified file 'src/maasserver/static/partials/subnet-details.html'
648--- src/maasserver/static/partials/subnet-details.html 2016-06-09 20:08:35 +0000
649+++ src/maasserver/static/partials/subnet-details.html 2016-06-16 14:50:25 +0000
650@@ -250,36 +250,33 @@
651 <table class="table-listing">
652 <thead>
653 <tr class="table-listing__row">
654- <th class="table-listing__header table-col--20">IP Address</th>
655- <th class="table-listing__header table-col--10">Type</th>
656- <th class="table-listing__header table-col--15">Node</th>
657- <th class="table-listing__header table-col--10">Interface</th>
658- <th class="table-listing__header table-col--10">Usage</th>
659- <th class="table-listing__header table-col--10">Owner</th>
660- <th class="table-listing__header table-col--25">Last seen</th>
661- <!-- XXX mpontillo need data for this -->
662- <!--
663- <th class="table-listing__header table-col--25" colspan="2">
664- <a class="table-listing__header-link" href="#">Purpose</a>
665- <span class="divide"></span>
666- <a class="table-listing__header-link active" href="#">Last seen</a>
667- <span class="divide"></span>
668- <a class="table-listing__header-link" href="#">Detected</a>
669- </th>
670- -->
671+ <th class="table-listing__header table-col--20">
672+ <a href="" data-ng-click="sortIPTable(ipSort)" data-ng-class="{sort: predicate === ipSort, 'sort-asc': reverse === false, 'sort-desc': reverse === true}">IP Address</a>
673+ </th>
674+ <th class="table-listing__header table-col--10">
675+ <a href="" data-ng-click="sortIPTable(allocTypeSort)" data-ng-class="{sort: predicate === allocTypeSort, 'sort-asc': reverse === false, 'sort-desc': reverse === true}">Type</a>
676+ </th>
677+ <th class="table-listing__header table-col--15">
678+ <a href="" data-ng-click="sortIPTable('node_summary.hostname')" data-ng-class="{sort: predicate === 'node_summary.hostname', 'sort-asc': reverse === false, 'sort-desc': reverse === true}">Node</a>
679+ </th>
680+ <th class="table-listing__header table-col--10">
681+ <a href="" data-ng-click="sortIPTable('node_summary.via')" data-ng-class="{sort: predicate === 'node_summary.via', 'sort-asc': reverse === false, 'sort-desc': reverse === true}">Interface</a>
682+ </th>
683+ <th class="table-listing__header table-col--10">
684+ <a href="" data-ng-click="sortIPTable(nodeTypeSort)" data-ng-class="{sort: predicate === nodeTypeSort, 'sort-asc': reverse === false, 'sort-desc': reverse === true}">Usage</a>
685+ </th>
686+ <th class="table-listing__header table-col--10">
687+ <a href="" data-ng-click="sortIPTable(ownerSort)" data-ng-class="{sort: predicate === ownerSort, 'sort-asc': reverse === false, 'sort-desc': reverse === true}">Owner</a>
688+ </th>
689+ <th class="table-listing__header table-col--25">
690+ <a href="" data-ng-click="sortIPTable('updated')" data-ng-class="{sort: predicate === 'updated', 'sort-asc': reverse === false, 'sort-desc': reverse === true}">Last seen</a>
691+ </th>
692 </tr>
693 </thead>
694 <tbody>
695- <tr data-ng-repeat="ip in subnet.ip_addresses">
696+ <tr data-ng-repeat="ip in subnet.ip_addresses | orderBy:predicate:reverse track by ip.ip">
697 <td class="table-col--20">{$ ip.ip $}</td>
698- <td class="table-col--10" data-ng-switch="ip.alloc_type">
699- <span data-ng-switch-when="0">Automatic</span>
700- <span data-ng-switch-when="1">Static</span>
701- <span data-ng-switch-when="4">User reserved</span>
702- <span data-ng-switch-when="5">DHCP</span>
703- <span data-ng-switch-when="6">Observed</span>
704- <span data-ng-switch-default>Unknown</span>
705- </td>
706+ <td class="table-col--10">{$ getAllocType(ip.alloc_type) $}</td>
707 <td class="table-col--15" data-ng-switch="ip.node_summary.node_type">
708 <span data-ng-switch-when="0"><a href="#/node/{$ ip.node_summary.system_id $}">{$ ip.node_summary.hostname $}</a></span>
709 <span data-ng-switch-when="1">{$ ip.node_summary.hostname $}</span>
710@@ -295,14 +292,7 @@
711 <span data-ng-switch-when="4">{$ ip.node_summary.via $}</span>
712 <span data-ng-switch-default>Unknown</span>
713 </td>
714- <td class="table-col--10" data-ng-switch="ip.node_summary.node_type">
715- <span data-ng-switch-when="0">Machine</span>
716- <span data-ng-switch-when="1">Device</span>
717- <span data-ng-switch-when="2">Rack controller</span>
718- <span data-ng-switch-when="3">Region controller</span>
719- <span data-ng-switch-when="4">Rack and region controller</span>
720- <span data-ng-switch-default>Unknown</span>
721- </td>
722+ <td class="table-col--10">{$ getNodeType(ip.node_summary.node_type) $}</td>
723 <td class="table-col--10">{$ ip.user ? ip.user : "MAAS" $}</td>
724 <td class="table-col--25">
725 <time>{$ ip.updated $}</time>