summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/compute/manager.py56
-rw-r--r--nova/exception.py4
-rw-r--r--nova/tests/test_compute.py25
-rw-r--r--nova/virt/libvirt/connection.py12
-rw-r--r--nova/virt/libvirt/firewall.py7
5 files changed, 66 insertions, 38 deletions
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index e596f81af..daffe0515 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -645,18 +645,24 @@ class ComputeManager(manager.SchedulerDependentManager):
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
@wrap_instance_fault
def run_instance(self, context, instance_uuid, **kwargs):
- self._run_instance(context, instance_uuid, **kwargs)
+ @utils.synchronized(instance_uuid)
+ def do_run_instance():
+ self._run_instance(context, instance_uuid, **kwargs)
+ do_run_instance()
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
@checks_instance_lock
@wrap_instance_fault
def start_instance(self, context, instance_uuid):
- """Starting an instance on this host."""
- # TODO(yamahata): injected_files isn't supported.
- # Anyway OSAPI doesn't support stop/start yet
- # FIXME(vish): I've kept the files during stop instance, but
- # I think start will fail due to the files still
- self._run_instance(context, instance_uuid)
+ @utils.synchronized(instance_uuid)
+ def do_start_instance():
+ """Starting an instance on this host."""
+ # TODO(yamahata): injected_files isn't supported.
+ # Anyway OSAPI doesn't support stop/start yet
+ # FIXME(vish): I've kept the files during stop instance, but
+ # I think start will fail due to the files still
+ self._run_instance(context, instance_uuid)
+ do_start_instance()
def _shutdown_instance(self, context, instance, action_str):
"""Shutdown an instance on this host."""
@@ -725,25 +731,35 @@ class ComputeManager(manager.SchedulerDependentManager):
@wrap_instance_fault
def terminate_instance(self, context, instance_uuid):
"""Terminate an instance on this host."""
- elevated = context.elevated()
- instance = self.db.instance_get_by_uuid(elevated, instance_uuid)
- compute_utils.notify_usage_exists(instance, current_period=True)
- try:
- self._delete_instance(context, instance)
- except exception.InstanceNotFound as e:
- LOG.warn(e)
+ @utils.synchronized(instance_uuid)
+ def do_terminate_instance():
+ elevated = context.elevated()
+ instance = self.db.instance_get_by_uuid(elevated, instance_uuid)
+ compute_utils.notify_usage_exists(instance, current_period=True)
+ try:
+ self._delete_instance(context, instance)
+ except exception.InstanceTerminationFailure as error:
+ msg = _('%s. Setting instance vm_state to ERROR')
+ LOG.error(msg % error)
+ self._set_instance_error_state(context, instance_uuid)
+ except exception.InstanceNotFound as e:
+ LOG.warn(e)
+ do_terminate_instance()
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
@checks_instance_lock
@wrap_instance_fault
def stop_instance(self, context, instance_uuid):
"""Stopping an instance on this host."""
- instance = self.db.instance_get_by_uuid(context, instance_uuid)
- self._shutdown_instance(context, instance, 'Stopping')
- self._instance_update(context,
- instance_uuid,
- vm_state=vm_states.STOPPED,
- task_state=None)
+ @utils.synchronized(instance_uuid)
+ def do_stop_instance():
+ instance = self.db.instance_get_by_uuid(context, instance_uuid)
+ self._shutdown_instance(context, instance, 'Stopping')
+ self._instance_update(context,
+ instance_uuid,
+ vm_state=vm_states.STOPPED,
+ task_state=None)
+ do_stop_instance()
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
@checks_instance_lock
diff --git a/nova/exception.py b/nova/exception.py
index 0e961730d..eb0bf382c 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -330,6 +330,10 @@ class InstanceRebootFailure(Invalid):
message = _("Failed to reboot instance") + ": %(reason)s"
+class InstanceTerminationFailure(Invalid):
+ message = _("Failed to terminate instance") + ": %(reason)s"
+
+
class ServiceUnavailable(Invalid):
message = _("Service is unavailable at this time.")
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index a291b2c80..ecb980106 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -932,26 +932,21 @@ class ComputeTestCase(BaseTestCase):
self.compute.terminate_instance(self.context, instance['uuid'])
- def test_instance_set_to_error_on_deleted_instance_doesnt_raise(self):
- """Test that we don't raise InstanceNotFound when trying to set
- an instance to ERROR that has already been deleted from under us.
- The original exception should be re-raised.
+ def test_instance_termination_exception_sets_error(self):
+ """Test that we handle InstanceTerminationFailure
+ which is propagated up from the underlying driver.
"""
instance = self._create_fake_instance()
- instance_uuid = instance['uuid']
- def fake_allocate_network(context, instance, requested_networks):
- # Remove the instance to simulate race condition
- self.compute.terminate_instance(self.context, instance['uuid'])
- raise rpc_common.RemoteError()
+ def fake_delete_instance(context, instance):
+ raise exception.InstanceTerminationFailure(reason='')
- self.stubs.Set(self.compute, '_allocate_network',
- fake_allocate_network)
+ self.stubs.Set(self.compute, '_delete_instance',
+ fake_delete_instance)
- self.assertRaises(rpc_common.RemoteError,
- self.compute.run_instance,
- self.context,
- instance_uuid)
+ self.compute.terminate_instance(self.context, instance['uuid'])
+ instance = db.instance_get_by_uuid(self.context, instance['uuid'])
+ self.assertEqual(instance['vm_state'], vm_states.ERROR)
def test_network_is_deallocated_on_spawn_failure(self):
"""When a spawn fails the network must be deallocated"""
diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py
index 2f35c664c..79cfd1652 100644
--- a/nova/virt/libvirt/connection.py
+++ b/nova/virt/libvirt/connection.py
@@ -406,8 +406,16 @@ class LibvirtConnection(driver.ComputeDriver):
timer = utils.LoopingCall(_wait_for_destroy)
timer.start(interval=0.5, now=True)
- self.firewall_driver.unfilter_instance(instance,
- network_info=network_info)
+ try:
+ self.firewall_driver.unfilter_instance(instance,
+ network_info=network_info)
+ except libvirt.libvirtError as e:
+ errcode = e.get_error_code()
+ LOG.warning(_("Error from libvirt during unfilter. "
+ "Code=%(errcode)s Error=%(e)s") %
+ locals(), instance=instance)
+ reason = "Error unfiltering instance."
+ raise exception.InstanceTerminationFailure(reason=reason)
# NOTE(vish): we disconnect from volumes regardless
block_device_mapping = driver.block_device_info_get_mapping(
diff --git a/nova/virt/libvirt/firewall.py b/nova/virt/libvirt/firewall.py
index 04b3cf42a..93f03d6df 100644
--- a/nova/virt/libvirt/firewall.py
+++ b/nova/virt/libvirt/firewall.py
@@ -156,7 +156,12 @@ class NWFilterFirewall(base_firewall.FirewallDriver):
try:
_nw = self._conn.nwfilterLookupByName(instance_filter_name)
_nw.undefine()
- except libvirt.libvirtError:
+ except libvirt.libvirtError as e:
+ errcode = e.get_error_code()
+ if errcode == libvirt.VIR_ERR_OPERATION_INVALID:
+ # This happens when the instance filter is still in
+ # use (ie. when the instance has not terminated properly)
+ raise
LOG.debug(_('The nwfilter(%(instance_filter_name)s) '
'is not found.') % locals(),
instance=instance)