summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/api/openstack/compute/contrib/hypervisors.py39
-rw-r--r--nova/cells/manager.py38
-rw-r--r--nova/cells/messaging.py43
-rw-r--r--nova/cells/rpcapi.py22
-rw-r--r--nova/compute/api.py14
-rw-r--r--nova/compute/cells_api.py16
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_hypervisors.py4
-rw-r--r--nova/tests/cells/test_cells_manager.py58
-rw-r--r--nova/tests/cells/test_cells_messaging.py96
-rw-r--r--nova/tests/cells/test_cells_rpcapi.py27
10 files changed, 337 insertions, 20 deletions
diff --git a/nova/api/openstack/compute/contrib/hypervisors.py b/nova/api/openstack/compute/contrib/hypervisors.py
index 6580212a9..7e477bbf3 100644
--- a/nova/api/openstack/compute/contrib/hypervisors.py
+++ b/nova/api/openstack/compute/contrib/hypervisors.py
@@ -20,8 +20,7 @@ import webob.exc
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
-from nova.compute import api as compute_api
-from nova import db
+from nova import compute
from nova import exception
from nova.openstack.common import log as logging
@@ -128,7 +127,7 @@ class HypervisorsController(object):
"""The Hypervisors API controller for the OpenStack API."""
def __init__(self):
- self.api = compute_api.HostAPI()
+ self.host_api = compute.HostAPI()
super(HypervisorsController, self).__init__()
def _view_hypervisor(self, hypervisor, detail, servers=None, **kwargs):
@@ -164,22 +163,24 @@ class HypervisorsController(object):
def index(self, req):
context = req.environ['nova.context']
authorize(context)
+ compute_nodes = self.host_api.compute_node_get_all(context)
return dict(hypervisors=[self._view_hypervisor(hyp, False)
- for hyp in db.compute_node_get_all(context)])
+ for hyp in compute_nodes])
@wsgi.serializers(xml=HypervisorDetailTemplate)
def detail(self, req):
context = req.environ['nova.context']
authorize(context)
+ compute_nodes = self.host_api.compute_node_get_all(context)
return dict(hypervisors=[self._view_hypervisor(hyp, True)
- for hyp in db.compute_node_get_all(context)])
+ for hyp in compute_nodes])
@wsgi.serializers(xml=HypervisorTemplate)
def show(self, req, id):
context = req.environ['nova.context']
authorize(context)
try:
- hyp = db.compute_node_get(context, int(id))
+ hyp = self.host_api.compute_node_get(context, id)
except (ValueError, exception.ComputeHostNotFound):
msg = _("Hypervisor with ID '%s' could not be found.") % id
raise webob.exc.HTTPNotFound(explanation=msg)
@@ -190,7 +191,7 @@ class HypervisorsController(object):
context = req.environ['nova.context']
authorize(context)
try:
- hyp = db.compute_node_get(context, int(id))
+ hyp = self.host_api.compute_node_get(context, id)
except (ValueError, exception.ComputeHostNotFound):
msg = _("Hypervisor with ID '%s' could not be found.") % id
raise webob.exc.HTTPNotFound(explanation=msg)
@@ -198,7 +199,7 @@ class HypervisorsController(object):
# Get the uptime
try:
host = hyp['service']['host']
- uptime = self.api.get_host_uptime(context, host)
+ uptime = self.host_api.get_host_uptime(context, host)
except NotImplementedError:
msg = _("Virt driver does not implement uptime function.")
raise webob.exc.HTTPNotImplemented(explanation=msg)
@@ -210,7 +211,8 @@ class HypervisorsController(object):
def search(self, req, id):
context = req.environ['nova.context']
authorize(context)
- hypervisors = db.compute_node_search_by_hypervisor(context, id)
+ hypervisors = self.host_api.compute_node_search_by_hypervisor(
+ context, id)
if hypervisors:
return dict(hypervisors=[self._view_hypervisor(hyp, False)
for hyp in hypervisors])
@@ -222,21 +224,24 @@ class HypervisorsController(object):
def servers(self, req, id):
context = req.environ['nova.context']
authorize(context)
- hypervisors = db.compute_node_search_by_hypervisor(context, id)
- if hypervisors:
- return dict(hypervisors=[self._view_hypervisor(hyp, False,
- db.instance_get_all_by_host(context,
- hyp['service']['host']))
- for hyp in hypervisors])
- else:
+ compute_nodes = self.host_api.compute_node_search_by_hypervisor(
+ context, id)
+ if not compute_nodes:
msg = _("No hypervisor matching '%s' could be found.") % id
raise webob.exc.HTTPNotFound(explanation=msg)
+ hypervisors = []
+ for compute_node in compute_nodes:
+ instances = self.host_api.instance_get_all_by_host(context,
+ compute_node['service']['host'])
+ hyp = self._view_hypervisor(compute_node, False, instances)
+ hypervisors.append(hyp)
+ return dict(hypervisors=hypervisors)
@wsgi.serializers(xml=HypervisorStatisticsTemplate)
def statistics(self, req):
context = req.environ['nova.context']
authorize(context)
- stats = db.compute_node_statistics(context)
+ stats = self.host_api.compute_node_statistics(context)
return dict(hypervisor_statistics=stats)
diff --git a/nova/cells/manager.py b/nova/cells/manager.py
index f69e3c931..c07a23ebb 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.3'
+ RPC_API_VERSION = '1.4'
def __init__(self, *args, **kwargs):
# Mostly for tests.
@@ -295,3 +295,39 @@ class CellsManager(manager.Manager):
response.cell_name)
ret_task_logs.append(task_log)
return ret_task_logs
+
+ def compute_node_get(self, ctxt, compute_id):
+ """Get a compute node by ID in a specific cell."""
+ cell_name, compute_id = cells_utils.split_cell_and_item(
+ compute_id)
+ response = self.msg_runner.compute_node_get(ctxt, cell_name,
+ compute_id)
+ node = response.value_or_raise()
+ cells_utils.add_cell_to_compute_node(node, cell_name)
+ return node
+
+ def compute_node_get_all(self, ctxt, hypervisor_match=None):
+ """Return list of compute nodes in all cells."""
+ responses = self.msg_runner.compute_node_get_all(ctxt,
+ hypervisor_match=hypervisor_match)
+ # 1 response per cell. Each response is a list of compute_node
+ # entries.
+ ret_nodes = []
+ for response in responses:
+ nodes = response.value_or_raise()
+ for node in nodes:
+ cells_utils.add_cell_to_compute_node(node,
+ response.cell_name)
+ ret_nodes.append(node)
+ return ret_nodes
+
+ def compute_node_stats(self, ctxt):
+ """Return compute node stats totals from all cells."""
+ responses = self.msg_runner.compute_node_stats(ctxt)
+ totals = {}
+ for response in responses:
+ data = response.value_or_raise()
+ for key, val in data.iteritems():
+ totals.setdefault(key, 0)
+ totals[key] += val
+ return totals
diff --git a/nova/cells/messaging.py b/nova/cells/messaging.py
index 8e66f57ca..5c7247085 100644
--- a/nova/cells/messaging.py
+++ b/nova/cells/messaging.py
@@ -711,6 +711,12 @@ class _TargetedMessageMethods(_BaseMessageMethods):
timeout=timeout)
rpc.cast(message.ctxt, topic, rpc_message)
+ def compute_node_get(self, message, compute_id):
+ """Get compute node by ID."""
+ compute_node = self.db.compute_node_get(message.ctxt,
+ compute_id)
+ return jsonutils.to_primitive(compute_node)
+
class _BroadcastMessageMethods(_BaseMessageMethods):
"""These are the methods that can be called as a part of a broadcast
@@ -848,6 +854,19 @@ class _BroadcastMessageMethods(_BaseMessageMethods):
ret_services.append(service)
return ret_services
+ def compute_node_get_all(self, message, hypervisor_match):
+ """Return compute nodes in this cell."""
+ if hypervisor_match is not None:
+ nodes = self.db.compute_node_search_by_hypervisor(message.ctxt,
+ hypervisor_match)
+ else:
+ nodes = self.db.compute_node_get_all(message.ctxt)
+ return jsonutils.to_primitive(nodes)
+
+ def compute_node_stats(self, message):
+ """Return compute node stats from this cell."""
+ return self.db.compute_node_statistics(message.ctxt)
+
_CELL_MESSAGE_TYPE_TO_MESSAGE_CLS = {'targeted': _TargetedMessage,
'broadcast': _BroadcastMessage,
@@ -1140,6 +1159,30 @@ class MessageRunner(object):
run_locally=True, need_response=True)
return message.process()
+ def compute_node_get_all(self, ctxt, hypervisor_match=None):
+ """Return list of compute nodes in all child cells."""
+ method_kwargs = dict(hypervisor_match=hypervisor_match)
+ message = _BroadcastMessage(self, ctxt, 'compute_node_get_all',
+ method_kwargs, 'down',
+ run_locally=True, need_response=True)
+ return message.process()
+
+ def compute_node_stats(self, ctxt):
+ """Return compute node stats from all child cells."""
+ method_kwargs = dict()
+ message = _BroadcastMessage(self, ctxt, 'compute_node_stats',
+ method_kwargs, 'down',
+ run_locally=True, need_response=True)
+ return message.process()
+
+ def compute_node_get(self, ctxt, cell_name, compute_id):
+ """Return compute node entry from a specific cell by ID."""
+ method_kwargs = dict(compute_id=compute_id)
+ message = _TargetedMessage(self, ctxt, 'compute_node_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 95cb35706..e7db2582f 100644
--- a/nova/cells/rpcapi.py
+++ b/nova/cells/rpcapi.py
@@ -44,6 +44,8 @@ class CellsAPI(rpc_proxy.RpcProxy):
1.2 - Adds service_get_all(), service_get_by_compute_host(),
and proxy_rpc_to_compute_manager()
1.3 - Adds task_log_get_all()
+ 1.4 - Adds compute_node_get(), compute_node_get_all(), and
+ compute_node_stats()
'''
BASE_RPC_API_VERSION = '1.0'
@@ -196,3 +198,23 @@ class CellsAPI(rpc_proxy.RpcProxy):
period_ending=period_ending,
host=host, state=state),
version='1.3')
+
+ def compute_node_get(self, ctxt, compute_id):
+ """Get a compute node by ID in a specific cell."""
+ return self.call(ctxt, self.make_msg('compute_node_get',
+ compute_id=compute_id),
+ version='1.4')
+
+ def compute_node_get_all(self, ctxt, hypervisor_match=None):
+ """Return list of compute nodes in all cells, optionally
+ filtering by hypervisor host.
+ """
+ return self.call(ctxt,
+ self.make_msg('compute_node_get_all',
+ hypervisor_match=hypervisor_match),
+ version='1.4')
+
+ def compute_node_stats(self, ctxt):
+ """Return compute node stats from all cells."""
+ return self.call(ctxt, self.make_msg('compute_node_stats'),
+ version='1.4')
diff --git a/nova/compute/api.py b/nova/compute/api.py
index f524a6705..0119f6602 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -2482,6 +2482,20 @@ class HostAPI(base.Base):
host=host,
state=state)
+ def compute_node_get(self, context, compute_id):
+ """Return compute node entry for particular integer ID."""
+ return self.db.compute_node_get(context, int(compute_id))
+
+ def compute_node_get_all(self, context):
+ return self.db.compute_node_get_all(context)
+
+ def compute_node_search_by_hypervisor(self, context, hypervisor_match):
+ return self.db.compute_node_search_by_hypervisor(context,
+ hypervisor_match)
+
+ def compute_node_statistics(self, context):
+ return self.db.compute_node_statistics(context)
+
class AggregateAPI(base.Base):
"""Sub-set of the Compute Manager API for managing host aggregates."""
diff --git a/nova/compute/cells_api.py b/nova/compute/cells_api.py
index 88ff70790..50449df04 100644
--- a/nova/compute/cells_api.py
+++ b/nova/compute/cells_api.py
@@ -649,3 +649,19 @@ class HostAPI(compute_api.HostAPI):
ending,
host=host,
state=state)
+
+ def compute_node_get(self, context, compute_id):
+ """Get a compute node from a particular cell by its integer ID.
+ compute_id should be in the format of 'path!to!cell@ID'.
+ """
+ return self.cells_rpcapi.compute_node_get(context, compute_id)
+
+ def compute_node_get_all(self, context):
+ return self.cells_rpcapi.compute_node_get_all(context)
+
+ def compute_node_search_by_hypervisor(self, context, hypervisor_match):
+ return self.cells_rpcapi.compute_node_get_all(context,
+ hypervisor_match=hypervisor_match)
+
+ def compute_node_statistics(self, context):
+ return self.cells_rpcapi.compute_node_stats(context)
diff --git a/nova/tests/api/openstack/compute/contrib/test_hypervisors.py b/nova/tests/api/openstack/compute/contrib/test_hypervisors.py
index 4e4d214cc..f93c64487 100644
--- a/nova/tests/api/openstack/compute/contrib/test_hypervisors.py
+++ b/nova/tests/api/openstack/compute/contrib/test_hypervisors.py
@@ -267,7 +267,7 @@ class HypervisorsTest(test.TestCase):
def fake_get_host_uptime(context, hyp):
raise exc.HTTPNotImplemented()
- self.stubs.Set(self.controller.api, 'get_host_uptime',
+ self.stubs.Set(self.controller.host_api, 'get_host_uptime',
fake_get_host_uptime)
req = fakes.HTTPRequest.blank('/v2/fake/os-hypervisors/1')
@@ -278,7 +278,7 @@ class HypervisorsTest(test.TestCase):
def fake_get_host_uptime(context, hyp):
return "fake uptime"
- self.stubs.Set(self.controller.api, 'get_host_uptime',
+ self.stubs.Set(self.controller.host_api, 'get_host_uptime',
fake_get_host_uptime)
req = fakes.HTTPRequest.blank('/v2/fake/os-hypervisors/1')
diff --git a/nova/tests/cells/test_cells_manager.py b/nova/tests/cells/test_cells_manager.py
index df670b91f..1ebbc407d 100644
--- a/nova/tests/cells/test_cells_manager.py
+++ b/nova/tests/cells/test_cells_manager.py
@@ -370,3 +370,61 @@ class CellsManagerClassTestCase(test.TestCase):
period_beginning='fake-begin', period_ending='fake-end',
host=cell_and_host, state='fake-state')
self.assertEqual(expected_response, response)
+
+ def test_compute_node_get_all(self):
+ responses = []
+ expected_response = []
+ # 3 cells... so 3 responses. Each response is a list of computes.
+ # Manager should turn these into a single list of responses.
+ for i in xrange(3):
+ cell_name = 'path!to!cell%i' % i
+ compute_nodes = []
+ for compute_node in FAKE_COMPUTE_NODES:
+ compute_nodes.append(copy.deepcopy(compute_node))
+ expected_compute_node = copy.deepcopy(compute_node)
+ cells_utils.add_cell_to_compute_node(expected_compute_node,
+ cell_name)
+ expected_response.append(expected_compute_node)
+ response = messaging.Response(cell_name, compute_nodes, False)
+ responses.append(response)
+ self.mox.StubOutWithMock(self.msg_runner,
+ 'compute_node_get_all')
+ self.msg_runner.compute_node_get_all(self.ctxt,
+ hypervisor_match='fake-match').AndReturn(responses)
+ self.mox.ReplayAll()
+ response = self.cells_manager.compute_node_get_all(self.ctxt,
+ hypervisor_match='fake-match')
+ self.assertEqual(expected_response, response)
+
+ def test_compute_node_stats(self):
+ raw_resp1 = {'key1': 1, 'key2': 2}
+ raw_resp2 = {'key2': 1, 'key3': 2}
+ raw_resp3 = {'key3': 1, 'key4': 2}
+ responses = [messaging.Response('cell1', raw_resp1, False),
+ messaging.Response('cell2', raw_resp2, False),
+ messaging.Response('cell2', raw_resp3, False)]
+ expected_resp = {'key1': 1, 'key2': 3, 'key3': 3, 'key4': 2}
+
+ self.mox.StubOutWithMock(self.msg_runner,
+ 'compute_node_stats')
+ self.msg_runner.compute_node_stats(self.ctxt).AndReturn(responses)
+ self.mox.ReplayAll()
+ response = self.cells_manager.compute_node_stats(self.ctxt)
+ self.assertEqual(expected_resp, response)
+
+ def test_compute_node_get(self):
+ fake_cell = 'fake-cell'
+ fake_response = messaging.Response(fake_cell,
+ FAKE_COMPUTE_NODES[0],
+ False)
+ expected_response = copy.deepcopy(FAKE_COMPUTE_NODES[0])
+ cells_utils.add_cell_to_compute_node(expected_response, fake_cell)
+ cell_and_id = cells_utils.cell_with_item(fake_cell, 'fake-id')
+ self.mox.StubOutWithMock(self.msg_runner,
+ 'compute_node_get')
+ self.msg_runner.compute_node_get(self.ctxt,
+ 'fake-cell', 'fake-id').AndReturn(fake_response)
+ self.mox.ReplayAll()
+ response = self.cells_manager.compute_node_get(self.ctxt,
+ compute_id=cell_and_id)
+ self.assertEqual(expected_response, response)
diff --git a/nova/tests/cells/test_cells_messaging.py b/nova/tests/cells/test_cells_messaging.py
index b505ea4f6..811ad17fd 100644
--- a/nova/tests/cells/test_cells_messaging.py
+++ b/nova/tests/cells/test_cells_messaging.py
@@ -811,6 +811,19 @@ class CellsTargetedMethodsTestCase(test.TestCase):
result = response[0].value_or_raise()
self.assertEqual(['fake_result'], result)
+ def test_compute_node_get(self):
+ compute_id = 'fake-id'
+ self.mox.StubOutWithMock(self.tgt_db_inst, 'compute_node_get')
+ self.tgt_db_inst.compute_node_get(self.ctxt,
+ compute_id).AndReturn('fake_result')
+
+ self.mox.ReplayAll()
+
+ response = self.src_msg_runner.compute_node_get(self.ctxt,
+ self.tgt_cell_name, compute_id)
+ result = response.value_or_raise()
+ self.assertEqual('fake_result', result)
+
class CellsBroadcastMethodsTestCase(test.TestCase):
"""Test case for _BroadcastMessageMethods class. Most of these
@@ -1140,3 +1153,86 @@ class CellsBroadcastMethodsTestCase(test.TestCase):
('api-cell!child-cell2', [3]),
('api-cell', [1, 2])]
self.assertEqual(expected, response_values)
+
+ def test_compute_node_get_all(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, 'compute_node_get_all')
+ self.mox.StubOutWithMock(self.mid_db_inst, 'compute_node_get_all')
+ self.mox.StubOutWithMock(self.tgt_db_inst, 'compute_node_get_all')
+
+ self.src_db_inst.compute_node_get_all(ctxt).AndReturn([1, 2])
+ self.mid_db_inst.compute_node_get_all(ctxt).AndReturn([3])
+ self.tgt_db_inst.compute_node_get_all(ctxt).AndReturn([4, 5])
+
+ self.mox.ReplayAll()
+
+ responses = self.src_msg_runner.compute_node_get_all(ctxt)
+ 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_compute_node_get_all_with_hyp_match(self):
+ # Reset this, as this is a broadcast down.
+ self._setup_attrs(up=False)
+ hypervisor_match = 'meow'
+
+ ctxt = self.ctxt.elevated()
+
+ self.mox.StubOutWithMock(self.src_db_inst,
+ 'compute_node_search_by_hypervisor')
+ self.mox.StubOutWithMock(self.mid_db_inst,
+ 'compute_node_search_by_hypervisor')
+ self.mox.StubOutWithMock(self.tgt_db_inst,
+ 'compute_node_search_by_hypervisor')
+
+ self.src_db_inst.compute_node_search_by_hypervisor(ctxt,
+ hypervisor_match).AndReturn([1, 2])
+ self.mid_db_inst.compute_node_search_by_hypervisor(ctxt,
+ hypervisor_match).AndReturn([3])
+ self.tgt_db_inst.compute_node_search_by_hypervisor(ctxt,
+ hypervisor_match).AndReturn([4, 5])
+
+ self.mox.ReplayAll()
+
+ responses = self.src_msg_runner.compute_node_get_all(ctxt,
+ hypervisor_match=hypervisor_match)
+ 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_compute_node_stats(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,
+ 'compute_node_statistics')
+ self.mox.StubOutWithMock(self.mid_db_inst,
+ 'compute_node_statistics')
+ self.mox.StubOutWithMock(self.tgt_db_inst,
+ 'compute_node_statistics')
+
+ self.src_db_inst.compute_node_statistics(ctxt).AndReturn([1, 2])
+ self.mid_db_inst.compute_node_statistics(ctxt).AndReturn([3])
+ self.tgt_db_inst.compute_node_statistics(ctxt).AndReturn([4, 5])
+
+ self.mox.ReplayAll()
+
+ responses = self.src_msg_runner.compute_node_stats(ctxt)
+ 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 c1e9b5ca8..d19ce5b2b 100644
--- a/nova/tests/cells/test_cells_rpcapi.py
+++ b/nova/tests/cells/test_cells_rpcapi.py
@@ -277,3 +277,30 @@ class CellsAPITestCase(test.TestCase):
self._check_result(call_info, 'task_log_get_all', expected_args,
version='1.3')
self.assertEqual(result, 'fake_response')
+
+ def test_compute_node_get_all(self):
+ call_info = self._stub_rpc_method('call', 'fake_response')
+ result = self.cells_rpcapi.compute_node_get_all(self.fake_context,
+ hypervisor_match='fake-match')
+
+ expected_args = {'hypervisor_match': 'fake-match'}
+ self._check_result(call_info, 'compute_node_get_all', expected_args,
+ version='1.4')
+ self.assertEqual(result, 'fake_response')
+
+ def test_compute_node_stats(self):
+ call_info = self._stub_rpc_method('call', 'fake_response')
+ result = self.cells_rpcapi.compute_node_stats(self.fake_context)
+ expected_args = {}
+ self._check_result(call_info, 'compute_node_stats',
+ expected_args, version='1.4')
+ self.assertEqual(result, 'fake_response')
+
+ def test_compute_node_get(self):
+ call_info = self._stub_rpc_method('call', 'fake_response')
+ result = self.cells_rpcapi.compute_node_get(self.fake_context,
+ 'fake_compute_id')
+ expected_args = {'compute_id': 'fake_compute_id'}
+ self._check_result(call_info, 'compute_node_get',
+ expected_args, version='1.4')
+ self.assertEqual(result, 'fake_response')