Merge ~mpontillo/maas:fix-subnet-links-for-juju--bug-1789495 into maas:master

Proposed by Mike Pontillo
Status: Merged
Approved by: Mike Pontillo
Approved revision: 1a3efec0c969a9aa815e803eeb41e60cb84eefe9
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~mpontillo/maas:fix-subnet-links-for-juju--bug-1789495
Merge into: maas:master
Diff against target: 188 lines (+64/-10)
4 files modified
src/maasserver/models/bmc.py (+16/-6)
src/maasserver/models/tests/test_bmc.py (+26/-4)
src/provisioningserver/utils/constraints.py (+5/-0)
src/provisioningserver/utils/tests/test_constraints.py (+17/-0)
Reviewer Review Type Date Requested Status
Newell Jensen (community) Approve
Review via email: mp+354141@code.launchpad.net

Commit message

LP: #1789495 - Fix subnet links for composed machines.

To post a comment you must log in.
Revision history for this message
Newell Jensen (newell-jensen) wrote :

Looks good. Approving but I did have a question inline and won't block.

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

Thanks for the comments; reply below!

7fcdd2e... by Mike Pontillo

Fix typo.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/models/bmc.py b/src/maasserver/models/bmc.py
2index 01f23a3..f813057 100644
3--- a/src/maasserver/models/bmc.py
4+++ b/src/maasserver/models/bmc.py
5@@ -83,6 +83,7 @@ from provisioningserver.drivers.power.registry import PowerDriverRegistry
6 from provisioningserver.enum import MACVLAN_MODE_CHOICES
7 from provisioningserver.logger import get_maas_logger
8 from provisioningserver.utils.constraints import LabeledConstraintMap
9+from provisioningserver.utils.network import get_ifname_for_label
10 from provisioningserver.utils.twisted import asynchronous
11 from twisted.internet.defer import inlineCallbacks
12
13@@ -97,7 +98,10 @@ def get_requested_ips(requested_machine):
14 requested_ips = {
15 interface.ifname: interface.requested_ips
16 for interface in requested_machine.interfaces
17- if interface.ifname is not None
18+ if (
19+ interface.ifname is not None and
20+ len(interface.requested_ips) > 0
21+ )
22 }
23 else:
24 requested_ips = {}
25@@ -748,7 +752,10 @@ class Pod(BMC):
26 # New machines get commission started immediately unless skipped.
27 if not skip_commissioning:
28 skip_networking = False
29- if len(requested_ips) > 0:
30+ # If an interfaces constraint was specified, don't reset the
31+ # networking parameters. (Instead, allow them to be set based on
32+ # what was requested in the constraints string.)
33+ if interfaces is not None and len(interfaces) > 0:
34 skip_networking = True
35 machine.start_commissioning(
36 commissioning_user, skip_networking=skip_networking)
37@@ -790,7 +797,7 @@ class Pod(BMC):
38 # dictionaries in Python 3.6+ preserve insertion order.)
39 if interface_constraints is not None:
40 interface_names = [
41- label[:15]
42+ get_ifname_for_label(label)
43 for label in interface_constraints
44 ]
45 else:
46@@ -882,9 +889,12 @@ class Pod(BMC):
47 # the VM has been attached to. Update the VLAN (but
48 # only if necessary).
49 host_vlan = host_attach_interface.vlan
50- if host_vlan != created_interfaces[idx].vlan:
51- created_interfaces[idx].vlan = host_vlan
52- created_interfaces[idx].save()
53+ interface = created_interfaces[idx]
54+ if host_vlan != interface.vlan:
55+ interface.vlan = host_vlan
56+ interface.save()
57+ if interface.ip_addresses.count() == 0:
58+ interface.force_auto_or_dhcp_link()
59 continue
60
61 def _sync_machine(self, discovered_machine, existing_machine):
62diff --git a/src/maasserver/models/tests/test_bmc.py b/src/maasserver/models/tests/test_bmc.py
63index bdb6624..00a5ad2 100644
64--- a/src/maasserver/models/tests/test_bmc.py
65+++ b/src/maasserver/models/tests/test_bmc.py
66@@ -1043,7 +1043,7 @@ class TestPod(MAASServerTestCase):
67 sip3 = StaticIPAddress.objects.filter(ip=ip3).first()
68 self.assertThat(sip3.get_interface().node, Equals(machine))
69
70- def test_create_machine_sets_interface_vlans_using_attach_names(self):
71+ def test_create_machine_sets_up_interface_vlans_correctly(self):
72 discovered_machine = self.make_discovered_machine()
73 self.patch(Machine, "set_default_storage_layout")
74 self.patch(Machine, "set_initial_networking_configuration")
75@@ -1052,11 +1052,14 @@ class TestPod(MAASServerTestCase):
76 controller = factory.make_RackController()
77 vlan = factory.make_VLAN(
78 fabric=fabric, dhcp_on=True, primary_rack=controller)
79- subnet = factory.make_Subnet()
80 vlan2 = factory.make_VLAN(
81 fabric=fabric, dhcp_on=False, primary_rack=controller)
82 vlan3 = factory.make_VLAN(
83 fabric=fabric, dhcp_on=False, primary_rack=controller)
84+ # Create subnets, so we can test to ensure they get linked up.
85+ subnet = factory.make_Subnet(vlan=vlan)
86+ factory.make_Subnet(vlan=vlan2)
87+ factory.make_Subnet(vlan=vlan3)
88 eth0 = factory.make_Interface(node=controller, vlan=vlan)
89 eth1 = factory.make_Interface(node=controller, vlan=vlan2)
90 eth2 = factory.make_Interface(node=controller, vlan=vlan3)
91@@ -1074,11 +1077,13 @@ class TestPod(MAASServerTestCase):
92 InterfaceAttachType.MACVLAN)
93 discovered_machine.interfaces[2].attach_name = eth2.name
94 pod = factory.make_Pod(ip_address=ip)
95- print(pod.ip_address)
96+ # Skip commissioning on creation so that we can test that VLANs
97+ # are properly set based on the interface constraint.
98 machine = pod.create_machine(
99 discovered_machine, factory.make_User(),
100+ # Use numeric names to mimic what Juju will do.
101 interfaces=LabeledConstraintMap(
102- 'eth0:vlan=id:%d;eth1:vlan=id:%d;eth2:vlan=id:%d' % (
103+ '0:vlan=id:%d;1:vlan=id:%d;2:vlan=id:%d' % (
104 vlan.id, vlan2.id, vlan3.id),
105 )
106 )
107@@ -1089,6 +1094,10 @@ class TestPod(MAASServerTestCase):
108 self.assertThat(interfaces['eth0'].vlan, Equals(vlan))
109 self.assertThat(interfaces['eth1'].vlan, Equals(vlan2))
110 self.assertThat(interfaces['eth2'].vlan, Equals(vlan3))
111+ # Make sure all interfaces also have a subnet link.
112+ self.assertThat(interfaces['eth0'].ip_addresses.count(), Equals(1))
113+ self.assertThat(interfaces['eth1'].ip_addresses.count(), Equals(1))
114+ self.assertThat(interfaces['eth2'].ip_addresses.count(), Equals(1))
115
116 def test_create_machine_uses_default_ifnames_if_discovered_mismatch(self):
117 """This makes sure that if the discovered machine comes back with
118@@ -1766,3 +1775,16 @@ class TestGetRequestedIPs(MAASServerTestCase):
119 "eth0": ['10.0.0.1', '2001:db8::1'],
120 "eth1": ['10.0.0.2', '2001:db8::2']
121 }))
122+
123+ def test__leaves_out_keys_with_no_assigned_ips(self):
124+ interface = RequestedMachineInterface(
125+ ifname='eth0', requested_ips=['10.0.0.1', '2001:db8::1'])
126+ interface2 = RequestedMachineInterface(
127+ ifname='eth1', requested_ips=[])
128+ interfaces = [interface, interface2]
129+ requested_machine = RequestedMachine(
130+ factory.make_hostname(), 'amd64', 1, 1024, [], interfaces)
131+ self.assertThat(
132+ get_requested_ips(requested_machine), Equals({
133+ "eth0": ['10.0.0.1', '2001:db8::1'],
134+ }))
135diff --git a/src/provisioningserver/utils/constraints.py b/src/provisioningserver/utils/constraints.py
136index e38fc23..d00c9c7 100644
137--- a/src/provisioningserver/utils/constraints.py
138+++ b/src/provisioningserver/utils/constraints.py
139@@ -30,6 +30,11 @@ class LabeledConstraintMap(object):
140 def __str__(self):
141 return self.value
142
143+ def __len__(self):
144+ if self.map is None:
145+ return 0
146+ return len(self.map)
147+
148 def __iter__(self):
149 if self.map is None:
150 return iter([])
151diff --git a/src/provisioningserver/utils/tests/test_constraints.py b/src/provisioningserver/utils/tests/test_constraints.py
152index d797d11..c3a26bf 100644
153--- a/src/provisioningserver/utils/tests/test_constraints.py
154+++ b/src/provisioningserver/utils/tests/test_constraints.py
155@@ -7,12 +7,14 @@ __all__ = []
156
157 from maastesting.testcase import MAASTestCase
158 from provisioningserver.utils.constraints import (
159+ LabeledConstraintMap,
160 parse_labeled_constraint_map,
161 validate_constraint_label_name,
162 )
163 from testtools import ExpectedException
164 from testtools.matchers import (
165 Equals,
166+ HasLength,
167 Is,
168 )
169
170@@ -113,3 +115,18 @@ class TestGetLabeledConstraintsMap(MAASTestCase):
171 "foo": {"a": ["b"], "c": ["d"]},
172 "bar": {"e": ["f"], "g": ["h"]},
173 }))
174+
175+
176+class TestLabeledConstraintMap(MAASTestCase):
177+
178+ def test__len__for_null_map(self):
179+ lcm = LabeledConstraintMap(None)
180+ self.assertThat(lcm, HasLength(0))
181+
182+ def test__len__for_empty_map(self):
183+ lcm = LabeledConstraintMap('')
184+ self.assertThat(lcm, HasLength(0))
185+
186+ def test__len__for_populated_map(self):
187+ lcm = LabeledConstraintMap('eth0:space=1;eth1:space=2')
188+ self.assertThat(lcm, HasLength(2))

Subscribers

People subscribed via source and target branches