Merge lp:~mpontillo/maas/fix-bridge-registration-1570104 into lp:~maas-committers/maas/trunk

Proposed by Mike Pontillo
Status: Merged
Approved by: Mike Pontillo
Approved revision: no longer in the source branch.
Merged at revision: 4928
Proposed branch: lp:~mpontillo/maas/fix-bridge-registration-1570104
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 266 lines (+116/-22)
5 files modified
src/maasserver/models/interface.py (+14/-8)
src/maasserver/models/node.py (+32/-10)
src/maasserver/models/tests/test_node.py (+60/-0)
src/maasserver/tests/test_preseed_network.py (+2/-2)
src/provisioningserver/utils/network.py (+8/-2)
To merge this branch: bzr merge lp:~mpontillo/maas/fix-bridge-registration-1570104
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Review via email: mp+291945@code.launchpad.net

Commit message

Allow racks that have bridges with no parent interfaces to register them with the region. Fixes bug #1570104.

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

Looks good. This code just keeps getting more complex.... ugh.

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

The attempt to merge lp:~mpontillo/maas/fix-bridge-registration-1570104 into lp:maas failed. Below is the output from the failed tests.

Hit:1 http://security.ubuntu.com/ubuntu xenial-security InRelease
Get:2 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial InRelease [247 kB]
Hit:3 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-updates InRelease
Hit:4 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-backports InRelease
Get:5 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial/universe Sources [7,753 kB]
Get:6 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial/universe amd64 Packages [7,539 kB]
Get:7 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial/universe Translation-en [4,358 kB]
Fetched 19.9 MB in 3s (5,536 kB/s)
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
    --no-install-recommends install apache2 archdetect-deb authbind bash 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 postgresql pxelinux python3-all python3-apt python3-bson python3-convoy python3-coverage python3-crochet python3-cssselect python3-curtin python3-dev python3-distro-info python3-django python3-django-nose python3-django-piston3 python3-dnspython python3-docutils python3-formencode python3-hivex python3-httplib2 python3-jinja2 python3-jsonschema python3-lxml python3-mock python3-netaddr python3-netifaces python3-oauth python3-oauthlib python3-openssl python3-paramiko python3-petname python3-pexpect python3-psycopg2 python3-pyinotify python3-pyparsing python3-pyvmomi python3-requests python3-seamicroclient python3-setuptools python3-simplestreams python3-sphinx python3-tempita python3-twisted python3-txtftp python3-tz python3-yaml python3-zope.interface python-bson python-crochet python-django python-django-piston python-djorm-ext-pgarray python-formencode python-lxml python-netaddr python-netifaces python-pocket-lint python-psycopg2 python-tempita python-twisted python-yaml socat syslinux-common tgt ubuntu-cloudimage-keyring wget xvfb
Reading package lists...
Building dependency tree...
Reading state information...
apache2 is already the newest version (2.4.18-2ubuntu2).
archdetect-deb is already the newest version (1.117ubuntu2).
authbind is already the newest version (2.1.1+nmu1).
bash is already the newest version (4.3-14ubuntu1).
bind9 is already the newest version (1:9.10.3.dfsg.P4-8).
bind9utils is already the newest version (1:9.10.3.dfsg.P4-8).
build-essential is already the newest version (12.1ubuntu2).
curl is already the newest version (7.47.0-1ubuntu2).
debhelper is already the newest version (9.20160115ubuntu3).
distro-info is already the newest version (0.14build1).
dnsutils is already the newest version (1:9.10.3.dfsg.P4-8).
firefox is already the newest version (45.0.2+build1-0ubuntu1).
freeipmi-tools is already the newest version (1.4.11-1ubuntu1).
git is already the n...

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

Heh, somehow this test is now happily engaging in infinite recursion:

maasserver.tests.test_preseed_network.TestBondNetworkLayout.test__renders_expected_output

Will look into it.

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

Hah, it was because we called .all() on a manager, then added an interface, then iterated on the .all() (which then included the bond, causing the bond to have itself as a parent).

I guess we should validate that interfaces cannot have themselves as parents. ;-)

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

