From a381726b53f74225ed9ba40015afb1597a05ed5a Mon Sep 17 00:00:00 2001 From: vikas Date: Fri, 30 Oct 2015 01:10:05 -0700 Subject: [PATCH] support multi segment network for l2gw connection Since l2gateway binds vxlan(virtual side) to vlan(physical side) segment. We are extracting only vxlan segment from the virtual network. Since multi segment network can have multiple segmentation id's belonging to different network types(vlan, gre, vxlan, etc), we are extracting the vxlan segment from it. We are checking for multiple vxlan segments in the network since ovsdb does not allow multiple vxlan segmentation id's to associate for same vlan (physical side) while creating l2_gateway_connection. For Ex: say multi segment network has two vxlan segment id's 1000 and 2000. If we create a l2_gateway_connection for this network by providing physical side vlan segment as 500, the bindings should be 1000:500 and 2000:500, this is not allowed in ovsdb hardware vtep schema. Closes-Bug:1511639 Change-Id: I5f704eaf40763686aa30320dff7a99a16b6c1772 --- networking_l2gw/db/l2gateway/db_query.py | 9 --- .../services/l2gateway/common/constants.py | 1 + .../services/l2gateway/exceptions.py | 13 +++- .../l2gateway/service_drivers/rpc_l2gw.py | 40 +++++----- .../service_drivers/test_rpc_l2gw.py | 77 ++++++++++++++++++- 5 files changed, 106 insertions(+), 34 deletions(-) diff --git a/networking_l2gw/db/l2gateway/db_query.py b/networking_l2gw/db/l2gateway/db_query.py index d69e660..19ce9fb 100644 --- a/networking_l2gw/db/l2gateway/db_query.py +++ b/networking_l2gw/db/l2gateway/db_query.py @@ -16,7 +16,6 @@ from neutron.common import exceptions from neutron.db import models_v2 from neutron.plugins.ml2 import driver_api as api -from neutron.plugins.ml2 import models import sqlalchemy as sa from sqlalchemy.orm import exc @@ -94,14 +93,6 @@ class L2GatewayCommonDbMixin(object): items.reverse() return items - def _get_network_segments(self, context, network_id): - """Get network segments for the given network_id.""" - with context.session.begin(subtransactions=True): - query = (context.session.query(models.NetworkSegment). - filter_by(network_id=network_id)) - records = query.all() - return [self._make_segment_dict(record) for record in records] - def _make_segment_dict(self, record): """Make a segment dictionary out of a DB record.""" return {api.ID: record.id, diff --git a/networking_l2gw/services/l2gateway/common/constants.py b/networking_l2gw/services/l2gateway/common/constants.py index 74585d0..41398cb 100644 --- a/networking_l2gw/services/l2gateway/common/constants.py +++ b/networking_l2gw/services/l2gateway/common/constants.py @@ -42,3 +42,4 @@ MAX_RETRIES = 1000 L2_GATEWAY_SERVICE_PLUGIN = "Neutron L2 gateway Service Plugin" PORT_FAULT_STATUS_UP = "UP" SWITCH_FAULT_STATUS_UP = "UP" +VXLAN = "vxlan" diff --git a/networking_l2gw/services/l2gateway/exceptions.py b/networking_l2gw/services/l2gateway/exceptions.py index 01bfed6..0f02403 100644 --- a/networking_l2gw/services/l2gateway/exceptions.py +++ b/networking_l2gw/services/l2gateway/exceptions.py @@ -79,8 +79,14 @@ class L2gatewaySegmentationIDNotFound(exceptions.NotFound): "'%(gateway_id)s'") -class MultipleSegmentsFound(exceptions.NeutronException): - message = _("Multiple segments found for the network '%(network_id)s'") +class MultipleVxlanSegmentsFound(exceptions.NeutronException): + message = _("Multiple Vxlan segments found for the network " + "'%(network_id)s'") + + +class VxlanSegmentationIDNotFound(exceptions.NotFound): + message = _("vxlan segmentation id not found for the " + "network '%(network_id)s'") class L2GatewayInterfaceRequired(exceptions.NeutronException): @@ -108,7 +114,8 @@ base.FAULT_MAP.update({L2GatewayInUse: web_exc.HTTPConflict, L2GatewayPortInUse: web_exc.HTTPConflict, L2GatewayConnectionExists: web_exc.HTTPConflict, L2GatewayConnectionNotFound: web_exc.HTTPNotFound, - MultipleSegmentsFound: web_exc.HTTPConflict, + MultipleVxlanSegmentsFound: web_exc.HTTPConflict, + VxlanSegmentationIDNotFound: web_exc.HTTPNotFound, L2GatewaySegmentationRequired: web_exc.HTTPConflict, L2MultipleGatewayConnections: web_exc.HTTPConflict, L2GatewayDuplicateSegmentationID: web_exc.HTTPConflict, diff --git a/networking_l2gw/services/l2gateway/service_drivers/rpc_l2gw.py b/networking_l2gw/services/l2gateway/service_drivers/rpc_l2gw.py index 4673d4f..9a4c921 100644 --- a/networking_l2gw/services/l2gateway/service_drivers/rpc_l2gw.py +++ b/networking_l2gw/services/l2gateway/service_drivers/rpc_l2gw.py @@ -177,19 +177,6 @@ class L2gwRpcDriver(service_drivers.L2gwDriver): physical_locator, [mac_remote]) - def _form_logical_switch_schema(self, context, network, ls_dict): - logical_switch_uuid = None - logical_switch = db.get_logical_switch_by_name( - context, ls_dict) - if logical_switch: - logical_switch_uuid = logical_switch.get('uuid') - logical_switch = self._get_dict(ovsdb_schema.LogicalSwitch( - uuid=logical_switch_uuid, - name=network['id'], - key=network['provider:segmentation_id'], - description=network['name'])) - return logical_switch - def _form_physical_locator_schema(self, context, pl_dict): locator_uuid = None locator = db.get_physical_locator_by_dst_ip( @@ -359,10 +346,6 @@ class L2gwRpcDriver(service_drivers.L2gwDriver): nw_map['l2_gateway_id'] = l2_gw_id if seg_id: nw_map[constants.SEG_ID] = gw_connection.get(constants.SEG_ID) - net_segments_list = self.service_plugin._get_network_segments( - context, network_id) - if len(net_segments_list) > 1: - raise l2gw_exc.MultipleSegmentsFound(network_id=network_id) if not self.service_plugin._get_network(context, network_id): raise exceptions.NetworkNotFound(net_id=network_id) if self.service_plugin._retrieve_gateway_connections(context, @@ -515,12 +498,29 @@ class L2gwRpcDriver(service_drivers.L2gwDriver): uuid = logical_switch.get('uuid') else: uuid = None + network_id = gw_connection.get('network_id') ls_dict = {'uuid': uuid, - 'name': gw_connection.get('network_id')} + 'name': network_id} network = self._get_network_details(context, - gw_connection.get('network_id')) + network_id) ls_dict['description'] = network.get('name') - ls_dict['key'] = network.get('provider:segmentation_id') + logical_segments = network.get('segments') + if logical_segments: + vxlan_seg_id = None + for seg in logical_segments: + if seg.get('provider:network_type') == constants.VXLAN: + if vxlan_seg_id: + raise l2gw_exc.MultipleVxlanSegmentsFound( + network_id=network_id) + vxlan_seg_id = seg.get('provider:segmentation_id') + ls_dict['key'] = vxlan_seg_id + if not vxlan_seg_id: + raise l2gw_exc.VxlanSegmentationIDNotFound( + network_id=network_id) + elif network.get('provider:network_type') == constants.VXLAN: + ls_dict['key'] = network.get('provider:segmentation_id') + else: + raise l2gw_exc.VxlanSegmentationIDNotFound(network_id=network_id) return ls_dict def _get_physical_locator_dict(self, dst_ip, diff --git a/networking_l2gw/tests/unit/services/l2gateway/service_drivers/test_rpc_l2gw.py b/networking_l2gw/tests/unit/services/l2gateway/service_drivers/test_rpc_l2gw.py index 060cc16..e8856d9 100644 --- a/networking_l2gw/tests/unit/services/l2gateway/service_drivers/test_rpc_l2gw.py +++ b/networking_l2gw/tests/unit/services/l2gateway/service_drivers/test_rpc_l2gw.py @@ -122,8 +122,6 @@ class TestL2gwRpcDriver(base.BaseTestCase): self.plugin._validate_connection(self.context, fake_connection) is_vlan.assert_called_with(self.context, 'fake_l2gw_id') val_ntwk.assert_called_with(fake_connection, False) - get_net_seg.assert_called_with(self.context, - 'fake_network_id') get_network.assert_called_with(self.context, 'fake_network_id') get_l2gw.assert_called_with(self.context, 'fake_l2gw_id') check_pf_sf.assert_called_with(self.context, 'fake_l2gw_id') @@ -308,6 +306,7 @@ class TestL2gwRpcDriver(base.BaseTestCase): 'segmentation_id': 100L} fake_network = {'id': 'fake_network_id', 'name': 'fake_network_name', + 'provider:network_type': 'vxlan', 'provider:segmentation_id': 'fake_key'} fake_ls_dict = {'uuid': 'fake_uuid', 'name': 'fake_network_id', @@ -328,6 +327,80 @@ class TestL2gwRpcDriver(base.BaseTestCase): self.assertEqual(ret_ls_dict, fake_ls_dict) self.assertEqual(ret_ls_dict_without_ls, fake_ls_dict_without_ls) + def test_get_logical_switch_dict_for_multi_segment_network(self): + fake_logical_switch = {'uuid': 'fake_uuid', + 'name': 'fake_network_id'} + fake_ls = None + fake_connection = {'l2_gateway_id': 'fake_l2gw_id', + 'network_id': 'fake_network_id', + 'segmentation_id': 100L} + fake_network = {'id': 'fake_network_id', + 'name': 'fake_network_name', + 'segments': [{"provider:network_type": "vxlan", + "provider:segmentation_id": 'fake_key'}, + {"provider:network_type": "vlan", + "provider:segmentation_id": 'fake_key'}]} + fake_ls_dict = {'uuid': 'fake_uuid', + 'name': 'fake_network_id', + 'description': 'fake_network_name', + 'key': 'fake_key'} + fake_ls_dict_without_ls = {'uuid': None, + 'name': 'fake_network_id', + 'description': 'fake_network_name', + 'key': 'fake_key'} + with mock.patch.object(self.plugin, + '_get_network_details', + return_value=fake_network) as get_network: + ret_ls_dict = self.plugin._get_logical_switch_dict( + self.context, fake_logical_switch, fake_connection) + ret_ls_dict_without_ls = self.plugin._get_logical_switch_dict( + self.context, fake_ls, fake_connection) + get_network.assert_called_with(self.context, 'fake_network_id') + self.assertEqual(fake_ls_dict, ret_ls_dict) + self.assertEqual(fake_ls_dict_without_ls, ret_ls_dict_without_ls) + + def test_get_logical_switch_dict_for_non_Vxlan_networks(self): + fake_logical_switch = {'uuid': 'fake_uuid', + 'name': 'fake_network_id'} + fake_connection = {'l2_gateway_id': 'fake_l2gw_id', + 'network_id': 'fake_network_id', + 'segmentation_id': 100L} + fake_network = {'id': 'fake_network_id', + 'name': 'fake_network_name', + 'segments': [{"provider:network_type": "vlan", + "provider:segmentation_id": 'fake_key'}, + {"provider:network_type": "gre", + "provider:segmentation_id": 'fake_key'}]} + with mock.patch.object(self.plugin, + '_get_network_details', + return_value=fake_network) as get_network: + self.assertRaises(l2gw_exc.VxlanSegmentationIDNotFound, + self.plugin._get_logical_switch_dict, + self.context, fake_logical_switch, + fake_connection) + get_network.assert_called_with(self.context, 'fake_network_id') + + def test_get_logical_switch_dict_for_multiple_vxlan_segments(self): + fake_logical_switch = {'uuid': 'fake_uuid', + 'name': 'fake_network_id'} + fake_connection = {'l2_gateway_id': 'fake_l2gw_id', + 'network_id': 'fake_network_id', + 'segmentation_id': 100L} + fake_network = {'id': 'fake_network_id', + 'name': 'fake_network_name', + 'segments': [{"provider:network_type": "vxlan", + "provider:segmentation_id": 'seg_1'}, + {"provider:network_type": "vxlan", + "provider:segmentation_id": 'seg_2'}]} + with mock.patch.object(self.plugin, + '_get_network_details', + return_value=fake_network) as get_network: + self.assertRaises(l2gw_exc.MultipleVxlanSegmentsFound, + self.plugin._get_logical_switch_dict, + self.context, fake_logical_switch, + fake_connection) + get_network.assert_called_with(self.context, 'fake_network_id') + def test_get_locator_list(self): fake_dst_ip = 'fake_tun_ip' fake_ovsdb_id = 'fake_ovsdb_id'