From 3cbe6fd5a33b5eb79d9c6669a056a3ec8e7c4663 Mon Sep 17 00:00:00 2001 From: Prakash Bailkeri Date: Mon, 6 Feb 2017 16:30:15 +0530 Subject: [PATCH] PBB EVPN changes Changes included: 1. Schema changes to make bridge-domain as child of virtua-network 2. Don't allow VMI to refer to different bridge domain for same vlan tag. e.g. Say, VMI refers to BD1 for vlan tag 0, don't allow this VMI to refer to another bridge domain(BD2) for vlan tag 0. 3. Add Unit test case for restricting VMI link to single bridge-domain for a given vlan tag Change-Id: Ic8599153a4a413b118b7e4968d9988b02aa6c539 Related-bug: 1645092 Closes-Bug: 1653941 Closes-Bug: 1653938 --- .../api-server/tests/test_crud_basic.py | 43 +++++++++++++++++++ src/config/api-server/vnc_cfg_types.py | 34 +++++++++++++++ src/schema/vnc_cfg.xsd | 4 +- 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/src/config/api-server/tests/test_crud_basic.py b/src/config/api-server/tests/test_crud_basic.py index 7ab4075d6df..3e0452c8792 100644 --- a/src/config/api-server/tests/test_crud_basic.py +++ b/src/config/api-server/tests/test_crud_basic.py @@ -415,6 +415,49 @@ def test_physical_router_credentials(self): if user_cred_read.password != '**Password Hidden**': raise Exception("ERROR: physical-router: password should be hidden") #end test_physical_router_credentials + + def test_bridge_domain_with_vmi_ref_multiple_bd(self): + vn1_name = self.id() + '-vn-1' + vn1 = VirtualNetwork(vn1_name) + logger.info('Creating VN %s', vn1_name) + self._vnc_lib.virtual_network_create(vn1) + + vmi_name = self.id() + '-port' + logger.info('Creating port %s', vmi_name) + vmi = VirtualMachineInterface(vmi_name, parent_obj=Project()) + vmi.add_virtual_network(vn1) + self._vnc_lib.virtual_machine_interface_create(vmi) + + bd1_name = self.id() + '-bd-1' + bd1 = BridgeDomain(bd1_name, parent_obj=vn1) + bd1.set_isid(200200) + logger.info('Creating Bridge Domain %s', bd1_name) + self._vnc_lib.bridge_domain_create(bd1) + + bd2_name = self.id() + '-bd-2' + bd2 = BridgeDomain(bd2_name, parent_obj=vn1) + bd2.set_isid(300300) + logger.info('Creating Bridge Domain %s', bd2_name) + self._vnc_lib.bridge_domain_create(bd2) + + bd_ref_data1 = BridgeDomainMembershipType(); + bd_ref_data1.set_vlan_tag(0); + + # VMI is referring to two bridge domain(bd1 and bd2) for vlan tag = 0 + vmi.add_bridge_domain(bd1, bd_ref_data1); + vmi.add_bridge_domain(bd2, bd_ref_data1); + with ExpectedException(BadRequest) as e: + self._vnc_lib.virtual_machine_interface_update(vmi) + + # Link the VMI to two bridge domain for different vlan tags(0, 1) + vmi_obj = self._vnc_lib.virtual_machine_interface_read(id=vmi.uuid) + bd_ref_data1 = BridgeDomainMembershipType(vlan_tag=1); + bd_ref_data2 = BridgeDomainMembershipType(vlan_tag=2); + vmi_obj.add_bridge_domain(bd1, bd_ref_data1); + vmi_obj.add_bridge_domain(bd2, bd_ref_data2); + self._vnc_lib.virtual_machine_interface_update(vmi_obj) + # end test_bridge_domain_with_vmi_ref_multiple_bd + # end class TestCrud class TestVncCfgApiServer(test_case.ApiServerTestCase): diff --git a/src/config/api-server/vnc_cfg_types.py b/src/config/api-server/vnc_cfg_types.py index f545d2da4aa..5813f3e3914 100644 --- a/src/config/api-server/vnc_cfg_types.py +++ b/src/config/api-server/vnc_cfg_types.py @@ -776,6 +776,30 @@ def _check_vrouter_link(cls, vmi_data, kvp_dict, obj_dict, db_conn): # end _check_vrouter_link + @classmethod + def _check_bridge_domain_vmi_association(cls, obj_dict, + db_conn, create): + bridge_domain_links = {} + bd_refs = obj_dict.get('bridge_domain_refs') or [] + for bd in bd_refs: + bd_fq_name = bd['to'] + bd_uuid = bd.get('uuid') + if not bd_uuid: + bd_uuid = db_conn.fq_name_to_uuid('bridge_domain', bd_fq_name) + + bdmt = bd['attr'] + vlan_tag = bdmt['vlan_tag'] + if vlan_tag in bridge_domain_links: + msg = "Virtual machine interface(%s) already refers to bridge "\ + "domain(%s) for vlan tag %d"\ + %(obj_dict['uuid'], bd_uuid, vlan_tag) + return (False, msg) + + bridge_domain_links[vlan_tag] = bd_uuid + + return (True, '') + # end _check_bridge_domain_vmi_association + @classmethod def pre_dbe_create(cls, tenant_name, obj_dict, db_conn): vn_dict = obj_dict['virtual_network_refs'][0] @@ -795,6 +819,11 @@ def pre_dbe_create(cls, tenant_name, obj_dict, db_conn): vn_dict = result + (ok, error) = cls._check_bridge_domain_vmi_association(obj_dict, + db_conn, True) + if not ok: + return (False, (400, error)) + inmac = None if 'virtual_machine_interface_mac_addresses' in obj_dict: mc = obj_dict['virtual_machine_interface_mac_addresses'] @@ -882,6 +911,11 @@ def pre_dbe_update(cls, id, fq_name, obj_dict, db_conn, if not ok: return ok, read_result + (ok, error) = cls._check_bridge_domain_vmi_association(obj_dict, + db_conn, False) + if not ok: + return (False, (400, error)) + # check if the vmi is a internal interface of a logical # router if (read_result.get('logical_router_back_refs') and diff --git a/src/schema/vnc_cfg.xsd b/src/schema/vnc_cfg.xsd index c3c15ce1232..ca263b24803 100644 --- a/src/schema/vnc_cfg.xsd +++ b/src/schema/vnc_cfg.xsd @@ -3030,8 +3030,8 @@ targetNamespace="http://www.contrailsystems.com/2012/VNC-CONFIG/0"> + 'virtual-network', 'bridge-domain', ['has'], 'optional', 'CRUD', + 'bridge-domains configured in a virtual network') -->