Added support for live migration on non-shared storage

Watcher applier should be able to live migrate instances on any storage
type. To do this watcher will catch error 400 returned from nova if we
try to live migrate instance which is not on shared storage and live
migrate instance using block_migrate.

Added unit tests, changed action in watcher applier.

Closes-bug: #1549307

Change-Id: I97e583c9b4a0bb9daa1d39e6d652d6474a5aaeb1
This commit is contained in:
Daniel Pawlik
2016-02-29 16:23:51 +00:00
parent 9af96114af
commit 5bb1b6cbf0
2 changed files with 59 additions and 4 deletions

View File

@@ -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 '

View File

@@ -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)