The attempt to merge lp:~mpontillo/maas/fix-bridge-registration-1570104 into lp:maas failed. Below is the output from the failed tests.

Hit:1 http://security.ubuntu.com/ubuntu xenial-security InRelease
Get:2 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial InRelease [247 kB]
Hit:3 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-updates InRelease
Hit:4 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-backports InRelease
Get:5 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial/main Sources [866 kB]
Get:6 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial/main amd64 Packages [1,183 kB]
Get:7 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial/universe amd64 Packages [7,538 kB]
Fetched 9,834 kB in 2s (4,810 kB/s)
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
    --no-install-recommends install apache2 archdetect-deb authbind bash 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 postgresql pxelinux python3-all python3-apt python3-bson python3-convoy python3-coverage python3-crochet python3-cssselect python3-curtin python3-dev python3-distro-info python3-django python3-django-nose python3-django-piston3 python3-dnspython python3-docutils python3-formencode python3-hivex python3-httplib2 python3-jinja2 python3-jsonschema python3-lxml python3-mock python3-netaddr python3-netifaces python3-oauth python3-oauthlib python3-openssl python3-paramiko python3-petname python3-pexpect python3-psycopg2 python3-pyinotify python3-pyparsing python3-pyvmomi python3-requests python3-seamicroclient python3-setuptools python3-simplestreams python3-sphinx python3-tempita python3-twisted python3-txtftp python3-tz python3-yaml python3-zope.interface python-bson python-crochet python-django python-django-piston python-djorm-ext-pgarray python-formencode python-lxml python-netaddr python-netifaces python-pocket-lint python-psycopg2 python-tempita python-twisted python-yaml socat syslinux-common tgt ubuntu-cloudimage-keyring wget xvfb
Reading package lists...
Building dependency tree...
Reading state information...
apache2 is already the newest version (2.4.18-2ubuntu2).
archdetect-deb is already the newest version (1.117ubuntu2).
authbind is already the newest version (2.1.1+nmu1).
bash is already the newest version (4.3-14ubuntu1).
bind9 is already the newest version (1:9.10.3.dfsg.P4-8).
bind9utils is already the newest version (1:9.10.3.dfsg.P4-8).
build-essential is already the newest version (12.1ubuntu2).
curl is already the newest version (7.47.0-1ubuntu2).
debhelper is already the newest version (9.20160115ubuntu3).
distro-info is already the newest version (0.14build1).
dnsutils is already the newest version (1:9.10.3.dfsg.P4-8).
firefox is already the newest version (45.0.2+build1-0ubuntu1).
freeipmi-tools is already the newest version (1.4.11-1ubuntu1).
git is already the newest ver...

Revision history for this message
MAAS Lander (maas-lander) wrote :

There are additional revisions which have not been approved in review. Please seek review and approval of these new revisions.

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

The attempt to merge lp:~mpontillo/maas/fix-bridge-registration-1570104 into lp:maas failed. Below is the output from the failed tests.

Hit:1 http://security.ubuntu.com/ubuntu xenial-security InRelease
Get:2 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial InRelease [247 kB]
Hit:3 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-updates InRelease
Hit:4 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-backports InRelease
Get:5 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial/main Sources [866 kB]
Get:6 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial/universe Sources [7,753 kB]
Get:7 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial/main amd64 Packages [1,183 kB]
Get:8 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial/universe amd64 Packages [7,538 kB]
Fetched 17.6 MB in 3s (5,424 kB/s)
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
    --no-install-recommends install apache2 archdetect-deb authbind bash 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 postgresql pxelinux python3-all python3-apt python3-bson python3-convoy python3-coverage python3-crochet python3-cssselect python3-curtin python3-dev python3-distro-info python3-django python3-django-nose python3-django-piston3 python3-dnspython python3-docutils python3-formencode python3-hivex python3-httplib2 python3-jinja2 python3-jsonschema python3-lxml python3-mock python3-netaddr python3-netifaces python3-oauth python3-oauthlib python3-openssl python3-paramiko python3-petname python3-pexpect python3-psycopg2 python3-pyinotify python3-pyparsing python3-pyvmomi python3-requests python3-seamicroclient python3-setuptools python3-simplestreams python3-sphinx python3-tempita python3-twisted python3-txtftp python3-tz python3-yaml python3-zope.interface python-bson python-crochet python-django python-django-piston python-djorm-ext-pgarray python-formencode python-lxml python-netaddr python-netifaces python-pocket-lint python-psycopg2 python-tempita python-twisted python-yaml socat syslinux-common tgt ubuntu-cloudimage-keyring wget xvfb
