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
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
'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
@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:
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,
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
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)
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)
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
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
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:
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'
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)