diff --git a/networking_arista/common/db_lib.py b/networking_arista/common/db_lib.py index 3f03cbca..01735054 100644 --- a/networking_arista/common/db_lib.py +++ b/networking_arista/common/db_lib.py @@ -157,7 +157,9 @@ def get_port_binding_level(filters): session = db.get_reader_session() with session.begin(): return (session.query(ml2_models.PortBindingLevel). - filter_by(**filters).all()) + filter_by(**filters). + order_by(ml2_models.PortBindingLevel.level). + all()) def get_network_segments_by_port_id(port_id): @@ -166,7 +168,9 @@ def get_network_segments_by_port_id(port_id): segments = (session.query(segment_models.NetworkSegment, ml2_models.PortBindingLevel). join(ml2_models.PortBindingLevel). - filter_by(port_id=port_id).all()) + filter_by(port_id=port_id). + order_by(ml2_models.PortBindingLevel.level). + all()) return [segment[0] for segment in segments] diff --git a/networking_arista/ml2/rpc/arista_json.py b/networking_arista/ml2/rpc/arista_json.py index b1950460..4fc7c22e 100644 --- a/networking_arista/ml2/rpc/arista_json.py +++ b/networking_arista/ml2/rpc/arista_json.py @@ -459,10 +459,9 @@ class AristaRPCWrapperJSON(AristaRPCWrapperBase): continue network_id = neutron_port['network_id'] - if network_id not in networkSegments: - networkSegments[ - network_id] = self._ndb.get_all_network_segments( - network_id) + if port_id not in networkSegments: + networkSegments[port_id] = ( + db_lib.get_network_segments_by_port_id(port_id)) port = self._create_port_data(port_id, tenant_id, network_id, inst_id, @@ -474,7 +473,7 @@ class AristaRPCWrapperJSON(AristaRPCWrapperBase): if instance_type in const.InstanceType.VIRTUAL_INSTANCE_TYPES: portBinding = self._get_host_bindings( port_id, inst_host, network_id, - networkSegments[network_id]) + networkSegments[port_id]) elif (instance_type in const.InstanceType.BAREMETAL_INSTANCE_TYPES): switch_profile = json.loads(port_profiles[ @@ -482,7 +481,7 @@ class AristaRPCWrapperJSON(AristaRPCWrapperBase): portBinding = self._get_switch_bindings( port_id, inst_host, network_id, switch_profile['local_link_information'], - networkSegments[network_id]) + networkSegments[port_id]) if port_id not in portBindings: portBindings[port_id] = portBinding else: diff --git a/networking_arista/tests/unit/common/test_db_lib.py b/networking_arista/tests/unit/common/test_db_lib.py index ac0bd1dc..f3550944 100644 --- a/networking_arista/tests/unit/common/test_db_lib.py +++ b/networking_arista/tests/unit/common/test_db_lib.py @@ -32,6 +32,74 @@ class DbLibTest(testlib_api.SqlTestCase): "neutron.db.db_base_plugin_v2.NeutronDbPluginV2") directory.add_plugin(plugin_constants.CORE, plugin_klass()) + def test_binding_level_and_network_segments(self): + """Test get_port_binding_level and get_network_segments_by_port_id""" + + tenant_id = 't1' + network_id = '11111111-2222-3333-4444-555555555555' + network_ctx = utils.create_network(tenant_id, + network_id, + 5000, + network_type='vxlan', + physical_network=None) + segments = [{'id': network_id, + 'segmentation_id': 5000, + 'physical_network': None, + 'network_type': 'vxlan', + 'is_dynamic': False}, + {'id': None, + 'segmentation_id': 500, + 'physical_network': 'physnet1', + 'network_type': 'vlan', + 'is_dynamic': True}, + {'id': None, + 'segmentation_id': 600, + 'physical_network': 'physnet2', + 'network_type': 'vlan', + 'is_dynamic': True}] + + for segment in segments: + if segment['is_dynamic']: + dyn_seg = utils.create_dynamic_segment( + network_id, segment['segmentation_id'], + segment['network_type'], segment['physical_network']) + segment['id'] = dyn_seg['id'] + + # create ports with different dynamic segments on different hosts + device_id_1 = 'dev1' + port_id_1 = 'p1' + host_1 = 'h1' + utils.create_port(tenant_id, network_id, device_id_1, + port_id_1, network_ctx, host=host_1, + dynamic_segment=segments[1]) + device_id_2 = 'dev2' + port_id_2 = 'p2' + host_2 = 'h2' + utils.create_port(tenant_id, network_id, device_id_2, + port_id_2, network_ctx, host=host_2, + dynamic_segment=segments[2]) + + # Verify get_port_binding_level result + filters = {'port_id': port_id_1, + 'host': host_1} + res_binding_level = db_lib.get_port_binding_level(filters) + self.assertEqual(len(res_binding_level), 2) + expected_ctxt = utils.get_port_context( + tenant_id, network_id, device_id_1, network_ctx, port_id_1, + host=host_1, dynamic_segment=segments[1]) + for i in range(0, len(res_binding_level)): + self.assertEqual(dict(res_binding_level[i]), + vars(expected_ctxt._binding_levels[i])) + + # Verify get_network_segments_by_port_id result + res_segs = db_lib.get_network_segments_by_port_id(port_id_1) + self.assertEqual(len(res_segs), 2) + subset_keys = {'id', 'network_type', 'physical_network', + 'segmentation_id', 'is_dynamic'} + for i, rs in enumerate(res_segs): + self.assertEqual(segments[i], + {k: v for k, v in rs if k in subset_keys}) + def test_get_tenants_empty(self): tenants = db_lib.get_tenants() self.assertEqual(tenants, set()) diff --git a/networking_arista/tests/unit/ml2/rpc/test_arista_eapi_rpc_wrapper.py b/networking_arista/tests/unit/ml2/rpc/test_arista_eapi_rpc_wrapper.py index cef91d45..cf849c03 100644 --- a/networking_arista/tests/unit/ml2/rpc/test_arista_eapi_rpc_wrapper.py +++ b/networking_arista/tests/unit/ml2/rpc/test_arista_eapi_rpc_wrapper.py @@ -25,10 +25,6 @@ from networking_arista.ml2.rpc import arista_eapi from networking_arista.tests.unit import utils -EAPI_SEND_FUNC = ('networking_arista.ml2.rpc.arista_eapi.AristaRPCWrapperEapi' - '._send_eapi_req') - - def setup_valid_config(): utils.setup_arista_wrapper_config(cfg) diff --git a/networking_arista/tests/unit/ml2/rpc/test_arista_json_rpc_wrapper.py b/networking_arista/tests/unit/ml2/rpc/test_arista_json_rpc_wrapper.py index e635192e..14fe71b4 100644 --- a/networking_arista/tests/unit/ml2/rpc/test_arista_json_rpc_wrapper.py +++ b/networking_arista/tests/unit/ml2/rpc/test_arista_json_rpc_wrapper.py @@ -36,6 +36,7 @@ from networking_arista.tests.unit import utils BASE_RPC = "networking_arista.ml2.rpc.arista_json.AristaRPCWrapperJSON." JSON_SEND_FUNC = BASE_RPC + "_send_api_request" RAND_FUNC = BASE_RPC + "_get_random_name" +DB_LIB_MODULE = 'networking_arista.ml2.rpc.arista_json.db_lib' def setup_valid_config(): @@ -262,7 +263,8 @@ class TestAristaJSONRPCWrapper(testlib_api.SqlTestCase): self._verify_send_api_request_call(mock_send_api_req, calls) @patch(JSON_SEND_FUNC) - def test_create_instance_bulk(self, mock_send_api_req): + @patch(DB_LIB_MODULE) + def test_create_instance_bulk(self, mock_db_lib, mock_send_api_req): tenant_id = 'ten-3' num_devices = 8 num_ports_per_device = 2 @@ -301,6 +303,19 @@ class TestAristaJSONRPCWrapper(testlib_api.SqlTestCase): 'network_id': 'network-id-%d' % net_count, 'name': 'port-%d-%d' % (device_id, port_id), 'tenant_id': tenant_id, + 'segments': [{ + 'network_id': 'network-id-%d' % net_count, + 'segment_type': 'static', + 'segmentation_id': (5000 + net_count), + 'is_dynamic': False, + 'network_type': 'vxlan', + 'id': 'segment-id-%d' % (5000 + net_count)}, + {'network_id': 'network-id-%d' % net_count, + 'segment_type': 'dynamic', + 'segmentation_id': (500 + net_count), + 'is_dynamic': True, + 'network_type': 'vlan', + 'id': 'segment-id-%d' % (500 + net_count)}], } port_list.append(port) net_count += 1 @@ -309,6 +324,10 @@ class TestAristaJSONRPCWrapper(testlib_api.SqlTestCase): for port in port_list: create_ports.update(utils.port_dict_representation(port)) + port_network_segments = {} + for port in port_list: + port_network_segments[port['portId']] = port['segments'] + profiles = {} for port in port_list: profiles[port['portId']] = {'vnic_type': 'normal'} @@ -317,6 +336,9 @@ class TestAristaJSONRPCWrapper(testlib_api.SqlTestCase): 'vnic_type': 'baremetal', 'profile': '{"local_link_information":' '[{"switch_id": "switch01", "port_id": "Ethernet1"}]}'} + + mock_db_lib.get_network_segments_by_port_id.side_effect = ( + port_network_segments.get) self.drv.create_instance_bulk(tenant_id, create_ports, devices, profiles) calls = [ @@ -407,64 +429,244 @@ class TestAristaJSONRPCWrapper(testlib_api.SqlTestCase): 'instanceType': 'router', 'vlanType': 'allowed'}]), ('region/RegionOne/port/port-id-0-0/binding', - 'POST', [{'portId': 'port-id-0-0', 'hostBinding': [ - {'segment': [], 'host': 'host_0'}]}]), + 'POST', [{'portId': 'port-id-0-0', + 'hostBinding': [{ + 'segment': [{'networkId': 'network-id-0', + 'segment_type': 'static', + 'segmentationId': 5000, + 'type': 'vxlan', + 'id': 'segment-id-5000'}, + {'networkId': 'network-id-0', + 'segment_type': 'dynamic', + 'segmentationId': 500, + 'type': 'vlan', + 'id': 'segment-id-500'}], + 'host': 'host_0'}]}]), ('region/RegionOne/port/port-id-0-1/binding', - 'POST', [{'portId': 'port-id-0-1', 'hostBinding': [ - {'segment': [], 'host': 'host_0'}]}]), + 'POST', [{'portId': 'port-id-0-1', + 'hostBinding': [{ + 'segment': [{'networkId': 'network-id-1', + 'segment_type': 'static', + 'segmentationId': 5001, + 'type': 'vxlan', + 'id': 'segment-id-5001'}, + {'networkId': 'network-id-1', + 'segment_type': 'dynamic', + 'segmentationId': 501, + 'type': 'vlan', + 'id': 'segment-id-501'}], + 'host': 'host_0'}]}]), ('region/RegionOne/port/port-id-1-0/binding', - 'POST', [{'portId': 'port-id-1-0', 'hostBinding': [ - {'segment': [], 'host': 'host_1'}]}]), + 'POST', [{'portId': 'port-id-1-0', + 'hostBinding': [{ + 'segment': [{'networkId': 'network-id-2', + 'segment_type': 'static', + 'segmentationId': 5002, + 'type': 'vxlan', + 'id': 'segment-id-5002'}, + {'networkId': 'network-id-2', + 'segment_type': 'dynamic', + 'segmentationId': 502, + 'type': 'vlan', + 'id': 'segment-id-502'}], + 'host': 'host_1'}]}]), ('region/RegionOne/port/port-id-1-1/binding', - 'POST', [{'portId': 'port-id-1-1', 'hostBinding': [ - {'segment': [], 'host': 'host_1'}]}]), + 'POST', [{'portId': 'port-id-1-1', + 'hostBinding': [{ + 'segment': [{'networkId': 'network-id-3', + 'segment_type': 'static', + 'segmentationId': 5003, + 'type': 'vxlan', + 'id': 'segment-id-5003'}, + {'networkId': 'network-id-3', + 'segment_type': 'dynamic', + 'segmentationId': 503, + 'type': 'vlan', + 'id': 'segment-id-503'}], + 'host': 'host_1'}]}]), ('region/RegionOne/port/port-id-2-0/binding', - 'POST', [{'portId': 'port-id-2-0', 'switchBinding': [ - {'interface': u'Ethernet1', 'host': 'host_2', - 'segment': [], 'switch': u'switch01'}]}]), + 'POST', [{'portId': 'port-id-2-0', + 'switchBinding': [{ + 'interface': u'Ethernet1', + 'host': 'host_2', + 'segment': [{'networkId': 'network-id-4', + 'segment_type': 'static', + 'segmentationId': 5004, + 'type': 'vxlan', + 'id': 'segment-id-5004'}, + {'networkId': 'network-id-4', + 'segment_type': 'dynamic', + 'segmentationId': 504, + 'type': 'vlan', + 'id': 'segment-id-504'}], + 'switch': u'switch01'}]}]), ('region/RegionOne/port/port-id-2-1/binding', - 'POST', [{'portId': 'port-id-2-1', 'switchBinding': [ - {'interface': u'Ethernet1', 'host': 'host_2', - 'segment': [], 'switch': u'switch01'}]}]), + 'POST', [{'portId': 'port-id-2-1', + 'switchBinding': [ + {'interface': u'Ethernet1', + 'host': 'host_2', + 'segment': [{'networkId': 'network-id-5', + 'segment_type': 'static', + 'segmentationId': 5005, + 'type': 'vxlan', + 'id': 'segment-id-5005'}, + {'networkId': 'network-id-5', + 'segment_type': 'dynamic', + 'segmentationId': 505, + 'type': 'vlan', + 'id': 'segment-id-505'}], + 'switch': u'switch01'}]}]), ('region/RegionOne/port/port-id-3-0/binding', - 'POST', [{'portId': 'port-id-3-0', 'hostBinding': [ - {'segment': [], 'host': 'host_3'}]}]), + 'POST', [{'portId': 'port-id-3-0', + 'hostBinding': [{ + 'segment': [{'networkId': 'network-id-6', + 'segment_type': 'static', + 'segmentationId': 5006, + 'type': 'vxlan', + 'id': 'segment-id-5006'}, + {'networkId': 'network-id-6', + 'segment_type': 'dynamic', + 'segmentationId': 506, + 'type': 'vlan', + 'id': 'segment-id-506'}], + 'host': 'host_3'}]}]), ('region/RegionOne/port/port-id-3-1/binding', - 'POST', [{'portId': 'port-id-3-1', 'hostBinding': [ - {'segment': [], 'host': 'host_3'}]}]), + 'POST', [{'portId': 'port-id-3-1', + 'hostBinding': [{ + 'segment': [{'networkId': 'network-id-7', + 'segment_type': 'static', + 'segmentationId': 5007, + 'type': 'vxlan', + 'id': 'segment-id-5007'}, + {'networkId': 'network-id-7', + 'segment_type': 'dynamic', + 'segmentationId': 507, + 'type': 'vlan', + 'id': 'segment-id-507'}], + 'host': 'host_3'}]}]), ('region/RegionOne/port/port-id-4-0/binding', - 'POST', [{'portId': 'port-id-4-0', 'hostBinding': [ - {'segment': [], 'host': 'host_4'}]}]), + 'POST', [{'portId': 'port-id-4-0', + 'hostBinding': [{ + 'segment': [{'networkId': 'network-id-8', + 'segment_type': 'static', + 'segmentationId': 5008, + 'type': 'vxlan', + 'id': 'segment-id-5008'}, + {'networkId': 'network-id-8', + 'segment_type': 'dynamic', + 'segmentationId': 508, + 'type': 'vlan', + 'id': 'segment-id-508'}], + 'host': 'host_4'}]}]), ('region/RegionOne/port/port-id-4-1/binding', - 'POST', [{'portId': 'port-id-4-1', 'hostBinding': [ - {'segment': [], 'host': 'host_4'}]}]), + 'POST', [{'portId': 'port-id-4-1', + 'hostBinding': [{ + 'segment': [{'networkId': 'network-id-9', + 'segment_type': 'static', + 'segmentationId': 5009, + 'type': 'vxlan', + 'id': 'segment-id-5009'}, + {'networkId': 'network-id-9', + 'segment_type': 'dynamic', + 'segmentationId': 509, + 'type': 'vlan', + 'id': 'segment-id-509'}], + 'host': 'host_4'}]}]), ('region/RegionOne/port/port-id-5-0/binding', - 'POST', [{'portId': 'port-id-5-0', 'hostBinding': [ - {'segment': [], 'host': 'host_5'}]}]), + 'POST', [{'portId': 'port-id-5-0', + 'hostBinding': [{ + 'segment': [{'networkId': 'network-id-10', + 'segment_type': 'static', + 'segmentationId': 5010, + 'type': 'vxlan', + 'id': 'segment-id-5010'}, + {'networkId': 'network-id-10', + 'segment_type': 'dynamic', + 'segmentationId': 510, + 'type': 'vlan', + 'id': 'segment-id-510'}], + 'host': 'host_5'}]}]), ('region/RegionOne/port/port-id-5-1/binding', - 'POST', [{'portId': 'port-id-5-1', 'hostBinding': [ - {'segment': [], 'host': 'host_5'}]}]), + 'POST', [{'portId': 'port-id-5-1', + 'hostBinding': [{ + 'segment': [{'networkId': 'network-id-11', + 'segment_type': 'static', + 'segmentationId': 5011, + 'type': 'vxlan', + 'id': 'segment-id-5011'}, + {'networkId': 'network-id-11', + 'segment_type': 'dynamic', + 'segmentationId': 511, + 'type': 'vlan', + 'id': 'segment-id-511'}], + 'host': 'host_5'}]}]), ('region/RegionOne/port/port-id-6-0/binding', - 'POST', [{'portId': 'port-id-6-0', 'switchBinding': [ - {'interface': u'Ethernet1', 'host': 'host_6', - 'segment': [], 'switch': u'switch01'}]}]), + 'POST', [{'portId': 'port-id-6-0', + 'switchBinding': [{ + 'interface': u'Ethernet1', + 'host': 'host_6', + 'segment': [{'networkId': 'network-id-12', + 'segment_type': 'static', + 'segmentationId': 5012, + 'type': 'vxlan', + 'id': 'segment-id-5012'}, + {'networkId': 'network-id-12', + 'segment_type': 'dynamic', + 'segmentationId': 512, + 'type': 'vlan', + 'id': 'segment-id-512'}], + 'switch': u'switch01'}]}]), ('region/RegionOne/port/port-id-6-1/binding', - 'POST', [{'portId': 'port-id-6-1', 'switchBinding': [ - {'interface': u'Ethernet1', 'host': 'host_6', - 'segment': [], 'switch': u'switch01'}]}]), + 'POST', [{'portId': 'port-id-6-1', + 'switchBinding': [{ + 'interface': u'Ethernet1', + 'host': 'host_6', + 'segment': [{'networkId': 'network-id-13', + 'segment_type': 'static', + 'segmentationId': 5013, + 'type': 'vxlan', + 'id': 'segment-id-5013'}, + {'networkId': 'network-id-13', + 'segment_type': 'dynamic', + 'segmentationId': 513, + 'type': 'vlan', + 'id': 'segment-id-513'}], + 'switch': u'switch01'}]}]), ('region/RegionOne/port/port-id-7-0/binding', - 'POST', [{'portId': 'port-id-7-0', 'hostBinding': [ - {'segment': [], 'host': 'host_7'}]}]), + 'POST', [{'portId': 'port-id-7-0', + 'hostBinding': [{ + 'segment': [{'networkId': 'network-id-14', + 'segment_type': 'static', + 'segmentationId': 5014, + 'type': 'vxlan', + 'id': 'segment-id-5014'}, + {'networkId': 'network-id-14', + 'segment_type': 'dynamic', + 'segmentationId': 514, + 'type': 'vlan', + 'id': 'segment-id-514'}], + 'host': 'host_7'}]}]), ('region/RegionOne/port/port-id-7-1/binding', - 'POST', [{'portId': 'port-id-7-1', 'hostBinding': [ - {'segment': [], 'host': 'host_7'}]}]), + 'POST', [{'portId': 'port-id-7-1', + 'hostBinding': [{ + 'segment': [{'networkId': 'network-id-15', + 'segment_type': 'static', + 'segmentationId': 5015, + 'type': 'vxlan', + 'id': 'segment-id-5015'}, + {'networkId': 'network-id-15', + 'segment_type': 'dynamic', + 'segmentationId': 515, + 'type': 'vlan', + 'id': 'segment-id-515'}], + 'host': 'host_7'}]}]), ] self._verify_send_api_request_call(mock_send_api_req, calls, True) @@ -720,10 +922,11 @@ class RPCWrapperJSONValidConfigTrunkTestCase(testlib_api.SqlTestCase): self.drv = arista_json.AristaRPCWrapperJSON(ndb) self.drv._server_ip = "10.11.12.13" self.region = 'RegionOne' - arista_json.db_lib = mock.MagicMock() @patch(JSON_SEND_FUNC) - def test_plug_virtual_trunk_port_into_network(self, mock_send_api_req): + @patch(DB_LIB_MODULE) + def test_plug_virtual_trunk_port_into_network(self, mock_db_lib, + mock_send_api_req): # vm tenant_id = 'ten-1' network_id = 'net-id-1' @@ -750,8 +953,8 @@ class RPCWrapperJSONValidConfigTrunkTestCase(testlib_api.SqlTestCase): 'segmentation_type': 'vlan'}], 'trunk_id': 'trunk_id'} self.drv._ndb.get_network_id_from_port_id.return_value = subport_net_id - arista_json.db_lib.get_network_segments_by_port_id.return_value = \ - subport_segments + mock_db_lib.get_network_segments_by_port_id.return_value = ( + subport_segments) self.drv.plug_port_into_network(vm_id, host, port_id, network_id, tenant_id, port_name, @@ -783,7 +986,9 @@ class RPCWrapperJSONValidConfigTrunkTestCase(testlib_api.SqlTestCase): self._verify_send_api_request_call(mock_send_api_req, calls) @patch(JSON_SEND_FUNC) - def test_plug_baremetal_trunk_port_into_network(self, mock_send_api_req): + @patch(DB_LIB_MODULE) + def test_plug_baremetal_trunk_port_into_network(self, mock_db_lib, + mock_send_api_req): # baremetal tenant_id = 'ten-2' network_id = 'net-id-1' @@ -813,8 +1018,8 @@ class RPCWrapperJSONValidConfigTrunkTestCase(testlib_api.SqlTestCase): 'switch_info': 'switch-1'}]} bindings = switch_bindings['local_link_information'] self.drv._ndb.get_network_id_from_port_id.return_value = subport_net_id - arista_json.db_lib.get_network_segments_by_port_id.return_value = \ - subport_segments + mock_db_lib.get_network_segments_by_port_id.return_value = ( + subport_segments) self.drv.plug_port_into_network(bm_id, host, port_id, network_id, tenant_id, port_name,