diff options
author | Jenkins <jenkins@review.openstack.org> | 2013-03-08 20:55:39 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2013-03-08 20:55:39 +0000 |
commit | cf9ab8413e0686442ef7e4d1a6b83c5bb224d06f (patch) | |
tree | 802c2ab6662d63f25ccc56f940448f8b81aadb74 | |
parent | 9d836d828fd264242f0436f33afe250850402edf (diff) | |
parent | ab2920726c0e2633c033a31a324f30a97fdce6bd (diff) | |
download | nova-cf9ab8413e0686442ef7e4d1a6b83c5bb224d06f.tar.gz nova-cf9ab8413e0686442ef7e4d1a6b83c5bb224d06f.tar.xz nova-cf9ab8413e0686442ef7e4d1a6b83c5bb224d06f.zip |
Merge "Rework instance actions to work with cells"
-rw-r--r-- | nova/api/openstack/compute/contrib/instance_actions.py | 10 | ||||
-rw-r--r-- | nova/cells/manager.py | 18 | ||||
-rw-r--r-- | nova/cells/messaging.py | 36 | ||||
-rw-r--r-- | nova/cells/rpcapi.py | 28 | ||||
-rw-r--r-- | nova/cells/scheduler.py | 11 | ||||
-rw-r--r-- | nova/compute/__init__.py | 12 | ||||
-rw-r--r-- | nova/compute/api.py | 14 | ||||
-rw-r--r-- | nova/compute/cells_api.py | 18 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_instance_actions.py | 5 | ||||
-rw-r--r-- | nova/tests/cells/test_cells_manager.py | 44 | ||||
-rw-r--r-- | nova/tests/cells/test_cells_messaging.py | 47 | ||||
-rw-r--r-- | nova/tests/cells/test_cells_rpcapi.py | 55 | ||||
-rw-r--r-- | nova/tests/integrated/test_api_samples.py | 4 |
13 files changed, 297 insertions, 5 deletions
diff --git a/nova/api/openstack/compute/contrib/instance_actions.py b/nova/api/openstack/compute/contrib/instance_actions.py index ecacde7bf..4eaa9a1ee 100644 --- a/nova/api/openstack/compute/contrib/instance_actions.py +++ b/nova/api/openstack/compute/contrib/instance_actions.py @@ -19,7 +19,6 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import compute -from nova import db authorize_actions = extensions.extension_authorizer('compute', 'instance_actions') @@ -67,6 +66,7 @@ class InstanceActionsController(wsgi.Controller): def __init__(self): super(InstanceActionsController, self).__init__() self.compute_api = compute.API() + self.action_api = compute.InstanceActionAPI() def _format_action(self, action_raw): action = {} @@ -86,7 +86,7 @@ class InstanceActionsController(wsgi.Controller): context = req.environ["nova.context"] instance = self.compute_api.get(context, server_id) authorize_actions(context, target=instance) - actions_raw = db.actions_get(context, server_id) + actions_raw = self.action_api.actions_get(context, instance) actions = [self._format_action(action) for action in actions_raw] return {'instanceActions': actions} @@ -96,14 +96,16 @@ class InstanceActionsController(wsgi.Controller): context = req.environ['nova.context'] instance = self.compute_api.get(context, server_id) authorize_actions(context, target=instance) - action = db.action_get_by_request_id(context, server_id, id) + action = self.action_api.action_get_by_request_id(context, instance, + id) if action is None: raise exc.HTTPNotFound() action_id = action['id'] action = self._format_action(action) if authorize_events(context): - events_raw = db.action_events_get(context, action_id) + events_raw = self.action_api.action_events_get(context, instance, + action_id) action['events'] = [self._format_event(evt) for evt in events_raw] return {'instanceAction': action} diff --git a/nova/cells/manager.py b/nova/cells/manager.py index 55b3dadf7..ec4bc447f 100644 --- a/nova/cells/manager.py +++ b/nova/cells/manager.py @@ -66,7 +66,7 @@ class CellsManager(manager.Manager): Scheduling requests get passed to the scheduler class. """ - RPC_API_VERSION = '1.4' + RPC_API_VERSION = '1.5' def __init__(self, *args, **kwargs): # Mostly for tests. @@ -332,3 +332,19 @@ class CellsManager(manager.Manager): totals.setdefault(key, 0) totals[key] += val return totals + + def actions_get(self, ctxt, cell_name, instance_uuid): + response = self.msg_runner.actions_get(ctxt, cell_name, instance_uuid) + return response.value_or_raise() + + def action_get_by_request_id(self, ctxt, cell_name, instance_uuid, + request_id): + response = self.msg_runner.action_get_by_request_id(ctxt, cell_name, + instance_uuid, + request_id) + return response.value_or_raise() + + def action_events_get(self, ctxt, cell_name, action_id): + response = self.msg_runner.action_events_get(ctxt, cell_name, + action_id) + return response.value_or_raise() diff --git a/nova/cells/messaging.py b/nova/cells/messaging.py index 449d037ea..f83f141dc 100644 --- a/nova/cells/messaging.py +++ b/nova/cells/messaging.py @@ -717,6 +717,19 @@ class _TargetedMessageMethods(_BaseMessageMethods): compute_id) return jsonutils.to_primitive(compute_node) + def actions_get(self, message, instance_uuid): + actions = self.db.actions_get(message.ctxt, instance_uuid) + return jsonutils.to_primitive(actions) + + def action_get_by_request_id(self, message, instance_uuid, request_id): + action = self.db.action_get_by_request_id(message.ctxt, instance_uuid, + request_id) + return jsonutils.to_primitive(action) + + def action_events_get(self, message, action_id): + action_events = self.db.action_events_get(message.ctxt, action_id) + return jsonutils.to_primitive(action_events) + class _BroadcastMessageMethods(_BaseMessageMethods): """These are the methods that can be called as a part of a broadcast @@ -1188,6 +1201,29 @@ class MessageRunner(object): cell_name, need_response=True) return message.process() + def actions_get(self, ctxt, cell_name, instance_uuid): + method_kwargs = dict(instance_uuid=instance_uuid) + message = _TargetedMessage(self, ctxt, 'actions_get', + method_kwargs, 'down', + cell_name, need_response=True) + return message.process() + + def action_get_by_request_id(self, ctxt, cell_name, instance_uuid, + request_id): + method_kwargs = dict(instance_uuid=instance_uuid, + request_id=request_id) + message = _TargetedMessage(self, ctxt, 'action_get_by_request_id', + method_kwargs, 'down', + cell_name, need_response=True) + return message.process() + + def action_events_get(self, ctxt, cell_name, action_id): + method_kwargs = dict(action_id=action_id) + message = _TargetedMessage(self, ctxt, 'action_events_get', + method_kwargs, 'down', + cell_name, need_response=True) + return message.process() + @staticmethod def get_message_types(): return _CELL_MESSAGE_TYPE_TO_MESSAGE_CLS.keys() diff --git a/nova/cells/rpcapi.py b/nova/cells/rpcapi.py index 910c4ab9d..be45e8f03 100644 --- a/nova/cells/rpcapi.py +++ b/nova/cells/rpcapi.py @@ -24,6 +24,7 @@ messging module. from oslo.config import cfg +from nova import exception from nova.openstack.common import jsonutils from nova.openstack.common import log as logging from nova.openstack.common.rpc import proxy as rpc_proxy @@ -47,6 +48,8 @@ class CellsAPI(rpc_proxy.RpcProxy): 1.3 - Adds task_log_get_all() 1.4 - Adds compute_node_get(), compute_node_get_all(), and compute_node_stats() + 1.5 - Adds actions_get(), action_get_by_request_id(), and + action_events_get() ''' BASE_RPC_API_VERSION = '1.0' @@ -219,3 +222,28 @@ class CellsAPI(rpc_proxy.RpcProxy): """Return compute node stats from all cells.""" return self.call(ctxt, self.make_msg('compute_node_stats'), version='1.4') + + def actions_get(self, ctxt, instance): + if not instance['cell_name']: + raise exception.InstanceUnknownCell(instance_uuid=instance['uuid']) + return self.call(ctxt, self.make_msg('actions_get', + cell_name=instance['cell_name'], + instance_uuid=instance['uuid']), + version='1.5') + + def action_get_by_request_id(self, ctxt, instance, request_id): + if not instance['cell_name']: + raise exception.InstanceUnknownCell(instance_uuid=instance['uuid']) + return self.call(ctxt, self.make_msg('action_get_by_request_id', + cell_name=instance['cell_name'], + instance_uuid=instance['uuid'], + request_id=request_id), + version='1.5') + + def action_events_get(self, ctxt, instance, action_id): + if not instance['cell_name']: + raise exception.InstanceUnknownCell(instance_uuid=instance['uuid']) + return self.call(ctxt, self.make_msg('action_events_get', + cell_name=instance['cell_name'], + action_id=action_id), + version='1.5') diff --git a/nova/cells/scheduler.py b/nova/cells/scheduler.py index 3b69b2eac..8ec65b2d0 100644 --- a/nova/cells/scheduler.py +++ b/nova/cells/scheduler.py @@ -22,6 +22,8 @@ import time from oslo.config import cfg from nova import compute +from nova.compute import instance_actions +from nova.compute import utils as compute_utils from nova.compute import vm_states from nova.db import base from nova import exception @@ -70,6 +72,12 @@ class CellsScheduler(base.Base): self.msg_runner.instance_update_at_top(ctxt, instance) + def _create_action_here(self, ctxt, instance_uuids): + for instance_uuid in instance_uuids: + action = compute_utils.pack_action_start(ctxt, instance_uuid, + instance_actions.CREATE) + self.db.action_start(ctxt, action) + def _get_possible_cells(self): cells = set(self.state_manager.get_child_cells()) our_cell = self.state_manager.get_my_state() @@ -102,6 +110,9 @@ class CellsScheduler(base.Base): # Need to create instance DB entries as the host scheduler # expects that the instance(s) already exists. self._create_instances_here(ctxt, request_spec) + # Need to record the create action in the db as the scheduler + # expects it to already exist. + self._create_action_here(ctxt, request_spec['instance_uuids']) self.scheduler_rpcapi.run_instance(ctxt, **host_sched_kwargs) return diff --git a/nova/compute/__init__.py b/nova/compute/__init__.py index f1451aab3..baa957ca8 100644 --- a/nova/compute/__init__.py +++ b/nova/compute/__init__.py @@ -48,3 +48,15 @@ def HostAPI(*args, **kwargs): compute_api_class = importutils.import_class(compute_api_class_name) class_name = compute_api_class.__module__ + ".HostAPI" return importutils.import_object(class_name, *args, **kwargs) + + +def InstanceActionAPI(*args, **kwargs): + """ + Returns the 'InstanceActionAPI' class from the same module as the + configured compute api. + """ + importutils = nova.openstack.common.importutils + compute_api_class_name = oslo.config.cfg.CONF.compute_api_class + compute_api_class = importutils.import_class(compute_api_class_name) + class_name = compute_api_class.__module__ + ".InstanceActionAPI" + return importutils.import_object(class_name, *args, **kwargs) diff --git a/nova/compute/api.py b/nova/compute/api.py index 252f326d4..76d4d06ba 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -2566,6 +2566,20 @@ class HostAPI(base.Base): return self.db.compute_node_statistics(context) +class InstanceActionAPI(base.Base): + """Sub-set of the Compute Manager API for managing instance actions.""" + + def actions_get(self, context, instance): + return self.db.actions_get(context, instance['uuid']) + + def action_get_by_request_id(self, context, instance, request_id): + return self.db.action_get_by_request_id(context, instance['uuid'], + request_id) + + def action_events_get(self, context, instance, action_id): + return self.db.action_events_get(context, action_id) + + class AggregateAPI(base.Base): """Sub-set of the Compute Manager API for managing host aggregates.""" def __init__(self, **kwargs): diff --git a/nova/compute/cells_api.py b/nova/compute/cells_api.py index b473d70b3..d5a07490b 100644 --- a/nova/compute/cells_api.py +++ b/nova/compute/cells_api.py @@ -655,3 +655,21 @@ class HostAPI(compute_api.HostAPI): def compute_node_statistics(self, context): return self.cells_rpcapi.compute_node_stats(context) + + +class InstanceActionAPI(compute_api.InstanceActionAPI): + """InstanceActionAPI() class for cells.""" + def __init__(self): + super(InstanceActionAPI, self).__init__() + self.cells_rpcapi = cells_rpcapi.CellsAPI() + + def actions_get(self, context, instance): + return self.cells_rpcapi.actions_get(context, instance) + + def action_get_by_request_id(self, context, instance, request_id): + return self.cells_rpcapi.action_get_by_request_id(context, instance, + request_id) + + def action_events_get(self, context, instance, action_id): + return self.cells_rpcapi.action_events_get(context, instance, + action_id) diff --git a/nova/tests/api/openstack/compute/contrib/test_instance_actions.py b/nova/tests/api/openstack/compute/contrib/test_instance_actions.py index 8650275a7..573385a52 100644 --- a/nova/tests/api/openstack/compute/contrib/test_instance_actions.py +++ b/nova/tests/api/openstack/compute/contrib/test_instance_actions.py @@ -20,6 +20,7 @@ from lxml import etree from webob import exc from nova.api.openstack.compute.contrib import instance_actions +from nova.compute import api as compute_api from nova import db from nova.db.sqlalchemy import models from nova import exception @@ -92,9 +93,13 @@ class InstanceActionsTest(test.TestCase): self.fake_actions = copy.deepcopy(fake_instance_actions.FAKE_ACTIONS) self.fake_events = copy.deepcopy(fake_instance_actions.FAKE_EVENTS) + def fake_get(self, context, instance_uuid): + return {'uuid': instance_uuid} + def fake_instance_get_by_uuid(context, instance_id): return {'name': 'fake', 'project_id': context.project_id} + self.stubs.Set(compute_api.API, 'get', fake_get) self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get_by_uuid) def test_list_actions(self): diff --git a/nova/tests/cells/test_cells_manager.py b/nova/tests/cells/test_cells_manager.py index d53fdbb7f..7dfb0fa02 100644 --- a/nova/tests/cells/test_cells_manager.py +++ b/nova/tests/cells/test_cells_manager.py @@ -27,6 +27,7 @@ from nova.openstack.common import rpc from nova.openstack.common import timeutils from nova import test from nova.tests.cells import fakes +from nova.tests import fake_instance_actions CONF = cfg.CONF CONF.import_opt('compute_topic', 'nova.compute.rpcapi') @@ -429,3 +430,46 @@ class CellsManagerClassTestCase(test.TestCase): response = self.cells_manager.compute_node_get(self.ctxt, compute_id=cell_and_id) self.assertEqual(expected_response, response) + + def test_actions_get(self): + fake_uuid = fake_instance_actions.FAKE_UUID + fake_req_id = fake_instance_actions.FAKE_REQUEST_ID1 + fake_act = fake_instance_actions.FAKE_ACTIONS[fake_uuid][fake_req_id] + fake_response = messaging.Response('fake-cell', [fake_act], False) + expected_response = [fake_act] + self.mox.StubOutWithMock(self.msg_runner, 'actions_get') + self.msg_runner.actions_get(self.ctxt, 'fake-cell', + 'fake-uuid').AndReturn(fake_response) + self.mox.ReplayAll() + response = self.cells_manager.actions_get(self.ctxt, 'fake-cell', + 'fake-uuid') + self.assertEqual(expected_response, response) + + def test_action_get_by_request_id(self): + fake_uuid = fake_instance_actions.FAKE_UUID + fake_req_id = fake_instance_actions.FAKE_REQUEST_ID1 + fake_act = fake_instance_actions.FAKE_ACTIONS[fake_uuid][fake_req_id] + fake_response = messaging.Response('fake-cell', fake_act, False) + expected_response = fake_act + self.mox.StubOutWithMock(self.msg_runner, 'action_get_by_request_id') + self.msg_runner.action_get_by_request_id(self.ctxt, 'fake-cell', + 'fake-uuid', 'req-fake').AndReturn(fake_response) + self.mox.ReplayAll() + response = self.cells_manager.action_get_by_request_id(self.ctxt, + 'fake-cell', + 'fake-uuid', + 'req-fake') + self.assertEqual(expected_response, response) + + def test_action_events_get(self): + fake_action_id = fake_instance_actions.FAKE_ACTION_ID1 + fake_events = fake_instance_actions.FAKE_EVENTS[fake_action_id] + fake_response = messaging.Response('fake-cell', fake_events, False) + expected_response = fake_events + self.mox.StubOutWithMock(self.msg_runner, 'action_events_get') + self.msg_runner.action_events_get(self.ctxt, 'fake-cell', + 'fake-action').AndReturn(fake_response) + self.mox.ReplayAll() + response = self.cells_manager.action_events_get(self.ctxt, 'fake-cell', + 'fake-action') + self.assertEqual(expected_response, response) diff --git a/nova/tests/cells/test_cells_messaging.py b/nova/tests/cells/test_cells_messaging.py index 30adfdcd7..effe27660 100644 --- a/nova/tests/cells/test_cells_messaging.py +++ b/nova/tests/cells/test_cells_messaging.py @@ -25,6 +25,7 @@ from nova.openstack.common import rpc from nova.openstack.common import timeutils from nova import test from nova.tests.cells import fakes +from nova.tests import fake_instance_actions CONF = cfg.CONF CONF.import_opt('name', 'nova.cells.opts', group='cells') @@ -825,6 +826,52 @@ class CellsTargetedMethodsTestCase(test.TestCase): result = response.value_or_raise() self.assertEqual('fake_result', result) + def test_actions_get(self): + fake_uuid = fake_instance_actions.FAKE_UUID + fake_req_id = fake_instance_actions.FAKE_REQUEST_ID1 + fake_act = fake_instance_actions.FAKE_ACTIONS[fake_uuid][fake_req_id] + + self.mox.StubOutWithMock(self.tgt_db_inst, 'actions_get') + self.tgt_db_inst.actions_get(self.ctxt, + 'fake-uuid').AndReturn([fake_act]) + self.mox.ReplayAll() + + response = self.src_msg_runner.actions_get(self.ctxt, + self.tgt_cell_name, + 'fake-uuid') + result = response.value_or_raise() + self.assertEqual([fake_act], result) + + def test_action_get_by_request_id(self): + fake_uuid = fake_instance_actions.FAKE_UUID + fake_req_id = fake_instance_actions.FAKE_REQUEST_ID1 + fake_act = fake_instance_actions.FAKE_ACTIONS[fake_uuid][fake_req_id] + + self.mox.StubOutWithMock(self.tgt_db_inst, 'action_get_by_request_id') + self.tgt_db_inst.action_get_by_request_id(self.ctxt, + 'fake-uuid', 'req-fake').AndReturn(fake_act) + self.mox.ReplayAll() + + response = self.src_msg_runner.action_get_by_request_id(self.ctxt, + self.tgt_cell_name, 'fake-uuid', 'req-fake') + result = response.value_or_raise() + self.assertEqual(fake_act, result) + + def test_action_events_get(self): + fake_action_id = fake_instance_actions.FAKE_ACTION_ID1 + fake_events = fake_instance_actions.FAKE_EVENTS[fake_action_id] + + self.mox.StubOutWithMock(self.tgt_db_inst, 'action_events_get') + self.tgt_db_inst.action_events_get(self.ctxt, + 'fake-action').AndReturn(fake_events) + self.mox.ReplayAll() + + response = self.src_msg_runner.action_events_get(self.ctxt, + self.tgt_cell_name, + 'fake-action') + result = response.value_or_raise() + self.assertEqual(fake_events, result) + class CellsBroadcastMethodsTestCase(test.TestCase): """Test case for _BroadcastMessageMethods class. Most of these diff --git a/nova/tests/cells/test_cells_rpcapi.py b/nova/tests/cells/test_cells_rpcapi.py index f00b1b290..c915a5bae 100644 --- a/nova/tests/cells/test_cells_rpcapi.py +++ b/nova/tests/cells/test_cells_rpcapi.py @@ -19,6 +19,7 @@ Tests For Cells RPCAPI from oslo.config import cfg from nova.cells import rpcapi as cells_rpcapi +from nova import exception from nova.openstack.common import rpc from nova import test @@ -305,3 +306,57 @@ class CellsAPITestCase(test.TestCase): self._check_result(call_info, 'compute_node_get', expected_args, version='1.4') self.assertEqual(result, 'fake_response') + + def test_actions_get(self): + fake_instance = {'uuid': 'fake-uuid', 'cell_name': 'region!child'} + call_info = self._stub_rpc_method('call', 'fake_response') + result = self.cells_rpcapi.actions_get(self.fake_context, + fake_instance) + expected_args = {'cell_name': 'region!child', + 'instance_uuid': fake_instance['uuid']} + self._check_result(call_info, 'actions_get', expected_args, + version='1.5') + self.assertEqual(result, 'fake_response') + + def test_actions_get_no_cell(self): + fake_instance = {'uuid': 'fake-uuid', 'cell_name': None} + self.assertRaises(exception.InstanceUnknownCell, + self.cells_rpcapi.actions_get, self.fake_context, + fake_instance) + + def test_action_get_by_request_id(self): + fake_instance = {'uuid': 'fake-uuid', 'cell_name': 'region!child'} + call_info = self._stub_rpc_method('call', 'fake_response') + result = self.cells_rpcapi.action_get_by_request_id(self.fake_context, + fake_instance, + 'req-fake') + expected_args = {'cell_name': 'region!child', + 'instance_uuid': fake_instance['uuid'], + 'request_id': 'req-fake'} + self._check_result(call_info, 'action_get_by_request_id', + expected_args, version='1.5') + self.assertEqual(result, 'fake_response') + + def test_action_get_by_request_id_no_cell(self): + fake_instance = {'uuid': 'fake-uuid', 'cell_name': None} + self.assertRaises(exception.InstanceUnknownCell, + self.cells_rpcapi.action_get_by_request_id, + self.fake_context, fake_instance, 'req-fake') + + def test_action_events_get(self): + fake_instance = {'uuid': 'fake-uuid', 'cell_name': 'region!child'} + call_info = self._stub_rpc_method('call', 'fake_response') + result = self.cells_rpcapi.action_events_get(self.fake_context, + fake_instance, + 'fake-action') + expected_args = {'cell_name': 'region!child', + 'action_id': 'fake-action'} + self._check_result(call_info, 'action_events_get', expected_args, + version='1.5') + self.assertEqual(result, 'fake_response') + + def test_action_events_get_no_cell(self): + fake_instance = {'uuid': 'fake-uuid', 'cell_name': None} + self.assertRaises(exception.InstanceUnknownCell, + self.cells_rpcapi.action_events_get, + self.fake_context, fake_instance, 'fake-action') diff --git a/nova/tests/integrated/test_api_samples.py b/nova/tests/integrated/test_api_samples.py index 0cb18da4a..9fe2c6566 100644 --- a/nova/tests/integrated/test_api_samples.py +++ b/nova/tests/integrated/test_api_samples.py @@ -3173,12 +3173,16 @@ class InstanceActionsSampleJsonTest(ApiSampleTestBase): def fake_instance_get_by_uuid(context, instance_id): return self.instance + def fake_get(self, context, instance_uuid): + return {'uuid': instance_uuid} + self.stubs.Set(db, 'action_get_by_request_id', fake_instance_action_get_by_request_id) self.stubs.Set(db, 'actions_get', fake_instance_actions_get) self.stubs.Set(db, 'action_events_get', fake_instance_action_events_get) self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get_by_uuid) + self.stubs.Set(compute_api.API, 'get', fake_get) def test_instance_action_get(self): fake_uuid = fake_instance_actions.FAKE_UUID |