Merge lp:~julian-edwards/maas/deallocate-static-ip into lp:~maas-committers/maas/trunk

Proposed by Julian Edwards
Status: Merged
Approved by: Julian Edwards
Approved revision: no longer in the source branch.
Merged at revision: 2409
Proposed branch: lp:~julian-edwards/maas/deallocate-static-ip
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 147 lines (+92/-3)
2 files modified
src/maasserver/models/staticipaddress.py (+32/-3)
src/maasserver/models/tests/test_staticipaddress.py (+60/-0)
To merge this branch: bzr merge lp:~julian-edwards/maas/deallocate-static-ip
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Approve
Julian Edwards (community) Abstain
Review via email: mp+221825@code.launchpad.net

Commit message

Add StaticIPAddress.deallocate(), StaticIPAddressManager.deallocate_by_node() and improve StaticIPAddress.__unicode__ to show its type.

To post a comment you must log in.
Revision history for this message
Julian Edwards (julian-edwards) wrote :

So damn trivial I am going to selfie this one.

review: Approve
Revision history for this message
Julian Edwards (julian-edwards) wrote :

Since the lander is borked and canonistack is not responding, I'm going to do some more work in this branch.

Revision history for this message
Julian Edwards (julian-edwards) :
review: Abstain
Revision history for this message
Raphaël Badin (rvb) wrote :

Looks good. Couple of inline comments though.

review: Approve
Revision history for this message
Julian Edwards (julian-edwards) wrote :

Thanks for the review! I replied inline, and implemented some of your suggestions or explained why I rejected the others.

Cheers.

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

