]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
BSN: Add context to backend request for debugging
authorKevin Benton <blak111@gmail.com>
Tue, 2 Sep 2014 18:27:51 +0000 (11:27 -0700)
committerKevin Benton <kevinbenton@buttewifi.com>
Mon, 15 Sep 2014 08:47:36 +0000 (08:47 +0000)
Include the request context with calls to the backend
Big Switch controllers to assist with event correlation
and debugging object provenance. The auth token is
stripped since this information is sensitive and
these requests will appear in debug logs.

This also removes mutable objects from default arguments
in some of the server manager function definitions that
were interferring with the new use of the headers dict.

Closes-Bug: #1364696
Change-Id: I5b80b1596cc145742457b3603cbcd67f6e0d9f36

neutron/plugins/bigswitch/plugin.py
neutron/plugins/bigswitch/servermanager.py
neutron/plugins/ml2/drivers/mech_bigswitch/driver.py
neutron/tests/unit/bigswitch/test_servermanager.py
neutron/tests/unit/ml2/drivers/test_bigswitch_mech.py

index cb0fb20bf2241c50424df400a456f2f95e01f981..5d119113fc27a1a26a6b7738ac6bdb3334a164a7 100644 (file)
@@ -448,7 +448,9 @@ class NeutronRestProxyV2Base(db_base_plugin_v2.NeutronDbPluginV2,
 def put_context_in_serverpool(f):
     @functools.wraps(f)
     def wrapper(self, context, *args, **kwargs):
-        self.servers.set_context(context)
+        # core plugin: context is top level object
+        # ml2: keeps context in _plugin_context
+        self.servers.set_context(getattr(context, '_plugin_context', context))
         return f(self, context, *args, **kwargs)
     return wrapper
 
index e36ec287774cb1a6490903603a820cce2074d2b8..1b072848fdb7d26969c1a05ca1884555a57ad587 100644 (file)
@@ -72,6 +72,7 @@ FAILURE_CODES = [0, 301, 302, 303, 400, 401, 403, 404, 500, 501, 502, 503,
 BASE_URI = '/networkService/v1.1'
 ORCHESTRATION_SERVICE_ID = 'Neutron v2.0'
 HASH_MATCH_HEADER = 'X-BSN-BVS-HASH-MATCH'
+REQ_CONTEXT_HEADER = 'X-REQ-CONTEXT'
 # error messages
 NXNETWORK = 'NXVNS'
 HTTP_SERVICE_UNAVAILABLE_RETRY_COUNT = 3
@@ -125,12 +126,11 @@ class ServerProxy(object):
                                                 'cap': self.capabilities})
         return self.capabilities
 
-    def rest_call(self, action, resource, data='', headers={}, timeout=False,
-                  reconnect=False, hash_handler=None):
+    def rest_call(self, action, resource, data='', headers=None,
+                  timeout=False, reconnect=False, hash_handler=None):
         uri = self.base_uri + resource
         body = jsonutils.dumps(data)
-        if not headers:
-            headers = {}
+        headers = headers or {}
         headers['Content-type'] = 'application/json'
         headers['Accept'] = 'application/json'
         headers['NeutronProxy-Agent'] = self.name
@@ -425,7 +425,15 @@ class ServerPool(object):
     @utils.synchronized('bsn-rest-call')
     def rest_call(self, action, resource, data, headers, ignore_codes,
                   timeout=False):
-        hash_handler = cdb.HashHandler(context=self.get_context_ref())
+        context = self.get_context_ref()
+        if context:
+            # include the requesting context information if available
+            cdict = context.to_dict()
+            # remove the auth token so it's not present in debug logs on the
+            # backend controller
+            cdict.pop('auth_token', None)
+            headers[REQ_CONTEXT_HEADER] = jsonutils.dumps(cdict)
+        hash_handler = cdb.HashHandler(context=context)
         good_first = sorted(self.servers, key=lambda x: x.failed)
         first_response = None
         for active_server in good_first:
@@ -479,13 +487,15 @@ class ServerPool(object):
         return first_response
 
     def rest_action(self, action, resource, data='', errstr='%s',
-                    ignore_codes=[], headers={}, timeout=False):
+                    ignore_codes=None, headers=None, timeout=False):
         """
         Wrapper for rest_call that verifies success and raises a
         RemoteRestError on failure with a provided error string
         By default, 404 errors on DELETE calls are ignored because
         they already do not exist on the backend.
         """
