summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2013-02-06 22:25:57 +0000
committerGerrit Code Review <review@openstack.org>2013-02-06 22:25:57 +0000
commit749c3db3f63f70d9a71ff26e253b3eeda5d9d742 (patch)
tree714012474f0f51cd1e92e4142a24ae68547b7062
parentab987734228f00afb34d7a1cb22f693d29a520fe (diff)
parent0dbd2c5d3360a71658a146cb3947047e797a56f6 (diff)
downloadnova-749c3db3f63f70d9a71ff26e253b3eeda5d9d742.tar.gz
nova-749c3db3f63f70d9a71ff26e253b3eeda5d9d742.tar.xz
nova-749c3db3f63f70d9a71ff26e253b3eeda5d9d742.zip
Merge "Cells: Add support for compute HostAPI()"
-rw-r--r--nova/api/openstack/compute/contrib/hosts.py5
-rw-r--r--nova/cells/manager.py33
-rw-r--r--nova/cells/messaging.py61
-rw-r--r--nova/cells/rpcapi.py30
-rw-r--r--nova/cells/utils.py43
-rw-r--r--nova/compute/api.py14
-rw-r--r--nova/compute/cells_api.py85
-rw-r--r--nova/tests/cells/test_cells_manager.py86
-rw-r--r--nova/tests/cells/test_cells_messaging.py137
-rw-r--r--nova/tests/cells/test_cells_rpcapi.py35
-rw-r--r--nova/tests/compute/test_host_api.py190
11 files changed, 689 insertions, 30 deletions
diff --git a/nova/api/openstack/compute/contrib/hosts.py b/nova/api/openstack/compute/contrib/hosts.py
index d1b39d6db..9ce278900 100644
--- a/nova/api/openstack/compute/contrib/hosts.py
+++ b/nova/api/openstack/compute/contrib/hosts.py
@@ -124,11 +124,12 @@ class HostController(object):
"""
context = req.environ['nova.context']
authorize(context)
- filters = {}
+ filters = {'disabled': False}
zone = req.GET.get('zone', None)
if zone:
filters['availability_zone'] = zone
- services = self.api.service_get_all(context, filters=filters)
+ services = self.api.service_get_all(context, filters=filters,
+ set_zones=True)
hosts = []
for service in services:
hosts.append({'host_name': service['host'],
diff --git a/nova/cells/manager.py b/nova/cells/manager.py
index 133946794..6acda00c0 100644
--- a/nova/cells/manager.py
+++ b/nova/cells/manager.py
@@ -65,7 +65,7 @@ class CellsManager(manager.Manager):
Scheduling requests get passed to the scheduler class.
"""
- RPC_API_VERSION = '1.1'
+ RPC_API_VERSION = '1.2'
def __init__(self, *args, **kwargs):
# Mostly for tests.
@@ -229,3 +229,34 @@ class CellsManager(manager.Manager):
"""
self.msg_runner.sync_instances(ctxt, project_id, updated_since,
deleted)
+
+ def service_get_all(self, ctxt, filters):
+ """Return services in this cell and in all child cells."""
+ responses = self.msg_runner.service_get_all(ctxt, filters)
+ ret_services = []
+ # 1 response per cell. Each response is a list of services.
+ for response in responses:
+ services = response.value_or_raise()
+ for service in services:
+ cells_utils.add_cell_to_service(service, response.cell_name)
+ ret_services.append(service)
+ return ret_services
+
+ def service_get_by_compute_host(self, ctxt, host_name):
+ """Return a service entry for a compute host in a certain cell."""
+ cell_name, host_name = cells_utils.split_cell_and_item(host_name)
+ response = self.msg_runner.service_get_by_compute_host(ctxt,
+ cell_name,
+ host_name)
+ service = response.value_or_raise()
+ cells_utils.add_cell_to_service(service, response.cell_name)
+ return service
+
+ def proxy_rpc_to_manager(self, ctxt, topic, rpc_message, call, timeout):
+ """Proxy an RPC message as-is to a manager."""
+ compute_topic = CONF.compute_topic
+ cell_and_host = topic[len(compute_topic) + 1:]
+ cell_name, host_name = cells_utils.split_cell_and_item(cell_and_host)
+ response = self.msg_runner.proxy_rpc_to_manager(ctxt, cell_name,
+ host_name, topic, rpc_message, call, timeout)
+ return response.value_or_raise()
diff --git a/nova/cells/messaging.py b/nova/cells/messaging.py
index 34ca74855..a1b3f36de 100644
--- a/nova/cells/messaging.py
+++ b/nova/cells/messaging.py
@@ -37,6 +37,7 @@ from nova.openstack.common import excutils
from nova.openstack.common import importutils
from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging
+from nova.openstack.common import rpc
from nova.openstack.common.rpc import common as rpc_common
from nova.openstack.common import timeutils
from nova.openstack.common import uuidutils
@@ -60,7 +61,7 @@ LOG = logging.getLogger(__name__)
# Separator used between cell names for the 'full cell name' and routing
# path.
-_PATH_CELL_SEP = '!'
+_PATH_CELL_SEP = cells_utils._PATH_CELL_SEP
def _reverse_path(path):
@@ -678,6 +679,22 @@ class _TargetedMessageMethods(_BaseMessageMethods):
"""
self.msg_runner.tell_parents_our_capacities(message.ctxt)
+ def service_get_by_compute_host(self, message, host_name):
+ """Return the service entry for a compute host."""
+ service = self.db.service_get_by_compute_host(message.ctxt,
+ host_name)
+ return jsonutils.to_primitive(service)
+
+ def proxy_rpc_to_manager(self, message, host_name, rpc_message,
+ topic, timeout):
+ """Proxy RPC to the given compute topic."""
+ # Check that the host exists.
+ self.db.service_get_by_compute_host(message.ctxt, host_name)
+ if message.need_response:
+ return rpc.call(message.ctxt, topic, rpc_message,
+ timeout=timeout)
+ rpc.cast(message.ctxt, topic, rpc_message)
+
class _BroadcastMessageMethods(_BaseMessageMethods):
"""These are the methods that can be called as a part of a broadcast
@@ -800,6 +817,21 @@ class _BroadcastMessageMethods(_BaseMessageMethods):
for instance in instances:
self._sync_instance(message.ctxt, instance)
+ def service_get_all(self, message, filters):
+ if filters is None:
+ filters = {}
+ disabled = filters.pop('disabled', None)
+ services = self.db.service_get_all(message.ctxt, disabled=disabled)
+ ret_services = []
+ for service in services:
+ service = jsonutils.to_primitive(service)
+ for key, val in filters.iteritems():
+ if service[key] != val:
+ break
+ else:
+ ret_services.append(service)
+ return ret_services
+
_CELL_MESSAGE_TYPE_TO_MESSAGE_CLS = {'targeted': _TargetedMessage,
'broadcast': _BroadcastMessage,
@@ -1038,6 +1070,33 @@ class MessageRunner(object):
run_locally=False)
message.process()
+ def service_get_all(self, ctxt, filters=None):
+ method_kwargs = dict(filters=filters)
+ message = _BroadcastMessage(self, ctxt, 'service_get_all',
+ method_kwargs, 'down',
+ run_locally=True, need_response=True)
+ return message.process()
+
+ def service_get_by_compute_host(self, ctxt, cell_name, host_name):
+ method_kwargs = dict(host_name=host_name)
+ message = _TargetedMessage(self, ctxt,
+ 'service_get_by_compute_host',
+ method_kwargs, 'down', cell_name,
+ need_response=True)
+ return message.process()
+
+ def proxy_rpc_to_manager(self, ctxt, cell_name, host_name, topic,
+ rpc_message, call, timeout):
+ method_kwargs = {'host_name': host_name,
+ 'topic': topic,
+ 'rpc_message': rpc_message,
+ 'timeout': timeout}
+ message = _TargetedMessage(self, ctxt,
+ 'proxy_rpc_to_manager',
+ method_kwargs, 'down', cell_name,
+ need_response=call)
+ 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 0ab4fc352..fde2d25e1 100644
--- a/nova/cells/rpcapi.py
+++ b/nova/cells/rpcapi.py
@@ -41,6 +41,8 @@ class CellsAPI(rpc_proxy.RpcProxy):
1.0 - Initial version.
1.1 - Adds get_cell_info_for_neighbors() and sync_instances()
+ 1.2 - Adds service_get_all(), service_get_by_compute_host(),
+ and proxy_rpc_to_compute_manager()
'''
BASE_RPC_API_VERSION = '1.0'
@@ -155,3 +157,31 @@ class CellsAPI(rpc_proxy.RpcProxy):
updated_since=updated_since,
deleted=deleted),
version='1.1')
+
+ def service_get_all(self, ctxt, filters=None):
+ """Ask all cells for their list of services."""
+ return self.call(ctxt,
+ self.make_msg('service_get_all',
+ filters=filters),
+ version='1.2')
+
+ def service_get_by_compute_host(self, ctxt, host_name):
+ """Get the service entry for a host in a particular cell. The
+ cell name should be encoded within the host_name.
+ """
+ return self.call(ctxt, self.make_msg('service_get_by_compute_host',
+ host_name=host_name),
+ version='1.2')
+
+ def proxy_rpc_to_manager(self, ctxt, rpc_message, topic, call=False,
+ timeout=None):
+ """Proxy RPC to a compute manager. The host in the topic
+ should be encoded with the target cell name.
+ """
+ return self.call(ctxt, self.make_msg('proxy_rpc_to_manager',
+ topic=topic,
+ rpc_message=rpc_message,
+ call=call,
+ timeout=timeout),
+ timeout=timeout,
+ version='1.2')
diff --git a/nova/cells/utils.py b/nova/cells/utils.py
index d25f98fab..76556e0dd 100644
--- a/nova/cells/utils.py
+++ b/nova/cells/utils.py
@@ -20,6 +20,12 @@ import random
from nova import db
+# Separator used between cell names for the 'full cell name' and routing
+# path
+_PATH_CELL_SEP = '!'
+# Separator used between cell name and item
+_CELL_ITEM_SEP = '@'
+
def get_instances_to_sync(context, updated_since=None, project_id=None,
deleted=True, shuffle=False, uuids_only=False):
@@ -46,3 +52,40 @@ def get_instances_to_sync(context, updated_since=None, project_id=None,
yield instance['uuid']
else:
yield instance
+
+
+def cell_with_item(cell_name, item):
+ """Turn cell_name and item into <cell_name>@<item>."""
+ return cell_name + _CELL_ITEM_SEP + str(item)
+
+
+def split_cell_and_item(cell_and_item):
+ """Split a combined cell@item and return them."""
+ return cell_and_item.rsplit(_CELL_ITEM_SEP, 1)
+
+
+def _add_cell_to_service(service, cell_name):
+ service['id'] = cell_with_item(cell_name, service['id'])
+ service['host'] = cell_with_item(cell_name, service['host'])
+
+
+def add_cell_to_compute_node(compute_node, cell_name):
+ """Fix compute_node attributes that should be unique. Allows
+ API cell to query the 'id' by cell@id.
+ """
+ compute_node['id'] = cell_with_item(cell_name, compute_node['id'])
+ # Might have a 'service' backref. But if is_primitive() was used
+ # on this and it recursed too deep, 'service' may be "?".
+ service = compute_node.get('service')
+ if isinstance(service, dict):
+ _add_cell_to_service(service, cell_name)
+
+
+def add_cell_to_service(service, cell_name):
+ """Fix service attributes that should be unique. Allows
+ API cell to query the 'id' or 'host' by cell@id/host.
+ """
+ _add_cell_to_service(service, cell_name)
+ compute_node = service.get('compute_node')
+ if compute_node:
+ add_cell_to_compute_node(compute_node[0], cell_name)
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 23024ba29..394f09e11 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -2417,16 +2417,12 @@ class HostAPI(base.Base):
def set_host_enabled(self, context, host_name, enabled):
"""Sets the specified host's ability to accept new instances."""
- # NOTE(comstud): No instance_uuid argument to this compute manager
- # call
self._assert_host_exists(context, host_name)
return self.rpcapi.set_host_enabled(context, enabled=enabled,
host=host_name)
def get_host_uptime(self, context, host_name):
"""Returns the result of calling "uptime" on the target host."""
- # NOTE(comstud): No instance_uuid argument to this compute manager
- # call
self._assert_host_exists(context, host_name)
return self.rpcapi.get_host_uptime(context, host=host_name)
@@ -2443,7 +2439,7 @@ class HostAPI(base.Base):
return self.rpcapi.host_maintenance_mode(context,
host_param=host_name, mode=mode, host=host_name)
- def service_get_all(self, context, filters=None):
+ def service_get_all(self, context, filters=None, set_zones=False):
"""Returns a list of services, optionally filtering the results.
If specified, 'filters' should be a dictionary containing services
@@ -2452,9 +2448,11 @@ class HostAPI(base.Base):
"""
if filters is None:
filters = {}
- services = self.db.service_get_all(context, False)
- services = availability_zones.set_availability_zones(context,
- services)
+ disabled = filters.pop('disabled', None)
+ services = self.db.service_get_all(context, disabled=disabled)
+ if set_zones or 'availability_zone' in filters:
+ services = availability_zones.set_availability_zones(context,
+ services)
ret_services = []
for service in services:
for key, val in filters.iteritems():
diff --git a/nova/compute/cells_api.py b/nova/compute/cells_api.py
index e2c2a9a4a..98a49417f 100644
--- a/nova/compute/cells_api.py
+++ b/nova/compute/cells_api.py
@@ -15,10 +15,13 @@
"""Compute API that proxies via Cells Service."""
+from nova import availability_zones
from nova import block_device
from nova.cells import rpcapi as cells_rpcapi
+from nova.cells import utils as cells_utils
from nova.compute import api as compute_api
from nova.compute import instance_types
+from nova.compute import rpcapi as compute_rpcapi
from nova.compute import vm_states
from nova import exception
from nova.openstack.common import excutils
@@ -61,6 +64,27 @@ class SchedulerRPCAPIRedirect(object):
self.cells_rpcapi.schedule_run_instance(context, **kwargs)
+class ComputeRPCProxyAPI(compute_rpcapi.ComputeAPI):
+ """Class used to substitute Compute RPC API that will proxy
+ via the cells manager to a compute manager in a child cell.
+ """
+ def __init__(self, *args, **kwargs):
+ super(ComputeRPCProxyAPI, self).__init__(*args, **kwargs)
+ self.cells_rpcapi = cells_rpcapi.CellsAPI()
+
+ def cast(self, ctxt, msg, topic=None, version=None):
+ self._set_version(msg, version)
+ topic = self._get_topic(topic)
+ self.cells_rpcapi.proxy_rpc_to_manager(ctxt, msg, topic)
+
+ def call(self, ctxt, msg, topic=None, version=None, timeout=None):
+ self._set_version(msg, version)
+ topic = self._get_topic(topic)
+ return self.cells_rpcapi.proxy_rpc_to_manager(ctxt, msg, topic,
+ call=True,
+ timeout=timeout)
+
+
class ComputeCellsAPI(compute_api.API):
def __init__(self, *args, **kwargs):
super(ComputeCellsAPI, self).__init__(*args, **kwargs)
@@ -550,3 +574,64 @@ class ComputeCellsAPI(compute_api.API):
except exception.InstanceUnknownCell:
pass
return rv
+
+
+class HostAPI(compute_api.HostAPI):
+ """HostAPI() class for cells.
+
+ Implements host management related operations. Works by setting the
+ RPC API used by the base class to proxy via the cells manager to the
+ compute manager in the correct cell. Hosts specified with cells will
+ need to be of the format 'path!to!cell@host'.
+
+ DB methods in the base class are also overridden to proxy via the
+ cells manager.
+ """
+ def __init__(self):
+ super(HostAPI, self).__init__(rpcapi=ComputeRPCProxyAPI())
+ self.cells_rpcapi = cells_rpcapi.CellsAPI()
+
+ def _assert_host_exists(self, context, host_name):
+ """Cannot check this in API cell. This will be checked in the
+ target child cell.
+ """
+ pass
+
+ def service_get_all(self, context, filters=None, set_zones=False):
+ if filters is None:
+ filters = {}
+ if 'availability_zone' in filters:
+ zone_filter = filters.pop('availability_zone')
+ set_zones = True
+ else:
+ zone_filter = None
+ services = self.cells_rpcapi.service_get_all(context,
+ filters=filters)
+ if set_zones:
+ services = availability_zones.set_availability_zones(context,
+ services)
+ if zone_filter is not None:
+ services = [s for s in services
+ if s['availability_zone'] == zone_filter]
+ return services
+
+ def service_get_by_compute_host(self, context, host_name):
+ return self.cells_rpcapi.service_get_by_compute_host(context,
+ host_name)
+
+ def instance_get_all_by_host(self, context, host_name):
+ """Get all instances by host. Host might have a cell prepended
+ to it, so we'll need to strip it out. We don't need to proxy
+ this call to cells, as we have instance information here in
+ the API cell.
+ """
+ try:
+ cell_name, host_name = cells_utils.split_cell_and_item(host_name)
+ except ValueError:
+ cell_name = None
+ instances = super(HostAPI, self).instance_get_all_by_host(context,
+ host_name)
+ if cell_name:
+ instances = [i for i in instances
+ if i['cell_name'] == cell_name]
+ return instances
diff --git a/nova/tests/cells/test_cells_manager.py b/nova/tests/cells/test_cells_manager.py
index ef165f4ed..d3d412af7 100644
--- a/nova/tests/cells/test_cells_manager.py
+++ b/nova/tests/cells/test_cells_manager.py
@@ -15,15 +15,29 @@
"""
Tests For CellsManager
"""
+import copy
import datetime
from nova.cells import messaging
from nova.cells import utils as cells_utils
from nova import context
+from nova.openstack.common import cfg
+from nova.openstack.common import rpc
from nova.openstack.common import timeutils
from nova import test
from nova.tests.cells import fakes
+CONF = cfg.CONF
+CONF.import_opt('compute_topic', 'nova.compute.rpcapi')
+
+
+FAKE_COMPUTE_NODES = [dict(id=1), dict(id=2)]
+FAKE_SERVICES = [dict(id=1, host='host1',
+ compute_node=[FAKE_COMPUTE_NODES[0]]),
+ dict(id=2, host='host2',
+ compute_node=[FAKE_COMPUTE_NODES[1]]),
+ dict(id=3, host='host3', compute_node=[])]
+
class CellsManagerClassTestCase(test.TestCase):
"""Test case for CellsManager class."""
@@ -46,6 +60,14 @@ class CellsManagerClassTestCase(test.TestCase):
expected_responses.append(('cell%s' % x, x))
return expected_responses, responses
+ def _get_fake_response(self, raw_response=None, exc=False):
+ if exc:
+ return messaging.Response('fake', test.TestingException(),
+ True)
+ if raw_response is None:
+ raw_response = 'fake-response'
+ return messaging.Response('fake', raw_response, False)
+
def test_get_cell_info_for_neighbors(self):
self.mox.StubOutWithMock(self.cells_manager.state_manager,
'get_cell_info_for_neighbors')
@@ -109,17 +131,13 @@ class CellsManagerClassTestCase(test.TestCase):
cell_name = 'fake-cell-name'
method_info = 'fake-method-info'
- fake_response = messaging.Response('fake', 'fake', False)
-
self.mox.StubOutWithMock(self.msg_runner,
'run_compute_api_method')
- self.mox.StubOutWithMock(fake_response,
- 'value_or_raise')
+ fake_response = self._get_fake_response()
self.msg_runner.run_compute_api_method(self.ctxt,
cell_name,
method_info,
True).AndReturn(fake_response)
- fake_response.value_or_raise().AndReturn('fake-response')
self.mox.ReplayAll()
response = self.cells_manager.run_compute_api_method(
self.ctxt, cell_name=cell_name, method_info=method_info,
@@ -237,3 +255,61 @@ class CellsManagerClassTestCase(test.TestCase):
project_id='fake-project',
updated_since='fake-time',
deleted='fake-deleted')
+
+ def test_service_get_all(self):
+ responses = []
+ expected_response = []
+ # 3 cells... so 3 responses. Each response is a list of services.
+ # Manager should turn these into a single list of responses.
+ for i in xrange(3):
+ cell_name = 'path!to!cell%i' % i
+ services = []
+ for service in FAKE_SERVICES:
+ services.append(copy.deepcopy(service))
+ expected_service = copy.deepcopy(service)
+ cells_utils.add_cell_to_service(expected_service, cell_name)
+ expected_response.append(expected_service)
+ response = messaging.Response(cell_name, services, False)
+ responses.append(response)
+
+ self.mox.StubOutWithMock(self.msg_runner,
+ 'service_get_all')
+ self.msg_runner.service_get_all(self.ctxt,
+ 'fake-filters').AndReturn(responses)
+ self.mox.ReplayAll()
+ response = self.cells_manager.service_get_all(self.ctxt,
+ filters='fake-filters')
+ self.assertEqual(expected_response, response)
+
+ def test_service_get_by_compute_host(self):
+ self.mox.StubOutWithMock(self.msg_runner,
+ 'service_get_by_compute_host')
+ fake_cell = 'fake-cell'
+ fake_response = messaging.Response(fake_cell, FAKE_SERVICES[0],
+ False)
+ expected_response = copy.deepcopy(FAKE_SERVICES[0])
+ cells_utils.add_cell_to_service(expected_response, fake_cell)
+
+ cell_and_host = cells_utils.cell_with_item('fake-cell', 'fake-host')
+ self.msg_runner.service_get_by_compute_host(self.ctxt,
+ fake_cell, 'fake-host').AndReturn(fake_response)
+ self.mox.ReplayAll()
+ response = self.cells_manager.service_get_by_compute_host(self.ctxt,
+ host_name=cell_and_host)
+ self.assertEqual(expected_response, response)
+
+ def test_proxy_rpc_to_manager(self):
+ self.mox.StubOutWithMock(self.msg_runner,
+ 'proxy_rpc_to_manager')
+ fake_response = self._get_fake_response()
+ cell_and_host = cells_utils.cell_with_item('fake-cell', 'fake-host')
+ topic = rpc.queue_get_for(self.ctxt, CONF.compute_topic,
+ cell_and_host)
+ self.msg_runner.proxy_rpc_to_manager(self.ctxt, 'fake-cell',
+ 'fake-host', topic, 'fake-rpc-msg',
+ True, -1).AndReturn(fake_response)
+ self.mox.ReplayAll()
+ response = self.cells_manager.proxy_rpc_to_manager(self.ctxt,
+ topic=topic, rpc_message='fake-rpc-msg', call=True,
+ timeout=-1)
+ self.assertEqual('fake-response', response)
diff --git a/nova/tests/cells/test_cells_messaging.py b/nova/tests/cells/test_cells_messaging.py
index 1208368c2..f6c51325a 100644
--- a/nova/tests/cells/test_cells_messaging.py
+++ b/nova/tests/cells/test_cells_messaging.py
@@ -19,6 +19,7 @@ from nova.cells import utils as cells_utils
from nova import context
from nova import exception
from nova.openstack.common import cfg
+from nova.openstack.common import rpc
from nova.openstack.common import timeutils
from nova import test
from nova.tests.cells import fakes
@@ -602,7 +603,7 @@ class CellsTargetedMethodsTestCase(test.TestCase):
self.tgt_cell_name,
host_sched_kwargs)
- def test_call_compute_api_method(self):
+ def test_run_compute_api_method(self):
instance_uuid = 'fake_instance_uuid'
method_info = {'method': 'reboot',
@@ -612,8 +613,7 @@ class CellsTargetedMethodsTestCase(test.TestCase):
self.mox.StubOutWithMock(self.tgt_db_inst, 'instance_get_by_uuid')
self.tgt_db_inst.instance_get_by_uuid(self.ctxt,
- instance_uuid).AndReturn(
- 'fake_instance')
+ instance_uuid).AndReturn('fake_instance')
self.tgt_compute_api.reboot(self.ctxt, 'fake_instance', 2, 3,
arg1='val1', arg2='val2').AndReturn('fake_result')
self.mox.ReplayAll()
@@ -626,7 +626,7 @@ class CellsTargetedMethodsTestCase(test.TestCase):
result = response.value_or_raise()
self.assertEqual('fake_result', result)
- def test_call_compute_api_method_unknown_instance(self):
+ def test_run_compute_api_method_unknown_instance(self):
# Unknown instance should send a broadcast up that instance
# is gone.
instance_uuid = 'fake_instance_uuid'
@@ -725,6 +725,70 @@ class CellsTargetedMethodsTestCase(test.TestCase):
self.src_msg_runner.ask_children_for_capacities(self.ctxt)
+ def test_service_get_by_compute_host(self):
+ fake_host_name = 'fake-host-name'
+
+ self.mox.StubOutWithMock(self.tgt_db_inst,
+ 'service_get_by_compute_host')
+
+ self.tgt_db_inst.service_get_by_compute_host(self.ctxt,
+ fake_host_name).AndReturn('fake-service')
+ self.mox.ReplayAll()
+
+ response = self.src_msg_runner.service_get_by_compute_host(
+ self.ctxt,
+ self.tgt_cell_name,
+ fake_host_name)
+ result = response.value_or_raise()
+ self.assertEqual('fake-service', result)
+
+ def test_proxy_rpc_to_manager_call(self):
+ fake_topic = 'fake-topic'
+ fake_rpc_message = 'fake-rpc-message'
+ fake_host_name = 'fake-host-name'
+
+ self.mox.StubOutWithMock(self.tgt_db_inst,
+ 'service_get_by_compute_host')
+ self.mox.StubOutWithMock(rpc, 'call')
+
+ self.tgt_db_inst.service_get_by_compute_host(self.ctxt,
+ fake_host_name)
+ rpc.call(self.ctxt, fake_topic,
+ fake_rpc_message, timeout=5).AndReturn('fake_result')
+
+ self.mox.ReplayAll()
+
+ response = self.src_msg_runner.proxy_rpc_to_manager(
+ self.ctxt,
+ self.tgt_cell_name,
+ fake_host_name,
+ fake_topic,
+ fake_rpc_message, True, timeout=5)
+ result = response.value_or_raise()
+ self.assertEqual('fake_result', result)
+
+ def test_proxy_rpc_to_manager_cast(self):
+ fake_topic = 'fake-topic'
+ fake_rpc_message = 'fake-rpc-message'
+ fake_host_name = 'fake-host-name'
+
+ self.mox.StubOutWithMock(self.tgt_db_inst,
+ 'service_get_by_compute_host')
+ self.mox.StubOutWithMock(rpc, 'cast')
+
+ self.tgt_db_inst.service_get_by_compute_host(self.ctxt,
+ fake_host_name)
+ rpc.cast(self.ctxt, fake_topic, fake_rpc_message)
+
+ self.mox.ReplayAll()
+
+ self.src_msg_runner.proxy_rpc_to_manager(
+ self.ctxt,
+ self.tgt_cell_name,
+ fake_host_name,
+ fake_topic,
+ fake_rpc_message, False, timeout=None)
+
class CellsBroadcastMethodsTestCase(test.TestCase):
"""Test case for _BroadcastMessageMethods class. Most of these
@@ -754,6 +818,13 @@ class CellsBroadcastMethodsTestCase(test.TestCase):
self.src_db_inst = methods_cls.db
self.src_compute_api = methods_cls.compute_api
+ if not up:
+ # fudge things so we only have 1 child to broadcast to
+ state_manager = self.src_msg_runner.state_manager
+ for cell in state_manager.get_child_cells():
+ if cell.name != 'child-cell2':
+ del state_manager.child_cells[cell.name]
+
self.mid_msg_runner = fakes.get_message_runner(mid_cell)
methods_cls = self.mid_msg_runner.methods_by_type['broadcast']
self.mid_methods_cls = methods_cls
@@ -956,3 +1027,61 @@ class CellsBroadcastMethodsTestCase(test.TestCase):
self.src_msg_runner.sync_instances(self.ctxt,
project_id, updated_since_raw, deleted)
+
+ def test_service_get_all_with_disabled(self):
+ # Reset this, as this is a broadcast down.
+ self._setup_attrs(up=False)
+
+ ctxt = self.ctxt.elevated()
+
+ self.mox.StubOutWithMock(self.src_db_inst, 'service_get_all')
+ self.mox.StubOutWithMock(self.mid_db_inst, 'service_get_all')
+ self.mox.StubOutWithMock(self.tgt_db_inst, 'service_get_all')
+
+ self.src_db_inst.service_get_all(ctxt,
+ disabled=None).AndReturn([1, 2])
+ self.mid_db_inst.service_get_all(ctxt,
+ disabled=None).AndReturn([3])
+ self.tgt_db_inst.service_get_all(ctxt,
+ disabled=None).AndReturn([4, 5])
+
+ self.mox.ReplayAll()
+
+ responses = self.src_msg_runner.service_get_all(ctxt,
+ filters={})
+ response_values = [(resp.cell_name, resp.value_or_raise())
+ for resp in responses]
+ expected = [('api-cell!child-cell2!grandchild-cell1', [4, 5]),
+ ('api-cell!child-cell2', [3]),
+ ('api-cell', [1, 2])]
+ self.assertEqual(expected, response_values)
+
+ def test_service_get_all_without_disabled(self):
+ # Reset this, as this is a broadcast down.
+ self._setup_attrs(up=False)
+ disabled = False
+ filters = {'disabled': disabled}
+
+ ctxt = self.ctxt.elevated()
+
+ self.mox.StubOutWithMock(self.src_db_inst, 'service_get_all')
+ self.mox.StubOutWithMock(self.mid_db_inst, 'service_get_all')
+ self.mox.StubOutWithMock(self.tgt_db_inst, 'service_get_all')
+
+ self.src_db_inst.service_get_all(ctxt,
+ disabled=disabled).AndReturn([1, 2])
+ self.mid_db_inst.service_get_all(ctxt,
+ disabled=disabled).AndReturn([3])
+ self.tgt_db_inst.service_get_all(ctxt,
+ disabled=disabled).AndReturn([4, 5])
+
+ self.mox.ReplayAll()
+
+ responses = self.src_msg_runner.service_get_all(ctxt,
+ filters=filters)
+ response_values = [(resp.cell_name, resp.value_or_raise())
+ for resp in responses]
+ expected = [('api-cell!child-cell2!grandchild-cell1', [4, 5]),
+ ('api-cell!child-cell2', [3]),
+ ('api-cell', [1, 2])]
+ self.assertEqual(expected, response_values)
diff --git a/nova/tests/cells/test_cells_rpcapi.py b/nova/tests/cells/test_cells_rpcapi.py
index 5e045aca9..876bc5ce5 100644
--- a/nova/tests/cells/test_cells_rpcapi.py
+++ b/nova/tests/cells/test_cells_rpcapi.py
@@ -224,3 +224,38 @@ class CellsAPITestCase(test.TestCase):
'deleted': True}
self._check_result(call_info, 'sync_instances', expected_args,
version='1.1')
+
+ def test_service_get_all(self):
+ call_info = self._stub_rpc_method('call', 'fake_response')
+ fake_filters = {'key1': 'val1', 'key2': 'val2'}
+ result = self.cells_rpcapi.service_get_all(self.fake_context,
+ filters=fake_filters)
+
+ expected_args = {'filters': fake_filters}
+ self._check_result(call_info, 'service_get_all', expected_args,
+ version='1.2')
+ self.assertEqual(result, 'fake_response')
+
+ def test_service_get_by_compute_host(self):
+ call_info = self._stub_rpc_method('call', 'fake_response')
+ result = self.cells_rpcapi.service_get_by_compute_host(
+ self.fake_context, host_name='fake-host-name')
+ expected_args = {'host_name': 'fake-host-name'}
+ self._check_result(call_info, 'service_get_by_compute_host',
+ expected_args,
+ version='1.2')
+ self.assertEqual(result, 'fake_response')
+
+ def test_proxy_rpc_to_manager(self):
+ call_info = self._stub_rpc_method('call', 'fake_response')
+ result = self.cells_rpcapi.proxy_rpc_to_manager(
+ self.fake_context, rpc_message='fake-msg',
+ topic='fake-topic', call=True, timeout=-1)
+ expected_args = {'rpc_message': 'fake-msg',
+ 'topic': 'fake-topic',
+ 'call': True,
+ 'timeout': -1}
+ self._check_result(call_info, 'proxy_rpc_to_manager',
+ expected_args,
+ version='1.2')
+ self.assertEqual(result, 'fake_response')
diff --git a/nova/tests/compute/test_host_api.py b/nova/tests/compute/test_host_api.py
index 95d3c4926..151743715 100644
--- a/nova/tests/compute/test_host_api.py
+++ b/nova/tests/compute/test_host_api.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+from nova.cells import utils as cells_utils
from nova import compute
from nova.compute import rpcapi as compute_rpcapi
from nova import context
@@ -84,6 +85,42 @@ class ComputeHostAPITestCase(test.TestCase):
'fake_mode')
self.assertEqual('fake-result', result)
+ def test_service_get_all_no_zones(self):
+ services = [dict(id=1, key1='val1', key2='val2', topic='compute',
+ host='host1'),
+ dict(id=2, key1='val2', key3='val3', topic='compute',
+ host='host2')]
+
+ self.mox.StubOutWithMock(self.host_api.db,
+ 'service_get_all')
+
+ # Test no filters
+ self.host_api.db.service_get_all(self.ctxt,
+ disabled=None).AndReturn(services)
+ self.mox.ReplayAll()
+ result = self.host_api.service_get_all(self.ctxt)
+ self.mox.VerifyAll()
+ self.assertEqual(services, result)
+
+ # Test no filters #2
+ self.mox.ResetAll()
+ self.host_api.db.service_get_all(self.ctxt,
+ disabled=None).AndReturn(services)
+ self.mox.ReplayAll()
+ result = self.host_api.service_get_all(self.ctxt, filters={})
+ self.mox.VerifyAll()
+ self.assertEqual(services, result)
+
+ # Test w/ filter
+ self.mox.ResetAll()
+ self.host_api.db.service_get_all(self.ctxt,
+ disabled=None).AndReturn(services)
+ self.mox.ReplayAll()
+ result = self.host_api.service_get_all(self.ctxt,
+ filters=dict(key1='val2'))
+ self.mox.VerifyAll()
+ self.assertEqual([services[1]], result)
+
def test_service_get_all(self):
services = [dict(id=1, key1='val1', key2='val2', topic='compute',
host='host1'),
@@ -99,28 +136,163 @@ class ComputeHostAPITestCase(test.TestCase):
'service_get_all')
# Test no filters
- self.host_api.db.service_get_all(self.ctxt, False).AndReturn(
- services)
+ self.host_api.db.service_get_all(self.ctxt,
+ disabled=None).AndReturn(services)
self.mox.ReplayAll()
- result = self.host_api.service_get_all(self.ctxt)
+ result = self.host_api.service_get_all(self.ctxt, set_zones=True)
self.mox.VerifyAll()
self.assertEqual(exp_services, result)
# Test no filters #2
self.mox.ResetAll()
- self.host_api.db.service_get_all(self.ctxt, False).AndReturn(
- services)
+ self.host_api.db.service_get_all(self.ctxt,
+ disabled=None).AndReturn(services)
self.mox.ReplayAll()
- result = self.host_api.service_get_all(self.ctxt, filters={})
+ result = self.host_api.service_get_all(self.ctxt, filters={},
+ set_zones=True)
self.mox.VerifyAll()
self.assertEqual(exp_services, result)
# Test w/ filter
self.mox.ResetAll()
- self.host_api.db.service_get_all(self.ctxt, False).AndReturn(
- services)
+ self.host_api.db.service_get_all(self.ctxt,
+ disabled=None).AndReturn(services)
self.mox.ReplayAll()
result = self.host_api.service_get_all(self.ctxt,
- filters=dict(key1='val2'))
+ filters=dict(key1='val2'),
+ set_zones=True)
self.mox.VerifyAll()
self.assertEqual([exp_services[1]], result)
+
+ # Test w/ zone filter but no set_zones arg.
+ self.mox.ResetAll()
+ self.host_api.db.service_get_all(self.ctxt,
+ disabled=None).AndReturn(services)
+ self.mox.ReplayAll()
+ filters = {'availability_zone': 'nova'}
+ result = self.host_api.service_get_all(self.ctxt,
+ filters=filters)
+ self.mox.VerifyAll()
+ self.assertEqual(exp_services, result)
+
+ def test_service_get_by_compute_host(self):
+ self.mox.StubOutWithMock(self.host_api.db,
+ 'service_get_by_compute_host')
+
+ self.host_api.db.service_get_by_compute_host(self.ctxt,
+ 'fake-host').AndReturn('fake-response')
+ self.mox.ReplayAll()
+ result = self.host_api.service_get_by_compute_host(self.ctxt,
+ 'fake-host')
+ self.assertEqual('fake-response', result)
+
+ def test_instance_get_all_by_host(self):
+ self.mox.StubOutWithMock(self.host_api.db,
+ 'instance_get_all_by_host')
+
+ self.host_api.db.instance_get_all_by_host(self.ctxt,
+ 'fake-host').AndReturn(['fake-responses'])
+ self.mox.ReplayAll()
+ result = self.host_api.instance_get_all_by_host(self.ctxt,
+ 'fake-host')
+ self.assertEqual(['fake-responses'], result)
+
+
+class ComputeHostAPICellsTestCase(ComputeHostAPITestCase):
+ def setUp(self):
+ self.flags(compute_api_class='nova.compute.cells_api.ComputeCellsAPI')
+ super(ComputeHostAPICellsTestCase, self).setUp()
+
+ def _mock_rpc_call(self, expected_message, result=None):
+ if result is None:
+ result = 'fake-result'
+ # Wrapped with cells call
+ expected_message = {'method': 'proxy_rpc_to_manager',
+ 'args': {'topic': 'compute.fake_host',
+ 'rpc_message': expected_message,
+ 'call': True,
+ 'timeout': None},
+ 'version': '1.2'}
+ self.mox.StubOutWithMock(rpc, 'call')
+ rpc.call(self.ctxt, 'cells', expected_message,
+ None).AndReturn(result)
+
+ def test_service_get_all_no_zones(self):
+ services = [dict(id=1, key1='val1', key2='val2', topic='compute',
+ host='host1'),
+ dict(id=2, key1='val2', key3='val3', topic='compute',
+ host='host2')]
+
+ fake_filters = {'key1': 'val1'}
+ self.mox.StubOutWithMock(self.host_api.cells_rpcapi,
+ 'service_get_all')
+ self.host_api.cells_rpcapi.service_get_all(self.ctxt,
+ filters=fake_filters).AndReturn(services)
+ self.mox.ReplayAll()
+ result = self.host_api.service_get_all(self.ctxt,
+ filters=fake_filters)
+ self.assertEqual(services, result)
+
+ def test_service_get_all(self):
+ services = [dict(id=1, key1='val1', key2='val2', topic='compute',
+ host='host1'),
+ dict(id=2, key1='val2', key3='val3', topic='compute',
+ host='host2')]
+ exp_services = []
+ for service in services:
+ exp_service = {}
+ exp_service.update(availability_zone='nova', **service)
+ exp_services.append(exp_service)
+
+ fake_filters = {'key1': 'val1'}
+ self.mox.StubOutWithMock(self.host_api.cells_rpcapi,
+ 'service_get_all')
+ self.host_api.cells_rpcapi.service_get_all(self.ctxt,
+ filters=fake_filters).AndReturn(services)
+ self.mox.ReplayAll()
+ result = self.host_api.service_get_all(self.ctxt,
+ filters=fake_filters,
+ set_zones=True)
+ self.mox.VerifyAll()
+ self.assertEqual(exp_services, result)
+
+ # Test w/ zone filter but no set_zones arg.
+ self.mox.ResetAll()
+ fake_filters = {'availability_zone': 'nova'}
+ # Zone filter is done client-size, so should be stripped
+ # from this call.
+ self.host_api.cells_rpcapi.service_get_all(self.ctxt,
+ filters={}).AndReturn(services)
+ self.mox.ReplayAll()
+ result = self.host_api.service_get_all(self.ctxt,
+ filters=fake_filters)
+ self.mox.VerifyAll()
+ self.assertEqual(exp_services, result)
+
+ def test_service_get_by_compute_host(self):
+ self.mox.StubOutWithMock(self.host_api.cells_rpcapi,
+ 'service_get_by_compute_host')
+
+ self.host_api.cells_rpcapi.service_get_by_compute_host(self.ctxt,
+ 'fake-host').AndReturn('fake-response')
+ self.mox.ReplayAll()
+ result = self.host_api.service_get_by_compute_host(self.ctxt,
+ 'fake-host')
+ self.assertEqual('fake-response', result)
+
+ def test_instance_get_all_by_host(self):
+ instances = [dict(id=1, cell_name='cell1', host='host1'),
+ dict(id=2, cell_name='cell2', host='host1'),
+ dict(id=3, cell_name='cell1', host='host2')]
+
+ self.mox.StubOutWithMock(self.host_api.db,
+ 'instance_get_all_by_host')
+
+ self.host_api.db.instance_get_all_by_host(self.ctxt,
+ 'fake-host').AndReturn(instances)
+ self.mox.ReplayAll()
+ expected_result = [instances[0], instances[2]]
+ cell_and_host = cells_utils.cell_with_item('cell1', 'fake-host')
+ result = self.host_api.instance_get_all_by_host(self.ctxt,
+ cell_and_host)
+ self.assertEqual(expected_result, result)