From 00615335f0edb2c124806f6a53427f17eb5feb42 Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Tue, 2 Sep 2014 11:27:51 -0700 Subject: [PATCH] BSN: Add context to backend request for debugging Include the request context with calls to the backend Big Switch controllers to assist with event correlation and debugging object provenance. 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. Note: The commit to master stripped out an auth_token field, however that logic is removed because Icehouse did not have the auth_token to begin with. Conflicts: neutron/plugins/bigswitch/servermanager.py neutron/tests/unit/bigswitch/test_servermanager.py neutron/tests/unit/ml2/drivers/test_bigswitch_mech.py Closes-Bug: #1364696 Change-Id: I5b80b1596cc145742457b3603cbcd67f6e0d9f36 (cherry picked from commit 50c65d16bee8e6e46840f232519e92d9ba9989b4) --- neutron/plugins/bigswitch/plugin.py | 4 +++- neutron/plugins/bigswitch/servermanager.py | 19 +++++++++++++------ .../ml2/drivers/mech_bigswitch/driver.py | 11 +++++++++-- .../unit/bigswitch/test_servermanager.py | 17 +++++++++++++++++ .../unit/ml2/drivers/test_bigswitch_mech.py | 11 ++++++++++- 5 files changed, 52 insertions(+), 10 deletions(-) diff --git a/neutron/plugins/bigswitch/plugin.py b/neutron/plugins/bigswitch/plugin.py index 64eb09fde7f..1bf106aa703 100644 --- a/neutron/plugins/bigswitch/plugin.py +++ b/neutron/plugins/bigswitch/plugin.py @@ -451,7 +451,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 736fe67f829..e9d84845e64 100644 --- a/neutron/plugins/bigswitch/servermanager.py +++ b/neutron/plugins/bigswitch/servermanager.py @@ -71,6 +71,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' @@ -122,12 +123,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 = json.dumps(data) - if not headers: - headers = {} + headers = headers or {} headers['Content-type'] = 'application/json' headers['Accept'] = 'application/json' headers['NeutronProxy-Agent'] = self.name @@ -412,7 +412,12 @@ 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() + headers[REQ_CONTEXT_HEADER] = json.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: @@ -461,13 +466,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 02a6a662597..b2e0b50303b 100644 --- a/neutron/plugins/ml2/drivers/mech_bigswitch/driver.py +++ b/neutron/plugins/ml2/drivers/mech_bigswitch/driver.py @@ -26,15 +26,16 @@ from neutron import context as ctx from neutron.extensions import portbindings from neutron.openstack.common import log from neutron.plugins.bigswitch import config as pl_config -from neutron.plugins.bigswitch.plugin import NeutronRestProxyV2Base +from neutron.plugins.bigswitch import plugin from neutron.plugins.bigswitch import servermanager from neutron.plugins.ml2 import driver_api as api LOG = log.getLogger(__name__) +put_context_in_serverpool = plugin.put_context_in_serverpool -class BigSwitchMechanismDriver(NeutronRestProxyV2Base, +class BigSwitchMechanismDriver(plugin.NeutronRestProxyV2Base, api.MechanismDriver): """Mechanism Driver for Big Switch Networks Controller. @@ -61,18 +62,22 @@ class BigSwitchMechanismDriver(NeutronRestProxyV2Base, self.segmentation_types = ', '.join(cfg.CONF.ml2.type_drivers) 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) @@ -80,6 +85,7 @@ class BigSwitchMechanismDriver(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) @@ -87,6 +93,7 @@ class BigSwitchMechanismDriver(NeutronRestProxyV2Base, self.servers.rest_update_port(port["network"]["tenant_id"], port["network"]["id"], port) + @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 756df444489..f08e01c3cd8 100644 --- a/neutron/tests/unit/bigswitch/test_servermanager.py +++ b/neutron/tests/unit/bigswitch/test_servermanager.py @@ -21,8 +21,10 @@ from contextlib import nested import mock from oslo.config import cfg +from neutron import context from neutron.manager import NeutronManager 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 @@ -154,6 +156,21 @@ class ServerManagerTests(test_rp.BigSwitchProxyPluginV2TestCase): mock.call.write('certdata') ]) + def test_req_context_header(self): + sp = 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() + 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 257b0165c0b..01f4e555f68 100644 --- a/neutron/tests/unit/ml2/drivers/test_bigswitch_mech.py +++ b/neutron/tests/unit/ml2/drivers/test_bigswitch_mech.py @@ -31,7 +31,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' @@ -120,3 +121,11 @@ class TestBigSwitchMechDriverPortsV2(test_db_plugin.TestPortsV2, self.assertEqual('host', pb['binding:host_id']) self.assertIn('bound_segment', pb) self.assertIn('network', pb) + + def test_req_context_header_present(self): + with 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)