Merge ~blake-rouse/maas:fix-1830103 into maas:master

Proposed by Blake Rouse
Status: Merged
Approved by: Newell Jensen
Approved revision: 3d3013ae0a61cba4037afd8e0547bc71423ece60
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~blake-rouse/maas:fix-1830103
Merge into: maas:master
Diff against target: 151 lines (+123/-0)
3 files modified
src/maasserver/models/signals/__init__.py (+2/-0)
src/maasserver/models/signals/subnet.py (+55/-0)
src/maasserver/models/signals/tests/test_subnet.py (+66/-0)
Reviewer Review Type Date Requested Status
Newell Jensen (community) Approve
Review via email: mp+367857@code.launchpad.net

Commit message

Fixes LP: #1830103 - Update StaticIPAddress subnet links for when a subnet is created or updated where that IP address now falls with in that subnet CIDR.

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

Looks good.

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/models/signals/__init__.py b/src/maasserver/models/signals/__init__.py
2index 4f020cd..2f6c1b9 100644
3--- a/src/maasserver/models/signals/__init__.py
4+++ b/src/maasserver/models/signals/__init__.py
5@@ -22,6 +22,7 @@ __all__ = [
6 "scriptresult",
7 "services",
8 "staticipaddress",
9+ "subnet",
10 ]
11
12 from maasserver.models.signals import (
13@@ -43,4 +44,5 @@ from maasserver.models.signals import (
14 scriptresult,
15 services,
16 staticipaddress,
17+ subnet,
18 )
19diff --git a/src/maasserver/models/signals/subnet.py b/src/maasserver/models/signals/subnet.py
20new file mode 100644
21index 0000000..e32dfde
22--- /dev/null
23+++ b/src/maasserver/models/signals/subnet.py
24@@ -0,0 +1,55 @@
25+# Copyright 2019 Canonical Ltd. This software is licensed under the
26+# GNU Affero General Public License version 3 (see the file LICENSE).
27+
28+"""Respond to Subnet CIDR changes."""
29+
30+__all__ = [
31+ "signals",
32+]
33+
34+from django.db.models.signals import post_save
35+from maasserver.enum import IPADDRESS_TYPE
36+from maasserver.models import (
37+ StaticIPAddress,
38+ Subnet,
39+)
40+from maasserver.utils.signals import SignalsManager
41+
42+
43+signals = SignalsManager()
44+
45+
46+def update_referenced_ip_addresses(subnet):
47+ """Updates the `StaticIPAddress`'s to ensure that they are linked to the
48+ correct subnet."""
49+
50+ # Remove the IP addresses that no longer fall with in the CIDR.
51+ remove_ips = StaticIPAddress.objects.filter(
52+ alloc_type=IPADDRESS_TYPE.USER_RESERVED, subnet_id=subnet.id)
53+ remove_ips = remove_ips.extra(
54+ where=['NOT(ip << %s)'], params=[subnet.cidr])
55+ remove_ips.update(subnet=None)
56+
57+ # Add the IP addresses that now fall into CIDR.
58+ add_ips = StaticIPAddress.objects.filter(subnet__isnull=True)
59+ add_ips = add_ips.extra(
60+ where=['ip << %s'], params=[subnet.cidr])
61+ add_ips.update(subnet_id=subnet.id)
62+
63+
64+def post_created(sender, instance, created, **kwargs):
65+ if created:
66+ update_referenced_ip_addresses(instance)
67+
68+
69+def updated_cidr(instance, old_values, **kwargs):
70+ update_referenced_ip_addresses(instance)
71+
72+
73+signals.watch(
74+ post_save, post_created, sender=Subnet)
75+signals.watch_fields(
76+ updated_cidr, Subnet, ['cidr'], delete=False)
77+
78+# Enable all signals by default.
79+signals.enable()
80diff --git a/src/maasserver/models/signals/tests/test_subnet.py b/src/maasserver/models/signals/tests/test_subnet.py
81new file mode 100644
82index 0000000..200acf7
83--- /dev/null
84+++ b/src/maasserver/models/signals/tests/test_subnet.py
85@@ -0,0 +1,66 @@
86+# Copyright 2019 Canonical Ltd. This software is licensed under the
87+# GNU Affero General Public License version 3 (see the file LICENSE).
88+
89+"""Test the behaviour of subnet signals."""
90+
91+__all__ = []
92+
93+from maasserver.enum import IPADDRESS_TYPE
94+from maasserver.testing.factory import factory
95+from maasserver.testing.testcase import MAASServerTestCase
96+
97+
98+class TestSubnetSignals(MAASServerTestCase):
99+
100+ scenarios = (
101+ ('ipv4', {
102+ 'network_maker': factory.make_ipv4_network,
103+ }),
104+ ('ipv6', {
105+ 'network_maker': factory.make_ipv6_network,
106+ }),
107+ )
108+
109+ def test_creating_subnet_links_to_existing_ip_address(self):
110+ network = self.network_maker()
111+ ip = factory.pick_ip_in_network(network)
112+ ip_address = factory.make_StaticIPAddress(
113+ ip=ip, alloc_type=IPADDRESS_TYPE.USER_RESERVED)
114+
115+ # Ensure that for this test to really be testing the logic the
116+ # `StaticIPAddress` needs to not have a subnet assigned.
117+ self.assertIsNone(ip_address.subnet)
118+
119+ # Creating the subnet, must link the created `StaticIPAddress` to
120+ # that subnet.
121+ subnet = factory.make_Subnet(cidr=network.cidr)
122+ ip_address.refresh_from_db()
123+ self.assertEqual(subnet, ip_address.subnet)
124+
125+ def test_updating_subnet_removes_existing_ip_address_adds_another(self):
126+ network1 = self.network_maker()
127+ network2 = self.network_maker(but_not=network1)
128+ ip1 = factory.pick_ip_in_network(network1)
129+ ip2 = factory.pick_ip_in_network(network2)
130+
131+ # Create the second IP address not linked to network2.
132+ ip_address2 = factory.make_StaticIPAddress(
133+ ip=ip2, alloc_type=IPADDRESS_TYPE.USER_RESERVED)
134+ self.assertIsNone(ip_address2.subnet)
135+
136+ # Create the first IP address assigned to the network.
137+ subnet = factory.make_Subnet(cidr=network1.cidr)
138+ ip_address1 = factory.make_StaticIPAddress(
139+ ip=ip1, alloc_type=IPADDRESS_TYPE.USER_RESERVED, subnet=subnet)
140+ self.assertEqual(subnet, ip_address1.subnet)
141+
142+ # Update the subnet to have the CIDR of network2.
143+ subnet.cidr = network2.cidr
144+ subnet.gateway_ip = None
145+ subnet.save()
146+
147+ # IP1 should not have a subnet, and IP2 should not have the subnet.
148+ ip_address1.refresh_from_db()
149+ ip_address2.refresh_from_db()
150+ self.assertIsNone(ip_address1.subnet)
151+ self.assertEqual(subnet, ip_address2.subnet)

Subscribers

People subscribed via source and target branches