From 2e5ec528b62d526b30835ac5c104599dcff89814 Mon Sep 17 00:00:00 2001 From: Thomas Bachman Date: Tue, 27 Sep 2022 16:32:10 +0000 Subject: [PATCH] Use top-level contract references Contract references in aci-integration-module (AIM) were previously created or destroyed by modifying list members of the ExternalNetwork resource. This caused problems when the ExternalNetwork was monitored state but the contract references were meant to be configured state, as the view of the monitored universe/state could be inconsistent from time to time, causing the contract references to inadvertently get deleted. A recent commit (9076bd8738e27052e75ec53052e509c54c4b91ea) in AIM made the contract references top-level resources, so that their creation or removal can only be made directly. The aim_lib module was changed to support passing lists of provided and consumed contracts expclicitly, in order to adopt these changes. Change-Id: I14b01bea751823c3e3b70df3e7f41ea5babd9522 --- .../drivers/apic_aim/data_migrations.py | 26 +- .../drivers/apic_aim/mechanism_driver.py | 178 ++++++++++---- .../neutron/services/sfc/aim/sfc_driver.py | 56 ++++- .../unit/plugins/ml2plus/test_apic_aim.py | 231 +++++++++++++----- .../unit/services/sfc/test_aim_sfc_driver.py | 31 ++- 5 files changed, 394 insertions(+), 128 deletions(-) diff --git a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/data_migrations.py b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/data_migrations.py index 28145bb96..0a5a0682b 100644 --- a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/data_migrations.py +++ b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/data_migrations.py @@ -330,11 +330,27 @@ def do_ap_name_change(session, conf=None): eid = (ext_net.tenant_name, ext_net.l3out_name, ext_net.name) for vrf in vrfs: if eid in clone_ext_nets: - ext_net.provided_contract_names = clone_ext_nets[ - eid].provided_contract_names - ext_net.consumed_contract_names = clone_ext_nets[ - eid].consumed_contract_names - ns.connect_vrf(aim_ctx, ext_net, vrf) + p_cons = clone_ext_nets[eid].provided_contract_names + c_cons = clone_ext_nets[eid].provided_contract_names + prov = [] + cons = [] + ext_net = clone_ext_nets[eid] + for p_con in p_cons: + prov.append( + aim_resource.ExternalNetworkProvidedContract( + tenant_name=ext_net.tenant_name, + l3out_name=ext_net.l3out_name, + ext_net_name=ext_net.ext_net_name, + name=p_con.name)) + for c_con in c_cons: + cons.append( + aim_resource.ExternalNetworkConsumedContract( + tenant_name=ext_net.tenant_name, + l3out_name=ext_net.l3out_name, + ext_net_name=ext_net.ext_net_name, + name=p_con.name)) + ns.connect_vrf(aim_ctx, ext_net, vrf, + provided_contracts=prov, consumed_contracts=cons) def do_apic_aim_security_group_migration(session): diff --git a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py index 5d6fbf7c3..2c6477661 100644 --- a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py +++ b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py @@ -743,6 +743,26 @@ class ApicMechanismDriver(api_plus.MechanismDriver, res = aim_resource.QosDppPol(**i) self.aim.create(aim_ctx, res, overwrite=True) + def _create_external_epg_contract(self, aim_ctx, klass, + ext_net, contract): + new_contract = klass( + tenant_name=ext_net.tenant_name, + l3out_name=ext_net.l3out_name, + ext_net_name=ext_net.name, + name=contract.name) + self.aim.create(aim_ctx, new_contract, overwrite=True) + + def _delete_external_epg_contract(self, aim_ctx, klass, + ext_net, contract): + old_contract = klass( + tenant_name=ext_net.tenant_name, + l3out_name=ext_net.l3out_name, + ext_net_name=ext_net.name, + name=contract.name) + old_contract_db = self.aim.get(aim_ctx, old_contract) + if old_contract_db: + self.aim.delete(aim_ctx, old_contract_db) + def create_qos_policy_precommit(self, context, policy): """Create a QoS policy. :param context: neutron api request context @@ -2347,16 +2367,24 @@ class ApicMechanismDriver(api_plus.MechanismDriver, # this could be internal or external EPG epg = self.aim.get(aim_ctx, epg) if epg: - contracts = epg.consumed_contract_names - if contract.name not in contracts: - contracts.append(contract.name) - epg = self.aim.update(aim_ctx, epg, - consumed_contract_names=contracts) - contracts = epg.provided_contract_names - if contract.name not in contracts: - contracts.append(contract.name) - epg = self.aim.update(aim_ctx, epg, - provided_contract_names=contracts) + if isinstance(epg, aim_resource.ExternalNetwork): + self._create_external_epg_contract(aim_ctx, + aim_resource.ExternalNetworkProvidedContract, + epg, contract) + self._create_external_epg_contract(aim_ctx, + aim_resource.ExternalNetworkConsumedContract, + epg, contract) + else: + contracts = epg.consumed_contract_names + if contract.name not in contracts: + contracts.append(contract.name) + epg = self.aim.update(aim_ctx, epg, + consumed_contract_names=contracts) + contracts = epg.provided_contract_names + if contract.name not in contracts: + contracts.append(contract.name) + epg = self.aim.update(aim_ctx, epg, + provided_contract_names=contracts) # If external-gateway is set, handle external-connectivity changes. # External network is not supported for SVI network for now. @@ -2458,16 +2486,23 @@ class ApicMechanismDriver(api_plus.MechanismDriver, # Contract. if router_id not in router_ids and epg: epg = self.aim.get(aim_ctx, epg) + if isinstance(epg, aim_resource.ExternalNetwork): + self._delete_external_epg_contract(aim_ctx, + aim_resource.ExternalNetworkProvidedContract, + epg, contract) + self._delete_external_epg_contract(aim_ctx, + aim_resource.ExternalNetworkConsumedContract, + epg, contract) + else: + contracts = [name for name in epg.consumed_contract_names + if name != contract.name] + epg = self.aim.update(aim_ctx, epg, + consumed_contract_names=contracts) - contracts = [name for name in epg.consumed_contract_names - if name != contract.name] - epg = self.aim.update(aim_ctx, epg, - consumed_contract_names=contracts) - - contracts = [name for name in epg.provided_contract_names - if name != contract.name] - epg = self.aim.update(aim_ctx, epg, - provided_contract_names=contracts) + contracts = [name for name in epg.provided_contract_names + if name != contract.name] + epg = self.aim.update(aim_ctx, epg, + provided_contract_names=contracts) nets_to_notify = set() ports_to_notify = set() @@ -4812,17 +4847,37 @@ class ApicMechanismDriver(api_plus.MechanismDriver, vrf = aim_resource.VRF(tenant_name=vrf.tenant_name, name=vrf.name) rtr_dbs = self._get_routers_for_vrf(session, vrf) - prov = set() - cons = set() + prov_dict = {} + cons_dict = {} - def update_contracts(router): + def update_contracts(router, ext_net): contract = self._map_router(session, router, True) - prov.add(contract.name) - cons.add(contract.name) + prov_dict[ + contract.name] = aim_resource.ExternalNetworkProvidedContract( + tenant_name=ext_net.tenant_name, + l3out_name=ext_net.l3out_name, + ext_net_name=ext_net.name, + name=contract.name) + cons_dict[ + contract.name] = aim_resource.ExternalNetworkConsumedContract( + tenant_name=ext_net.tenant_name, + l3out_name=ext_net.l3out_name, + ext_net_name=ext_net.name, + name=contract.name) r_info = self.get_router_extn_db(session, router['id']) - prov.update(r_info[a_l3.EXTERNAL_PROVIDED_CONTRACTS]) - cons.update(r_info[a_l3.EXTERNAL_CONSUMED_CONTRACTS]) + for p_con in r_info[a_l3.EXTERNAL_PROVIDED_CONTRACTS]: + prov_dict[ + p_con] = aim_resource.ExternalNetworkProvidedContract( + tenant_name=ext_net.tenant_name, + l3out_name=ext_net.l3out_name, + ext_net_name=ext_net.name, name=p_con) + for c_con in r_info[a_l3.EXTERNAL_CONSUMED_CONTRACTS]: + cons_dict[ + c_con] = aim_resource.ExternalNetworkConsumedContract( + tenant_name=ext_net.tenant_name, + l3out_name=ext_net.l3out_name, + ext_net_name=ext_net.name, name=c_con) if old_network: _, ext_net, ns = self._get_aim_nat_strategy(old_network) @@ -4833,15 +4888,19 @@ class ApicMechanismDriver(api_plus.MechanismDriver, rtr_old = [r for r in rtr_dbs if (r.gw_port_id and r.gw_port.network_id in eqv_nets)] - prov = set() - cons = set() + prov_dict = {} + cons_dict = {} for r in rtr_old: - update_contracts(r) + update_contracts(r, ext_net) if rtr_old: - ext_net.provided_contract_names = sorted(prov) - ext_net.consumed_contract_names = sorted(cons) - ns.connect_vrf(aim_ctx, ext_net, vrf) + prov_list = list(prov_dict.values()) + cons_list = list(cons_dict.values()) + provided = sorted(prov_list, key=lambda x: x.name) + consumed = sorted(cons_list, key=lambda x: x.name) + ns.connect_vrf(aim_ctx, ext_net, vrf, + provided_contracts=provided, + consumed_contracts=consumed) else: ns.disconnect_vrf(aim_ctx, ext_net, vrf) if new_network: @@ -4853,14 +4912,18 @@ class ApicMechanismDriver(api_plus.MechanismDriver, rtr_new = [r for r in rtr_dbs if (r.gw_port_id and r.gw_port.network_id in eqv_nets)] - prov = set() - cons = set() + prov_dict = {} + cons_dict = {} for r in rtr_new: - update_contracts(r) - update_contracts(router) - ext_net.provided_contract_names = sorted(prov) - ext_net.consumed_contract_names = sorted(cons) - ns.connect_vrf(aim_ctx, ext_net, vrf) + update_contracts(r, ext_net) + update_contracts(router, ext_net) + prov_list = list(prov_dict.values()) + cons_list = list(cons_dict.values()) + provided = sorted(prov_list, key=lambda x: x.name) + consumed = sorted(cons_list, key=lambda x: x.name) + ns.connect_vrf(aim_ctx, ext_net, vrf, + provided_contracts=provided, + consumed_contracts=consumed) def _is_port_bound(self, port): return port.get(portbindings.VIF_TYPE) not in [ @@ -6507,6 +6570,10 @@ class ApicMechanismDriver(api_plus.MechanismDriver, mgr.register_aim_resource_class(aim_resource.ContractSubject) mgr.register_aim_resource_class(aim_resource.EndpointGroup) mgr.register_aim_resource_class(aim_resource.ExternalNetwork) + mgr.register_aim_resource_class( + aim_resource.ExternalNetworkProvidedContract) + mgr.register_aim_resource_class( + aim_resource.ExternalNetworkConsumedContract) mgr.register_aim_resource_class(aim_resource.ExternalSubnet) mgr.register_aim_resource_class(aim_resource.Filter) mgr.register_aim_resource_class(aim_resource.FilterEntry) @@ -7209,12 +7276,30 @@ class ApicMechanismDriver(api_plus.MechanismDriver, for router_id in routers: contract = self._map_router( mgr.expected_session, router_dbs[router_id], True) - prov.add(contract.name) - cons.add(contract.name) - prov.update(router_ext_prov[router_id]) - cons.update(router_ext_cons[router_id]) - ext_net.provided_contract_names = sorted(prov) - ext_net.consumed_contract_names = sorted(cons) + prov.add(aim_resource.ExternalNetworkProvidedContract( + tenant_name=ext_net.tenant_name, + l3out_name=ext_net.l3out_name, + ext_net_name=ext_net.name, + name=contract.name)) + cons.add(aim_resource.ExternalNetworkProvidedContract( + tenant_name=ext_net.tenant_name, + l3out_name=ext_net.l3out_name, + ext_net_name=ext_net.name, + name=contract.name)) + for p_con in router_ext_prov[router_id]: + prov.add(aim_resource.ExternalNetworkProvidedContract( + tenant_name=ext_net.tenant_name, + l3out_name=ext_net.l3out_name, + ext_net_name=ext_net.name, + name=p_con)) + for c_con in router_ext_cons[router_id]: + cons.add(aim_resource.ExternalNetworkConsumedContract( + tenant_name=ext_net.tenant_name, + l3out_name=ext_net.l3out_name, + ext_net_name=ext_net.name, + name=c_con)) + provided = sorted(prov, key=lambda x: x.name) + consumed = sorted(cons, key=lambda x: x.name) int_vrf = int_vrfs[key] # Keep only the identity attributes of the VRF so that @@ -7224,7 +7309,8 @@ class ApicMechanismDriver(api_plus.MechanismDriver, # of the L3Outside's display_name. int_vrf = aim_resource.VRF( tenant_name=int_vrf.tenant_name, name=int_vrf.name) - ns.connect_vrf(mgr.expected_aim_ctx, ext_net, int_vrf) + ns.connect_vrf(mgr.expected_aim_ctx, ext_net, int_vrf, + provided_contracts=provided, consumed_contracts=consumed) return bd, epg, vrf diff --git a/gbpservice/neutron/services/sfc/aim/sfc_driver.py b/gbpservice/neutron/services/sfc/aim/sfc_driver.py index b9718126b..9c578dba9 100644 --- a/gbpservice/neutron/services/sfc/aim/sfc_driver.py +++ b/gbpservice/neutron/services/sfc/aim/sfc_driver.py @@ -584,10 +584,20 @@ class SfcAIMDriver(SfcAIMDriverBase): contract = self._get_flc_contract(prov_group, sg) # TODO(ivar): if provider/consumer are in different tenants, export # the contract - if contract.name not in cons_group.consumed_contract_names: - cons_group.consumed_contract_names.append(contract.name) - if contract.name not in prov_group.provided_contract_names: - prov_group.provided_contract_names.append(contract.name) + if isinstance(cons_group, aim_resource.ExternalNetwork): + self.aim_mech._create_external_epg_contract(aim_ctx, + aim_resource.ExternalNetworkConsumedContract, + cons_group, contract) + else: + if contract.name not in cons_group.consumed_contract_names: + cons_group.consumed_contract_names.append(contract.name) + if isinstance(prov_group, aim_resource.ExternalNetwork): + self.aim_mech._create_external_epg_contract(aim_ctx, + aim_resource.ExternalNetworkProvidedContract, + prov_group, contract) + else: + if contract.name not in prov_group.provided_contract_names: + prov_group.provided_contract_names.append(contract.name) self.aim.create(aim_ctx, cons_group, overwrite=True) self.aim.create(aim_ctx, prov_group, overwrite=True) @@ -651,18 +661,44 @@ class SfcAIMDriver(SfcAIMDriverBase): contract = self._get_flc_contract(p_group, sg) try: if prefix == FLOWC_SRC: - epg.consumed_contract_names.remove(contract.name) + if isinstance(epg, aim_resource.ExternalNetwork): + self.aim_mech._delete_external_epg_contract(aim_ctx, + aim_resource.ExternalNetworkConsumedContract, + epg, contract) + else: + epg.consumed_contract_names.remove(contract.name) else: - epg.provided_contract_names.remove(contract.name) + if isinstance(epg, aim_resource.ExternalNetwork): + self.aim_mech._delete_external_epg_contract(aim_ctx, + aim_resource.ExternalNetworkProvidedContract, + epg, contract) + else: + epg.provided_contract_names.remove(contract.name) except ValueError: LOG.warning("Contract %(name)s not present in EPG %(epg)s", {'name': contract.name, 'epg': epg}) else: epg = self.aim.create(aim_ctx, epg, overwrite=True) - if (ext_net and not epg.consumed_contract_names and not - epg.provided_contract_names): - # Only remove external network if completely empty - self.aim.delete(aim_ctx, epg, cascade=True) + if ext_net: + if isinstance(epg, aim_resource.ExternalNetwork): + contract_params = { + 'tenant_name': ext_net.tenant_name, + 'l3out_name': ext_net.l3out_name, + 'ext_net_name': ext_net.name} + p_cons = self.aim.find(aim_ctx, + aim_resource.ExternalNetworkProvidedContract, + **contract_params) + c_cons = self.aim.find(aim_ctx, + aim_resource.ExternalNetworkProvidedContract, + **contract_params) + if not p_cons and not c_cons: + # Only remove if completely empty + self.aim.delete(aim_ctx, epg, cascade=True) + else: + if (not epg.consumed_contract_names and + not epg.provided_contract_names): + # Only remove if completely empty + self.aim.delete(aim_ctx, epg, cascade=True) def _get_chains_by_classifier_id(self, plugin_context, flowc_id): context = plugin_context diff --git a/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py b/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py index f16659665..bb2c4d6f8 100644 --- a/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py +++ b/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py @@ -8179,33 +8179,54 @@ class TestExternalConnectivityBase(object): # Connect the router interfaces to the subnets vrf_objs = {} + rtr_contracts = {} for tenant, router_list in six.iteritems(objs): tenant_aname = self.name_mapper.project(None, tenant) a_vrf = aim_resource.VRF(tenant_name=tenant_aname, name='DefaultVRF') a_ext_net = aim_resource.ExternalNetwork( tenant_name=self.t1_aname, l3out_name='l1', name='n1') + prov = [] + cons = [] for router, subnets, addr_scope in router_list: if addr_scope: a_vrf.name = self.name_mapper.address_scope( None, addr_scope['id']) self.fix_l3out_vrf(self.t1_aname, 'l1', a_vrf.name) contract = self.name_mapper.router(None, router['id']) - a_ext_net.provided_contract_names.append(contract) - a_ext_net.provided_contract_names.extend( - router[PROV]) - a_ext_net.provided_contract_names.sort() - a_ext_net.consumed_contract_names.append(contract) - a_ext_net.consumed_contract_names.extend( - router[CONS]) - a_ext_net.consumed_contract_names.sort() + prov.append(aim_resource.ExternalNetworkProvidedContract( + tenant_name=a_ext_net.tenant_name, + l3out_name=a_ext_net.l3out_name, + ext_net_name=a_ext_net.name, + name=contract)) + cons.append(aim_resource.ExternalNetworkConsumedContract( + tenant_name=a_ext_net.tenant_name, + l3out_name=a_ext_net.l3out_name, + ext_net_name=a_ext_net.name, + name=contract)) + for con in router[PROV]: + prov.append(aim_resource.ExternalNetworkProvidedContract( + tenant_name=a_ext_net.tenant_name, + l3out_name=a_ext_net.l3out_name, + ext_net_name=a_ext_net.name, + name=con)) + for con in router[CONS]: + cons.append(aim_resource.ExternalNetworkConsumedContract( + tenant_name=a_ext_net.tenant_name, + l3out_name=a_ext_net.l3out_name, + ext_net_name=a_ext_net.name, + name=con)) for idx in range(0, len(subnets)): self.mock_ns.reset_mock() self._router_interface_action('add', router['id'], subnets[idx]['id'], None) if idx == 0: - cv.assert_called_once_with(mock.ANY, a_ext_net, a_vrf) + provided = sorted(prov, key=lambda x: x.name) + consumed = sorted(cons, key=lambda x: x.name) + cv.assert_called_once_with(mock.ANY, a_ext_net, a_vrf, + provided_contracts=provided, + consumed_contracts=consumed) else: cv.assert_not_called() @@ -8216,6 +8237,7 @@ class TestExternalConnectivityBase(object): self._check_bd_l3out(aim_ctx, aim_bd, 'l1') self._validate() + rtr_contracts[router['id']] = (prov, cons) vrf_objs[tenant] = a_ext_net # Remove the router interfaces @@ -8226,17 +8248,30 @@ class TestExternalConnectivityBase(object): a_ext_net = vrf_objs.pop(tenant) num_router = len(router_list) for router, subnets, addr_scope in router_list: + prov, cons = rtr_contracts[router['id']] if addr_scope: a_vrf.name = self.name_mapper.address_scope( None, addr_scope['id']) self.fix_l3out_vrf(self.t1_aname, 'l1', a_vrf.name) contract = self.name_mapper.router(None, router['id']) - a_ext_net.provided_contract_names.remove(contract) - a_ext_net.consumed_contract_names.remove(contract) + prov.remove(aim_resource.ExternalNetworkProvidedContract( + tenant_name=a_ext_net.tenant_name, + l3out_name=a_ext_net.l3out_name, + ext_net_name=a_ext_net.name, name=contract)) + cons.remove(aim_resource.ExternalNetworkConsumedContract( + tenant_name=a_ext_net.tenant_name, + l3out_name=a_ext_net.l3out_name, + ext_net_name=a_ext_net.name, name=contract)) for c in router[PROV]: - a_ext_net.provided_contract_names.remove(c) + prov.remove(aim_resource.ExternalNetworkProvidedContract( + tenant_name=a_ext_net.tenant_name, + l3out_name=a_ext_net.l3out_name, + ext_net_name=a_ext_net.name, name=c)) for c in router[CONS]: - a_ext_net.consumed_contract_names.remove(c) + cons.remove(aim_resource.ExternalNetworkConsumedContract( + tenant_name=a_ext_net.tenant_name, + l3out_name=a_ext_net.l3out_name, + ext_net_name=a_ext_net.name, name=c)) for idx in range(0, len(subnets)): self.mock_ns.reset_mock() @@ -8250,8 +8285,12 @@ class TestExternalConnectivityBase(object): if idx == len(subnets) - 1: num_router -= 1 if num_router: - cv.assert_called_once_with(mock.ANY, a_ext_net, - a_vrf) + provided = sorted(prov, key=lambda x: x.name) + consumed = sorted(cons, key=lambda x: x.name) + cv.assert_called_once_with( + mock.ANY, a_ext_net, a_vrf, + provided_contracts=provided, + consumed_contracts=consumed) else: dv.assert_called_once_with(mock.ANY, a_ext_net, a_vrf) @@ -8333,10 +8372,27 @@ class TestExternalConnectivityBase(object): ext_net1['id']}}}) contract = self.name_mapper.router(None, router['id']) a_ext_net1 = aim_resource.ExternalNetwork( - tenant_name=self.t1_aname, l3out_name='l1', name='n1', - provided_contract_names=sorted(['pr-1', contract]), - consumed_contract_names=sorted(['co-1', contract])) - cv.assert_called_once_with(mock.ANY, a_ext_net1, a_vrf) + tenant_name=self.t1_aname, l3out_name='l1', name='n1') + p1_ext1 = aim_resource.ExternalNetworkProvidedContract( + tenant_name=a_ext_net1.tenant_name, + l3out_name=a_ext_net1.l3out_name, + ext_net_name=a_ext_net1.name, name='pr-1') + prc_ext1 = aim_resource.ExternalNetworkProvidedContract( + tenant_name=a_ext_net1.tenant_name, + l3out_name=a_ext_net1.l3out_name, + ext_net_name=a_ext_net1.name, name=contract) + c1_ext1 = aim_resource.ExternalNetworkConsumedContract( + tenant_name=a_ext_net1.tenant_name, + l3out_name=a_ext_net1.l3out_name, + ext_net_name=a_ext_net1.name, name='co-1') + crc_ext1 = aim_resource.ExternalNetworkConsumedContract( + tenant_name=a_ext_net1.tenant_name, + l3out_name=a_ext_net1.l3out_name, + ext_net_name=a_ext_net1.name, name=contract) + provided = sorted([prc_ext1, p1_ext1], key=lambda x: x.name) + consumed = sorted([crc_ext1, c1_ext1], key=lambda x: x.name) + cv.assert_called_once_with(mock.ANY, a_ext_net1, a_vrf, + provided_contracts=provided, consumed_contracts=consumed) self.mock_ns.reset_mock() self._update('routers', router['id'], @@ -8344,39 +8400,58 @@ class TestExternalConnectivityBase(object): {'external_gateway_info': {'network_id': ext_net2['id']}}}) a_ext_net2 = aim_resource.ExternalNetwork( - tenant_name=self.t1_aname, l3out_name='l2', name='n2', - provided_contract_names=sorted(['pr-1', contract]), - consumed_contract_names=sorted(['co-1', contract])) + tenant_name=self.t1_aname, l3out_name='l2', name='n2') + p1_ext2 = aim_resource.ExternalNetworkProvidedContract( + tenant_name=a_ext_net2.tenant_name, + l3out_name=a_ext_net2.l3out_name, + ext_net_name=a_ext_net2.name, name='pr-1') + prc_ext2 = aim_resource.ExternalNetworkProvidedContract( + tenant_name=a_ext_net2.tenant_name, + l3out_name=a_ext_net2.l3out_name, + ext_net_name=a_ext_net2.name, name=contract) + c1_ext2 = aim_resource.ExternalNetworkConsumedContract( + tenant_name=a_ext_net2.tenant_name, + l3out_name=a_ext_net2.l3out_name, + ext_net_name=a_ext_net2.name, name='co-1') + c2_ext2 = aim_resource.ExternalNetworkConsumedContract( + tenant_name=a_ext_net2.tenant_name, + l3out_name=a_ext_net2.l3out_name, + ext_net_name=a_ext_net2.name, name='co-2') + crc_ext2 = aim_resource.ExternalNetworkConsumedContract( + tenant_name=a_ext_net2.tenant_name, + l3out_name=a_ext_net2.l3out_name, + ext_net_name=a_ext_net2.name, name=contract) a_ext_net1.provided_contract_names = [] a_ext_net1.consumed_contract_names = [] dv.assert_called_once_with(mock.ANY, a_ext_net1, a_vrf) - cv.assert_called_once_with(mock.ANY, a_ext_net2, a_vrf) + provided = sorted([prc_ext2, p1_ext2], key=lambda x: x.name) + consumed = sorted([crc_ext2, c1_ext2], key=lambda x: x.name) + cv.assert_called_once_with(mock.ANY, a_ext_net2, a_vrf, + provided_contracts=provided, consumed_contracts=consumed) self.mock_ns.reset_mock() self._update('routers', router['id'], {'router': {PROV: []}}) a_ext_net2 = aim_resource.ExternalNetwork( - tenant_name=self.t1_aname, l3out_name='l2', name='n2', - provided_contract_names=sorted([contract]), - consumed_contract_names=sorted(['co-1', contract])) - cv.assert_called_once_with(mock.ANY, a_ext_net2, a_vrf) + tenant_name=self.t1_aname, l3out_name='l2', name='n2') + consumed = sorted([crc_ext2, c1_ext2], key=lambda x: x.name) + cv.assert_called_once_with(mock.ANY, a_ext_net2, a_vrf, + provided_contracts=[prc_ext2], consumed_contracts=consumed) self.mock_ns.reset_mock() self._update('routers', router['id'], {'router': {CONS: ['co-1', 'co-2']}}) a_ext_net2 = aim_resource.ExternalNetwork( - tenant_name=self.t1_aname, l3out_name='l2', name='n2', - provided_contract_names=sorted([contract]), - consumed_contract_names=sorted(['co-1', 'co-2', contract])) - cv.assert_called_once_with(mock.ANY, a_ext_net2, a_vrf) + tenant_name=self.t1_aname, l3out_name='l2', name='n2') + consumed = sorted([crc_ext2, c1_ext2, c2_ext2], key=lambda x: x.name) + cv.assert_called_once_with(mock.ANY, a_ext_net2, a_vrf, + provided_contracts=[prc_ext2], consumed_contracts=consumed) self.mock_ns.reset_mock() self._update('routers', router['id'], {'router': {'external_gateway_info': {}}}) - a_ext_net2.provided_contract_names = [] - a_ext_net2.consumed_contract_names = [] dv.assert_called_once_with(mock.ANY, a_ext_net2, a_vrf) def test_router_gateway(self): @@ -8482,40 +8557,62 @@ class TestExternalConnectivityBase(object): self._validate() self._add_external_gateway_to_router(routers[0], ext_nets[0]) - a_ext_nets[0].provided_contract_names = [contracts[0]] - a_ext_nets[0].consumed_contract_names = [contracts[0]] - cv.assert_called_once_with(mock.ANY, a_ext_nets[0], a_vrf) + prov_ext1 = [] + cons_ext1 = [] + for con in contracts: + prov_ext1.append(aim_resource.ExternalNetworkProvidedContract( + tenant_name=a_ext_nets[0].tenant_name, + l3out_name=a_ext_nets[0].l3out_name, + ext_net_name=a_ext_nets[0].name, name=con)) + cons_ext1.append(aim_resource.ExternalNetworkConsumedContract( + tenant_name=a_ext_nets[0].tenant_name, + l3out_name=a_ext_nets[0].l3out_name, + ext_net_name=a_ext_nets[0].name, name=con)) + cv.assert_called_once_with(mock.ANY, a_ext_nets[0], a_vrf, + provided_contracts=[prov_ext1[0]], + consumed_contracts=[cons_ext1[0]]) self._validate() self.mock_ns.reset_mock() + prov_ext2 = [] + cons_ext2 = [] + for con in contracts: + prov_ext2.append(aim_resource.ExternalNetworkProvidedContract( + tenant_name=a_ext_nets[1].tenant_name, + l3out_name=a_ext_nets[1].l3out_name, + ext_net_name=a_ext_nets[1].name, name=con)) + cons_ext2.append(aim_resource.ExternalNetworkConsumedContract( + tenant_name=a_ext_nets[1].tenant_name, + l3out_name=a_ext_nets[1].l3out_name, + ext_net_name=a_ext_nets[1].name, name=con)) self._add_external_gateway_to_router(routers[1], ext_nets[1]) if shared_l3out: - a_ext_nets[1].provided_contract_names = sorted(contracts) - a_ext_nets[1].consumed_contract_names = sorted(contracts) + provided = sorted(prov_ext2, key=lambda x: x.name) + consumed = sorted(cons_ext2, key=lambda x: x.name) + p_con = provided + c_con = consumed else: - a_ext_nets[1].provided_contract_names = [contracts[1]] - a_ext_nets[1].consumed_contract_names = [contracts[1]] - cv.assert_called_once_with(mock.ANY, a_ext_nets[1], a_vrf) + p_con = [prov_ext2[1]] + c_con = [cons_ext2[1]] + cv.assert_called_once_with(mock.ANY, a_ext_nets[1], a_vrf, + provided_contracts=p_con, + consumed_contracts=c_con) self._validate() self.mock_ns.reset_mock() self._router_interface_action('remove', routers[0], sub1['id'], None) if shared_l3out: - a_ext_nets[0].provided_contract_names = [contracts[1]] - a_ext_nets[0].consumed_contract_names = [contracts[1]] - cv.assert_called_once_with(mock.ANY, a_ext_nets[0], a_vrf) + cv.assert_called_once_with(mock.ANY, a_ext_nets[0], a_vrf, + provided_contracts=[prov_ext2[1]], + consumed_contracts=[cons_ext2[1]]) dv.assert_not_called() else: - a_ext_nets[0].provided_contract_names = [] - a_ext_nets[0].consumed_contract_names = [] dv.assert_called_once_with(mock.ANY, a_ext_nets[0], a_vrf) cv.assert_not_called() self._validate() self.mock_ns.reset_mock() self._router_interface_action('remove', routers[1], sub1['id'], None) - a_ext_nets[1].provided_contract_names = [] - a_ext_nets[1].consumed_contract_names = [] dv.assert_called_once_with(mock.ANY, a_ext_nets[1], a_vrf) self._validate() @@ -8786,9 +8883,15 @@ class TestExternalConnectivityBase(object): contract = self.name_mapper.router(None, router['id']) a_ext_net1 = aim_resource.ExternalNetwork( - tenant_name=self.t1_aname, l3out_name='l1', name='n1', - provided_contract_names=[contract], - consumed_contract_names=[contract]) + tenant_name=self.t1_aname, l3out_name='l1', name='n1') + prov = aim_resource.ExternalNetworkProvidedContract( + tenant_name=a_ext_net1.tenant_name, + l3out_name=a_ext_net1.l3out_name, + ext_net_name=a_ext_net1.name, name=contract) + cons = aim_resource.ExternalNetworkConsumedContract( + tenant_name=a_ext_net1.tenant_name, + l3out_name=a_ext_net1.l3out_name, + ext_net_name=a_ext_net1.name, name=contract) a_ext_net1_no_contracts = aim_resource.ExternalNetwork( tenant_name=self.t1_aname, l3out_name='l1', name='n1') @@ -8802,7 +8905,8 @@ class TestExternalConnectivityBase(object): a_vrf1 = aim_resource.VRF( tenant_name=self.name_mapper.project(None, 'tenant_1'), name='DefaultVRF') - cv.assert_called_once_with(mock.ANY, a_ext_net1, a_vrf1) + cv.assert_called_once_with(mock.ANY, a_ext_net1, a_vrf1, + provided_contracts=[prov], consumed_contracts=[cons]) dv.assert_not_called() # 2. Create shared network net2 in tenant tenant_2, then connect @@ -8816,7 +8920,8 @@ class TestExternalConnectivityBase(object): a_vrf2 = aim_resource.VRF( tenant_name=self.name_mapper.project(None, 'tenant_2'), name='DefaultVRF') - cv.assert_called_once_with(mock.ANY, a_ext_net1, a_vrf2) + cv.assert_called_once_with(mock.ANY, a_ext_net1, a_vrf2, + provided_contracts=[prov], consumed_contracts=[cons]) dv.assert_called_once_with(mock.ANY, a_ext_net1_no_contracts, a_vrf1) # 3. Create unshared network net3 in tenant test-tenant, then connect @@ -8839,7 +8944,8 @@ class TestExternalConnectivityBase(object): # 5. Disconnect net2 from r1 self.mock_ns.reset_mock() self._router_interface_action('remove', router['id'], sub2['id'], None) - cv.assert_called_once_with(mock.ANY, a_ext_net1, a_vrf1) + cv.assert_called_once_with(mock.ANY, a_ext_net1, a_vrf1, + provided_contracts=[prov], consumed_contracts=[cons]) dv.assert_called_once_with(mock.ANY, a_ext_net1_no_contracts, a_vrf2) # 6. Disconnect net1 from r1 @@ -8881,9 +8987,15 @@ class TestExternalConnectivityBase(object): contract = self.name_mapper.router(None, router['id']) a_ext_net1 = aim_resource.ExternalNetwork( - tenant_name=self.t1_aname, l3out_name='l1', name='n1', - provided_contract_names=[contract], - consumed_contract_names=[contract]) + tenant_name=self.t1_aname, l3out_name='l1', name='n1') + prov = aim_resource.ExternalNetworkProvidedContract( + tenant_name=a_ext_net1.tenant_name, + l3out_name=a_ext_net1.l3out_name, + ext_net_name=a_ext_net1.name, name=contract) + cons = aim_resource.ExternalNetworkConsumedContract( + tenant_name=a_ext_net1.tenant_name, + l3out_name=a_ext_net1.l3out_name, + ext_net_name=a_ext_net1.name, name=contract) # create private stuff net = self._make_network(self.fmt, 'net1', True)['network'] @@ -8895,12 +9007,11 @@ class TestExternalConnectivityBase(object): dv.assert_not_called() self._router_interface_action('add', router['id'], subnet['id'], None) - cv.assert_called_once_with(mock.ANY, a_ext_net1, vrf) + cv.assert_called_once_with(mock.ANY, a_ext_net1, vrf, + provided_contracts=[prov], consumed_contracts=[cons]) dv.assert_not_called() self.mock_ns.reset_mock() - a_ext_net1.provided_contract_names = [] - a_ext_net1.consumed_contract_names = [] self._router_interface_action('remove', router['id'], subnet['id'], None) cv.assert_not_called() diff --git a/gbpservice/neutron/tests/unit/services/sfc/test_aim_sfc_driver.py b/gbpservice/neutron/tests/unit/services/sfc/test_aim_sfc_driver.py index 771ae6bb1..5031deb0a 100644 --- a/gbpservice/neutron/tests/unit/services/sfc/test_aim_sfc_driver.py +++ b/gbpservice/neutron/tests/unit/services/sfc/test_aim_sfc_driver.py @@ -479,10 +479,18 @@ class TestAIMServiceFunctionChainingBase(test_aim_base.AIMBaseTestCase): self.assertIsNotNone(ext_sub) self.assertIsNotNone(ext_net) - self.assertTrue( - contract.name in (ext_net.consumed_contract_names if - pref == 'src_' else - ext_net.provided_contract_names), + ext_contract_params = { + 'tenant_name': ext_net.tenant_name, + 'l3out_name': ext_net.l3out_name, + 'ext_net_name': ext_net.name} + if pref == 'src_': + klass = aim_res.ExternalNetworkConsumedContract + else: + klass = aim_res.ExternalNetworkProvidedContract + contracts = self.aim_mgr.find(ctx, klass, + **ext_contract_params) + contract_names = [c.name for c in contracts] + self.assertTrue(contract.name in contract_names, "%s not in ext net %s" % (contract.name, ext_net.__dict__)) else: @@ -1390,10 +1398,19 @@ class TestPortChain(TestAIMServiceFunctionChainingBase): self._aim_context, aim_res.ExternalNetwork, name=fc['destination_ip_prefix'].replace( '/', '_') + '_' + 'net_' + dst_net_id)[0] - self.assertEqual(2, len(ext_net.provided_contract_names)) + ext_params = { + 'tenant_name': ext_net.tenant_name, + 'l3out_name': ext_net.l3out_name, + 'ext_net_name': ext_net.name} + prov = self.aim_mgr.find(self._aim_context, + aim_res.ExternalNetworkProvidedContract, + **ext_params) + self.assertEqual(2, len(prov)) self.delete_port_chain(pc1['id']) - ext_net = self.aim_mgr.get(self._aim_context, ext_net) - self.assertEqual(1, len(ext_net.provided_contract_names)) + prov = self.aim_mgr.find(self._aim_context, + aim_res.ExternalNetworkProvidedContract, + **ext_params) + self.assertEqual(1, len(prov)) self.delete_port_chain(pc2['id']) self.assertIsNone(self.aim_mgr.get(self._aim_context, ext_net))