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

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: no longer in the source branch.
Merged at revision: 3438
Proposed branch: lp:~blake-rouse/maas/fix-1403909
Merge into: lp:~maas-committers/maas/trunk
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
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Approve
Review via email: mp+245253@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
Raphaël Badin (rvb) wrote :

Looks good!

review: Approve
Revision history for this message
Blake Rouse (blake-rouse) wrote :

Thanks for the review.

I will wait for the CI to be back online to test this before it lands.

Revision history for this message
Blake Rouse (blake-rouse) wrote :

Passed CI.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/models/node.py'
--- src/maasserver/models/node.py 2014-12-15 08:07:20 +0000
+++ src/maasserver/models/node.py 2014-12-19 23:02:23 +0000
@@ -1228,6 +1228,7 @@
1228 unicode(ex))1228 unicode(ex))
1229 raise1229 raise
12301230
1231 deallocate_ip_address = True
1231 if self.power_state == POWER_STATE.OFF:1232 if self.power_state == POWER_STATE.OFF:
1232 # Node is already off.1233 # Node is already off.
1233 self.status = NODE_STATUS.READY1234 self.status = NODE_STATUS.READY
@@ -1235,7 +1236,9 @@
1235 elif self.get_effective_power_info().can_be_queried:1236 elif self.get_effective_power_info().can_be_queried:
1236 # Controlled power type (one for which we can query the power1237 # Controlled power type (one for which we can query the power
1237 # state): update_power_state() will take care of making the node1238 # state): update_power_state() will take care of making the node
1238 # READY and unowned when the power is finally off.1239 # READY, remove the owned, and deallocate the assigned static ip
1240 # address when the power is finally off.
1241 deallocate_ip_address = False
1239 self.status = NODE_STATUS.RELEASING1242 self.status = NODE_STATUS.RELEASING
1240 self.start_transition_monitor(self.get_releasing_time())1243 self.start_transition_monitor(self.get_releasing_time())
1241 else:1244 else:
@@ -1259,10 +1262,8 @@
12591262
1260 # Do these after updating the node to avoid creating deadlocks with1263 # Do these after updating the node to avoid creating deadlocks with
1261 # other node editing operations.1264 # other node editing operations.
1262 deallocated_ips = StaticIPAddress.objects.deallocate_by_node(self)1265 if deallocate_ip_address:
1263 self.delete_host_maps(deallocated_ips)1266 self.deallocate_static_ip_addresses()
1264 from maasserver.dns.config import change_dns_zones
1265 change_dns_zones([self.nodegroup])
12661267
1267 def release_or_erase(self):1268 def release_or_erase(self):
1268 """Either release the node or erase the node then release it, depending1269 """Either release the node or erase the node then release it, depending
@@ -1365,6 +1366,7 @@
1365 self.status = NODE_STATUS.READY1366 self.status = NODE_STATUS.READY
1366 self.owner = None1367 self.owner = None
1367 self.stop_transition_monitor()1368 self.stop_transition_monitor()
1369 self.deallocate_static_ip_addresses()
1368 self.save()1370 self.save()
13691371
1370 def claim_static_ip_addresses(self):1372 def claim_static_ip_addresses(self):
@@ -1388,6 +1390,17 @@
1388 # because it's all-or-nothing (hence the atomic context).1390 # because it's all-or-nothing (hence the atomic context).
1389 return [(static_ip.ip, unicode(mac)) for static_ip in static_ips]1391 return [(static_ip.ip, unicode(mac)) for static_ip in static_ips]
13901392
1393 def deallocate_static_ip_addresses(self):
1394 """Release the `StaticIPAddress` that is assigned to this node and
1395 remove the host mapping on the cluster.
1396
1397 This should only be done when the node is in an unused state.
1398 """
1399 deallocated_ips = StaticIPAddress.objects.deallocate_by_node(self)
1400 self.delete_host_maps(deallocated_ips)
1401 from maasserver.dns.config import change_dns_zones
1402 change_dns_zones([self.nodegroup])
1403
1391 def get_boot_purpose(self):1404 def get_boot_purpose(self):
1392 """1405 """
1393 Return a suitable "purpose" for this boot, e.g. "install".1406 Return a suitable "purpose" for this boot, e.g. "install".
13941407
=== modified file 'src/maasserver/models/tests/test_node.py'
--- src/maasserver/models/tests/test_node.py 2014-12-15 08:07:20 +0000
+++ src/maasserver/models/tests/test_node.py 2014-12-19 23:02:23 +0000
@@ -55,10 +55,7 @@
55 PowerInfo,55 PowerInfo,
56 validate_hostname,56 validate_hostname,
57 )57 )
58from maasserver.models.staticipaddress import (58from maasserver.models.staticipaddress import StaticIPAddress
59 StaticIPAddress,
60 StaticIPAddressManager,
61 )
62from maasserver.models.user import create_auth_token59from maasserver.models.user import create_auth_token
63from maasserver.node_status import (60from maasserver.node_status import (
64 get_failed_status,61 get_failed_status,
@@ -920,20 +917,6 @@
920 self.expectThat(node.distro_series, Equals(''))917 self.expectThat(node.distro_series, Equals(''))
921 self.expectThat(node.license_key, Equals(''))918 self.expectThat(node.license_key, Equals(''))
922919
923 def test_release_deletes_static_ip_host_maps(self):
924 remove_host_maps = self.patch_autospec(
925 node_module, "remove_host_maps")
926 user = factory.make_User()
927 node = factory.make_node_with_mac_attached_to_nodegroupinterface(
928 owner=user, status=NODE_STATUS.ALLOCATED)
929 self.patch(node, 'start_transition_monitor')
930 sips = node.get_primary_mac().claim_static_ips()
931 node.release()
932 expected = {sip.ip.format() for sip in sips}
933 self.assertThat(
934 remove_host_maps, MockCalledOnceWith(
935 {node.nodegroup: expected}))
936
937 def test_release_clears_installation_results(self):920 def test_release_clears_installation_results(self):
938 agent_name = factory.make_name('agent-name')921 agent_name = factory.make_name('agent-name')
939 owner = factory.make_User()922 owner = factory.make_User()
@@ -1172,17 +1155,57 @@
1172 self.assertThat(1155 self.assertThat(
1173 node_stop, MockCalledOnceWith(user))1156 node_stop, MockCalledOnceWith(user))
11741157
1175 def test_release_deallocates_static_ips(self):1158 def test_release_deallocates_static_ip_when_node_is_off(self):
1176 deallocate = self.patch(StaticIPAddressManager, 'deallocate_by_node')1159 user = factory.make_User()
1177 deallocate.return_value = set()1160 node = factory.make_node_with_mac_attached_to_nodegroupinterface(
1178 node = factory.make_Node(1161 owner=user, status=NODE_STATUS.ALLOCATED,
1179 status=NODE_STATUS.ALLOCATED, owner=factory.make_User(),1162 power_state=POWER_STATE.OFF)
1180 power_type='ether_wake')1163 deallocate_static_ip_addresses = self.patch_autospec(
1181 node.release()1164 node, "deallocate_static_ip_addresses")
1182 self.assertThat(deallocate, MockCalledOnceWith(node))1165 self.patch(node, 'start_transition_monitor')
11831166 node.release()
1184 def test_release_updates_dns(self):1167 self.assertThat(
1185 self.patch(node_module, 'wait_for_power_command')1168 deallocate_static_ip_addresses, MockCalledOnceWith())
1169
1170 def test_release_deallocates_static_ip_when_node_cannot_be_queried(self):
1171 user = factory.make_User()
1172 node = factory.make_node_with_mac_attached_to_nodegroupinterface(
1173 owner=user, status=NODE_STATUS.ALLOCATED,
1174 power_state=POWER_STATE.ON, power_type='ether_wake')
1175 deallocate_static_ip_addresses = self.patch_autospec(
1176 node, "deallocate_static_ip_addresses")
1177 self.patch(node, 'start_transition_monitor')
1178 node.release()
1179 self.assertThat(
1180 deallocate_static_ip_addresses, MockCalledOnceWith())
1181
1182 def test_release_doesnt_deallocate_static_ip_when_node_releasing(self):
1183 user = factory.make_User()
1184 node = factory.make_node_with_mac_attached_to_nodegroupinterface(
1185 owner=user, status=NODE_STATUS.ALLOCATED,
1186 power_state=POWER_STATE.ON, power_type='virsh')
1187 deallocate_static_ip_addresses = self.patch_autospec(
1188 node, "deallocate_static_ip_addresses")
1189 self.patch_autospec(node, 'stop')
1190 self.patch(node, 'start_transition_monitor')
1191 node.release()
1192 self.assertThat(
1193 deallocate_static_ip_addresses, MockNotCalled())
1194
1195 def test_deallocate_static_ip_deletes_static_ip_host_maps(self):
1196 remove_host_maps = self.patch_autospec(
1197 node_module, "remove_host_maps")
1198 user = factory.make_User()
1199 node = factory.make_node_with_mac_attached_to_nodegroupinterface(
1200 owner=user, status=NODE_STATUS.ALLOCATED)
1201 sips = node.get_primary_mac().claim_static_ips()
1202 node.release()
1203 expected = {sip.ip.format() for sip in sips}
1204 self.assertThat(
1205 remove_host_maps, MockCalledOnceWith(
1206 {node.nodegroup: expected}))
1207
1208 def test_deallocate_static_ip_updates_dns(self):
1186 change_dns_zones = self.patch(dns_config, 'change_dns_zones')1209 change_dns_zones = self.patch(dns_config, 'change_dns_zones')
1187 nodegroup = factory.make_NodeGroup(1210 nodegroup = factory.make_NodeGroup(
1188 management=NODEGROUPINTERFACE_MANAGEMENT.DHCP_AND_DNS,1211 management=NODEGROUPINTERFACE_MANAGEMENT.DHCP_AND_DNS,
@@ -1701,6 +1724,24 @@
1701 node.update_power_state(POWER_STATE.ON)1724 node.update_power_state(POWER_STATE.ON)
1702 self.expectThat(node.status, Equals(NODE_STATUS.ALLOCATED))1725 self.expectThat(node.status, Equals(NODE_STATUS.ALLOCATED))
17031726
1727 def test_update_power_state_deallocates_static_ips_if_releasing(self):
1728 node = factory.make_Node(
1729 power_state=POWER_STATE.ON, status=NODE_STATUS.RELEASING,
1730 owner=None)
1731 deallocate_static_ip_addresses = self.patch_autospec(
1732 node, 'deallocate_static_ip_addresses')
1733 self.patch(node, 'stop_transition_monitor')
1734 node.update_power_state(POWER_STATE.OFF)
1735 self.assertThat(deallocate_static_ip_addresses, MockCalledOnceWith())
1736
1737 def test_update_power_state_doesnt_deallocates_static_ips_if_not_off(self):
1738 node = factory.make_Node(
1739 power_state=POWER_STATE.OFF, status=NODE_STATUS.ALLOCATED)
1740 deallocate_static_ip_addresses = self.patch_autospec(
1741 node, 'deallocate_static_ip_addresses')
1742 node.update_power_state(POWER_STATE.ON)
1743 self.assertThat(deallocate_static_ip_addresses, MockNotCalled())
1744
1704 def test_end_deployment_changes_state(self):1745 def test_end_deployment_changes_state(self):
1705 node = factory.make_Node(status=NODE_STATUS.DEPLOYING)1746 node = factory.make_Node(status=NODE_STATUS.DEPLOYING)
1706 node.end_deployment()1747 node.end_deployment()