Merge lp:~trapnine/maas/dynamic_range_overlaps_discovered_ip_1580772 into lp:~maas-committers/maas/trunk

Proposed by Jeffrey C Jones
Status: Merged
Approved by: Jeffrey C Jones
Approved revision: no longer in the source branch.
Merged at revision: 5097
Proposed branch: lp:~trapnine/maas/dynamic_range_overlaps_discovered_ip_1580772
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 162 lines (+69/-31)
2 files modified
src/maasserver/models/subnet.py (+14/-17)
src/maasserver/models/tests/test_iprange.py (+55/-14)
To merge this branch: bzr merge lp:~trapnine/maas/dynamic_range_overlaps_discovered_ip_1580772
Reviewer Review Type Date Requested Status
Mike Pontillo (community) Approve
Review via email: mp+297010@code.launchpad.net

Commit message

Dynamic ranges can overlap DISCOVERED IPs.

Description of the change

Dynamic ranges can overlap DISCOVERED IPs.

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

Looks good. Thanks for the fixes.

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/subnet.py'
2--- src/maasserver/models/subnet.py 2016-06-02 18:16:20 +0000
3+++ src/maasserver/models/subnet.py 2016-06-09 23:39:12 +0000
4@@ -27,6 +27,7 @@
5 from django.db.models.query import QuerySet
6 from maasserver import DefaultMeta
7 from maasserver.enum import (
8+ IPADDRESS_TYPE,
9 IPRANGE_TYPE,
10 RDNS_MODE,
11 RDNS_MODE_CHOICES,
12@@ -401,20 +402,21 @@
13 "IP range. (Delete the dynamic range or disable DHCP first.)")
14 super().delete(*args, **kwargs)
15
16- def get_staticipaddresses_in_use(self):
17- """Returns a list of `netaddr.IPAddress` objects to represent each
18- IP address in use in this `Subnet`."""
19- # We could exclude DISCOVERED addresses here, but that wouldn't be
20- # genuine. (we'd be allowing something we have observed as an in-use
21- # address to potentially be claimed for something else, which could
22- # be a conflict.)
23+ def _get_ranges_for_allocated_ips(self, ipnetwork):
24+ """Returns a set of IPrange objects created from the set of allocated
25+ StaticIPAddress objects.."""
26+ # We exclude DISCOVERED addresses here because they aren't considered
27+ # allocated ranges, and new ranges can overlap them - lp:1580772).
28 # Note, the original implementation used .exclude() to filter,
29 # but we'll filter at runtime so that prefetch_related in the
30 # websocket works properly.
31- return set(
32- IPAddress(ip.ip)
33- for ip in self.staticipaddress_set.all()
34- if ip.ip)
35+ ranges = set()
36+ for sip in self.staticipaddress_set.all():
37+ if sip.ip and sip.alloc_type != IPADDRESS_TYPE.DISCOVERED:
38+ ip = IPAddress(sip.ip)
39+ if ip in ipnetwork:
40+ ranges.add(make_iprange(ip, purpose="assigned-ip"))
41+ return ranges
42
43 def get_ipranges_in_use(self, exclude_addresses=[], ranges_only=False):
44 """Returns a `MAASIPSet` of `MAASIPRange` objects which are currently
45@@ -447,7 +449,6 @@
46 first, first, purpose="rfc-4291-2.6.1")}
47 ipnetwork = self.get_ipnetwork()
48 if not ranges_only:
49- assigned_ip_addresses = self.get_staticipaddresses_in_use()
50 if (self.gateway_ip is not None and self.gateway_ip != '' and
51 self.gateway_ip in ipnetwork):
52 ranges |= {make_iprange(self.gateway_ip, purpose="gateway-ip")}
53@@ -457,11 +458,7 @@
54 for server in self.dns_servers
55 if server in ipnetwork
56 )
57- ranges |= set(
58- make_iprange(ip, purpose="assigned-ip")
59- for ip in assigned_ip_addresses
60- if ip in ipnetwork
61- )
62+ ranges |= self._get_ranges_for_allocated_ips(ipnetwork)
63 ranges |= set(
64 make_iprange(address, purpose="excluded")
65 for address in exclude_addresses
66
67=== modified file 'src/maasserver/models/tests/test_iprange.py'
68--- src/maasserver/models/tests/test_iprange.py 2016-05-12 19:07:37 +0000
69+++ src/maasserver/models/tests/test_iprange.py 2016-06-09 23:39:12 +0000
70@@ -5,6 +5,8 @@
71
72 __all__ = []
73
74+import random
75+
76 from django.core.exceptions import ValidationError
77 from maasserver.enum import (
78 IPADDRESS_TYPE,
79@@ -432,23 +434,15 @@
80 with ExpectedException(ValidationError, self.reserved_no_fit):
81 iprange.save()
82
83- def test__dynamic_range_cannot_overlap_static_ip(self):
84+ def test__reserved_range_can_overlap_most_ip_types(self):
85 subnet = make_plain_subnet()
86 factory.make_StaticIPAddress(
87- alloc_type=IPADDRESS_TYPE.USER_RESERVED, subnet=subnet)
88- iprange = IPRange(
89 subnet=subnet,
90- type=IPRANGE_TYPE.DYNAMIC,
91- start_ip="192.168.0.2",
92- end_ip="192.168.0.254",
93- )
94- with ExpectedException(ValidationError, self.dynamic_overlaps):
95- iprange.save()
96-
97- def test__reserved_range_can_overlap_static_ip(self):
98- subnet = make_plain_subnet()
99- factory.make_StaticIPAddress(
100- alloc_type=IPADDRESS_TYPE.USER_RESERVED, subnet=subnet)
101+ alloc_type=random.choice((
102+ IPADDRESS_TYPE.AUTO,
103+ IPADDRESS_TYPE.STICKY,
104+ IPADDRESS_TYPE.USER_RESERVED,
105+ IPADDRESS_TYPE.DISCOVERED)))
106 iprange = IPRange(
107 subnet=subnet,
108 type=IPRANGE_TYPE.RESERVED,
109@@ -457,6 +451,53 @@
110 )
111 iprange.save()
112
113+ def test__dynamic_range_cannot_overlap_most_ip_types(self):
114+ subnet = make_plain_subnet()
115+ factory.make_StaticIPAddress(
116+ subnet=subnet,
117+ alloc_type=random.choice((
118+ IPADDRESS_TYPE.AUTO,
119+ IPADDRESS_TYPE.STICKY,
120+ IPADDRESS_TYPE.USER_RESERVED)))
121+ iprange = IPRange(
122+ subnet=subnet,
123+ type=IPRANGE_TYPE.DYNAMIC,
124+ start_ip="192.168.0.2",
125+ end_ip="192.168.0.254",
126+ )
127+ with ExpectedException(ValidationError, self.dynamic_overlaps):
128+ iprange.save()
129+
130+ # Regression for lp:1580772.
131+ def test__dynamic_range_can_overlap_discovered_ip(self):
132+ subnet = make_plain_subnet()
133+ factory.make_StaticIPAddress(
134+ ip="192.168.0.3",
135+ alloc_type=IPADDRESS_TYPE.DISCOVERED,
136+ subnet=subnet)
137+ iprange = IPRange(
138+ subnet=subnet,
139+ type=IPRANGE_TYPE.DYNAMIC,
140+ start_ip="192.168.0.2",
141+ end_ip="192.168.0.254",
142+ )
143+ iprange.save()
144+
145+ # Regression for lp:1580772.
146+ def test__dynamic_range_can_match_discovered_ip(self):
147+ subnet = make_plain_subnet()
148+ factory.make_StaticIPAddress(
149+ ip="192.168.0.3",
150+ alloc_type=IPADDRESS_TYPE.DISCOVERED,
151+ subnet=subnet)
152+ iprange = IPRange(
153+ subnet=subnet,
154+ type=IPRANGE_TYPE.DYNAMIC,
155+ start_ip="192.168.0.3",
156+ end_ip="192.168.0.3",
157+ )
158+ iprange.save()
159+
160 def test__dynamic_range_cannot_overlap_dns_servers(self):
161 subnet = factory.make_Subnet(
162 cidr='192.168.0.0/24',