Merge lp:~blake-rouse/maas/read-only-view-node-networking 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: 4447
Proposed branch: lp:~blake-rouse/maas/read-only-view-node-networking
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 451 lines (+227/-13)
12 files modified
src/maasserver/api/interfaces.py (+5/-0)
src/maasserver/api/nodes.py (+1/-0)
src/maasserver/api/tests/test_interfaces.py (+11/-2)
src/maasserver/forms_interface.py (+6/-1)
src/maasserver/models/interface.py (+25/-1)
src/maasserver/models/tests/test_interface.py (+33/-1)
src/maasserver/static/js/angular/controllers/node_details_networking.js (+5/-0)
src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js (+14/-0)
src/maasserver/static/partials/node-details.html (+15/-5)
src/maasserver/tests/test_forms_interface.py (+55/-0)
src/maasserver/websockets/handlers/node.py (+16/-1)
src/maasserver/websockets/handlers/tests/test_node.py (+41/-2)
To merge this branch: bzr merge lp:~blake-rouse/maas/read-only-view-node-networking
Reviewer Review Type Date Requested Status
Andres Rodriguez (community) Approve
Review via email: mp+276315@code.launchpad.net

Commit message

Add the discovered field to the API and websocket for the interface. Use that field in the WebUI to show the current discovered IP address when the node is commissioning. Also update the WebUI to be all in one line once a node is out of edit mode.

To post a comment you must log in.
Revision history for this message
Blake Rouse (blake-rouse) wrote :

Here is an screenshot of what it looks like. This screenshot is a little bit older, as the subnet is also correct now in the branch.

http://imgur.com/f3ck2HN

Revision history for this message
Andres Rodriguez (andreserl) wrote :

lgtm!

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :
Download full text (1010.2 KiB)

The attempt to merge lp:~blake-rouse/maas/read-only-view-node-networking into lp:maas failed. Below is the output from the failed tests.

Hit http://security.ubuntu.com trusty-security InRelease
Ign http://nova.clouds.archive.ubuntu.com trusty InRelease
Get:1 http://nova.clouds.archive.ubuntu.com trusty-updates InRelease [64.4 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty Release.gpg
Hit http://nova.clouds.archive.ubuntu.com trusty Release
Hit http://security.ubuntu.com trusty-security/main Sources
Hit http://security.ubuntu.com trusty-security/universe Sources
Hit http://security.ubuntu.com trusty-security/main amd64 Packages
Hit http://security.ubuntu.com trusty-security/universe amd64 Packages
Hit http://security.ubuntu.com trusty-security/main Translation-en
Get:2 http://nova.clouds.archive.ubuntu.com trusty-updates/main Sources [242 kB]
Hit http://security.ubuntu.com trusty-security/universe Translation-en
Get:3 http://nova.clouds.archive.ubuntu.com trusty-updates/universe Sources [143 kB]
Get:4 http://nova.clouds.archive.ubuntu.com trusty-updates/main amd64 Packages [639 kB]
Get:5 http://nova.clouds.archive.ubuntu.com trusty-updates/universe amd64 Packages [326 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/universe Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty/main Sources
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Sources
Hit http://nova.clouds.archive.ubuntu.com trusty/main amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/universe amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en
Ign http://nova.clouds.archive.ubuntu.com trusty/main Translation-en_US
Ign http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en_US
Fetched 1,415 kB in 3s (376 kB/s)
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
     --no-install-recommends install apache2 authbind bind9 bind9utils build-essential bzr-builddeb chromium-browser chromium-chromedriver curl daemontools debhelper dh-apport dh-systemd distro-info dnsutils firefox freeipmi-tools git gjs ipython isc-dhcp-common libjs-angularjs libjs-jquery libjs-jquery-hotkeys libjs-yui3-full libjs-yui3-min libpq-dev make nodejs-legacy npm pep8 phantomjs postgresql pyflakes python-apt python-bson python-bzrlib python-convoy python-coverage python-crochet python-cssselect python-curtin python-dev python-distro-info python-django python-django-piston python-django-south python-djorm-ext-pgarray python-docutils python-extras python-fixtures python-flake8 python-formencode python-hivex python-httplib2 python-jinja2 python-jsonschema python-lxml python-mock python-netaddr python-netifaces python-nose python-oauth python-openssl python-paramiko python-pexpect python-pip python-pocket-lint python-psycopg2 python-pyinotify python-pyparsing python-seamicroclient python-simplejson python-simplestreams python-sphinx python-subunit python-tempita python-testresources python-testscenarios python-testtools python-twisted python-txtftp pyt...