+        ignore_codes = ignore_codes or []
+        headers = headers or {}
         if not ignore_codes and action == 'DELETE':
             ignore_codes = [404]
         resp = self.rest_call(action, resource, data, headers, ignore_codes,
index dc8c12c33974e233d3e9d84386539cf4bf474ec8..349d8870bd360a82dd8e48e7a14ed6398195ea73 100644 (file)
@@ -36,6 +36,7 @@ from neutron.plugins.ml2 import driver_api as api
 
 EXTERNAL_PORT_OWNER = 'neutron:external_port'
 LOG = log.getLogger(__name__)
+put_context_in_serverpool = plugin.put_context_in_serverpool
 
 # time in seconds to maintain existence of vswitch response
 CACHE_VSWITCH_TIME = 60
@@ -71,18 +72,22 @@ class BigSwitchMechanismDriver(plugin.NeutronRestProxyV2Base,
 
         LOG.debug(_("Initialization done"))
 
+    @put_context_in_serverpool
     def create_network_postcommit(self, context):
         # create network on the network controller
         self._send_create_network(context.current)
 
+    @put_context_in_serverpool
     def update_network_postcommit(self, context):
         # update network on the network controller
         self._send_update_network(context.current)
 
+    @put_context_in_serverpool
     def delete_network_postcommit(self, context):
         # delete network on the network controller
         self._send_delete_network(context.current)
 
+    @put_context_in_serverpool
     def create_port_postcommit(self, context):
         # create port on the network controller
         port = self._prepare_port_for_controller(context)
@@ -90,6 +95,7 @@ class BigSwitchMechanismDriver(plugin.NeutronRestProxyV2Base,
             self.async_port_create(port["network"]["tenant_id"],
                                    port["network"]["id"], port)
 
+    @put_context_in_serverpool
     def update_port_postcommit(self, context):
         # update port on the network controller
         port = self._prepare_port_for_controller(context)
@@ -113,6 +119,7 @@ class BigSwitchMechanismDriver(plugin.NeutronRestProxyV2Base,
                             triggered_by_tenant=port["network"]["tenant_id"]
                         )
 
+    @put_context_in_serverpool
     def delete_port_postcommit(self, context):
         # delete port on the network controller
         port = context.current
index fd575abd176bfc72d046ae92e56aba6882e0d298..b30ca83479f438f019f577fce31cb7ccb1ccc89e 100644 (file)
@@ -22,8 +22,10 @@ import ssl
 import mock
 from oslo.config import cfg
 
+from neutron import context
 from neutron import manager
 from neutron.openstack.common import importutils
+from neutron.openstack.common import jsonutils
 from neutron.plugins.bigswitch import servermanager
 from neutron.tests.unit.bigswitch import test_restproxy_plugin as test_rp
 
@@ -211,6 +213,23 @@ class ServerManagerTests(test_rp.BigSwitchProxyPluginV2TestCase):
         self.assertIn('EXTRA-HEADER', callheaders)
         self.assertEqual(callheaders['EXTRA-HEADER'], 'HI')
 
+    def test_req_context_header(self):
+        sp = manager.NeutronManager.get_plugin().servers
+        ncontext = context.Context('uid', 'tid')
+        sp.set_context(ncontext)
+        with mock.patch(HTTPCON) as conmock:
+            rv = conmock.return_value
+            rv.getresponse.return_value.getheader.return_value = 'HASHHEADER'
+            sp.rest_action('GET', '/')
+        callheaders = rv.request.mock_calls[0][1][3]
+        self.assertIn(servermanager.REQ_CONTEXT_HEADER, callheaders)
+        ctxdct = ncontext.to_dict()
+        # auth token is not included
+        ctxdct.pop('auth_token')
+        self.assertEqual(
+            ctxdct, jsonutils.loads(
+                  callheaders[servermanager.REQ_CONTEXT_HEADER]))
+
     def test_capabilities_retrieval(self):
         sp = servermanager.ServerPool()
         with mock.patch(HTTPCON) as conmock:
index f1c844734b9338c3e7a136017e927e1b0d068cd1..127a5a95bed4ce7c66798af116b161c658d6a8b3 100644 (file)
@@ -35,7 +35,8 @@ from neutron.tests.unit import test_db_plugin
 PHYS_NET = 'physnet1'
 VLAN_START = 1000
 VLAN_END = 1100
-SERVER_POOL = 'neutron.plugins.bigswitch.servermanager.ServerPool'
+SERVER_MANAGER = 'neutron.plugins.bigswitch.servermanager'
+SERVER_POOL = SERVER_MANAGER + '.ServerPool'
 DRIVER_MOD = 'neutron.plugins.ml2.drivers.mech_bigswitch.driver'
 DRIVER = DRIVER_MOD + '.BigSwitchMechanismDriver'
 
@@ -212,3 +213,11 @@ class TestBigSwitchMechDriverPortsV2(test_db_plugin.TestPortsV2,
             create_body = rmock.mock_calls[-1][1][2]
             self.assertIsNotNone(create_body['bound_segment'])
             self.assertEqual(create_body[portbindings.HOST_ID], ext_id)
+
+    def test_req_context_header_present(self):
+        with contextlib.nested(
+            mock.patch(SERVER_MANAGER + '.ServerProxy.rest_call'),
+            self.port(**{'device_id': 'devid', 'binding:host_id': 'host'})
+        ) as (mock_rest, p):
+            headers = mock_rest.mock_calls[0][1][3]
+            self.assertIn('X-REQ-CONTEXT', headers)