diff --git a/quantum/agent/dhcp_agent.py b/quantum/agent/dhcp_agent.py index 18f9660040..773b90fcc8 100644 --- a/quantum/agent/dhcp_agent.py +++ b/quantum/agent/dhcp_agent.py @@ -30,8 +30,8 @@ from quantum.agent.linux import ip_lib from quantum.agent import rpc as agent_rpc from quantum.common import exceptions from quantum.common import topics +from quantum import context from quantum.openstack.common import cfg -from quantum.openstack.common import context from quantum.openstack.common import importutils from quantum.openstack.common import jsonutils from quantum.openstack.common import log as logging @@ -59,7 +59,7 @@ class DhcpAgent(object): self.cache = NetworkCache() self.dhcp_driver_cls = importutils.import_class(conf.dhcp_driver) - ctx = context.RequestContext('quantum', 'quantum', is_admin=True) + ctx = context.get_admin_context_without_session() self.plugin_rpc = DhcpPluginApi(topics.PLUGIN, ctx) self.device_manager = DeviceManager(self.conf, self.plugin_rpc) diff --git a/quantum/common/rpc.py b/quantum/common/rpc.py new file mode 100644 index 0000000000..dd431b7817 --- /dev/null +++ b/quantum/common/rpc.py @@ -0,0 +1,41 @@ +# Copyright (c) 2012 OpenStack, LLC. +# +# 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. + +from quantum.openstack.common import log as logging +from quantum.openstack.common.rpc import dispatcher +from quantum import context + + +LOG = logging.getLogger(__name__) + + +class PluginRpcDispatcher(dispatcher.RpcDispatcher): + """This class is used to convert RPC common context into + Quantum Context.""" + + def __init__(self, callbacks): + super(PluginRpcDispatcher, self).__init__(callbacks) + + def dispatch(self, rpc_ctxt, version, method, **kwargs): + rpc_ctxt_dict = rpc_ctxt.to_dict() + user_id = rpc_ctxt_dict.pop('user_id', None) + if not user_id: + user_id = rpc_ctxt_dict.pop('user', None) + tenant_id = rpc_ctxt_dict.pop('tenant_id', None) + if not tenant_id: + tenant_id = rpc_ctxt_dict.pop('project_id', None) + quantum_ctxt = context.Context(user_id, tenant_id, **rpc_ctxt_dict) + return super(PluginRpcDispatcher, self).dispatch( + quantum_ctxt, version, method, **kwargs) diff --git a/quantum/context.py b/quantum/context.py index 0013c9f436..f65535f342 100644 --- a/quantum/context.py +++ b/quantum/context.py @@ -29,7 +29,7 @@ from quantum.openstack.common import log as logging LOG = logging.getLogger(__name__) -class Context(common_context.RequestContext): +class ContextBase(common_context.RequestContext): """Security context and request information. Represents the user taking a given action within the system. @@ -46,10 +46,8 @@ class Context(common_context.RequestContext): if kwargs: LOG.warn(_('Arguments dropped when creating ' 'context: %s') % str(kwargs)) - super(Context, self).__init__(user=user_id, tenant=tenant_id, - is_admin=is_admin) - self.user_id = user_id - self.tenant_id = tenant_id + super(ContextBase, self).__init__(user=user_id, tenant=tenant_id, + is_admin=is_admin) self.roles = roles or [] if self.is_admin is None: self.is_admin = 'admin' in [x.lower() for x in self.roles] @@ -61,6 +59,26 @@ class Context(common_context.RequestContext): self.timestamp = timestamp self._session = None + @property + def project_id(self): + return self.tenant + + @property + def tenant_id(self): + return self.tenant + + @tenant_id.setter + def tenant_id(self, tenant_id): + self.tenant = tenant_id + + @property + def user_id(self): + return self.user + + @user_id.setter + def user_id(self, user_id): + self.user = user_id + def _get_read_deleted(self): return self._read_deleted @@ -76,15 +94,10 @@ class Context(common_context.RequestContext): read_deleted = property(_get_read_deleted, _set_read_deleted, _del_read_deleted) - @property - def session(self): - if self._session is None: - self._session = db_api.get_session() - return self._session - def to_dict(self): return {'user_id': self.user_id, 'tenant_id': self.tenant_id, + 'project_id': self.project_id, 'is_admin': self.is_admin, 'read_deleted': self.read_deleted, 'roles': self.roles, @@ -108,8 +121,23 @@ class Context(common_context.RequestContext): return context +class Context(ContextBase): + @property + def session(self): + if self._session is None: + self._session = db_api.get_session() + return self._session + + def get_admin_context(read_deleted="no"): return Context(user_id=None, tenant_id=None, is_admin=True, read_deleted=read_deleted) + + +def get_admin_context_without_session(read_deleted="no"): + return ContextBase(user_id=None, + tenant_id=None, + is_admin=True, + read_deleted=read_deleted) diff --git a/quantum/db/dhcp_rpc_base.py b/quantum/db/dhcp_rpc_base.py index bc4bf29387..d2370ec119 100644 --- a/quantum/db/dhcp_rpc_base.py +++ b/quantum/db/dhcp_rpc_base.py @@ -16,21 +16,13 @@ from sqlalchemy.orm import exc from quantum.api.v2 import attributes -from quantum import context as quantum_context from quantum import manager -from quantum.openstack.common import context from quantum.openstack.common import log as logging LOG = logging.getLogger(__name__) -def augment_context(context): - """Augments RPC with additional attributes, so that plugin calls work.""" - return quantum_context.Context(context.user, None, is_admin=True, - roles=['admin']) - - class DhcpRpcCallbackMixin(object): """A mix-in that enable DHCP agent support in plugin implementations.""" @@ -39,7 +31,6 @@ class DhcpRpcCallbackMixin(object): host = kwargs.get('host') LOG.debug('Network list requested from %s', host) plugin = manager.QuantumManager.get_plugin() - context = augment_context(context) filters = dict(admin_state_up=[True]) return [net['id'] for net in @@ -48,7 +39,6 @@ class DhcpRpcCallbackMixin(object): def get_network_info(self, context, **kwargs): """Retrieve and return a extended information about a network.""" network_id = kwargs.get('network_id') - context = augment_context(context) plugin = manager.QuantumManager.get_plugin() network = plugin.get_network(context, network_id) @@ -68,12 +58,11 @@ class DhcpRpcCallbackMixin(object): host = kwargs.get('host') network_id = kwargs.get('network_id') device_id = kwargs.get('device_id') - # There could be more than one dhcp server per network, so create - # a device id that combines host and network ids + # There could be more than one dhcp server per network, so create + # a device id that combines host and network ids LOG.debug('Port %s for %s requested from %s', device_id, network_id, host) - context = augment_context(context) plugin = manager.QuantumManager.get_plugin() retval = None @@ -136,7 +125,6 @@ class DhcpRpcCallbackMixin(object): LOG.debug('DHCP port deletion for %s d request from %s', network_id, host) - context = augment_context(context) plugin = manager.QuantumManager.get_plugin() filters = dict(network_id=[network_id], device_id=[device_id]) ports = plugin.get_ports(context, filters=filters) @@ -154,8 +142,6 @@ class DhcpRpcCallbackMixin(object): LOG.debug('DHCP port remove fixed_ip for %s d request from %s', subnet_id, host) - - context = augment_context(context) plugin = manager.QuantumManager.get_plugin() filters = dict(network_id=[network_id], device_id=[device_id]) ports = plugin.get_ports(context, filters=filters) @@ -179,8 +165,6 @@ class DhcpRpcCallbackMixin(object): LOG.debug('Updating lease expiration for %s on network %s from %s.', ip_address, network_id, host) - - context = augment_context(context) plugin = manager.QuantumManager.get_plugin() plugin.update_fixed_ip_lease_expiration(context, network_id, diff --git a/quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py b/quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py index 3f10e97d1a..e720648b1e 100755 --- a/quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py +++ b/quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py @@ -33,13 +33,11 @@ from quantum.agent.linux import ip_lib from quantum.agent.linux import utils from quantum.agent import rpc as agent_rpc from quantum.common import config as logging_config -from quantum.common import constants from quantum.common import topics from quantum.common import utils as q_utils +from quantum import context from quantum.openstack.common import cfg -from quantum.openstack.common import context from quantum.openstack.common import log as logging -from quantum.openstack.common import rpc from quantum.openstack.common.rpc import dispatcher from quantum.plugins.linuxbridge.common import config from quantum.plugins.linuxbridge.common import constants as lconst @@ -454,8 +452,7 @@ class LinuxBridgeQuantumAgentRPC: self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN) # RPC network init - self.context = context.RequestContext('quantum', 'quantum', - is_admin=False) + self.context = context.get_admin_context_without_session() # Handle updates from service self.callbacks = LinuxBridgeRpcCallbacks(self.context, self.linux_br) diff --git a/quantum/plugins/linuxbridge/lb_quantum_plugin.py b/quantum/plugins/linuxbridge/lb_quantum_plugin.py index 68ec2d7ef2..5086662b7d 100644 --- a/quantum/plugins/linuxbridge/lb_quantum_plugin.py +++ b/quantum/plugins/linuxbridge/lb_quantum_plugin.py @@ -18,18 +18,16 @@ import sys from quantum.api.v2 import attributes from quantum.common import constants as q_const from quantum.common import exceptions as q_exc +from quantum.common import rpc as q_rpc from quantum.common import topics from quantum.db import api as db_api from quantum.db import db_base_plugin_v2 from quantum.db import dhcp_rpc_base from quantum.db import l3_db -from quantum.db import models_v2 from quantum.extensions import providernet as provider from quantum.openstack.common import cfg -from quantum.openstack.common import context from quantum.openstack.common import log as logging from quantum.openstack.common import rpc -from quantum.openstack.common.rpc import dispatcher from quantum.openstack.common.rpc import proxy from quantum.plugins.linuxbridge.common import constants from quantum.plugins.linuxbridge.db import l2network_db_v2 as db @@ -46,16 +44,13 @@ class LinuxBridgeRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin): # Device names start with "tap" TAP_PREFIX_LEN = 3 - def __init__(self, rpc_context): - self.rpc_context = rpc_context - def create_rpc_dispatcher(self): '''Get the rpc dispatcher for this manager. If a manager would like to set an rpc API version, or support more than one class as the target of rpc messages, override this method. ''' - return dispatcher.RpcDispatcher([self]) + return q_rpc.PluginRpcDispatcher([self]) def get_device_details(self, rpc_context, **kwargs): """Agent requests device details""" @@ -173,10 +168,8 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2, def _setup_rpc(self): # RPC support self.topic = topics.PLUGIN - self.rpc_context = context.RequestContext('quantum', 'quantum', - is_admin=False) self.conn = rpc.create_connection(new=True) - self.callbacks = LinuxBridgeRpcCallbacks(self.rpc_context) + self.callbacks = LinuxBridgeRpcCallbacks() self.dispatcher = self.callbacks.create_rpc_dispatcher() self.conn.create_consumer(self.topic, self.dispatcher, fanout=False) @@ -377,7 +370,7 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2, binding.vlan_id, self.network_vlan_ranges) # the network_binding record is deleted via cascade from # the network record, so explicit removal is not necessary - self.notifier.network_delete(self.rpc_context, id) + self.notifier.network_delete(context, id) def get_network(self, context, id, fields=None): net = super(LinuxBridgePluginV2, self).get_network(context, id, None) @@ -404,7 +397,7 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2, if original_port['admin_state_up'] != port['admin_state_up']: binding = db.get_network_binding(context.session, port['network_id']) - self.notifier.port_update(self.rpc_context, port, + self.notifier.port_update(context, port, binding.physical_network, binding.vlan_id) return port diff --git a/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py b/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py index c7f46c2085..b9a0b7819f 100755 --- a/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py +++ b/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py @@ -30,13 +30,11 @@ from quantum.agent.linux import ovs_lib from quantum.agent.linux import utils from quantum.agent import rpc as agent_rpc from quantum.common import config as logging_config -from quantum.common import constants as q_const from quantum.common import topics from quantum.common import utils as q_utils +from quantum import context from quantum.openstack.common import cfg -from quantum.openstack.common import context from quantum.openstack.common import log as logging -from quantum.openstack.common import rpc from quantum.openstack.common.rpc import dispatcher from quantum.plugins.openvswitch.common import config from quantum.plugins.openvswitch.common import constants @@ -171,8 +169,7 @@ class OVSQuantumAgent(object): self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN) # RPC network init - self.context = context.RequestContext('quantum', 'quantum', - is_admin=False) + self.context = context.get_admin_context_without_session() # Handle updates from service self.dispatcher = self.create_rpc_dispatcher() # Define the listening consumers for the agent diff --git a/quantum/plugins/openvswitch/ovs_quantum_plugin.py b/quantum/plugins/openvswitch/ovs_quantum_plugin.py index 2d174d27fb..f37ea634d2 100644 --- a/quantum/plugins/openvswitch/ovs_quantum_plugin.py +++ b/quantum/plugins/openvswitch/ovs_quantum_plugin.py @@ -20,22 +20,20 @@ # @author: Aaron Rosen, Nicira Networks, Inc. # @author: Bob Kukura, Red Hat, Inc. -import os import sys from quantum.api.v2 import attributes from quantum.common import constants as q_const from quantum.common import exceptions as q_exc +from quantum.common import rpc as q_rpc from quantum.common import topics from quantum.db import db_base_plugin_v2 from quantum.db import dhcp_rpc_base from quantum.db import l3_db from quantum.extensions import providernet as provider from quantum.openstack.common import cfg -from quantum.openstack.common import context from quantum.openstack.common import log as logging from quantum.openstack.common import rpc -from quantum.openstack.common.rpc import dispatcher from quantum.openstack.common.rpc import proxy from quantum.plugins.openvswitch.common import config from quantum.plugins.openvswitch.common import constants @@ -51,8 +49,7 @@ class OVSRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin): # Set RPC API version to 1.0 by default. RPC_API_VERSION = '1.0' - def __init__(self, rpc_context, notifier): - self.rpc_context = rpc_context + def __init__(self, notifier): self.notifier = notifier def create_rpc_dispatcher(self): @@ -61,7 +58,7 @@ class OVSRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin): If a manager would like to set an rpc API version, or support more than one class as the target of rpc messages, override this method. ''' - return dispatcher.RpcDispatcher([self]) + return q_rpc.PluginRpcDispatcher([self]) def get_device_details(self, rpc_context, **kwargs): """Agent requests device details""" @@ -116,7 +113,7 @@ class OVSRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin): entry = dict() entry['tunnels'] = tunnels # Notify all other listening agents - self.notifier.tunnel_update(self.rpc_context, tunnel.ip_address, + self.notifier.tunnel_update(rpc_context, tunnel.ip_address, tunnel.id) # Return the list of tunnels IP's to the agent return entry @@ -218,11 +215,9 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2, def setup_rpc(self): # RPC support self.topic = topics.PLUGIN - self.rpc_context = context.RequestContext('quantum', 'quantum', - is_admin=False) self.conn = rpc.create_connection(new=True) self.notifier = AgentNotifierApi(topics.AGENT) - self.callbacks = OVSRpcCallbacks(self.rpc_context, self.notifier) + self.callbacks = OVSRpcCallbacks(self.notifier) self.dispatcher = self.callbacks.create_rpc_dispatcher() self.conn.create_consumer(self.topic, self.dispatcher, fanout=False) @@ -460,7 +455,7 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2, self.network_vlan_ranges) # the network_binding record is deleted via cascade from # the network record, so explicit removal is not necessary - self.notifier.network_delete(self.rpc_context, id) + self.notifier.network_delete(context, id) def get_network(self, context, id, fields=None): net = super(OVSQuantumPluginV2, self).get_network(context, id, None) @@ -487,7 +482,7 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2, if original_port['admin_state_up'] != port['admin_state_up']: binding = ovs_db_v2.get_network_binding(None, port['network_id']) - self.notifier.port_update(self.rpc_context, port, + self.notifier.port_update(context, port, binding.network_type, binding.segmentation_id, binding.physical_network) diff --git a/quantum/tests/unit/test_db_rpc_base.py b/quantum/tests/unit/test_db_rpc_base.py index dc61617950..689cbefb1e 100644 --- a/quantum/tests/unit/test_db_rpc_base.py +++ b/quantum/tests/unit/test_db_rpc_base.py @@ -20,24 +20,9 @@ import mock from quantum.db import dhcp_rpc_base -class TestDhcpAugmentContext(unittest.TestCase): - def test_augment_context(self): - context = mock.Mock() - context.user = 'quantum' - context.tenant = None - context.is_admin = True - - new_context = dhcp_rpc_base.augment_context(context) - - self.assertEqual(new_context.user_id, context.user) - self.assertEqual(new_context.roles, ['admin']) - - class TestDhcpRpcCallackMixin(unittest.TestCase): - def setUp(self): - self.context_p = mock.patch('quantum.db.dhcp_rpc_base.augment_context') - self.context_p.start() + def setUp(self): self.plugin_p = mock.patch('quantum.manager.QuantumManager.get_plugin') get_plugin = self.plugin_p.start() self.plugin = mock.Mock() @@ -49,7 +34,6 @@ class TestDhcpRpcCallackMixin(unittest.TestCase): def tearDown(self): self.log_p.stop() self.plugin_p.stop() - self.context_p.stop() def test_get_active_networks(self): plugin_retval = [dict(id='a'), dict(id='b')] diff --git a/quantum/tests/unit/test_quantum_context.py b/quantum/tests/unit/test_quantum_context.py new file mode 100644 index 0000000000..5c6a338eb6 --- /dev/null +++ b/quantum/tests/unit/test_quantum_context.py @@ -0,0 +1,66 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Nicira, Inc. +# 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. + +import unittest2 as unittest + +import mock + +from quantum import context + + +class TestQuantumContext(unittest.TestCase): + + def setUp(self): + db_api = 'quantum.db.api.get_session' + self._db_api_session_patcher = mock.patch(db_api) + self.db_api_session = self._db_api_session_patcher.start() + + def tearDown(self): + self._db_api_session_patcher.stop() + + def testQuantumContextCreate(self): + cxt = context.Context('user_id', 'tenant_id') + self.assertEquals('user_id', cxt.user_id) + self.assertEquals('tenant_id', cxt.project_id) + + def testQuantumContextToDict(self): + cxt = context.Context('user_id', 'tenant_id') + cxt_dict = cxt.to_dict() + self.assertEquals('user_id', cxt_dict['user_id']) + self.assertEquals('tenant_id', cxt_dict['project_id']) + + def testQuantumContextAdminToDict(self): + self.db_api_session.return_value = 'fakesession' + cxt = context.get_admin_context() + cxt_dict = cxt.to_dict() + self.assertIsNone(cxt_dict['user_id']) + self.assertIsNone(cxt_dict['tenant_id']) + self.assertIsNotNone(cxt.session) + self.assertFalse('session' in cxt_dict) + + def testQuantumContextAdminWithoutSessionToDict(self): + cxt = context.get_admin_context_without_session() + cxt_dict = cxt.to_dict() + self.assertIsNone(cxt_dict['user_id']) + self.assertIsNone(cxt_dict['tenant_id']) + try: + session = cxt.session + except Exception: + pass + else: + self.assertFalse(True, 'without_session admin context' + 'should has no session property!')