Revision history for this message
Andres Rodriguez (andreserl) wrote :

metadataserver.tests.test_api.TestCommissioningAPI.test_signal_power_type_lower_case_works ... ok
metadataserver.tests.test_api.TestCommissioningAPI.test_signal_power_type_stores_params ... FAIL
metadataserver.tests.test_api.TestCommissioningAPI.test_signal_refuses_bad_power_type ... ok

Revision history for this message
MAAS Lander (maas-lander) wrote :
Download full text (1010.0 KiB)

The attempt to merge lp:~blake-rouse/maas/read-only-view-node-networking into lp:maas failed. Below is the output from the failed tests.

Hit http://security.ubuntu.com trusty-security InRelease
Ign http://nova.clouds.archive.ubuntu.com trusty InRelease
Get:1 http://nova.clouds.archive.ubuntu.com trusty-updates InRelease [64.4 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty Release.gpg
Hit http://nova.clouds.archive.ubuntu.com trusty Release
Hit http://security.ubuntu.com trusty-security/main Sources
Hit http://security.ubuntu.com trusty-security/universe Sources
Hit http://security.ubuntu.com trusty-security/main amd64 Packages
Hit http://security.ubuntu.com trusty-security/universe amd64 Packages
Get:2 http://nova.clouds.archive.ubuntu.com trusty-updates/main Sources [242 kB]
Hit http://security.ubuntu.com trusty-security/main Translation-en
Hit http://security.ubuntu.com trusty-security/universe Translation-en
Get:3 http://nova.clouds.archive.ubuntu.com trusty-updates/universe Sources [143 kB]
Get:4 http://nova.clouds.archive.ubuntu.com trusty-updates/main amd64 Packages [639 kB]
Get:5 http://nova.clouds.archive.ubuntu.com trusty-updates/universe amd64 Packages [326 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/universe Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty/main Sources
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Sources
Hit http://nova.clouds.archive.ubuntu.com trusty/main amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/universe amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en
Ign http://nova.clouds.archive.ubuntu.com trusty/main Translation-en_US
Ign http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en_US
Fetched 1,415 kB in 3s (373 kB/s)
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
     --no-install-recommends install apache2 authbind bind9 bind9utils build-essential bzr-builddeb chromium-browser chromium-chromedriver curl daemontools debhelper dh-apport dh-systemd distro-info dnsutils firefox freeipmi-tools git gjs ipython isc-dhcp-common libjs-angularjs libjs-jquery libjs-jquery-hotkeys libjs-yui3-full libjs-yui3-min libpq-dev make nodejs-legacy npm pep8 phantomjs postgresql pyflakes python-apt python-bson python-bzrlib python-convoy python-coverage python-crochet python-cssselect python-curtin python-dev python-distro-info python-django python-django-piston python-django-south python-djorm-ext-pgarray python-docutils python-extras python-fixtures python-flake8 python-formencode python-hivex python-httplib2 python-jinja2 python-jsonschema python-lxml python-mock python-netaddr python-netifaces python-nose python-oauth python-openssl python-paramiko python-pexpect python-pip python-pocket-lint python-psycopg2 python-pyinotify python-pyparsing python-seamicroclient python-simplejson python-simplestreams python-sphinx python-subunit python-tempita python-testresources python-testscenarios python-testtools python-twisted python-txtftp pyt...

Revision history for this message
MAAS Lander (maas-lander) wrote :
Download full text (1010.0 KiB)

The attempt to merge lp:~blake-rouse/maas/read-only-view-node-networking into lp:maas failed. Below is the output from the failed tests.

