Merge lp:~mpontillo/maas/fix-bridge-registration-1570104 into lp:~maas-committers/maas/trunk
- fix-bridge-registration-1570104
- Merge into trunk
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 |
Related bugs: |
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.
Description of the change
MAAS Lander (maas-lander) wrote : | # |
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://
Get:2 http://
Hit:3 http://
Hit:4 http://
Get:5 http://
Get:6 http://
Get:7 http://
Fetched 19.9 MB in 3s (5,536 kB/s)
Reading package lists...
sudo DEBIAN_
--no-
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.
bind9utils is already the newest version (1:9.10.
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.20160115ubun
distro-info is already the newest version (0.14build1).
dnsutils is already the newest version (1:9.10.
firefox is already the newest version (45.0.2+
freeipmi-tools is already the newest version (1.4.11-1ubuntu1).
git is already the n...
Mike Pontillo (mpontillo) wrote : | # |
Heh, somehow this test is now happily engaging in infinite recursion:
maasserver.
Will look into it.
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. ;-)
MAAS Lander (maas-lander) wrote : | # |
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://
Get:2 http://
Hit:3 http://
Hit:4 http://
Get:5 http://
Get:6 http://
Get:7 http://
Fetched 9,834 kB in 2s (4,810 kB/s)
Reading package lists...
sudo DEBIAN_
--no-
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.
bind9utils is already the newest version (1:9.10.
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.20160115ubun
distro-info is already the newest version (0.14build1).
dnsutils is already the newest version (1:9.10.
firefox is already the newest version (45.0.2+
freeipmi-tools is already the newest version (1.4.11-1ubuntu1).
git is already the newest ver...
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.
MAAS Lander (maas-lander) wrote : | # |
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://
Get:2 http://
Hit:3 http://
Hit:4 http://
Get:5 http://
Get:6 http://
Get:7 http://
Get:8 http://
Fetched 17.6 MB in 3s (5,424 kB/s)
Reading package lists...
sudo DEBIAN_
--no-
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.
bind9utils is already the newest version (1:9.10.
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.20160115ubun
distro-info is already the newest version (0.14build1).
dnsutils is already the newest version (1:9.10.
firefox is already the newest version (45.0.2+
Mike Pontillo (mpontillo) wrote : | # |
Random failure in:
maasserver.
Retrying.
Preview Diff
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 |
Looks good. This code just keeps getting more complex.... ugh.