]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Fix context problem
authorgongysh <gongysh@cn.ibm.com>
Fri, 9 Nov 2012 14:45:58 +0000 (22:45 +0800)
committergongysh <gongysh@cn.ibm.com>
Wed, 21 Nov 2012 00:13:26 +0000 (08:13 +0800)
On plugin side, we use normal API context if any to call RPC methods.
We use our plugin dispatcher to convert RPC context into quantum admin
context. After that the callback's functions have the first argument context
as normal quantum context.

On agent side, we use admin context without session property as its
RPC calling context. Call back context is default RPCCommonContext in
Openstack common modules.

This patch also fixes the problem in the following bug:

Bug #1077012

Change-Id: I913b48dcd84d275cd7de30ca990be00c243e63ea

quantum/agent/dhcp_agent.py
quantum/common/rpc.py [new file with mode: 0644]
quantum/context.py
quantum/db/dhcp_rpc_base.py
quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py
quantum/plugins/linuxbridge/lb_quantum_plugin.py
quantum/plugins/openvswitch/agent/ovs_quantum_agent.py
quantum/plugins/openvswitch/ovs_quantum_plugin.py
quantum/tests/unit/test_db_rpc_base.py
quantum/tests/unit/test_quantum_context.py [new file with mode: 0644]

index 18f9660040e71c9cd8cefadcea6ce6827da12dea..773b90fcc81723b8b7fc061a245eb219aff0bfb7 100644 (file)
@@ -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 (file)
index 0000000..dd431b7
--- /dev/null
@@ -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)
index 0013c9f43679ba2d2915720cee907d7bab472e2b..f65535f34259ff1f02dbf3068869b9944bb90bdc 100644 (file)
@@ -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)
index bc4bf293876fea23d2168bc6289420be78833525..d2370ec11978741785255877e18ffd48b7d420e1 100644 (file)
 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,
index 3f10e97d1a41ba50264090733d1d3c06059dd728..e720648b1ee19c235325757bbabee1d7cfba822f 100755 (executable)
@@ -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)
index 68ec2d7ef203c2c439e02f3ed5fa0d6abb61cbe0..5086662b7daab2ebff7015bb34ebea0e02de22dd 100644 (file)
@@ -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
index c7f46c20855b76550b84dad81bc04514d0ee5a65..b9a0b7819f956d1a95fa8101cf9aa26d66d2b8a1 100755 (executable)
@@ -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
index 2d174d27fb337e2fd6324eee39cca5da24e8e3d2..f37ea634d26c29b20c6cad389a99c86ea5dee445 100644 (file)
 # @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)
index dc61617950679eb573bf2bd7652155776957519b..689cbefb1e66b0c53bac429b1d43efd5e220ba17 100644 (file)
@@ -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 (file)
index 0000000..5c6a338
--- /dev/null
@@ -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!')