From a77d24d155552e5f975e8928a027ba917fa14912 Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Thu, 19 Dec 2013 03:52:20 +0000 Subject: [PATCH] BigSwitch: Fixes floating IP backend updates Changes BigSwitch plugin to correctly use admin context on floating IP updates to the backend controller so they correctly contain floating IPs for all tenants. Closes-Bug: #1262488 Change-Id: I6f2666c242e6d9b0684943db073a2284d01fa1e0 --- neutron/plugins/bigswitch/plugin.py | 20 ++---- neutron/tests/unit/bigswitch/fake_server.py | 36 +++++++++- .../tests/unit/bigswitch/test_router_db.py | 69 +++++++++++++++++++ 3 files changed, 110 insertions(+), 15 deletions(-) diff --git a/neutron/plugins/bigswitch/plugin.py b/neutron/plugins/bigswitch/plugin.py index c9e8faffc6..8f1a48a274 100644 --- a/neutron/plugins/bigswitch/plugin.py +++ b/neutron/plugins/bigswitch/plugin.py @@ -1032,12 +1032,9 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2, new_fl_ip = super(NeutronRestProxyV2, self).create_floatingip(context, floatingip) - net_id = new_fl_ip['floating_network_id'] - orig_net = super(NeutronRestProxyV2, self).get_network(context, - net_id) # create floatingip on the network controller try: - self._send_update_network(orig_net, context) + self._send_floatingip_update(context) except RemoteRestError as e: with excutils.save_and_reraise_exception(): LOG.error( @@ -1054,32 +1051,27 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2, new_fl_ip = super(NeutronRestProxyV2, self).update_floatingip(context, id, floatingip) - net_id = new_fl_ip['floating_network_id'] - orig_net = super(NeutronRestProxyV2, self).get_network(context, - net_id) # update network on network controller - self._send_update_network(orig_net, context) + self._send_floatingip_update(context) return new_fl_ip def delete_floatingip(self, context, id): LOG.debug(_("NeutronRestProxyV2: delete_floatingip() called")) - orig_fl_ip = super(NeutronRestProxyV2, self).get_floatingip(context, - id) with context.session.begin(subtransactions=True): # delete floating IP in DB - net_id = orig_fl_ip['floating_network_id'] super(NeutronRestProxyV2, self).delete_floatingip(context, id) - orig_net = super(NeutronRestProxyV2, self).get_network(context, - net_id) # update network on network controller - self._send_update_network(orig_net, context) + self._send_floatingip_update(context) def disassociate_floatingips(self, context, port_id): LOG.debug(_("NeutronRestProxyV2: diassociate_floatingips() called")) super(NeutronRestProxyV2, self).disassociate_floatingips(context, port_id) + self._send_floatingip_update(context) + + def _send_floatingip_update(self, context): try: ext_net_id = self.get_external_network_id(context) if ext_net_id: diff --git a/neutron/tests/unit/bigswitch/fake_server.py b/neutron/tests/unit/bigswitch/fake_server.py index bc392c651e..6b21a1a712 100644 --- a/neutron/tests/unit/bigswitch/fake_server.py +++ b/neutron/tests/unit/bigswitch/fake_server.py @@ -17,6 +17,12 @@ # @author: Kevin Benton, # +import json + +from neutron.openstack.common import log as logging + +LOG = logging.getLogger(__name__) + class HTTPResponseMock(): status = 200 @@ -50,7 +56,7 @@ class HTTPResponseMock500(HTTPResponseMock): return "{'status': '%s'}" % self.errmsg -class HTTPConnectionMock(): +class HTTPConnectionMock(object): def __init__(self, server, port, timeout): self.response = None @@ -62,6 +68,10 @@ class HTTPConnectionMock(): self.response = HTTPResponseMock500(None, errmsg=errmsg) def request(self, action, uri, body, headers): + LOG.debug(_("Request: action=%(action)s, uri=%(uri)r, " + "body=%(body)s, headers=%(headers)s"), + {'action': action, 'uri': uri, + 'body': body, 'headers': headers}) if self.broken and "ExceptOnBadServer" in uri: raise Exception("Broken server got an unexpected request") if self.response: @@ -94,3 +104,27 @@ class HTTPConnectionMock500(HTTPConnectionMock): def __init__(self, server, port, timeout): self.response = HTTPResponseMock500(None) self.broken = True + + +class VerifyMultiTenantFloatingIP(HTTPConnectionMock): + + def request(self, action, uri, body, headers): + # Only handle network update requests + if 'network' in uri and 'tenant' in uri and 'ports' not in uri: + req = json.loads(body) + if 'network' not in req or 'floatingips' not in req['network']: + msg = _("No floating IPs in request" + "uri=%(uri)s, body=%(body)s") % {'uri': uri, + 'body': body} + raise Exception(msg) + distinct_tenants = [] + for flip in req['network']['floatingips']: + if flip['tenant_id'] not in distinct_tenants: + distinct_tenants.append(flip['tenant_id']) + if len(distinct_tenants) < 2: + msg = _("Expected floating IPs from multiple tenants." + "uri=%(uri)s, body=%(body)s") % {'uri': uri, + 'body': body} + raise Exception(msg) + super(VerifyMultiTenantFloatingIP, + self).request(action, uri, body, headers) diff --git a/neutron/tests/unit/bigswitch/test_router_db.py b/neutron/tests/unit/bigswitch/test_router_db.py index d062d38821..5ea8e00d2e 100644 --- a/neutron/tests/unit/bigswitch/test_router_db.py +++ b/neutron/tests/unit/bigswitch/test_router_db.py @@ -18,6 +18,7 @@ # @author: Sumit Naiksatam, sumitnaiksatam@gmail.com # +import contextlib import copy import os @@ -31,6 +32,7 @@ from neutron.extensions import l3 from neutron.manager import NeutronManager from neutron.openstack.common.notifier import api as notifier_api from neutron.openstack.common.notifier import test_notifier +from neutron.openstack.common import uuidutils from neutron.plugins.bigswitch.extensions import routerrule from neutron.tests.unit.bigswitch import fake_server from neutron.tests.unit.bigswitch import test_base @@ -39,6 +41,9 @@ from neutron.tests.unit import test_extension_extradhcpopts as test_extradhcp from neutron.tests.unit import test_l3_plugin +_uuid = uuidutils.generate_uuid + + def new_L3_setUp(self): test_config['plugin_name_v2'] = ( 'neutron.plugins.bigswitch.plugin.NeutronRestProxyV2') @@ -141,6 +146,70 @@ class RouterDBTestCase(test_base.BigSwitchTestBase, # remove extra port created self._delete('ports', p2['port']['id']) + def test_multi_tenant_flip_alllocation(self): + tenant1_id = _uuid() + tenant2_id = _uuid() + with contextlib.nested( + self.network(tenant_id=tenant1_id), + self.network(tenant_id=tenant2_id)) as (n1, n2): + with contextlib.nested( + self.subnet(network=n1, cidr='11.0.0.0/24'), + self.subnet(network=n2, cidr='12.0.0.0/24'), + self.subnet(cidr='13.0.0.0/24')) as (s1, s2, psub): + with contextlib.nested( + self.router(tenant_id=tenant1_id), + self.router(tenant_id=tenant2_id), + self.port(subnet=s1, tenant_id=tenant1_id), + self.port(subnet=s2, tenant_id=tenant2_id)) as (r1, r2, + p1, p2): + self._set_net_external(psub['subnet']['network_id']) + s1id = p1['port']['fixed_ips'][0]['subnet_id'] + s2id = p2['port']['fixed_ips'][0]['subnet_id'] + s1 = {'subnet': {'id': s1id}} + s2 = {'subnet': {'id': s2id}} + self._add_external_gateway_to_router( + r1['router']['id'], + psub['subnet']['network_id']) + self._add_external_gateway_to_router( + r2['router']['id'], + psub['subnet']['network_id']) + self._router_interface_action( + 'add', r1['router']['id'], + s1['subnet']['id'], None) + self._router_interface_action( + 'add', r2['router']['id'], + s2['subnet']['id'], None) + fl1 = self._make_floatingip_for_tenant_port( + net_id=psub['subnet']['network_id'], + port_id=p1['port']['id'], + tenant_id=tenant1_id) + multiFloatPatch = patch( + 'httplib.HTTPConnection', + create=True, + new=fake_server.VerifyMultiTenantFloatingIP) + multiFloatPatch.start() + fl2 = self._make_floatingip_for_tenant_port( + net_id=psub['subnet']['network_id'], + port_id=p2['port']['id'], + tenant_id=tenant2_id) + multiFloatPatch.stop() + self._delete('floatingips', fl1['floatingip']['id']) + self._delete('floatingips', fl2['floatingip']['id']) + self._router_interface_action( + 'remove', r1['router']['id'], + s1['subnet']['id'], None) + self._router_interface_action( + 'remove', r2['router']['id'], + s2['subnet']['id'], None) + + def _make_floatingip_for_tenant_port(self, net_id, port_id, tenant_id): + data = {'floatingip': {'floating_network_id': net_id, + 'tenant_id': tenant_id, + 'port_id': port_id}} + floatingip_req = self.new_create_request('floatingips', data, self.fmt) + res = floatingip_req.get_response(self.ext_api) + return self.deserialize(self.fmt, res) + def test_floatingip_with_invalid_create_port(self): self._test_floatingip_with_invalid_create_port( 'neutron.plugins.bigswitch.plugin.NeutronRestProxyV2')