Hit http://security.ubuntu.com trusty-security InRelease
Ign http://nova.clouds.archive.ubuntu.com trusty InRelease
Get:1 http://nova.clouds.archive.ubuntu.com trusty-updates InRelease [64.4 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty Release.gpg
Hit http://nova.clouds.archive.ubuntu.com trusty Release
Hit http://security.ubuntu.com trusty-security/main Sources
Hit http://security.ubuntu.com trusty-security/universe Sources
Hit http://security.ubuntu.com trusty-security/main amd64 Packages
Hit http://security.ubuntu.com trusty-security/universe amd64 Packages
Get:2 http://nova.clouds.archive.ubuntu.com trusty-updates/main Sources [242 kB]
Hit http://security.ubuntu.com trusty-security/main Translation-en
Hit http://security.ubuntu.com trusty-security/universe Translation-en
Get:3 http://nova.clouds.archive.ubuntu.com trusty-updates/universe Sources [143 kB]
Get:4 http://nova.clouds.archive.ubuntu.com trusty-updates/main amd64 Packages [639 kB]
Get:5 http://nova.clouds.archive.ubuntu.com trusty-updates/universe amd64 Packages [326 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/universe Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty/main Sources
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Sources
Hit http://nova.clouds.archive.ubuntu.com trusty/main amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/universe amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en
Ign http://nova.clouds.archive.ubuntu.com trusty/main Translation-en_US
Ign http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en_US
Fetched 1,415 kB in 4s (323 kB/s)
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
     --no-install-recommends install apache2 authbind bind9 bind9utils build-essential bzr-builddeb chromium-browser chromium-chromedriver curl daemontools debhelper dh-apport dh-systemd distro-info dnsutils firefox freeipmi-tools git gjs ipython isc-dhcp-common libjs-angularjs libjs-jquery libjs-jquery-hotkeys libjs-yui3-full libjs-yui3-min libpq-dev make nodejs-legacy npm pep8 phantomjs postgresql pyflakes python-apt python-bson python-bzrlib python-convoy python-coverage python-crochet python-cssselect python-curtin python-dev python-distro-info python-django python-django-piston python-django-south python-djorm-ext-pgarray python-docutils python-extras python-fixtures python-flake8 python-formencode python-hivex python-httplib2 python-jinja2 python-jsonschema python-lxml python-mock python-netaddr python-netifaces python-nose python-oauth python-openssl python-paramiko python-pexpect python-pip python-pocket-lint python-psycopg2 python-pyinotify python-pyparsing python-seamicroclient python-simplejson python-simplestreams python-sphinx python-subunit python-tempita python-testresources python-testscenarios python-testtools python-twisted python-txtftp pyt...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/api/interfaces.py'
