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