Reading package lists...
Building dependency tree...
Reading state information...
apache2 is already the newest version (2.4.18-2ubuntu2).
archdetect-deb is already the newest version (1.117ubuntu2).
authbind is already the newest version (2.1.1+nmu1).
bash is already the newest version (4.3-14ubuntu1).
bind9 is already the newest version (1:9.10.3.dfsg.P4-8).
bind9utils is already the newest version (1:9.10.3.dfsg.P4-8).
build-essential is already the newest version (12.1ubuntu2).
curl is already the newest version (7.47.0-1ubuntu2).
debhelper is already the newest version (9.20160115ubuntu3).
distro-info is already the newest version (0.14build1).
dnsutils is already the newest version (1:9.10.3.dfsg.P4-8).
firefox is already the newest version (45.0.2+build1-0ubuntu...

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

Random failure in:

maasserver.rpc.tests.test_regionservice.TestRegionService.test_start_up_logs_failure_if_all_endpoint_options_fail

Retrying.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/models/interface.py'
2--- src/maasserver/models/interface.py 2016-04-11 16:23:26 +0000
3+++ src/maasserver/models/interface.py 2016-04-15 23:41:24 +0000
4@@ -1115,23 +1115,29 @@
5
6 def get_node(self):
7 if self.id is None:
8- return None
9+ # This should be correct since the interface was just now created.
10+ return self.node
11 else:
12 parent = self.parents.first()
13- if parent is not None:
14+ if parent is not None and parent != self:
15 return parent.get_node()
16 else:
17- return None
18+ # This could be a bridge interface without a parent.
19+ return self.node
20
21 def is_enabled(self):
22 if self.id is None:
23 return True
24 else:
25- is_enabled = {
26- parent.is_enabled()
27- for parent in self.parents.all()
28- }
29- return True in is_enabled
30+ if self.parents.count() > 0:
31+ is_enabled = {
32+ parent.is_enabled()
33+ for parent in self.parents.all()
34+ if parent != self
35+ }
36+ return True in is_enabled
37+ else:
38+ return self.enabled
39
40 def _validate_acceptable_parent_types(self, parent_types):
41 """Raises a ValidationError if the interface has parents which are not
42
43=== modified file 'src/maasserver/models/node.py'
44--- src/maasserver/models/node.py 2016-04-14 19:54:36 +0000
45+++ src/maasserver/models/node.py 2016-04-15 23:41:24 +0000
46@@ -3256,9 +3256,11 @@
47 parent_nics = Interface.objects.get_interfaces_on_node_by_name(
48 self, ifnames)
49
50- # Ignore child interfaces that don't have parents. MAAS won't know what
51- # to do with them since they can't be connected to a fabric.
52- if len(parent_nics) == 0:
53+ # Ignore most child interfaces that don't have parents. MAAS won't know
54+ # what to do with them since they can't be connected to a fabric.
55+ # Bridges are an exception since some MAAS demo/test environments
56+ # contain virtual bridges.
57+ if len(parent_nics) == 0 and child_type is not BridgeInterface:
58 return None
59
60 mac_address = config["mac_address"]
61@@ -3266,12 +3268,14 @@
62 self, name, mac_address, parent_nics)
63
64 links = config["links"]
65- self._configure_vlan_from_links(interface, parent_nics, links)
66+ found_vlan = self._configure_vlan_from_links(
67+ interface, parent_nics, links)
68
69 # Update all the IP address on this interface. Fix the VLAN the
70 # interface belongs to so its the same as the links and all parents to
71 # be on the same VLAN.
72- update_ip_addresses = self._update_links(interface, links)
73+ update_ip_addresses = self._update_links(
74+ interface, links, use_interface_vlan=found_vlan)
75 self._update_parent_vlans(interface, parent_nics, update_ip_addresses)
76 return interface
77
78@@ -3314,16 +3318,26 @@
79 parent_nic.save()
80
81 def _configure_vlan_from_links(self, interface, parent_nics, links):
82+ """Attempt to configure the interface VLAN based on the links and
83+ connected subnets. Returns True if the VLAN was configured; otherwise,
84+ returns False."""
85 # Make sure that the VLAN on the interface is correct. When
86 # links exists on this interface we place it into the correct
87 # VLAN. If it cannot be determined it is placed on the same fabric
88 # as its first parent interface.
89 connected_to_subnets = self._get_connected_subnets(links)
90- if len(connected_to_subnets) == 0:
91+ if len(connected_to_subnets) == 0 and len(parent_nics) > 0:
92 # Not connected to any known subnets. We add it to the same
93 # VLAN as its first parent.
94 interface.vlan = parent_nics[0].vlan
95- interface.save()
96+ interface.save()
97+ return True
98+ elif len(connected_to_subnets) > 0:
99+ subnet = next(iter(connected_to_subnets))
100+ interface.vlan = subnet.vlan
101+ interface.save()
102+ return True
103+ return False
104
105 def _get_connected_subnets(self, links):
106 """Return a set of subnets that `links` belongs to."""
107@@ -3358,7 +3372,8 @@
108 return ip_address.subnet.vlan
109 return None
110
111- def _update_links(self, interface, links, force_vlan=False):
112+ def _update_links(
113+ self, interface, links, force_vlan=False, use_interface_vlan=True):
114 """Update the links on `interface`."""
115 interface.ip_addresses.filter(
116 alloc_type=IPADDRESS_TYPE.DISCOVERED).delete()
117@@ -3366,6 +3381,13 @@
118 interface.ip_addresses.exclude(
119 alloc_type=IPADDRESS_TYPE.DISCOVERED))
120 updated_ip_addresses = set()
121+ if use_interface_vlan:
122+ vlan = interface.vlan
123+ elif len(links) > 0:
124+ fabric = Fabric.objects.create()
125+ vlan = fabric.get_default_vlan()
126+ interface.vlan = vlan
127+ interface.save()
128 for link in links:
129 if link["mode"] == "dhcp":
130 dhcp_address = self._get_alloc_type_from_ip_addresses(
131@@ -3388,7 +3410,7 @@
132 subnet, _ = Subnet.objects.get_or_create(
133 cidr=str(ip_network.cidr), defaults={
134 "name": str(ip_network.cidr),
135- "vlan": interface.vlan,
136+ "vlan": vlan,
137 "space": Space.objects.get_default_space(),
138 })
139
140@@ -3427,7 +3449,7 @@
141 subnet, _ = Subnet.objects.get_or_create(
142 cidr=str(ip_network.cidr), defaults={
143 "name": str(ip_network.cidr),
144- "vlan": interface.vlan,
145+ "vlan": vlan,
146 "space": Space.objects.get_default_space(),
147 })
148
149
150=== modified file 'src/maasserver/models/tests/test_node.py'
151--- src/maasserver/models/tests/test_node.py 2016-04-14 19:54:36 +0000
152+++ src/maasserver/models/tests/test_node.py 2016-04-15 23:41:24 +0000
153@@ -6652,6 +6652,66 @@
154 vlan=br0_vlan,
155 ))
156
157+ def test_registers_bridge_with_no_parents_and_links(self):
158+ rack = self.create_empty_rack_controller()
159+ interfaces = {
160+ 'br0': {
161+ 'enabled': True,
162+ 'mac_address': '4e:4d:9a:a8:a5:5f',
163+ 'parents': [],
164+ 'source': 'ipaddr',
165+ 'type': 'bridge',
166+ 'links': [{
167+ 'mode': "static",
168+ 'address': "192.168.0.1/24"
169+ }]
170+ },
171+ 'eth0': {
172+ 'enabled': True,
173+ 'mac_address': '52:54:00:77:15:e3',
174+ 'links': [],
175+ 'parents': [],
176+ 'source': 'ipaddr',
177+ 'type': 'physical'
178+ }
179+ }
180+ rack.update_interfaces(interfaces)
181+ eth0 = get_one(
182+ PhysicalInterface.objects.filter(node=rack, name='eth0'))
183+ br0 = get_one(BridgeInterface.objects.filter(node=rack, name='br0'))
184+ self.assertIsNotNone(eth0)
185+ self.assertIsNotNone(br0)
186+ subnet = get_one(Subnet.objects.filter(cidr='192.168.0.0/24'))
187+ self.assertIsNotNone(subnet)
188+ self.assertThat(subnet.vlan, Equals(br0.vlan))
189+
190+ def test_registers_bridge_with_no_parents_and_no_links(self):
191+ rack = self.create_empty_rack_controller()
192+ interfaces = {
193+ 'br0': {
194+ 'enabled': True,
195+ 'links': [],
196+ 'mac_address': '4e:4d:9a:a8:a5:5f',
197+ 'parents': [],
198+ 'source': 'ipaddr',
199+ 'type': 'bridge'
200+ },
201+ 'eth0': {
202+ 'enabled': True,
203+ 'links': [],
204+ 'mac_address': '52:54:00:77:15:e3',
205+ 'parents': [],
206+ 'source': 'ipaddr',
207+ 'type': 'physical'
208+ },
209+ }
210+ rack.update_interfaces(interfaces)
211+ eth0 = get_one(
212+ PhysicalInterface.objects.filter(node=rack, name='eth0'))
213+ br0 = get_one(BridgeInterface.objects.filter(node=rack, name='br0'))
214+ self.assertIsNotNone(eth0)
215+ self.assertIsNotNone(br0)
216+
217
218 class TestRackController(MAASServerTestCase):
219
220
221=== modified file 'src/maasserver/tests/test_preseed_network.py'
222--- src/maasserver/tests/test_preseed_network.py 2016-03-28 13:54:47 +0000
223+++ src/maasserver/tests/test_preseed_network.py 2016-04-15 23:41:24 +0000
224@@ -244,7 +244,7 @@
225 def test__renders_expected_output(self):
226 node = factory.make_Node_with_Interface_on_Subnet(
227 interface_count=2)
228- interfaces = node.interface_set.all()
229+ interfaces = list(node.interface_set.all())
230 vlan = node.interface_set.first().vlan
231 bond_iface = factory.make_Interface(
232 iftype=INTERFACE_TYPE.BOND, node=node, vlan=vlan,
233@@ -286,7 +286,7 @@
234 def test__renders_expected_output(self):
235 node = factory.make_Node_with_Interface_on_Subnet(
236 interface_count=2)
237- phys_ifaces = node.interface_set.all()
238+ phys_ifaces = list(node.interface_set.all())
239 phys_vlan = node.interface_set.first().vlan
240 bond_iface = factory.make_Interface(iftype=INTERFACE_TYPE.BOND,
241 node=node, vlan=phys_vlan,
242
243=== modified file 'src/provisioningserver/utils/network.py'
244--- src/provisioningserver/utils/network.py 2016-04-15 18:08:15 +0000
245+++ src/provisioningserver/utils/network.py 2016-04-15 23:41:24 +0000
246@@ -332,12 +332,18 @@
247 @property
248 def first(self):
249 """Returns the first IP address in this set."""
250- return self.ranges[0].first
251+ if len(self.ranges) > 0:
252+ return self.ranges[0].first
253+ else:
254+ return None
255
256 @property
257 def last(self):
258 """Returns the last IP address in this set."""
259- return self.ranges[-1].last
260+ if len(self.ranges) > 0:
261+ return self.ranges[-1].last
262+ else:
263+ return None
264
265 def ip_has_purpose(self, ip, purpose):
266 """Returns True if the specified IP address has the specified purpose