Initial skeleton for an RPC layer.
This patch implements the a framework for RPC between the API service and the Manager service. It implements only the 'get_node_power_state' method, and adds a unit test. Change-Id: I19e4b761ef212de4c8fd0e600e98368e520a44aa
This commit is contained in:
parent
378dfac26b
commit
e64f64cbc6
ironic
api
cmd
manager
tests/manager
@ -41,7 +41,9 @@ def get_pecan_config():
|
||||
|
||||
def setup_app(pecan_config=None, extra_hooks=None):
|
||||
app_hooks = [hooks.ConfigHook(),
|
||||
hooks.DBHook()]
|
||||
hooks.DBHook(),
|
||||
hooks.ContextHook(),
|
||||
hooks.RPCHook()]
|
||||
if extra_hooks:
|
||||
app_hooks.extend(extra_hooks)
|
||||
|
||||
|
@ -140,6 +140,16 @@ class NodeIfaceController(rest.RestController):
|
||||
for r in pecan.request.dbapi.get_ifaces_for_node(node_id)]
|
||||
|
||||
|
||||
class NodePowerController(rest.RestController):
|
||||
"""Initial mock of an API for /node/<id>/power."""
|
||||
|
||||
@wsme_pecan.wsexpose(unicode, unicode)
|
||||
def get_one(self, node_id):
|
||||
return pecan.request.rpcapi.get_node_power_state(
|
||||
pecan.request.context,
|
||||
node_id)
|
||||
|
||||
|
||||
class NodesController(rest.RestController):
|
||||
"""REST controller for Nodes."""
|
||||
|
||||
@ -177,6 +187,7 @@ class NodesController(rest.RestController):
|
||||
pass
|
||||
|
||||
ifaces = NodeIfaceController()
|
||||
power = NodePowerController()
|
||||
|
||||
|
||||
class Controller(object):
|
||||
|
@ -20,6 +20,8 @@ from oslo.config import cfg
|
||||
from pecan import hooks
|
||||
|
||||
from ironic.db import api as dbapi
|
||||
from ironic.manager import rpcapi
|
||||
from ironic.openstack.common import context
|
||||
|
||||
|
||||
class ConfigHook(hooks.PecanHook):
|
||||
@ -35,3 +37,17 @@ class DBHook(hooks.PecanHook):
|
||||
|
||||
def before(self, state):
|
||||
state.request.dbapi = dbapi.get_instance()
|
||||
|
||||
|
||||
class ContextHook(hooks.PecanHook):
|
||||
|
||||
def before(self, state):
|
||||
# TODO(deva): Making all requests have admin context for early
|
||||
# development. This needs to be fixed later!
|
||||
state.request.context = context.get_admin_context()
|
||||
|
||||
|
||||
class RPCHook(hooks.PecanHook):
|
||||
|
||||
def before(self, state):
|
||||
state.request.rpcapi = rpcapi.ManagerAPI()
|
||||
|
@ -38,7 +38,6 @@ def main():
|
||||
# Pase config file and command line options, then start logging
|
||||
ironic_service.prepare_service(sys.argv)
|
||||
|
||||
topic = 'ironic.manager'
|
||||
mgr = manager.ManagerService(CONF.host, topic)
|
||||
mgr = manager.ManagerService(CONF.host, manager.MANAGER_TOPIC)
|
||||
launcher = service.launch(mgr)
|
||||
launcher.wait()
|
||||
|
@ -17,9 +17,12 @@
|
||||
# under the License.
|
||||
|
||||
from ironic.common import service
|
||||
from ironic.db import api as dbapi
|
||||
from ironic.manager import task_manager
|
||||
from ironic.openstack.common import log
|
||||
|
||||
MANAGER_TOPIC = 'ironic.manager'
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
@ -35,16 +38,17 @@ class ManagerService(service.PeriodicService):
|
||||
which is also used to coordinate locks between ManagerServices.
|
||||
"""
|
||||
|
||||
RPC_API_VERSION = '1.0'
|
||||
|
||||
def __init__(self, host, topic):
|
||||
super(ManagerService, self).__init__(host, topic)
|
||||
|
||||
def start(self):
|
||||
super(ManagerService, self).start()
|
||||
# TODO(deva): connect with storage driver
|
||||
self.dbapi = dbapi.get_instance()
|
||||
|
||||
def initialize(self, service):
|
||||
LOG.debug(_('Manager initializing service hooks'))
|
||||
# TODO(deva)
|
||||
def initialize_service_hook(self, service):
|
||||
pass
|
||||
|
||||
def process_notification(self, notification):
|
||||
LOG.debug(_('Received notification: %r') %
|
||||
@ -55,10 +59,10 @@ class ManagerService(service.PeriodicService):
|
||||
# TODO(deva)
|
||||
pass
|
||||
|
||||
def get_node_power_state(self, id):
|
||||
def get_node_power_state(self, context, node_id):
|
||||
"""Get and return the power state for a single node."""
|
||||
|
||||
with task_manager.acquire([id], shared=True) as task:
|
||||
with task_manager.acquire([node_id], shared=True) as task:
|
||||
node = task.resources[0].node
|
||||
driver = task.resources[0].controller
|
||||
state = driver.get_power_state(task, node)
|
||||
|
54
ironic/manager/rpcapi.py
Normal file
54
ironic/manager/rpcapi.py
Normal file
@ -0,0 +1,54 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
# coding=utf-8
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Client side of the manager RPC API.
|
||||
"""
|
||||
|
||||
import ironic.openstack.common.rpc.proxy
|
||||
|
||||
MANAGER_TOPIC = 'ironic.manager'
|
||||
|
||||
|
||||
class ManagerAPI(ironic.openstack.common.rpc.proxy.RpcProxy):
|
||||
"""Client side of the manager RPC API.
|
||||
|
||||
API version history:
|
||||
|
||||
1.0 - Initial version.
|
||||
"""
|
||||
|
||||
RPC_API_VERSION = '1.0'
|
||||
|
||||
def __init__(self, topic=None):
|
||||
if topic is None:
|
||||
topic = MANAGER_TOPIC
|
||||
|
||||
super(ManagerAPI, self).__init__(
|
||||
topic=topic,
|
||||
default_version=self.RPC_API_VERSION)
|
||||
|
||||
def get_node_power_state(self, context, node_id):
|
||||
"""Ask a manager for the node power state.
|
||||
|
||||
:param context: request context.
|
||||
:param node_id: node id or uuid.
|
||||
:returns: power status.
|
||||
"""
|
||||
return self.call(context,
|
||||
self.make_msg('get_node_power_state',
|
||||
node_id=node_id))
|
@ -23,6 +23,7 @@ import mox
|
||||
from ironic.common import states
|
||||
from ironic.db import api as dbapi
|
||||
from ironic.manager import manager
|
||||
from ironic.openstack.common import context
|
||||
from ironic.tests.db import base
|
||||
from ironic.tests.db import utils
|
||||
from ironic.tests.manager import utils as mgr_utils
|
||||
@ -33,6 +34,7 @@ class ManagerTestCase(base.DbTestCase):
|
||||
def setUp(self):
|
||||
super(ManagerTestCase, self).setUp()
|
||||
self.service = manager.ManagerService('test-host', 'test-topic')
|
||||
self.context = context.get_admin_context()
|
||||
self.dbapi = dbapi.get_instance()
|
||||
(self.controller, self.deployer) = mgr_utils.get_mocked_node_manager()
|
||||
|
||||
@ -43,7 +45,7 @@ class ManagerTestCase(base.DbTestCase):
|
||||
|
||||
# FakeControlDriver.get_power_state will "pass"
|
||||
# and states.NOSTATE is None, so this test should pass.
|
||||
state = self.service.get_node_power_state(n['uuid'])
|
||||
state = self.service.get_node_power_state(self.context, n['uuid'])
|
||||
self.assertEqual(state, states.NOSTATE)
|
||||
|
||||
def test_get_power_state_with_mock(self):
|
||||
@ -59,9 +61,9 @@ class ManagerTestCase(base.DbTestCase):
|
||||
AndReturn(states.POWER_ON)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
state = self.service.get_node_power_state(n['uuid'])
|
||||
state = self.service.get_node_power_state(self.context, n['uuid'])
|
||||
self.assertEqual(state, states.POWER_OFF)
|
||||
state = self.service.get_node_power_state(n['uuid'])
|
||||
state = self.service.get_node_power_state(self.context, n['uuid'])
|
||||
self.assertEqual(state, states.POWER_ON)
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
83
ironic/tests/manager/test_rpcapi.py
Normal file
83
ironic/tests/manager/test_rpcapi.py
Normal file
@ -0,0 +1,83 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
# coding=utf-8
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Unit Tests for :py:class:`ironic.manager.rpcapi.ManagerAPI`.
|
||||
"""
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from ironic.db import api as dbapi
|
||||
from ironic.manager import rpcapi as manager_rpcapi
|
||||
from ironic.openstack.common import context
|
||||
from ironic.openstack.common import jsonutils as json
|
||||
from ironic.openstack.common import rpc
|
||||
from ironic.tests.db import base
|
||||
from ironic.tests.db import utils as dbutils
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class ManagerRpcAPITestCase(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ManagerRpcAPITestCase, self).setUp()
|
||||
self.context = context.get_admin_context()
|
||||
self.dbapi = dbapi.get_instance()
|
||||
self.fake_node = json.to_primitive(dbutils.get_test_node(
|
||||
control_driver='fake',
|
||||
deploy_driver='fake'))
|
||||
|
||||
def test_serialized_instance_has_uuid(self):
|
||||
self.assertTrue('uuid' in self.fake_node)
|
||||
|
||||
def _test_rpcapi(self, method, rpc_method, **kwargs):
|
||||
ctxt = context.get_admin_context()
|
||||
rpcapi = manager_rpcapi.ManagerAPI(topic='fake-topic')
|
||||
|
||||
expected_retval = 'hello world' if method == 'call' else None
|
||||
expected_version = kwargs.pop('version', rpcapi.RPC_API_VERSION)
|
||||
expected_msg = rpcapi.make_msg(method, **kwargs)
|
||||
|
||||
expected_msg['version'] = expected_version
|
||||
|
||||
expected_topic = 'fake-topic'
|
||||
if 'host' in kwargs:
|
||||
expected_topic += ".%s" % kwargs['host']
|
||||
|
||||
self.fake_args = None
|
||||
self.fake_kwargs = None
|
||||
|
||||
def _fake_rpc_method(*args, **kwargs):
|
||||
self.fake_args = args
|
||||
self.fake_kwargs = kwargs
|
||||
if expected_retval:
|
||||
return expected_retval
|
||||
|
||||
self.stubs.Set(rpc, rpc_method, _fake_rpc_method)
|
||||
|
||||
retval = getattr(rpcapi, method)(ctxt, **kwargs)
|
||||
|
||||
self.assertEqual(retval, expected_retval)
|
||||
expected_args = [ctxt, expected_topic, expected_msg]
|
||||
for arg, expected_arg in zip(self.fake_args, expected_args):
|
||||
self.assertEqual(arg, expected_arg)
|
||||
|
||||
def test_get_node_power_state(self):
|
||||
self._test_rpcapi('get_node_power_state',
|
||||
'call',
|
||||
node_id=123)
|
Loading…
x
Reference in New Issue
Block a user