Merge lp:~blake-rouse/maas/fix-1403909-1.7 into lp:maas/1.7

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: no longer in the source branch.
Merged at revision: 3345
Proposed branch: lp:~blake-rouse/maas/fix-1403909-1.7
Merge into: lp:maas/1.7
Diff against target: 192 lines (+88/-34)
2 files modified
src/maasserver/models/node.py (+18/-5)
src/maasserver/models/tests/test_node.py (+70/-29)
To merge this branch: bzr merge lp:~blake-rouse/maas/fix-1403909-1.7
Reviewer Review Type Date Requested Status
Andres Rodriguez (community) Approve
Review via email: mp+250758@code.launchpad.net

Commit message

Don't remove static ip address assignment from node until it has been completely powered off.

To post a comment you must log in.
Revision history for this message
Andres Rodriguez (andreserl) wrote :

lgtm!

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/node.py'
2--- src/maasserver/models/node.py 2014-12-15 10:22:46 +0000
3+++ src/maasserver/models/node.py 2015-02-24 14:32:03 +0000
4@@ -1216,6 +1216,7 @@
5 unicode(ex))
6 raise
7
8+ deallocate_ip_address = True
9 if self.power_state == POWER_STATE.OFF:
10 # Node is already off.
11 self.status = NODE_STATUS.READY
12@@ -1223,7 +1224,9 @@
13 elif self.get_effective_power_info().can_be_queried:
14 # Controlled power type (one for which we can query the power
15 # state): update_power_state() will take care of making the node
16- # READY and unowned when the power is finally off.
17+ # READY, remove the owned, and deallocate the assigned static ip
18+ # address when the power is finally off.
19+ deallocate_ip_address = False
20 self.status = NODE_STATUS.RELEASING
21 self.start_transition_monitor(self.get_releasing_time())
22 else:
23@@ -1247,10 +1250,8 @@
24
25 # Do these after updating the node to avoid creating deadlocks with
26 # other node editing operations.
27- deallocated_ips = StaticIPAddress.objects.deallocate_by_node(self)
28- self.delete_host_maps(deallocated_ips)
29- from maasserver.dns.config import change_dns_zones
30- change_dns_zones([self.nodegroup])
31+ if deallocate_ip_address:
32+ self.deallocate_static_ip_addresses()
33
34 # We explicitly commit here because during bulk node actions we
35 # want to make sure that each successful state transition is
36@@ -1358,6 +1359,7 @@
37 self.status = NODE_STATUS.READY
38 self.owner = None
39 self.stop_transition_monitor()
40+ self.deallocate_static_ip_addresses()
41 self.save()
42
43 def claim_static_ip_addresses(self):
44@@ -1381,6 +1383,17 @@
45 # because it's all-or-nothing (hence the atomic context).
46 return [(static_ip.ip, unicode(mac)) for static_ip in static_ips]
47
48+ def deallocate_static_ip_addresses(self):
49+ """Release the `StaticIPAddress` that is assigned to this node and
50+ remove the host mapping on the cluster.
51+
52+ This should only be done when the node is in an unused state.
53+ """
54+ deallocated_ips = StaticIPAddress.objects.deallocate_by_node(self)
55+ self.delete_host_maps(deallocated_ips)
56+ from maasserver.dns.config import change_dns_zones
57+ change_dns_zones([self.nodegroup])
58+
59 def get_boot_purpose(self):
60 """
61 Return a suitable "purpose" for this boot, e.g. "install".
62
63=== modified file 'src/maasserver/models/tests/test_node.py'
64--- src/maasserver/models/tests/test_node.py 2014-12-10 18:04:44 +0000
65+++ src/maasserver/models/tests/test_node.py 2015-02-24 14:32:03 +0000
66@@ -54,10 +54,7 @@
67 PowerInfo,
68 validate_hostname,
69 )
70-from maasserver.models.staticipaddress import (
71- StaticIPAddress,
72- StaticIPAddressManager,
73- )
74+from maasserver.models.staticipaddress import StaticIPAddress
75 from maasserver.models.user import create_auth_token
76 from maasserver.node_status import (
77 get_failed_status,
78@@ -918,20 +915,6 @@
79 self.expectThat(node.distro_series, Equals(''))
80 self.expectThat(node.license_key, Equals(''))
81
82- def test_release_deletes_static_ip_host_maps(self):
83- remove_host_maps = self.patch_autospec(
84- node_module, "remove_host_maps")
85- user = factory.make_User()
86- node = factory.make_node_with_mac_attached_to_nodegroupinterface(
87- owner=user, status=NODE_STATUS.ALLOCATED)
88- self.patch(node, 'start_transition_monitor')
89- sips = node.get_primary_mac().claim_static_ips()
90- node.release()
91- expected = {sip.ip.format() for sip in sips}
92- self.assertThat(
93- remove_host_maps, MockCalledOnceWith(
94- {node.nodegroup: expected}))
95-
96 def test_release_clears_installation_results(self):
97 agent_name = factory.make_name('agent-name')
98 owner = factory.make_User()
99@@ -1170,17 +1153,57 @@
100 self.assertThat(
101 node_stop, MockCalledOnceWith(user))
102
103- def test_release_deallocates_static_ips(self):
104- deallocate = self.patch(StaticIPAddressManager, 'deallocate_by_node')
105- deallocate.return_value = set()
106- node = factory.make_Node(
107- status=NODE_STATUS.ALLOCATED, owner=factory.make_User(),
108- power_type='ether_wake')
109- node.release()
110- self.assertThat(deallocate, MockCalledOnceWith(node))
111-
112- def test_release_updates_dns(self):
113- self.patch(node_module, 'wait_for_power_command')
114+ def test_release_deallocates_static_ip_when_node_is_off(self):
115+ user = factory.make_User()
116+ node = factory.make_node_with_mac_attached_to_nodegroupinterface(
117+ owner=user, status=NODE_STATUS.ALLOCATED,
118+ power_state=POWER_STATE.OFF)
119+ deallocate_static_ip_addresses = self.patch_autospec(
120+ node, "deallocate_static_ip_addresses")
121+ self.patch(node, 'start_transition_monitor')
122+ node.release()
123+ self.assertThat(
124+ deallocate_static_ip_addresses, MockCalledOnceWith())
125+
126+ def test_release_deallocates_static_ip_when_node_cannot_be_queried(self):
127+ user = factory.make_User()
128+ node = factory.make_node_with_mac_attached_to_nodegroupinterface(
129+ owner=user, status=NODE_STATUS.ALLOCATED,
130+ power_state=POWER_STATE.ON, power_type='ether_wake')
131+ deallocate_static_ip_addresses = self.patch_autospec(
132+ node, "deallocate_static_ip_addresses")
133+ self.patch(node, 'start_transition_monitor')
134+ node.release()
135+ self.assertThat(
136+ deallocate_static_ip_addresses, MockCalledOnceWith())
137+
138+ def test_release_doesnt_deallocate_static_ip_when_node_releasing(self):
139+ user = factory.make_User()
140+ node = factory.make_node_with_mac_attached_to_nodegroupinterface(
141+ owner=user, status=NODE_STATUS.ALLOCATED,
142+ power_state=POWER_STATE.ON, power_type='virsh')
143+ deallocate_static_ip_addresses = self.patch_autospec(
144+ node, "deallocate_static_ip_addresses")
145+ self.patch_autospec(node, 'stop')
146+ self.patch(node, 'start_transition_monitor')
147+ node.release()
148+ self.assertThat(
149+ deallocate_static_ip_addresses, MockNotCalled())
150+
151+ def test_deallocate_static_ip_deletes_static_ip_host_maps(self):
152+ remove_host_maps = self.patch_autospec(
153+ node_module, "remove_host_maps")
154+ user = factory.make_User()
155+ node = factory.make_node_with_mac_attached_to_nodegroupinterface(
156+ owner=user, status=NODE_STATUS.ALLOCATED)
157+ sips = node.get_primary_mac().claim_static_ips()
158+ node.release()
159+ expected = {sip.ip.format() for sip in sips}
160+ self.assertThat(
161+ remove_host_maps, MockCalledOnceWith(
162+ {node.nodegroup: expected}))
163+
164+ def test_deallocate_static_ip_updates_dns(self):
165 change_dns_zones = self.patch(dns_config, 'change_dns_zones')
166 nodegroup = factory.make_NodeGroup(
167 management=NODEGROUPINTERFACE_MANAGEMENT.DHCP_AND_DNS,
168@@ -1706,6 +1729,24 @@
169 node.update_power_state(POWER_STATE.ON)
170 self.expectThat(node.status, Equals(NODE_STATUS.ALLOCATED))
171
172+ def test_update_power_state_deallocates_static_ips_if_releasing(self):
173+ node = factory.make_Node(
174+ power_state=POWER_STATE.ON, status=NODE_STATUS.RELEASING,
175+ owner=None)
176+ deallocate_static_ip_addresses = self.patch_autospec(
177+ node, 'deallocate_static_ip_addresses')
178+ self.patch(node, 'stop_transition_monitor')
179+ node.update_power_state(POWER_STATE.OFF)
180+ self.assertThat(deallocate_static_ip_addresses, MockCalledOnceWith())
181+
182+ def test_update_power_state_doesnt_deallocates_static_ips_if_not_off(self):
183+ node = factory.make_Node(
184+ power_state=POWER_STATE.OFF, status=NODE_STATUS.ALLOCATED)
185+ deallocate_static_ip_addresses = self.patch_autospec(
186+ node, 'deallocate_static_ip_addresses')
187+ node.update_power_state(POWER_STATE.ON)
188+ self.assertThat(deallocate_static_ip_addresses, MockNotCalled())
189+
190 def test_end_deployment_changes_state(self):
191 node = factory.make_Node(status=NODE_STATUS.DEPLOYING)
192 node.end_deployment()

Subscribers

People subscribed via source and target branches

to all changes: