diff --git a/neutron/plugins/bigswitch/plugin.py b/neutron/plugins/bigswitch/plugin.py index cb0fb20bf2..5d119113fc 100644 --- a/neutron/plugins/bigswitch/plugin.py +++ b/neutron/plugins/bigswitch/plugin.py @@ -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 diff --git a/neutron/plugins/bigswitch/servermanager.py b/neutron/plugins/bigswitch/servermanager.py index e36ec28777..1b072848fd 100644 --- a/neutron/plugins/bigswitch/servermanager.py +++ b/neutron/plugins/bigswitch/servermanager.py @@ -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, diff --git a/neutron/plugins/ml2/drivers/mech_bigswitch/driver.py b/neutron/plugins/ml2/drivers/mech_bigswitch/driver.py index dc8c12c339..349d8870bd 100644 --- a/neutron/plugins/ml2/drivers/mech_bigswitch/driver.py +++ b/neutron/plugins/ml2/drivers/mech_bigswitch/driver.py @@ -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 diff --git a/neutron/tests/unit/bigswitch/test_servermanager.py b/neutron/tests/unit/bigswitch/test_servermanager.py index fd575abd17..b30ca83479 100644 --- a/neutron/tests/unit/bigswitch/test_servermanager.py +++ b/neutron/tests/unit/bigswitch/test_servermanager.py @@ -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: diff --git a/neutron/tests/unit/ml2/drivers/test_bigswitch_mech.py b/neutron/tests/unit/ml2/drivers/test_bigswitch_mech.py index f1c844734b..127a5a95be 100644 --- a/neutron/tests/unit/ml2/drivers/test_bigswitch_mech.py +++ b/neutron/tests/unit/ml2/drivers/test_bigswitch_mech.py @@ -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)