summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/api/openstack/compute/contrib/instance_actions.py10
-rw-r--r--nova/cells/manager.py18
-rw-r--r--nova/cells/messaging.py36
-rw-r--r--nova/cells/rpcapi.py28
-rw-r--r--nova/cells/scheduler.py11
-rw-r--r--nova/compute/__init__.py12
-rw-r--r--nova/compute/api.py14
-rw-r--r--nova/compute/cells_api.py18
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_instance_actions.py5
-rw-r--r--nova/tests/cells/test_cells_manager.py44
-rw-r--r--nova/tests/cells/test_cells_messaging.py47
-rw-r--r--nova/tests/cells/test_cells_rpcapi.py55
-rw-r--r--nova/tests/integrated/test_api_samples.py4
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 50a673464..db49cc8fd 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
@@ -1183,6 +1196,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 bba6ee1eb..d45ee6703 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -2557,6 +2557,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 22e31a8e1..32f09ee90 100644
--- a/nova/compute/cells_api.py
+++ b/nova/compute/cells_api.py
@@ -667,3 +667,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 aaf22ff9d..d48ef5091 100644
--- a/nova/tests/integrated/test_api_samples.py
+++ b/nova/tests/integrated/test_api_samples.py
@@ -3165,12 +3165,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