Merge lp:~rvb/maas/connect-mac-when-parsing-leases into lp:~maas-committers/maas/trunk

Proposed by Raphaël Badin
Status: Merged
Approved by: Raphaël Badin
Approved revision: no longer in the source branch.
Merged at revision: 3612
Proposed branch: lp:~rvb/maas/connect-mac-when-parsing-leases
Merge into: lp:~maas-committers/maas/trunk
Prerequisite: lp:~rvb/maas/rm-cluster-interf-connexion
Diff against target: 267 lines (+111/-43)
4 files modified
src/maasserver/models/dhcplease.py (+7/-1)
src/maasserver/models/macaddress.py (+30/-16)
src/maasserver/models/tests/test_dhcplease.py (+18/-0)
src/maasserver/models/tests/test_macaddress.py (+56/-26)
To merge this branch: bzr merge lp:~rvb/maas/connect-mac-when-parsing-leases
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Mike Pontillo (community) Approve
Review via email: mp+251976@code.launchpad.net

Commit message

Create MAC->cluster interface connection when parsing leases. This is so that one can "claim and IP address" without providing an IP address: if the device has DHCPed off MAAS: MAAS will know from which cluster interface the IP should be taken from.

To post a comment you must log in.
Revision history for this message
Mike Pontillo (mpontillo) wrote :

Looks like a nice improvement; a few comments below.

It would be nice if someone else could review, since I'm not yet very qualified!

review: Approve
Revision history for this message
Gavin Panella (allenap) wrote :

A few comments, that's all. Looks good :)

review: Approve
Revision history for this message
Raphaël Badin (rvb) wrote :

