Merge lp:~trapnine/maas/fix-1501982 into lp:~maas-committers/maas/trunk
- fix-1501982
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Jeffrey C Jones | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 4416 | ||||
Proposed branch: | lp:~trapnine/maas/fix-1501982 | ||||
Merge into: | lp:~maas-committers/maas/trunk | ||||
Diff against target: |
252 lines (+144/-11) 5 files modified
src/maasserver/static/js/angular/factories/nodes.js (+3/-1) src/maasserver/static/js/angular/factories/tests/test_nodes.js (+2/-1) src/maasserver/static/partials/nodes-list.html (+26/-6) src/maasserver/websockets/handlers/node.py (+31/-1) src/maasserver/websockets/handlers/tests/test_node.py (+82/-2) |
||||
To merge this branch: | bzr merge lp:~trapnine/maas/fix-1501982 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Blake Rouse (community) | Approve | ||
Andres Rodriguez (community) | Approve | ||
Ricardo Bánffy (community) | Approve | ||
Review via email: mp+275617@code.launchpad.net |
Commit message
Replaced node list page 'Networks' filter with 'Subnets' and added new 'Fabrics' and 'Spaces' filters.
Description of the change
Blake Rouse (blake-rouse) wrote : | # |
Looks good. But missing 3 unit tests. You have unit tests on the overall generated result, but you should unit test each of those new methods explicitly.
Jeffrey C Jones (trapnine) wrote : | # |
> Looks good. But missing 3 unit tests. You have unit tests on the overall
> generated result, but you should unit test each of those new methods
> explicitly.
added, thanks for review.
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~trapnine/maas/fix-1501982 into lp:maas failed. Below is the output from the failed tests.
Get:1 http://
Ign http://
Get:2 http://
Hit http://
Hit http://
Get:3 http://
Get:4 http://
Get:5 http://
Get:6 http://
Get:7 http://
Get:8 http://
Get:9 http://
Hit http://
Hit http://
Get:10 http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Ign http://
Ign http://
Fetched 2,071 kB in 3s (527 kB/s)
Reading package lists...
sudo DEBIAN_
--
Preview Diff
1 | === modified file 'src/maasserver/static/js/angular/factories/nodes.js' |
2 | --- src/maasserver/static/js/angular/factories/nodes.js 2015-10-23 04:03:23 +0000 |
3 | +++ src/maasserver/static/js/angular/factories/nodes.js 2015-10-27 02:33:24 +0000 |
4 | @@ -26,7 +26,9 @@ |
5 | "zone": function(node) { |
6 | return node.zone.name; |
7 | }, |
8 | - "networks": null, |
9 | + "subnets": null, |
10 | + "fabrics": null, |
11 | + "spaces": null, |
12 | "storage_tags": null |
13 | }; |
14 | |
15 | |
16 | === modified file 'src/maasserver/static/js/angular/factories/tests/test_nodes.js' |
17 | --- src/maasserver/static/js/angular/factories/tests/test_nodes.js 2015-10-23 16:34:24 +0000 |
18 | +++ src/maasserver/static/js/angular/factories/tests/test_nodes.js 2015-10-27 02:33:24 +0000 |
19 | @@ -47,7 +47,8 @@ |
20 | expect(NodesManager._pk).toBe("system_id"); |
21 | expect(NodesManager._handler).toBe("node"); |
22 | expect(Object.keys(NodesManager._metadataAttributes)).toEqual( |
23 | - ["status", "owner", "tags", "zone", "networks", "storage_tags"]); |
24 | + ["status", "owner", "tags", "zone", "subnets", "fabrics", |
25 | + "spaces", "storage_tags"]); |
26 | }); |
27 | |
28 | describe("create", function() { |
29 | |
30 | === modified file 'src/maasserver/static/partials/nodes-list.html' |
31 | --- src/maasserver/static/partials/nodes-list.html 2015-10-13 09:38:21 +0000 |
32 | +++ src/maasserver/static/partials/nodes-list.html 2015-10-27 02:33:24 +0000 |
33 | @@ -447,12 +447,32 @@ |
34 | </ul> |
35 | </div> |
36 | </div> |
37 | - <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.networks.length"> |
38 | - <h4 class="accordion__tab-title maas-accordion-tab">Networks</h4> |
39 | - <div class="accordion__tab-content"> |
40 | - <ul class="accordion__tab-list"> |
41 | - <li class="accordion__tab-item" data-ng-repeat="network in tabs.nodes.metadata.networks | orderBy:['name', '-count']" data-ng-class="{ active: isFilterActive('networks', network.name, 'nodes') }"> |
42 | - <a class="accordion__tab-link" href="" data-ng-click="toggleFilter('networks', network.name, 'nodes')">{$ network.name $} ({$ network.count $})</a> |
43 | + <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.subnets.length"> |
44 | + <h4 class="accordion__tab-title maas-accordion-tab">Subnets</h4> |
45 | + <div class="accordion__tab-content"> |
46 | + <ul class="accordion__tab-list"> |
47 | + <li class="accordion__tab-item" data-ng-repeat="subnet in tabs.nodes.metadata.subnets | orderBy:['name', '-count']" data-ng-class="{ active: isFilterActive('subnets', subnet.name, 'nodes') }"> |
48 | + <a class="accordion__tab-link" href="" data-ng-click="toggleFilter('subnets', subnet.name, 'nodes')">{$ subnet.name $} ({$ subnet.count $})</a> |
49 | + </li> |
50 | + </ul> |
51 | + </div> |
52 | + </div> |
53 | + <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.fabrics.length"> |
54 | + <h4 class="accordion__tab-title maas-accordion-tab">Fabrics</h4> |
55 | + <div class="accordion__tab-content"> |
56 | + <ul class="accordion__tab-list"> |
57 | + <li class="accordion__tab-item" data-ng-repeat="fabric in tabs.nodes.metadata.fabrics | orderBy:['name', '-count']" data-ng-class="{ active: isFilterActive('fabrics', fabric.name, 'nodes') }"> |
58 | + <a class="accordion__tab-link" href="" data-ng-click="toggleFilter('fabrics', fabric.name, 'nodes')">{$ fabric.name $} ({$ fabric.count $})</a> |
59 | + </li> |
60 | + </ul> |
61 | + </div> |
62 | + </div> |
63 | + <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.spaces.length"> |
64 | + <h4 class="accordion__tab-title maas-accordion-tab">Spaces</h4> |
65 | + <div class="accordion__tab-content"> |
66 | + <ul class="accordion__tab-list"> |
67 | + <li class="accordion__tab-item" data-ng-repeat="space in tabs.nodes.metadata.spaces | orderBy:['name', '-count']" data-ng-class="{ active: isFilterActive('spaces', space.name, 'nodes') }"> |
68 | + <a class="accordion__tab-link" href="" data-ng-click="toggleFilter('spaces', space.name, 'nodes')">{$ space.name $} ({$ space.count $})</a> |
69 | </li> |
70 | </ul> |
71 | </div> |
72 | |
73 | === modified file 'src/maasserver/websockets/handlers/node.py' |
74 | --- src/maasserver/websockets/handlers/node.py 2015-10-23 04:03:23 +0000 |
75 | +++ src/maasserver/websockets/handlers/node.py 2015-10-27 02:33:24 +0000 |
76 | @@ -115,8 +115,11 @@ |
77 | queryset = ( |
78 | Node.nodes.filter(installable=True) |
79 | .select_related('nodegroup', 'pxe_mac', 'owner') |
80 | - .prefetch_related('interface_set__ip_addresses__subnet__vlan') |
81 | + .prefetch_related( |
82 | + 'interface_set__ip_addresses__subnet__vlan__fabric') |
83 | + .prefetch_related('interface_set__ip_addresses__subnet__space') |
84 | .prefetch_related('nodegroup__nodegroupinterface_set__subnet') |
85 | + .prefetch_related('interface_set__vlan__fabric') |
86 | .prefetch_related('zone') |
87 | .prefetch_related('tags') |
88 | .prefetch_related('blockdevice_set__physicalblockdevice') |
89 | @@ -260,6 +263,11 @@ |
90 | ]) / (1000 ** 3)) |
91 | data["storage_tags"] = self.get_all_storage_tags(blockdevices) |
92 | |
93 | + subnets = self.get_all_subnets(obj) |
94 | + data["subnets"] = [subnet.name for subnet in subnets] |
95 | + data["fabrics"] = self.get_all_fabric_names(obj, subnets) |
96 | + data["spaces"] = self.get_all_space_names(subnets) |
97 | + |
98 | data["tags"] = [ |
99 | tag.name |
100 | for tag in obj.tags.all() |
101 | @@ -577,6 +585,28 @@ |
102 | tags = tags.union(blockdevice.tags) |
103 | return list(tags) |
104 | |
105 | + def get_all_subnets(self, obj): |
106 | + subnets = set() |
107 | + for interface in obj.interface_set.all(): |
108 | + for ip_address in interface.ip_addresses.all(): |
109 | + if ip_address.subnet is not None: |
110 | + subnets.add(ip_address.subnet) |
111 | + return list(subnets) |
112 | + |
113 | + def get_all_fabric_names(self, obj, subnets): |
114 | + fabric_names = set() |
115 | + for interface in obj.interface_set.all(): |
116 | + fabric_names.add(interface.vlan.fabric.name) |
117 | + for subnet in subnets: |
118 | + fabric_names.add(subnet.vlan.fabric.name) |
119 | + return list(fabric_names) |
120 | + |
121 | + def get_all_space_names(self, subnets): |
122 | + space_names = set() |
123 | + for subnet in subnets: |
124 | + space_names.add(subnet.space.name) |
125 | + return list(space_names) |
126 | + |
127 | def get_blockdevices_for(self, obj): |
128 | """Return only `BlockDevice`s using the prefetched query.""" |
129 | return [ |
130 | |
131 | === modified file 'src/maasserver/websockets/handlers/tests/test_node.py' |
132 | --- src/maasserver/websockets/handlers/tests/test_node.py 2015-10-23 18:39:01 +0000 |
133 | +++ src/maasserver/websockets/handlers/tests/test_node.py 2015-10-27 02:33:24 +0000 |
134 | @@ -134,6 +134,7 @@ |
135 | for cache_set in CacheSet.objects.get_cache_sets_for_node(node) |
136 | ] |
137 | disks = sorted(disks, key=itemgetter("name")) |
138 | + subnets = handler.get_all_subnets(node) |
139 | data = { |
140 | "actions": compile_node_actions(node, handler.user).keys(), |
141 | "architecture": node.architecture, |
142 | @@ -201,6 +202,9 @@ |
143 | for blockdevice in node.physicalblockdevice_set.all() |
144 | ]) / (1000 ** 3)), |
145 | "storage_tags": handler.get_all_storage_tags(blockdevices), |
146 | + "subnets": [subnet.name for subnet in subnets], |
147 | + "fabrics": handler.get_all_fabric_names(node, subnets), |
148 | + "spaces": handler.get_all_space_names(subnets), |
149 | "swap_size": node.swap_size, |
150 | "system_id": node.system_id, |
151 | "tags": [ |
152 | @@ -223,7 +227,9 @@ |
153 | "pxe_mac_vendor", |
154 | "extra_macs", |
155 | "tags", |
156 | - "networks", |
157 | + "subnets", |
158 | + "fabrics", |
159 | + "spaces", |
160 | "physical_disk_count", |
161 | "storage", |
162 | "storage_tags", |
163 | @@ -660,6 +666,80 @@ |
164 | factory.make_Event(node=node, type=event_type) |
165 | self.assertEquals([], handler.dehydrate_events(node)) |
166 | |
167 | + def make_node_with_subnets(self): |
168 | + user = factory.make_User() |
169 | + handler = NodeHandler(user, {}) |
170 | + space1 = factory.make_Space() |
171 | + fabric1 = factory.make_Fabric(name=factory.make_name("fabric")) |
172 | + vlan1 = factory.make_VLAN(fabric=fabric1) |
173 | + subnet1 = factory.make_Subnet(space=space1, vlan=vlan1) |
174 | + node = factory.make_Node_with_Interface_on_Subnet( |
175 | + subnet=subnet1, vlan=vlan1) |
176 | + node.save() |
177 | + |
178 | + # Bond interface with a VLAN on top. With the bond set to STATIC |
179 | + # and the VLAN set to AUTO. |
180 | + fabric2 = factory.make_Fabric(name=factory.make_name("fabric")) |
181 | + vlan2 = factory.make_VLAN(fabric=fabric2) |
182 | + space2 = factory.make_Space() |
183 | + bond_subnet = factory.make_Subnet(space=space1, vlan=vlan1) |
184 | + vlan_subnet = factory.make_Subnet(space=space2, vlan=vlan2) |
185 | + nic1 = factory.make_Interface( |
186 | + INTERFACE_TYPE.PHYSICAL, node=node, vlan=vlan1) |
187 | + nic2 = factory.make_Interface( |
188 | + INTERFACE_TYPE.PHYSICAL, node=node, vlan=vlan2) |
189 | + bond = factory.make_Interface( |
190 | + INTERFACE_TYPE.BOND, parents=[nic1, nic2], vlan=vlan1) |
191 | + vlan_int = factory.make_Interface( |
192 | + INTERFACE_TYPE.VLAN, vlan=vlan2, parents=[bond]) |
193 | + factory.make_StaticIPAddress( |
194 | + alloc_type=IPADDRESS_TYPE.STICKY, |
195 | + ip=factory.pick_ip_in_network(bond_subnet.get_ipnetwork()), |
196 | + subnet=bond_subnet, interface=bond) |
197 | + factory.make_StaticIPAddress( |
198 | + alloc_type=IPADDRESS_TYPE.STICKY, ip="", |
199 | + subnet=vlan_subnet, interface=vlan_int) |
200 | + |
201 | + # LINK_UP interface with no subnet. |
202 | + fabric3 = factory.make_Fabric(name=factory.make_name("fabric")) |
203 | + vlan3 = factory.make_VLAN(fabric=fabric3) |
204 | + nic3 = factory.make_Interface( |
205 | + INTERFACE_TYPE.PHYSICAL, vlan=vlan3, node=node) |
206 | + nic3_ip = factory.make_StaticIPAddress( |
207 | + alloc_type=IPADDRESS_TYPE.STICKY, ip="", |
208 | + subnet=None, interface=nic3) |
209 | + nic3_ip.subnet = None |
210 | + nic3_ip.save() |
211 | + |
212 | + self.patch_autospec(interface_module, "update_host_maps") |
213 | + boot_interface = node.get_boot_interface() |
214 | + boot_interface.claim_static_ips() |
215 | + node.boot_interface = boot_interface |
216 | + node.save() |
217 | + |
218 | + subnets = [subnet1, bond_subnet, vlan_subnet] |
219 | + fabrics = [fabric1, fabric2, fabric3] |
220 | + spaces = [space1, space2] |
221 | + return (handler, node, subnets, fabrics, spaces) |
222 | + |
223 | + def test_get_all_subnets(self): |
224 | + (handler, node, subnets, _, _) = self.make_node_with_subnets() |
225 | + self.assertItemsEqual(subnets, handler.get_all_subnets(node)) |
226 | + |
227 | + def test_get_all_fabric_names(self): |
228 | + (handler, node, _, fabrics, _) = self.make_node_with_subnets() |
229 | + fabric_names = [fabric.name for fabric in fabrics] |
230 | + node_subnets = handler.get_all_subnets(node) |
231 | + self.assertItemsEqual( |
232 | + fabric_names, handler.get_all_fabric_names(node, node_subnets)) |
233 | + |
234 | + def test_get_all_space_names(self): |
235 | + (handler, node, _, _, spaces) = self.make_node_with_subnets() |
236 | + space_names = [space.name for space in spaces] |
237 | + node_subnets = handler.get_all_subnets(node) |
238 | + self.assertItemsEqual( |
239 | + space_names, handler.get_all_space_names(node_subnets)) |
240 | + |
241 | def test_get(self): |
242 | user = factory.make_User() |
243 | handler = NodeHandler(user, {}) |
244 | @@ -757,7 +837,7 @@ |
245 | # number means regiond has to do more work slowing down its process |
246 | # and slowing down the client waiting for the response. |
247 | self.assertEquals( |
248 | - query_10_count, 9, |
249 | + query_10_count, 11, |
250 | "Number of queries has changed; make sure this is expected.") |
251 | self.assertEquals( |
252 | query_10_count, query_20_count, |
LGTM. Just add docstrings for the new methods. Remember you'll eventually forget what you did here (this you can find out by re-reading the code) and, most important, why it is the way it is (something code doesn't say most of the time).