diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 0e75b05f423a..5cbcd532c4ac 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -73,6 +73,7 @@ from nova.openstack.common import log as logging from nova.openstack.common import periodic_task from nova.openstack.common import rpc from nova.openstack.common.rpc import common as rpc_common +from nova.openstack.common import strutils from nova.openstack.common import timeutils from nova import paths from nova import safe_utils @@ -1238,6 +1239,12 @@ class ComputeManager(manager.Manager): dhcp_options=dhcp_options) LOG.debug(_('Instance network_info: |%s|'), nwinfo, instance=instance) + # NOTE(alaski): This can be done more cleanly once we're sure + # we'll receive an object. + sys_meta = utils.metadata_to_dict(instance['system_metadata']) + sys_meta['network_allocated'] = 'True' + self._instance_update(context, instance['uuid'], + system_metadata=sys_meta) return nwinfo except Exception: exc_info = sys.exc_info() @@ -1260,10 +1267,10 @@ class ComputeManager(manager.Manager): def _build_networks_for_instance(self, context, instance, requested_networks, security_groups): - # May have been allocated previously and rescheduled to this host - network_info = self._get_instance_nw_info(context, instance) - if len(network_info) > 0: - return network_info + # If we're here from a reschedule the network may already be allocated. + if strutils.bool_from_string( + instance.system_metadata.get('network_allocated', 'False')): + return self._get_instance_nw_info(context, instance) if not self.is_neutron_security_groups: security_groups = [] @@ -1722,6 +1729,9 @@ class ComputeManager(manager.Manager): raise exception.RescheduledException( instance_uuid=instance.uuid, reason=str(e)) + # NOTE(alaski): This is only useful during reschedules, remove it now. + instance.system_metadata.pop('network_allocated', None) + instance.power_state = self._get_power_state(context, instance) instance.vm_state = vm_states.ACTIVE instance.task_state = None @@ -1799,6 +1809,8 @@ class ComputeManager(manager.Manager): requested_networks): try: self._deallocate_network(context, instance, requested_networks) + instance.system_metadata['network_allocated'] = 'False' + instance.save() except Exception: msg = _('Failed to deallocate networks') LOG.exception(msg, instance=instance) diff --git a/nova/tests/compute/test_compute_mgr.py b/nova/tests/compute/test_compute_mgr.py index 72e68f556634..059456909395 100644 --- a/nova/tests/compute/test_compute_mgr.py +++ b/nova/tests/compute/test_compute_mgr.py @@ -53,9 +53,10 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase): nwapi = self.compute.network_api self.mox.StubOutWithMock(nwapi, 'allocate_for_instance') + self.mox.StubOutWithMock(self.compute, '_instance_update') self.mox.StubOutWithMock(time, 'sleep') - instance = {} + instance = fake_instance.fake_db_instance(system_metadata={}) is_vpn = 'fake-is-vpn' req_networks = 'fake-req-networks' macs = 'fake-macs' @@ -79,6 +80,8 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase): requested_networks=req_networks, macs=macs, security_groups=sec_groups, dhcp_options=dhcp_options).AndReturn(final_result) + self.compute._instance_update(self.context, instance['uuid'], + system_metadata={'network_allocated': 'True'}) self.mox.ReplayAll() @@ -1383,28 +1386,47 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase): self.compute._cleanup_build_resources, self.context, self.instance, self.block_device_mapping) - def test_build_networks_if_none_found(self): + def test_build_networks_if_not_allocated(self): + instance = fake_instance.fake_instance_obj(self.context, + system_metadata={}, + expected_attrs=['system_metadata']) + self.mox.StubOutWithMock(self.compute, '_get_instance_nw_info') self.mox.StubOutWithMock(self.compute, '_allocate_network') - self.compute._get_instance_nw_info(self.context, - self.instance).AndReturn(self.network_info) - self.compute._allocate_network(self.context, self.instance, + self.compute._allocate_network(self.context, instance, self.requested_networks, None, self.security_groups, None) self.mox.ReplayAll() - self.compute._build_networks_for_instance(self.context, self.instance, + self.compute._build_networks_for_instance(self.context, instance, + self.requested_networks, self.security_groups) + + def test_build_networks_if_allocated_false(self): + instance = fake_instance.fake_instance_obj(self.context, + system_metadata=dict(network_allocated='False'), + expected_attrs=['system_metadata']) + + self.mox.StubOutWithMock(self.compute, '_get_instance_nw_info') + self.mox.StubOutWithMock(self.compute, '_allocate_network') + self.compute._allocate_network(self.context, instance, + self.requested_networks, None, self.security_groups, None) + self.mox.ReplayAll() + + self.compute._build_networks_for_instance(self.context, instance, self.requested_networks, self.security_groups) def test_return_networks_if_found(self): + instance = fake_instance.fake_instance_obj(self.context, + system_metadata=dict(network_allocated='True'), + expected_attrs=['system_metadata']) + def fake_network_info(): return network_model.NetworkInfo([{'address': '123.123.123.123'}]) self.mox.StubOutWithMock(self.compute, '_get_instance_nw_info') self.mox.StubOutWithMock(self.compute, '_allocate_network') - self.compute._get_instance_nw_info(self.context, - self.instance).AndReturn( + self.compute._get_instance_nw_info(self.context, instance).AndReturn( network_model.NetworkInfoAsyncWrapper(fake_network_info)) self.mox.ReplayAll() - self.compute._build_networks_for_instance(self.context, self.instance, + self.compute._build_networks_for_instance(self.context, instance, self.requested_networks, self.security_groups)