There are additional revisions which have not been approved in review. Please seek review and approval of these new revisions.

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 2014-06-03 00:35:04 +0000
3+++ src/maasserver/models/staticipaddress.py 2014-06-05 01:29:18 +0000
4@@ -87,6 +87,13 @@
5 ipaddress.save()
6 return ipaddress
7
8+ def deallocate_by_node(self, node):
9+ """Given a node, deallocate all of its AUTO StaticIPAddresses."""
10+ qs = self.filter(
11+ alloc_type=IPADDRESS_TYPE.AUTO).filter(
12+ macaddress__node=node)
13+ qs.delete()
14+
15
16 class StaticIPAddress(CleanSave, TimestampedModel):
17
18@@ -97,8 +104,10 @@
19 ip = GenericIPAddressField(
20 unique=True, null=False, editable=False, blank=False)
21
22- # The MACIPAddressLink table is used to link IPAddress to
23- # MACAddress. See MACAddress.ip_addresses.
24+ # The MACStaticIPAddressLink table is used to link StaticIPAddress to
25+ # MACAddress. See MACAddress.ip_addresses, and the reverse relation
26+ # self.macaddress_set (which will only ever contain one MAC due to
27+ # the unique FK restriction on the link table).
28
29 alloc_type = IntegerField(
30 editable=False, null=False, blank=False, default=IPADDRESS_TYPE.AUTO)
31@@ -106,4 +115,24 @@
32 objects = StaticIPAddressManager()
33
34 def __unicode__(self):
35- return "<IPAddress %s>" % self.ip
36+ # Attempt to show the symbolic alloc_type name if possible.
37+
38+ # __iter__ does not work here for some reason, so using
39+ # iteritems().
40+ # XXX: convert this into a reverse_map_enum in maasserver.utils.
41+ for k,v in IPADDRESS_TYPE.__dict__.iteritems():
42+ if v == self.alloc_type:
43+ strtype = k
44+ break
45+ else:
46+ # Should never get here, but defensive coding FTW.
47+ strtype = "%s" % self.alloc_type
48+ return "<StaticIPAddress: <%s:type=%s>>" % (self.ip, strtype)
49+
50+ def deallocate(self):
51+ """Mark this IP address as no longer in use.
52+
53+ After return, this object is no longer valid.
54+ """
55+ self.delete()
56+
57
58=== modified file 'src/maasserver/models/tests/test_staticipaddress.py'
59--- src/maasserver/models/tests/test_staticipaddress.py 2014-06-03 00:35:04 +0000
60+++ src/maasserver/models/tests/test_staticipaddress.py 2014-06-05 01:29:18 +0000
61@@ -16,12 +16,14 @@
62
63
64 from django.core.exceptions import ValidationError
65+from maasserver.enum import IPADDRESS_TYPE
66 from maasserver.models.staticipaddress import (
67 StaticIPAddress,
68 StaticIPAddressExhaustion,
69 )
70 from maasserver.testing.factory import factory
71 from maasserver.testing.testcase import MAASServerTestCase
72+from maasserver.utils import map_enum
73 from netaddr import (
74 IPAddress,
75 IPRange,
76@@ -44,9 +46,56 @@
77 StaticIPAddressExhaustion,
78 StaticIPAddress.objects.allocate_new, low, high)
79
80+ def test_deallocate_by_node_removes_addresses(self):
81+ node = factory.make_node()
82+ [mac1, mac2] = [
83+ factory.make_mac_address(node=node) for _ in range(2)]
84+ factory.make_staticipaddress(mac=mac1)
85+ factory.make_staticipaddress(mac=mac2)
86+ StaticIPAddress.objects.deallocate_by_node(node)
87+ # Check the DB is cleared.
88+ self.assertEqual([], list(StaticIPAddress.objects.all()))
89+ # Check the link table is cleared.
90+ self.assertEqual([], list(node.static_ip_addresses()))
91+
92+ def test_deallocate_by_node_ignores_other_nodes(self):
93+ node1 = factory.make_node()
94+ mac1 = factory.make_mac_address(node=node1)
95+ factory.make_staticipaddress(mac=mac1)
96+ node2 = factory.make_node()
97+ mac2 = factory.make_mac_address(node=node2)
98+ ip2 = factory.make_staticipaddress(mac=mac2)
99+
100+ StaticIPAddress.objects.deallocate_by_node(node1)
101+ self.assertItemsEqual([ip2.ip], node2.static_ip_addresses())
102+
103+ def test_deallocate_only_deletes_auto_types(self):
104+ node = factory.make_node()
105+ mac = factory.make_mac_address(node=node)
106+ alloc_types = map_enum(IPADDRESS_TYPE).values()
107+ for alloc_type in alloc_types:
108+ factory.make_staticipaddress(mac=mac, alloc_type=alloc_type)
109+
110+ StaticIPAddress.objects.deallocate_by_node(node)
111+ types_without_auto = set(alloc_types)
112+ types_without_auto.discard(IPADDRESS_TYPE.AUTO)
113+ self.assertItemsEqual(
114+ types_without_auto,
115+ [ip.alloc_type for ip in StaticIPAddress.objects.all()])
116+
117
118 class StaticIPAddressTest(MAASServerTestCase):
119
120+ def test_repr_with_valid_type(self):
121+ actual = "%s" % factory.make_staticipaddress(
122+ ip="10.0.0.1", alloc_type=IPADDRESS_TYPE.AUTO)
123+ self.assertEqual("<StaticIPAddress: <10.0.0.1:type=AUTO>>", actual)
124+
125+ def test_repr_with_invalid_type(self):
126+ actual = "%s" % factory.make_staticipaddress(
127+ ip="10.0.0.1", alloc_type=99999)
128+ self.assertEqual("<StaticIPAddress: <10.0.0.1:type=99999>>", actual)
129+
130 def test_stores_to_database(self):
131 ipaddress = factory.make_staticipaddress()
132 self.assertEqual([ipaddress], list(StaticIPAddress.objects.all()))
133@@ -54,3 +103,14 @@
134 def test_invalid_address_raises_validation_error(self):
135 ip = StaticIPAddress(ip='256.0.0.0.0')
136 self.assertRaises(ValidationError, ip.full_clean)
137+
138+ def test_deallocate_removes_object(self):
139+ ipaddress = factory.make_staticipaddress()
140+ ipaddress.deallocate()
141+ self.assertEqual([], list(StaticIPAddress.objects.all()))
142+
143+ def test_deallocate_ignores_other_objects(self):
144+ ipaddress = factory.make_staticipaddress()
145+ ipaddress2 = factory.make_staticipaddress()
146+ ipaddress.deallocate()
147+ self.assertEqual([ipaddress2], list(StaticIPAddress.objects.all()))