summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKravchenko Pavel <kpavel@il.ibm.com>2013-02-04 18:21:18 +0200
committerKravchenko Pavel <kpavel@il.ibm.com>2013-02-04 18:21:18 +0200
commit064fefb810eab07d4ddde3dd50537a6236567423 (patch)
treee7b710ac88d17b42cff45dc5e4ad6a6c3d506ce2
parent47bbf12a6c9705e5abca29a1d44b753c8506505d (diff)
downloadnova-064fefb810eab07d4ddde3dd50537a6236567423.tar.gz
nova-064fefb810eab07d4ddde3dd50537a6236567423.tar.xz
nova-064fefb810eab07d4ddde3dd50537a6236567423.zip
Adds evacuate method to compute.api
Added decorator to check whether the vm is suitable to be evacuated. The decorator validates that vm host is down. The new evacuate method calls rebuild with the recreate flag and specified target host. This implements blueprint rebuild-for-ha DocImpact Change-Id: I877b29928c922fa366fb85deb85ddfc72d97daf8 Co-authored-by: Oshrit Feder <oshritf@il.ibm.com>
-rw-r--r--nova/compute/api.py33
-rw-r--r--nova/compute/cells_api.py7
-rw-r--r--nova/tests/compute/test_compute.py101
-rw-r--r--nova/tests/compute/test_compute_cells.py3
4 files changed, 144 insertions, 0 deletions
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 034b875a6..7ce0a74b8 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -2320,6 +2320,39 @@ class API(base.Base):
self.scheduler_rpcapi.live_migration(context, block_migration,
disk_over_commit, instance, host_name)
+ @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED],
+ task_state=[None])
+ def evacuate(self, context, instance, host, on_shared_storage,
+ admin_password=None):
+ """Running evacuate to target host.
+
+ Checking vm compute host state, if the host not in expected_state,
+ raising an exception.
+ """
+ LOG.debug(_('vm evacuation scheduled'))
+ host = instance['host']
+ service = self.db.service_get_by_compute_host(context, host)
+ if self.servicegroup_api.service_is_up(service):
+ msg = (_('Instance compute service state on %(host)s '
+ 'expected to be down, but it was up.'
+ ) % locals())
+ LOG.error(msg)
+ raise exception.ComputeServiceUnavailable(msg)
+
+ instance = self.update(context, instance, expected_task_state=None,
+ task_state=task_states.REBUILDING)
+ return self.compute_rpcapi.rebuild_instance(context,
+ instance=instance,
+ new_pass=admin_password,
+ injected_files=None,
+ image_ref=None,
+ orig_image_ref=None,
+ orig_sys_metadata=None,
+ bdms=None,
+ recreate=True,
+ on_shared_storage=on_shared_storage,
+ host=host)
+
class HostAPI(base.Base):
"""Sub-set of the Compute Manager API for managing host operations."""
diff --git a/nova/compute/cells_api.py b/nova/compute/cells_api.py
index d5427a04b..f3a6fb1af 100644
--- a/nova/compute/cells_api.py
+++ b/nova/compute/cells_api.py
@@ -278,6 +278,13 @@ class ComputeCellsAPI(compute_api.API):
**kwargs)
self._cast_to_cells(context, instance, 'rebuild', *args, **kwargs)
+ @validate_cell
+ def evacuate(self, context, instance, *args, **kwargs):
+ """Evacuate the given instance with the provided attributes."""
+ super(ComputeCellsAPI, self).evacuate(context, instance, *args,
+ **kwargs)
+ self._cast_to_cells(context, instance, 'evacuate', *args, **kwargs)
+
@check_instance_state(vm_state=[vm_states.RESIZED])
@validate_cell
def revert_resize(self, context, instance):
diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py
index b5a8b91a2..a745a8874 100644
--- a/nova/tests/compute/test_compute.py
+++ b/nova/tests/compute/test_compute.py
@@ -5933,6 +5933,107 @@ class ComputeAPITestCase(BaseTestCase):
db.instance_destroy(self.context, instance['uuid'])
+ def test_evacuate(self):
+ instance = jsonutils.to_primitive(self._create_fake_instance())
+ instance_uuid = instance['uuid']
+ instance = db.instance_get_by_uuid(self.context, instance_uuid)
+ self.assertEqual(instance['task_state'], None)
+
+ def fake_service_is_up(*args, **kwargs):
+ return False
+
+ self.stubs.Set(self.compute_api.servicegroup_api, 'service_is_up',
+ fake_service_is_up)
+ self.compute_api.evacuate(self.context.elevated(),
+ instance,
+ host='fake_dest_host',
+ on_shared_storage=True,
+ admin_password=None)
+
+ instance = db.instance_get_by_uuid(self.context, instance_uuid)
+ self.assertEqual(instance['task_state'], task_states.REBUILDING)
+
+ db.instance_destroy(self.context, instance['uuid'])
+
+ def test_fail_evacuate_from_non_existing_host(self):
+ inst = {}
+ inst['vm_state'] = vm_states.ACTIVE
+ inst['image_ref'] = FAKE_IMAGE_REF
+ inst['reservation_id'] = 'r-fakeres'
+ inst['launch_time'] = '10'
+ inst['user_id'] = self.user_id
+ inst['project_id'] = self.project_id
+ inst['host'] = 'fake_host'
+ inst['node'] = NODENAME
+ type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
+ inst['instance_type_id'] = type_id
+ inst['ami_launch_index'] = 0
+ inst['memory_mb'] = 0
+ inst['vcpus'] = 0
+ inst['root_gb'] = 0
+ inst['ephemeral_gb'] = 0
+ inst['architecture'] = 'x86_64'
+ inst['os_type'] = 'Linux'
+
+ instance = jsonutils.to_primitive(db.instance_create(self.context,
+ inst))
+ instance_uuid = instance['uuid']
+ instance = db.instance_get_by_uuid(self.context, instance_uuid)
+ self.assertEqual(instance['task_state'], None)
+
+ self.assertRaises(exception.ComputeHostNotFound,
+ self.compute_api.evacuate, self.context.elevated(), instance,
+ host='fake_dest_host', on_shared_storage=True,
+ admin_password=None)
+
+ db.instance_destroy(self.context, instance['uuid'])
+
+ def test_fail_evacuate_from_running_host(self):
+ instance = jsonutils.to_primitive(self._create_fake_instance())
+ instance_uuid = instance['uuid']
+ instance = db.instance_get_by_uuid(self.context, instance_uuid)
+ self.assertEqual(instance['task_state'], None)
+
+ def fake_service_is_up(*args, **kwargs):
+ return True
+
+ self.stubs.Set(self.compute_api.servicegroup_api, 'service_is_up',
+ fake_service_is_up)
+
+ self.assertRaises(exception.ComputeServiceUnavailable,
+ self.compute_api.evacuate, self.context.elevated(), instance,
+ host='fake_dest_host', on_shared_storage=True,
+ admin_password=None)
+
+ db.instance_destroy(self.context, instance['uuid'])
+
+ def test_fail_evacuate_instance_in_wrong_state(self):
+ instances = [
+ jsonutils.to_primitive(self._create_fake_instance(
+ {'vm_state': vm_states.BUILDING})),
+ jsonutils.to_primitive(self._create_fake_instance(
+ {'vm_state': vm_states.PAUSED})),
+ jsonutils.to_primitive(self._create_fake_instance(
+ {'vm_state': vm_states.SUSPENDED})),
+ jsonutils.to_primitive(self._create_fake_instance(
+ {'vm_state': vm_states.RESCUED})),
+ jsonutils.to_primitive(self._create_fake_instance(
+ {'vm_state': vm_states.RESIZED})),
+ jsonutils.to_primitive(self._create_fake_instance(
+ {'vm_state': vm_states.SOFT_DELETED})),
+ jsonutils.to_primitive(self._create_fake_instance(
+ {'vm_state': vm_states.DELETED})),
+ jsonutils.to_primitive(self._create_fake_instance(
+ {'vm_state': vm_states.ERROR}))
+ ]
+
+ for instance in instances:
+ self.assertRaises(exception.InstanceInvalidState,
+ self.compute_api.evacuate, self.context, instance,
+ host='fake_dest_host', on_shared_storage=True,
+ admin_password=None)
+ db.instance_destroy(self.context, instance['uuid'])
+
def fake_rpc_method(context, topic, msg, do_cast=True):
pass
diff --git a/nova/tests/compute/test_compute_cells.py b/nova/tests/compute/test_compute_cells.py
index df78c37f3..8ba35e033 100644
--- a/nova/tests/compute/test_compute_cells.py
+++ b/nova/tests/compute/test_compute_cells.py
@@ -168,6 +168,9 @@ class CellsComputeAPITestCase(test_compute.ComputeAPITestCase):
self.skipTest("This test is failing due to TypeError: "
"detach_volume() takes exactly 3 arguments (4 given).")
+ def test_evacuate(self):
+ self.skipTest("Test is incompatible with cells.")
+
class CellsComputePolicyTestCase(test_compute.ComputePolicyTestCase):
def setUp(self):