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')