Thanks both for the reviews!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/models/dhcplease.py'
2--- src/maasserver/models/dhcplease.py 2014-09-01 13:32:35 +0000
3+++ src/maasserver/models/dhcplease.py 2015-03-06 11:15:40 +0000
4@@ -32,7 +32,10 @@
5 MACAddressField,
6 )
7 from maasserver.models.cleansave import CleanSave
8-from maasserver.models.macaddress import MACAddress
9+from maasserver.models.macaddress import (
10+ MACAddress,
11+ update_macs_cluster_interfaces,
12+ )
13 from maasserver.utils import strip_domain
14
15
16@@ -97,6 +100,8 @@
17
18 This deletes entries that are no longer current, adds new ones,
19 and updates or replaces ones that have changed.
20+ This method also updates the MAC address objects to link them to
21+ their respective cluster interface.
22
23 :param nodegroup: The node group that these updates are for.
24 :param leases: A dict describing all current IP/MAC mappings as
25@@ -108,6 +113,7 @@
26 """
27 self._delete_obsolete_leases(nodegroup, leases)
28 new_leases = self._add_missing_leases(nodegroup, leases)
29+ update_macs_cluster_interfaces(leases, nodegroup)
30 return new_leases
31
32 def get_hostname_ip_mapping(self, nodegroup):
33
34=== modified file 'src/maasserver/models/macaddress.py'
35--- src/maasserver/models/macaddress.py 2015-01-28 09:13:11 +0000
36+++ src/maasserver/models/macaddress.py 2015-03-06 11:15:40 +0000
37@@ -67,12 +67,15 @@
38
39 def update_mac_cluster_interfaces(ip, mac, cluster):
40 """Calculate and store which interface a MAC is attached to."""
41- try:
42- mac_address = MACAddress.objects.get(mac_address=mac)
43- except MACAddress.DoesNotExist:
44- # Silently ignore MAC addresses that we don't know about.
45- return
46-
47+ # Create a `leases` dict with only one lease: this is so we can re-use
48+ # update_macs_cluster_interfaces() which is designed to deal with multiple
49+ # leases.
50+ leases = {ip: mac}
51+ update_macs_cluster_interfaces(leases, cluster)
52+
53+
54+def update_macs_cluster_interfaces(leases, cluster):
55+ """Calculate and store which interface a set of MACs are attached to."""
56 interface_ranges = {}
57 # Only consider configured interfaces.
58 interfaces = (
59@@ -92,15 +95,25 @@
60 static_range = []
61 interface_ranges[interface] = (ip_range, static_range)
62
63- # Look through the interface ranges to see if any match the passed
64- # IP address.
65- for interface, (ip_range, static_range) in interface_ranges.items():
66- ipaddress = IPAddress(ip)
67- # Set the cluster interface only if it's new/changed.
68- # This is only an optimisation to prevent repeated logging.
69- changed = mac_address.cluster_interface != interface
70- in_range = ipaddress in ip_range or ipaddress in static_range
71- if in_range and changed:
72+ for ip, mac in leases.viewitems():
73+ # Look through the interface ranges to see if any match the passed
74+ # IP address.
75+ try:
76+ mac_address = MACAddress.objects.get(mac_address=mac)
77+ except MACAddress.DoesNotExist:
78+ # Silently ignore MAC addresses that we don't know about.
79+ continue
80+
81+ for interface, (ip_range, static_range) in interface_ranges.items():
82+ ipaddress = IPAddress(ip)
83+ # Set the cluster interface only if it's new/changed.
84+ # This is only an optimisation to prevent repeated logging.
85+ changed = mac_address.cluster_interface != interface
86+ if not changed:
87+ continue
88+ in_range = ipaddress in ip_range or ipaddress in static_range
89+ if not in_range:
90+ continue
91 mac_address.cluster_interface = interface
92 mac_address.save()
93 maaslog.info(
94@@ -112,9 +125,10 @@
95 if ipnetwork is not None:
96 try:
97 network = Network.objects.get(ip=ipnetwork.ip.format())
98- network.macaddress_set.add(mac_address)
99 except Network.DoesNotExist:
100 pass
101+ else:
102+ network.macaddress_set.add(mac_address)
103
104
105 class MACAddress(CleanSave, TimestampedModel):
106
107=== modified file 'src/maasserver/models/tests/test_dhcplease.py'
108--- src/maasserver/models/tests/test_dhcplease.py 2014-12-05 12:48:49 +0000
109+++ src/maasserver/models/tests/test_dhcplease.py 2015-03-06 11:15:40 +0000
110@@ -23,8 +23,10 @@
111 )
112 from maasserver.models import DHCPLease
113 from maasserver.testing.factory import factory
114+from maasserver.testing.orm import reload_object
115 from maasserver.testing.testcase import MAASServerTestCase
116 from maasserver.utils import ignore_unused
117+from netaddr import IPRange
118
119
120 def get_leases(nodegroup):
121@@ -165,6 +167,22 @@
122 },
123 map_leases(nodegroup))
124
125+ def test_update_leases_updates_macs_cluster_interfaces(self):
126+ cluster = factory.make_NodeGroup()
127+ mac_address = factory.make_MACAddress_with_Node()
128+ interface = factory.make_NodeGroupInterface(nodegroup=cluster)
129+ ip_range = IPRange(
130+ interface.static_ip_range_low, interface.static_ip_range_high)
131+ ip = unicode(random.choice(ip_range))
132+
133+ leases = {
134+ factory.make_ipv4_address(): factory.make_mac_address(),
135+ ip: mac_address.mac_address,
136+ }
137+ DHCPLease.objects.update_leases(cluster, leases)
138+ self.assertEquals(
139+ interface, reload_object(mac_address).cluster_interface)
140+
141 def test_update_leases_does_not_update_dns_zone_if_nothing_added(self):
142 self.patch(dns, 'dns_update_zones')
143 nodegroup = factory.make_NodeGroup()
144
145=== modified file 'src/maasserver/models/tests/test_macaddress.py'
146--- src/maasserver/models/tests/test_macaddress.py 2015-01-26 08:45:19 +0000
147+++ src/maasserver/models/tests/test_macaddress.py 2015-03-06 11:15:40 +0000
148@@ -32,6 +32,7 @@
149 find_cluster_interface_responsible_for_ip,
150 MACAddress,
151 update_mac_cluster_interfaces,
152+ update_macs_cluster_interfaces,
153 )
154 from maasserver.models.network import Network
155 from maasserver.testing.factory import factory
156@@ -732,42 +733,44 @@
157 self.assertItemsEqual([my_interface], my_mac.get_cluster_interfaces())
158
159
160+def make_cluster_with_macs_and_leases(use_static_range=False):
161+ cluster = factory.make_NodeGroup()
162+ mac_addresses = {
163+ factory.make_MACAddress_with_Node():
164+ factory.make_NodeGroupInterface(nodegroup=cluster)
165+ for _ in range(4)
166+ }
167+ leases = {
168+ get_random_ip_from_interface_range(interface, use_static_range): (
169+ mac_address.mac_address)
170+ for mac_address, interface in mac_addresses.viewitems()
171+ }
172+ return cluster, mac_addresses, leases
173+
174+
175+def make_cluster_with_mac_and_node_and_ip(use_static_range=False):
176+ cluster = factory.make_NodeGroup()
177+ mac_address = factory.make_MACAddress_with_Node()
178+ interface = factory.make_NodeGroupInterface(nodegroup=cluster)
179+ ip = get_random_ip_from_interface_range(interface, use_static_range)
180+ return cluster, interface, mac_address, ip
181+
182+
183 class TestUpdateMacClusterInterfaces(MAASServerTestCase):
184 """Tests for `update_mac_cluster_interfaces`()."""
185
186- def make_cluster_with_macs_and_leases(self, use_static_range=False):
187- cluster = factory.make_NodeGroup()
188- mac_addresses = {
189- factory.make_MACAddress_with_Node():
190- factory.make_NodeGroupInterface(nodegroup=cluster)
191- for _ in range(4)
192- }
193- leases = {
194- get_random_ip_from_interface_range(interface, use_static_range): (
195- mac_address.mac_address)
196- for mac_address, interface in mac_addresses.viewitems()
197- }
198- return cluster, mac_addresses, leases
199-
200- def make_cluster_with_mac_and_node_and_ip(self, use_static_range=False):
201- cluster = factory.make_NodeGroup()
202- mac_address = factory.make_MACAddress_with_Node()
203- interface = factory.make_NodeGroupInterface(nodegroup=cluster)
204- ip = get_random_ip_from_interface_range(interface, use_static_range)
205- return cluster, interface, mac_address, ip
206-
207 def test_updates_mac_cluster_interfaces(self):
208 cluster, interface, mac_address, ip = (
209- self.make_cluster_with_mac_and_node_and_ip())
210+ make_cluster_with_mac_and_node_and_ip())
211 update_mac_cluster_interfaces(ip, mac_address.mac_address, cluster)
212 mac_address = reload_object(mac_address)
213 self.assertEqual(interface, mac_address.cluster_interface)
214
215 def test_considers_static_range_when_updating_interfaces(self):
216 cluster, mac_addresses, leases = (
217- self.make_cluster_with_macs_and_leases(use_static_range=True))
218+ make_cluster_with_macs_and_leases(use_static_range=True))
219 cluster, interface, mac_address, ip = (
220- self.make_cluster_with_mac_and_node_and_ip(use_static_range=True))
221+ make_cluster_with_mac_and_node_and_ip(use_static_range=True))
222 update_mac_cluster_interfaces(ip, mac_address.mac_address, cluster)
223 mac_address = reload_object(mac_address)
224 self.assertEqual(interface, mac_address.cluster_interface)
225@@ -776,9 +779,9 @@
226 # update_mac_cluster_interfaces should also associate the mac
227 # with the network on which it resides.
228 cluster, mac_addresses, leases = (
229- self.make_cluster_with_macs_and_leases())
230+ make_cluster_with_macs_and_leases())
231 cluster, interface, mac_address, ip = (
232- self.make_cluster_with_mac_and_node_and_ip())
233+ make_cluster_with_mac_and_node_and_ip())
234 net = create_Network_from_NodeGroupInterface(interface)
235 update_mac_cluster_interfaces(ip, mac_address.mac_address, cluster)
236 [observed_macddress] = net.macaddress_set.all()
237@@ -835,3 +838,30 @@
238 # The real test is that update_mac_cluster_interfaces() doesn't
239 # stacktrace because of the unconfigured interface (see bug
240 # 1332596).
241+
242+
243+class TestUpdateMacsClusterInterfaces(MAASServerTestCase):
244+ """Tests for `update_macs_cluster_interfaces`()."""
245+
246+ def test_updates_macs_cluster_interfaces(self):
247+ cluster, mac_addresses, leases = (
248+ make_cluster_with_macs_and_leases())
249+ update_macs_cluster_interfaces(leases, cluster)
250+ interfaces = [inter for _, inter in mac_addresses.items()]
251+ linked_interfaces = (
252+ [reload_object(mac).cluster_interface
253+ for mac, _ in mac_addresses.items()])
254+ self.assertEquals(interfaces, linked_interfaces)
255+
256+ def test_ignores_unknown_macs(self):
257+ mac_address = factory.make_mac_address()
258+ ip = factory.make_ipv4_address()
259+ cluster, mac_addresses, leases = (
260+ make_cluster_with_macs_and_leases())
261+ leases[ip] = mac_address
262+ update_macs_cluster_interfaces(leases, cluster)
263+ interfaces = [inter for _, inter in mac_addresses.items()]
264+ linked_interfaces = (
265+ [reload_object(mac).cluster_interface
266+ for mac, _ in mac_addresses.items()])
267+ self.assertEquals(interfaces, linked_interfaces)