Merge lp:~lamont/maas/bug-1513485 into lp:~maas-committers/maas/trunk

Proposed by LaMont Jones
Status: Merged
Approved by: LaMont Jones
Approved revision: no longer in the source branch.
Merged at revision: 4490
Proposed branch: lp:~lamont/maas/bug-1513485
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 67 lines (+42/-4)
2 files modified
src/maasserver/models/staticipaddress.py (+13/-4)
src/maasserver/models/tests/test_staticipaddress.py (+29/-0)
To merge this branch: bzr merge lp:~lamont/maas/bug-1513485
Reviewer Review Type Date Requested Status
Mike Pontillo (community) Approve
Review via email: mp+277267@code.launchpad.net

Commit message

Fix handling of multiple StaticIPAddress rows with empty IP addresses. In some circumstances, (possibly migration from 1.8, or other unknown reasons) there can be multiple StaticIPAddress entries for a subnet with no IP address. Previously, this caused the code to crash on a get_or_create() call.

Description of the change

Migration means that sometimes, there are multiple empty StaticIPAddress entries for a subnet.

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

I'm going to approve this since it's a pretty simple change, but please see address my comments below before landing this.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/models/staticipaddress.py'
2--- src/maasserver/models/staticipaddress.py 2015-11-02 15:48:12 +0000
3+++ src/maasserver/models/staticipaddress.py 2015-11-12 13:57:06 +0000
4@@ -470,10 +470,19 @@
5 ipaddress.interface_set.exclude(mac_address__in=mac_list))
6 if len(other_interfaces) > 0:
7 # Get or create an empty DISCOVERED IP address for these
8- # other interfaces, linked to the old subnet.
9- empty_ip, _ = StaticIPAddress.objects.get_or_create(
10- alloc_type=IPADDRESS_TYPE.DISCOVERED, ip=None,
11- subnet=ipaddress.subnet)
12+ # other interfaces, linked to the old subnet. if we have
13+ # migrated from 1.8 to 1.9, it's possible to have more
14+ # than one empty_ip for the subnet. Use the first one if
15+ # it is there, if it isn't, then let get_or_create make
16+ # one for us.
17+ empty_ip = StaticIPAddress.objects.filter(
18+ ip=None,
19+ alloc_type=IPADDRESS_TYPE.DISCOVERED,
20+ subnet=ipaddress.subnet).first()
21+ if empty_ip is None:
22+ empty_ip, _ = StaticIPAddress.objects.get_or_create(
23+ alloc_type=IPADDRESS_TYPE.DISCOVERED, ip=None,
24+ subnet=ipaddress.subnet)
25 for other_interface in other_interfaces:
26 other_interface.ip_addresses.remove(ipaddress)
27 other_interface.ip_addresses.add(empty_ip)
28
29=== modified file 'src/maasserver/models/tests/test_staticipaddress.py'
30--- src/maasserver/models/tests/test_staticipaddress.py 2015-11-02 15:57:19 +0000
31+++ src/maasserver/models/tests/test_staticipaddress.py 2015-11-12 13:57:06 +0000
32@@ -395,6 +395,35 @@
33 self.assertIsNone(extra_ip.ip)
34 self.assertEquals(subnet, lease_ip.subnet)
35
36+ def test_update_leases_handles_multiple_empty_ips(self):
37+ cidr = unicode(factory.make_ipv4_network().cidr)
38+ node = factory.make_Node_with_Interface_on_Subnet(cidr=cidr)
39+ boot_interface = node.get_boot_interface()
40+ ip_address = boot_interface.ip_addresses.first()
41+ subnet = ip_address.subnet
42+ ngi = subnet.nodegroupinterface_set.first()
43+ # Create a pre-1.9 condition in the StaticIPAddress table.
44+ factory.make_StaticIPAddress(
45+ alloc_type=IPADDRESS_TYPE.DISCOVERED, ip=None, subnet=subnet)
46+ discovered_ip = unicode(IPAddress(ngi.get_dynamic_ip_range()[0]))
47+ macs = [factory.make_mac_address() for i in range(2)]
48+ StaticIPAddress.objects.update_leases(
49+ node.nodegroup, [(discovered_ip, macs[0])])
50+ # Now move to the new MAC, and ensure that the table is correctly
51+ # updated, even when multiple empty IP addresses are in the table.
52+ # (See also bug #1513485).
53+ StaticIPAddress.objects.update_leases(
54+ node.nodegroup, [(discovered_ip, macs[1])])
55+ new_ips = StaticIPAddress.objects.filter(ip=discovered_ip)
56+ self.assertEqual(1, len(new_ips))
57+ self.assertEquals(
58+ IPADDRESS_TYPE.DISCOVERED, new_ips[0].alloc_type)
59+ self.assertEquals(
60+ INTERFACE_TYPE.UNKNOWN, new_ips[0].interface_set.first().type)
61+ empty_ips = StaticIPAddress.objects.filter(
62+ ip=None, alloc_type=IPADDRESS_TYPE.DISCOVERED, subnet=subnet)
63+ self.assertEqual(2, len(empty_ips))
64+
65 def test_update_leases_only_keeps_one_DISCOVERED_address(self):
66 vlan = VLAN.objects.get_default_vlan()
67 node = factory.make_Node_with_Interface_on_Subnet(vlan=vlan)