diff --git a/watcher/applier/actions/migration.py b/watcher/applier/actions/migration.py index 7594d81ab..ce6219f26 100644 --- a/watcher/applier/actions/migration.py +++ b/watcher/applier/actions/migration.py @@ -21,7 +21,7 @@ from oslo_log import log import six import voluptuous -from watcher._i18n import _ +from watcher._i18n import _, _LC from watcher.applier.actions import base from watcher.common import exception from watcher.common import nova_helper @@ -76,6 +76,31 @@ class Migrate(base.BaseAction): def src_hypervisor(self): return self.input_parameters.get(self.SRC_HYPERVISOR) + def _live_migrate_instance(self, nova, destination): + result = None + try: + result = nova.live_migrate_instance(instance_id=self.instance_uuid, + dest_hostname=destination) + except nova_helper.nvexceptions.ClientException as e: + if e.code == 400: + LOG.debug("Live migration of instance %s failed. " + "Trying to live migrate using block migration." + % self.instance_uuid) + result = nova.live_migrate_instance( + instance_id=self.instance_uuid, + dest_hostname=destination, + block_migration=True) + else: + LOG.debug("Nova client exception occured while live migrating " + "instance %s.Exception: %s" % + (self.instance_uuid, e)) + except Exception: + LOG.critical(_LC("Unexpected error occured. Migration failed for" + "instance %s. Leaving instance on previous " + "host."), self.instance_uuid) + + return result + def migrate(self, destination): nova = nova_helper.NovaHelper(osc=self.osc) LOG.debug("Migrate instance %s to %s", self.instance_uuid, @@ -83,8 +108,7 @@ class Migrate(base.BaseAction): instance = nova.find_instance(self.instance_uuid) if instance: if self.migration_type == 'live': - return nova.live_migrate_instance( - instance_id=self.instance_uuid, dest_hostname=destination) + return self._live_migrate_instance(nova, destination) else: raise exception.Invalid( message=(_('Migration of type %(migration_type)s is not ' diff --git a/watcher/tests/applier/actions/test_migration.py b/watcher/tests/applier/actions/test_migration.py index 82409ed00..6bee69c34 100644 --- a/watcher/tests/applier/actions/test_migration.py +++ b/watcher/tests/applier/actions/test_migration.py @@ -157,7 +157,10 @@ class TestMigration(base.TestCase): def test_execute_live_migration(self): self.m_helper.find_instance.return_value = self.INSTANCE_UUID - self.action.execute() + try: + self.action.execute() + except Exception as exc: + self.fail(exc) self.m_helper.live_migrate_instance.assert_called_once_with( instance_id=self.INSTANCE_UUID, @@ -173,3 +176,31 @@ class TestMigration(base.TestCase): instance_id=self.INSTANCE_UUID, dest_hostname="hypervisor1-hostname" ) + + def test_live_migrate_non_shared_storage_instance(self): + self.m_helper.find_instance.return_value = self.INSTANCE_UUID + + self.m_helper.live_migrate_instance.side_effect = [ + nova_helper.nvexceptions.ClientException(400, "BadRequest"), True] + + try: + self.action.execute() + except Exception as exc: + self.fail(exc) + + self.m_helper.live_migrate_instance.assert_has_calls([ + mock.call(instance_id=self.INSTANCE_UUID, + dest_hostname="hypervisor2-hostname"), + mock.call(instance_id=self.INSTANCE_UUID, + dest_hostname="hypervisor2-hostname", + block_migration=True) + ]) + + expected = [mock.call.first(instance_id=self.INSTANCE_UUID, + dest_hostname="hypervisor2-hostname"), + mock.call.second(instance_id=self.INSTANCE_UUID, + dest_hostname="hypervisor2-hostname", + block_migration=True) + ] + self.m_helper.live_migrate_instance.mock_calls == expected + self.assertEqual(2, self.m_helper.live_migrate_instance.call_count)