--- src/maasserver/api/interfaces.py 2015-10-23 21:33:14 +0000
+++ src/maasserver/api/interfaces.py 2015-10-31 23:55:40 +0000
@@ -63,6 +63,7 @@
63 'enabled',63 'enabled',
64 'links',64 'links',
65 'params',65 'params',
66 'discovered',
66)67)
6768
6869
@@ -279,6 +280,10 @@
279 def links(cls, interface):280 def links(cls, interface):
280 return interface.get_links()281 return interface.get_links()
281282
283 @classmethod
284 def discovered(cls, interface):
285 return interface.get_discovered()
286
282 def read(self, request, system_id, interface_id):287 def read(self, request, system_id, interface_id):
283 """Read interface on node.288 """Read interface on node.
284289
285290
=== modified file 'src/maasserver/api/nodes.py'
--- src/maasserver/api/nodes.py 2015-10-26 18:15:08 +0000
+++ src/maasserver/api/nodes.py 2015-10-31 23:55:40 +0000
@@ -132,6 +132,7 @@
132 'enabled',132 'enabled',
133 'links',133 'links',
134 'params',134 'params',
135 'discovered',
135 )),136 )),
136 'routers',137 'routers',
137 'zone',138 'zone',
138139
=== modified file 'src/maasserver/api/tests/test_interfaces.py'
--- src/maasserver/api/tests/test_interfaces.py 2015-10-23 21:57:59 +0000
+++ src/maasserver/api/tests/test_interfaces.py 2015-10-31 23:55:40 +0000
@@ -443,13 +443,19 @@
443 dhcp_ip = factory.make_StaticIPAddress(443 dhcp_ip = factory.make_StaticIPAddress(
444 alloc_type=IPADDRESS_TYPE.DHCP, ip="",444 alloc_type=IPADDRESS_TYPE.DHCP, ip="",
445 subnet=dhcp_subnet, interface=bond)445 subnet=dhcp_subnet, interface=bond)
446 discovered_ip = factory.pick_ip_in_network(
447 dhcp_subnet.get_ipnetwork())
448 factory.make_StaticIPAddress(
449 alloc_type=IPADDRESS_TYPE.DISCOVERED, ip=discovered_ip,
450 subnet=dhcp_subnet, interface=bond)
446 links.append(451 links.append(
447 MatchesDict({452 MatchesDict({
448 "id": Equals(dhcp_ip.id),453 "id": Equals(dhcp_ip.id),
449 "mode": Equals(INTERFACE_LINK_TYPE.DHCP),454 "mode": Equals(INTERFACE_LINK_TYPE.DHCP),
450 "subnet": ContainsDict({455 "subnet": ContainsDict({
451 "id": Equals(dhcp_subnet.id)456 "id": Equals(dhcp_subnet.id)
452 })457 }),
458 "ip_address": Equals(discovered_ip),
453 }))459 }))
454460
455 # Second link is a STATIC ip link.461 # Second link is a STATIC ip link.
@@ -506,7 +512,7 @@
506 "tags": Equals(bond.tags),512 "tags": Equals(bond.tags),
507 "resource_uri": Equals(get_node_interface_uri(bond)),513 "resource_uri": Equals(get_node_interface_uri(bond)),
508 "params": Equals(bond.params),514 "params": Equals(bond.params),
509 }))515 }))
510 self.assertEquals(sorted(516 self.assertEquals(sorted(
511 nic.name517 nic.name
512 for nic in parents518 for nic in parents
@@ -516,6 +522,9 @@
516 for nic in children522 for nic in children
517 ), parsed_interface["children"])523 ), parsed_interface["children"])
518 self.assertThat(parsed_interface["links"], MatchesListwise(links))524 self.assertThat(parsed_interface["links"], MatchesListwise(links))
525 json_discovered = parsed_interface["discovered"][0]
526 self.assertEquals(dhcp_subnet.id, json_discovered["subnet"]["id"])
527 self.assertEquals(discovered_ip, json_discovered["ip_address"])
519528
520 def test_read_404_when_invalid_id(self):529 def test_read_404_when_invalid_id(self):
521 node = factory.make_Node()530 node = factory.make_Node()
522531
=== modified file 'src/maasserver/forms_interface.py'
--- src/maasserver/forms_interface.py 2015-10-04 20:47:36 +0000
+++ src/maasserver/forms_interface.py 2015-10-31 23:55:40 +0000
@@ -346,7 +346,12 @@
346 ]346 ]
347 for bond_field in bond_fields:347 for bond_field in bond_fields:
348 value = self.cleaned_data.get(bond_field)348 value = self.cleaned_data.get(bond_field)
349 if value:349 if (value is not None and
350 isinstance(value, (bytes, unicode)) and
351 len(value) > 0 and not value.isspace()):
352 interface.params[bond_field] = value
353 elif (value is not None and
354 not isinstance(value, (bytes, unicode))):
350 interface.params[bond_field] = value355 interface.params[bond_field] = value
351 elif created:356 elif created:
352 interface.params[bond_field] = self.fields[bond_field].initial357 interface.params[bond_field] = self.fields[bond_field].initial
353358
=== modified file 'src/maasserver/models/interface.py'
--- src/maasserver/models/interface.py 2015-10-27 17:45:01 +0000
+++ src/maasserver/models/interface.py 2015-10-31 23:55:40 +0000
@@ -253,7 +253,7 @@
253 {253 {
254 "id": 1,254 "id": 1,
255 "mode": "dhcp",255 "mode": "dhcp",
256 "ip": "192.168.1.2",256 "ip_address": "192.168.1.2",
257 "subnet": <Subnet object>257 "subnet": <Subnet object>
258 }258 }
259259
@@ -276,6 +276,30 @@
276 links.append(link)276 links.append(link)
277 return links277 return links
278278
279 def get_discovered(self):
280 """Return the definition of discovered IP addresses belonging to this
281 interface.
282
283 Example definition:
284 {
285 "ip_address": "192.168.1.2",
286 "subnet": <Subnet object>
287 }
288 """
289 discovered_ips = self.ip_addresses.filter(
290 alloc_type=IPADDRESS_TYPE.DISCOVERED)
291 if len(discovered_ips) > 0:
292 discovered = []
293 for discovered_ip in discovered_ips:
294 if discovered_ip.ip is not None and discovered_ip.ip != "":
295 discovered.append({
296 "subnet": discovered_ip.subnet,
297 "ip_address": "%s" % discovered_ip.ip,
298 })
299 return discovered
300 else:
301 return None
302
279 def only_has_link_up(self):303 def only_has_link_up(self):
280 """Return True if this interface is only set to LINK_UP."""304 """Return True if this interface is only set to LINK_UP."""
281 ip_addresses = self.ip_addresses.exclude(305 ip_addresses = self.ip_addresses.exclude(
282306
=== modified file 'src/maasserver/models/tests/test_interface.py'
--- src/maasserver/models/tests/test_interface.py 2015-10-30 16:14:33 +0000
+++ src/maasserver/models/tests/test_interface.py 2015-10-31 23:55:40 +0000
@@ -222,6 +222,38 @@
222 }))222 }))
223 self.assertThat(interface.get_links(), MatchesListwise(links))223 self.assertThat(interface.get_links(), MatchesListwise(links))
224224
225 def test_get_discovered_returns_None_when_empty(self):
226 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
227 self.assertIsNone(interface.get_discovered())
228
229 def test_get_discovered_returns_discovered_address_for_ipv4_and_ipv6(self):
230 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
231 discovered_ips = []
232 network_v4 = factory.make_ipv4_network()
233 subnet_v4 = factory.make_Subnet(cidr=unicode(network_v4.cidr))
234 ip_v4 = factory.pick_ip_in_network(network_v4)
235 factory.make_StaticIPAddress(
236 alloc_type=IPADDRESS_TYPE.DISCOVERED, ip=ip_v4,
237 subnet=subnet_v4, interface=interface)
238 discovered_ips.append(
239 MatchesDict({
240 "ip_address": Equals(ip_v4),
241 "subnet": Equals(subnet_v4),
242 }))
243 network_v6 = factory.make_ipv6_network()
244 subnet_v6 = factory.make_Subnet(cidr=unicode(network_v6.cidr))
245 ip_v6 = factory.pick_ip_in_network(network_v6)
246 factory.make_StaticIPAddress(
247 alloc_type=IPADDRESS_TYPE.DISCOVERED, ip=ip_v6,
248 subnet=subnet_v6, interface=interface)
249 discovered_ips.append(
250 MatchesDict({
251 "ip_address": Equals(ip_v6),
252 "subnet": Equals(subnet_v6),
253 }))
254 self.assertThat(
255 interface.get_discovered(), MatchesListwise(discovered_ips))
256
225 def test_delete_deletes_related_ip_addresses(self):257 def test_delete_deletes_related_ip_addresses(self):
226 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)258 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
227 discovered_ip = factory.make_StaticIPAddress(259 discovered_ip = factory.make_StaticIPAddress(
@@ -806,7 +838,7 @@
806 "Unknown interfaces should have been deleted.")838 "Unknown interfaces should have been deleted.")
807 self.assertEqual(num_connections, interface.ip_addresses.count())839 self.assertEqual(num_connections, interface.ip_addresses.count())
808 for i in range(num_connections):840 for i in range(num_connections):
809 ip = interface.ip_addresses.all()[i]841 ip = interface.ip_addresses.order_by('id')[i]
810 self.assertThat(ip, MatchesStructure.byEquality(842 self.assertThat(ip, MatchesStructure.byEquality(
811 alloc_type=IPADDRESS_TYPE.DISCOVERED, subnet=subnet_list[i],843 alloc_type=IPADDRESS_TYPE.DISCOVERED, subnet=subnet_list[i],
812 ip=unicode(IPNetwork(cidr_list[i]).ip)))844 ip=unicode(IPNetwork(cidr_list[i]).ip)))
813845
=== modified file 'src/maasserver/static/js/angular/controllers/node_details_networking.js'
--- src/maasserver/static/js/angular/controllers/node_details_networking.js 2015-10-30 16:35:25 +0000
+++ src/maasserver/static/js/angular/controllers/node_details_networking.js 2015-10-31 23:55:40 +0000
@@ -557,6 +557,11 @@
557 }557 }
558 };558 };
559559
560 // Get the subnet from its ID.
561 $scope.getSubnet = function(subnetId) {
562 return SubnetsManager.getItemFromList(subnetId);
563 };
564
560 // Toggle showing or hiding the members of the interface.565 // Toggle showing or hiding the members of the interface.
561 $scope.toggleMembers = function(nic) {566 $scope.toggleMembers = function(nic) {
562 var idx = $scope.showingMembers.indexOf(nic.id);567 var idx = $scope.showingMembers.indexOf(nic.id);
563568
=== modified file 'src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js'
--- src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js 2015-10-30 16:35:25 +0000
+++ src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js 2015-10-31 23:55:40 +0000
@@ -1035,6 +1035,20 @@
1035 });1035 });
1036 });1036 });
10371037
1038 describe("getSubnet", function() {
1039
1040 it("calls SubnetsManager.getItemFromList", function() {
1041 var controller = makeController();
1042 var subnetId = makeInteger(0, 100);
1043 var subnet = {};
1044 spyOn(SubnetsManager, "getItemFromList").and.returnValue(subnet);
1045
1046 expect($scope.getSubnet(subnetId)).toBe(subnet);
1047 expect(SubnetsManager.getItemFromList).toHaveBeenCalledWith(
1048 subnetId);
1049 });
1050 });
1051
1038 describe("toggleMembers", function() {1052 describe("toggleMembers", function() {
10391053
1040 it("adds interface id to showingMembers", function() {1054 it("adds interface id to showingMembers", function() {
10411055
=== modified file 'src/maasserver/static/partials/node-details.html'
--- src/maasserver/static/partials/node-details.html 2015-10-30 16:43:21 +0000
+++ src/maasserver/static/partials/node-details.html 2015-10-31 23:55:40 +0000
@@ -415,14 +415,18 @@
415 </div>415 </div>
416 <div class="table__data table__column--18">416 <div class="table__data table__column--18">
417 <select class="table__input" name="subnet" id="subnet"417 <select class="table__input" name="subnet" id="subnet"
418 data-ng-hide="isAllNetworkingDisabled() && interface.discovered[0].subnet_id"
418 data-ng-model="interface.subnet"419 data-ng-model="interface.subnet"
419 data-ng-change="subnetChanged(interface)"420 data-ng-change="subnetChanged(interface)"
420 data-ng-options="subnet as getSubnetText(subnet) for subnet in subnets | filterByVLAN:interface.vlan">421 data-ng-options="subnet as getSubnetText(subnet) for subnet in subnets | filterByVLAN:interface.vlan">
421 <option value="" data-ng-hide="interface.links.length > 1">Unconfigured</option>422 <option value="" data-ng-hide="interface.links.length > 1">Unconfigured</option>
422 </select>423 </select>
424 <span class="ng-hide" data-ng-show="isAllNetworkingDisabled() && interface.discovered[0].subnet_id">
425 {$ getSubnetText(getSubnet(interface.discovered[0].subnet_id)) $}
426 </span>
423 </div>427 </div>
424 <div class="table__data table__column--14">428 <div class="table__data table__column--21">
425 <ul class="no-bullets">429 <ul class="no-bullets" data-ng-hide="isAllNetworkingDisabled()">
426 <li>430 <li>
427 <select class="table__input" name="link-mode" id="link-mode"431 <select class="table__input" name="link-mode" id="link-mode"
428 data-ng-model="interface.mode"432 data-ng-model="interface.mode"
@@ -441,8 +445,14 @@
441 data-ng-disabled="interface.mode != 'static'">445 data-ng-disabled="interface.mode != 'static'">
442 </li>446 </li>
443 </ul>447 </ul>
448 <span class="ng-hide" data-ng-show="isAllNetworkingDisabled() && !interface.discovered[0].ip_address">
449 {$ interface.ip_address $} ({$ getLinkModeText(interface) $})
450 </span>
451 <span class="ng-hide" data-ng-show="isAllNetworkingDisabled() && interface.discovered[0].ip_address">
452 {$ interface.discovered[0].ip_address $} (DHCP)
453 </span>
444 </div>454 </div>
445 <div class="table__data table__column--13">455 <div class="table__data table__column--6">
446 <div class="table__controls align-right">456 <div class="table__controls align-right">
447 <a class="icon add"457 <a class="icon add"
448 data-ng-click="quickAdd(interface)"458 data-ng-click="quickAdd(interface)"
@@ -500,14 +510,14 @@
500 <option value="" data-ng-hide="newInterface.type === 'alias'">Unconfigured</option>510 <option value="" data-ng-hide="newInterface.type === 'alias'">Unconfigured</option>
501 </select>511 </select>
502 </div>512 </div>
503 <div class="table__data table__column--14">513 <div class="table__data table__column--21">
504 <select class="table__input" name="link-mode" id="link-mode"514 <select class="table__input" name="link-mode" id="link-mode"
505 data-ng-model="newInterface.mode"515 data-ng-model="newInterface.mode"
506 data-ng-disabled="isLinkModeDisabled(newInterface)"516 data-ng-disabled="isLinkModeDisabled(newInterface)"
507 data-ng-options="mode.mode as mode.text for mode in modes | filterLinkModes:newInterface">517 data-ng-options="mode.mode as mode.text for mode in modes | filterLinkModes:newInterface">
508 </select>518 </select>
509 </div>519 </div>
510 <div class="table__data table_column--13"></div>520 <div class="table__data table_column--6"></div>
511 </div>521 </div>
512 <div class="table__row table__dropdown-row">522 <div class="table__row table__dropdown-row">
513 <div class="ng-hide" data-ng-show="isShowingInterfaceOptions()">523 <div class="ng-hide" data-ng-show="isShowingInterfaceOptions()">
514524
=== modified file 'src/maasserver/tests/test_forms_interface.py'
--- src/maasserver/tests/test_forms_interface.py 2015-09-16 20:14:09 +0000
+++ src/maasserver/tests/test_forms_interface.py 2015-10-31 23:55:40 +0000
@@ -806,3 +806,58 @@
806 "bond_lacp_rate": new_bond_lacp_rate,806 "bond_lacp_rate": new_bond_lacp_rate,
807 "bond_xmit_hash_policy": new_bond_xmit_hash_policy,807 "bond_xmit_hash_policy": new_bond_xmit_hash_policy,
808 }, interface.params)808 }, interface.params)
809
810 def test__edit_allows_zero_params(self):
811 parent1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
812 parent2 = factory.make_Interface(
813 INTERFACE_TYPE.PHYSICAL, node=parent1.node)
814 interface = factory.make_Interface(
815 INTERFACE_TYPE.BOND,
816 parents=[parent1, parent2])
817 bond_mode = factory.pick_choice(BOND_MODE_CHOICES)
818 bond_miimon = random.randint(0, 1000)
819 bond_downdelay = random.randint(0, 1000)
820 bond_updelay = random.randint(0, 1000)
821 bond_lacp_rate = factory.pick_choice(BOND_LACP_RATE_CHOICES)
822 bond_xmit_hash_policy = factory.pick_choice(
823 BOND_XMIT_HASH_POLICY_CHOICES)
824 interface.params = {
825 "bond_mode": bond_mode,
826 "bond_miimon": bond_miimon,
827 "bond_downdelay": bond_downdelay,
828 "bond_updelay": bond_updelay,
829 "bond_lacp_rate": bond_lacp_rate,
830 "bond_xmit_hash_policy": bond_xmit_hash_policy,
831 }
832 interface.save()
833 new_vlan = factory.make_VLAN(vid=33)
834 new_name = factory.make_name()
835 new_bond_mode = factory.pick_choice(BOND_MODE_CHOICES)
836 new_bond_miimon = 0
837 new_bond_downdelay = 0
838 new_bond_updelay = 0
839 new_bond_lacp_rate = factory.pick_choice(BOND_LACP_RATE_CHOICES)
840 new_bond_xmit_hash_policy = factory.pick_choice(
841 BOND_XMIT_HASH_POLICY_CHOICES)
842 form = BondInterfaceForm(
843 instance=interface,
844 data={
845 'vlan': new_vlan.id,
846 'name': new_name,
847 'bond_mode': new_bond_mode,
848 'bond_miimon': new_bond_miimon,
849 'bond_downdelay': new_bond_downdelay,
850 'bond_updelay': new_bond_updelay,
851 'bond_lacp_rate': new_bond_lacp_rate,
852 'bond_xmit_hash_policy': new_bond_xmit_hash_policy,
853 })
854 self.assertTrue(form.is_valid(), form.errors)
855 interface = form.save()
856 self.assertEquals({
857 "bond_mode": new_bond_mode,
858 "bond_miimon": new_bond_miimon,
859 "bond_downdelay": new_bond_downdelay,
860 "bond_updelay": new_bond_updelay,
861 "bond_lacp_rate": new_bond_lacp_rate,
862 "bond_xmit_hash_policy": new_bond_xmit_hash_policy,
863 }, interface.params)
809864
=== modified file 'src/maasserver/websockets/handlers/node.py'
--- src/maasserver/websockets/handlers/node.py 2015-10-29 20:10:11 +0000
+++ src/maasserver/websockets/handlers/node.py 2015-10-31 23:55:40 +0000
@@ -498,7 +498,7 @@
498 subnet = link.pop("subnet", None)498 subnet = link.pop("subnet", None)
499 if subnet is not None:499 if subnet is not None:
500 link["subnet_id"] = subnet.id500 link["subnet_id"] = subnet.id
501 return {501 data = {
502 "id": interface.id,502 "id": interface.id,
503 "type": interface.type,503 "type": interface.type,
504 "name": interface.get_name(),504 "name": interface.get_name(),
@@ -517,6 +517,21 @@
517 "links": links,517 "links": links,
518 }518 }
519519
520 # When the node is commissioning display the discovered IP address for
521 # this interface. This will only be shown on interfaces that are
522 # connected to a MAAS managed subnet.
523 if obj.status == NODE_STATUS.COMMISSIONING:
524 discovereds = interface.get_discovered()
525 if discovereds is not None:
526 for discovered in discovereds:
527 # Replace the subnet object with the subnet_id. The client
528 # will use this information to pull the subnet information
529 # from the websocket.
530 discovered["subnet_id"] = discovered.pop("subnet").id
531 data["discovered"] = discovereds
532
533 return data
534
520 def dehydrate_summary_output(self, obj, data):535 def dehydrate_summary_output(self, obj, data):
521 """Dehydrate the machine summary output."""536 """Dehydrate the machine summary output."""
522 # Produce a "clean" composite details document.537 # Produce a "clean" composite details document.
523538
=== modified file 'src/maasserver/websockets/handlers/tests/test_node.py'
--- src/maasserver/websockets/handlers/tests/test_node.py 2015-10-29 20:10:11 +0000
+++ src/maasserver/websockets/handlers/tests/test_node.py 2015-10-31 23:55:40 +0000
@@ -557,9 +557,9 @@
557 filesystem.fstype in FILESYSTEM_FORMAT_TYPE_CHOICES_DICT),557 filesystem.fstype in FILESYSTEM_FORMAT_TYPE_CHOICES_DICT),
558 }, handler.dehydrate_filesystem(filesystem))558 }, handler.dehydrate_filesystem(filesystem))
559559
560 def test_dehydrate_interface(self):560 def test_dehydrate_interface_for_ready_node(self):
561 owner = factory.make_User()561 owner = factory.make_User()
562 node = factory.make_Node(owner=owner)562 node = factory.make_Node(owner=owner, status=NODE_STATUS.READY)
563 handler = NodeHandler(owner, {})563 handler = NodeHandler(owner, {})
564 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)564 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
565 factory.make_StaticIPAddress(565 factory.make_StaticIPAddress(
@@ -587,6 +587,45 @@
587 "links": expected_links,587 "links": expected_links,
588 }, handler.dehydrate_interface(interface, node))588 }, handler.dehydrate_interface(interface, node))
589589
590 def test_dehydrate_interface_for_commissioning_node(self):
591 owner = factory.make_User()
592 node = factory.make_Node(owner=owner, status=NODE_STATUS.COMMISSIONING)
593 handler = NodeHandler(owner, {})
594 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
595 factory.make_StaticIPAddress(
596 alloc_type=IPADDRESS_TYPE.AUTO, ip="",
597 subnet=factory.make_Subnet(), interface=interface)
598 expected_links = interface.get_links()
599 for link in expected_links:
600 link["subnet_id"] = link.pop("subnet").id
601 discovered_subnet = factory.make_Subnet()
602 factory.make_StaticIPAddress(
603 alloc_type=IPADDRESS_TYPE.DISCOVERED,
604 ip=factory.pick_ip_in_network(discovered_subnet.get_ipnetwork()),
605 subnet=discovered_subnet, interface=interface)
606 expected_discovered = interface.get_discovered()
607 for discovered in expected_discovered:
608 discovered["subnet_id"] = discovered.pop("subnet").id
609 self.assertEquals({
610 "id": interface.id,
611 "type": interface.type,
612 "name": interface.get_name(),
613 "enabled": interface.is_enabled(),
614 "is_boot": interface == node.boot_interface,
615 "mac_address": "%s" % interface.mac_address,
616 "vlan_id": interface.vlan_id,
617 "parents": [
618 nic.id
619 for nic in interface.parents.all()
620 ],
621 "children": [
622 nic.child.id
623 for nic in interface.children_relationships.all()
624 ],
625 "links": expected_links,
626 "discovered": expected_discovered,
627 }, handler.dehydrate_interface(interface, node))
628
590 def test_dehydrate_summary_output_returns_None(self):629 def test_dehydrate_summary_output_returns_None(self):
591 owner = factory.make_User()630 owner = factory.make_User()
592 node = factory.make_Node(owner=owner)631 node = factory.make_Node(owner=owner)