From 0dd067f557d19100661a1ab14683430b388665e6 Mon Sep 17 00:00:00 2001 From: Manoj Prabhakar Date: Thu, 25 Feb 2021 06:57:53 -0800 Subject: [PATCH] Deny action support for contracts Six new AIM resources are introduced as children of ContractSubject, which replaces filters and graph fields. Action field is added to filter to subject relation resources for the user to configure deny from AIM --- aim/agent/aid/universes/aci/converter.py | 115 +++-- aim/aim_lib/nat_strategy.py | 10 +- aim/aim_manager.py | 6 + aim/aim_store.py | 12 + aim/api/resource.py | 150 ++++++ .../185efc4806b2_contract_subject_filter.py | 159 +++++++ .../data_migration/contract_subject_filter.py | 205 ++++++++ aim/db/models.py | 108 +++++ .../agent/aid_universes/test_aci_universe.py | 9 +- .../aid_universes/test_aim_db_universe.py | 4 +- .../agent/aid_universes/test_converter.py | 447 ++++++++++++------ aim/tests/unit/agent/test_agent.py | 8 +- aim/tests/unit/aim_lib/test_nat_strategy.py | 6 +- aim/tests/unit/test_aim_manager.py | 184 ++++++- aim/tests/unit/test_hashtree_db_listener.py | 33 +- aim/tests/unit/test_structured_hash_tree.py | 24 +- aim/tests/unit/tools/cli/test_manager.py | 42 ++ 17 files changed, 1318 insertions(+), 204 deletions(-) create mode 100644 aim/db/migration/alembic_migrations/versions/185efc4806b2_contract_subject_filter.py create mode 100644 aim/db/migration/data_migration/contract_subject_filter.py diff --git a/aim/agent/aid/universes/aci/converter.py b/aim/agent/aid/universes/aci/converter.py index 8b08540f..b2014728 100644 --- a/aim/agent/aid/universes/aci/converter.py +++ b/aim/agent/aid/universes/aci/converter.py @@ -35,7 +35,6 @@ MODIFIED_STATUS = "modified" CREATED_STATUS = "created" - # TODO(amitbose) Instead of aliasing, replace local references with the # ones from utils default_identity_converter = utils.default_identity_converter @@ -553,6 +552,56 @@ def bgp_extp_converter(object_dict, otype, helper, return result +def rsFilt_converter(aci_mo=None): + def func(object_dict, otype, helper, source_identity_attributes, + destination_identity_attributes, to_aim=True): + result = [] + id_conv = (helper.get('identity_converter') or + default_identity_converter) + if to_aim: + res_dict = {} + aci_type = aci_mo or otype + try: + id = id_conv(object_dict, aci_type, helper, to_aim=True) + except apic_client.DNManager.InvalidNameFormat: + return [] + for index, attr in enumerate(destination_identity_attributes): + res_dict[attr] = id[index] + if object_dict.get('action'): + res_dict['action'] = object_dict['action'] + result.append(default_to_resource(res_dict, helper, to_aim=True)) + else: + aci_type = aci_mo or helper['resource'] + dn = id_conv(object_dict, otype, helper, + aci_mo_type=aci_type, to_aim=False)[0] + action = 'permit' + if object_dict.get('action'): + action = object_dict['action'] + result.append({aci_type: + {'attributes': + {'dn': dn, + 'action': action, + 'tnVzFilterName': object_dict['filter_name']}}}) + return result + return func + + +def vzterm_converter(object_dict, otype, helper, source_identity_attributes, + destination_identity_attributes, to_aim=True): + result = [] + id_conv = (helper.get('identity_converter') or + default_identity_converter) + if to_aim: + pass + else: + aci_type = helper['resource'] + dn = id_conv(object_dict, otype, helper, + aci_mo_type=aci_type, to_aim=False)[0] + result.append({aci_type: + {'attributes': + {'dn': dn}}}) + return result + # Resource map maps APIC objects into AIM ones. the key of this map is the # object APIC type, while the values contain the followings: # - Resource: AIM resource when direct mapping is applicable @@ -581,11 +630,9 @@ def bgp_extp_converter(object_dict, otype, helper, 'tnSpanVSrcGrpName') infraRsSpanVDestGrp_converter = child_list('span_vdest_group_names', 'tnSpanVDestGrpName') -vzRsSubjFiltAtt_converter = child_list('bi_filters', 'tnVzFilterName') -vzInTerm_vzRsFiltAtt_converter = child_list('in_filters', 'tnVzFilterName', - aci_mo='vzRsFiltAtt__In') -vzOutTerm_vzRsFiltAtt_converter = child_list('out_filters', 'tnVzFilterName', - aci_mo='vzRsFiltAtt__Out') +vzRsSubjFiltAtt_converter = rsFilt_converter() +vzRsFiltAtt_in_converter = rsFilt_converter(aci_mo='vzRsFiltAtt__In') +vzRsFiltAtt_out_converter = rsFilt_converter(aci_mo='vzRsFiltAtt__Out') fvRsProv_Ext_converter = child_list('provided_contract_names', 'tnVzBrCPName', aci_mo='fvRsProv__Ext') fvRsCons_Ext_converter = child_list('consumed_contract_names', 'tnVzBrCPName', @@ -629,6 +676,7 @@ def bgp_as_id_converter(object_dict, otype, helper, to_aim=True): aci_mo_type='bgpAsP__Peer', to_aim=to_aim) + resource_map = { 'fvBD': [{ 'resource': resource.BridgeDomain, @@ -788,41 +836,34 @@ def bgp_as_id_converter(object_dict, otype, helper, to_aim=True): 'out_service_graph_name'], }], 'vzRsSubjFiltAtt': [{ - 'resource': resource.ContractSubject, - 'converter': vzRsSubjFiltAtt_converter + 'resource': resource.ContractSubjFilter, + 'converter': vzRsSubjFiltAtt_converter, }], 'vzRsSubjGraphAtt': [{ - 'resource': resource.ContractSubject, - 'exceptions': {'tnVnsAbsGraphName': {'other': 'service_graph_name', - 'skip_if_empty': True}}, + 'resource': resource.ContractSubjGraph, + 'exceptions': {'tnVnsAbsGraphName': {'other': 'graph_name'}}, 'to_resource': default_to_resource_strict, }], - 'vzRsFiltAtt': [{'resource': resource.ContractSubject, - 'converter': vzInTerm_vzRsFiltAtt_converter}, - {'resource': resource.ContractSubject, - 'converter': vzOutTerm_vzRsFiltAtt_converter}], - 'vzInTerm': [{ - 'resource': resource.ContractSubject, - 'to_resource': to_resource_filter_container, - 'skip': ['display_name'] - }], - 'vzOutTerm': [{ - 'resource': resource.ContractSubject, - 'to_resource': to_resource_filter_container, - 'skip': ['display_name'] - }], + 'vzRsFiltAtt': [{'resource': resource.ContractSubjInFilter, + 'converter': vzRsFiltAtt_in_converter}, + {'resource': resource.ContractSubjOutFilter, + 'converter': vzRsFiltAtt_out_converter}], + 'vzInTerm': [{'resource': resource.ContractSubjInFilter, + 'converter': vzterm_converter}, + {'resource': resource.ContractSubjInGraph, + 'converter': vzterm_converter}], + 'vzOutTerm': [{'resource': resource.ContractSubjOutFilter, + 'converter': vzterm_converter}, + {'resource': resource.ContractSubjOutGraph, + 'converter': vzterm_converter}], 'vzRsInTermGraphAtt': [{ - 'resource': resource.ContractSubject, - 'exceptions': {'tnVnsAbsGraphName': - {'other': 'in_service_graph_name', - 'skip_if_empty': True}}, + 'resource': resource.ContractSubjInGraph, + 'exceptions': {'tnVnsAbsGraphName': {'other': 'graph_name'}}, 'to_resource': default_to_resource_strict, }], 'vzRsOutTermGraphAtt': [{ - 'resource': resource.ContractSubject, - 'exceptions': {'tnVnsAbsGraphName': - {'other': 'out_service_graph_name', - 'skip_if_empty': True}}, + 'resource': resource.ContractSubjOutGraph, + 'exceptions': {'tnVnsAbsGraphName': {'other': 'graph_name'}}, 'to_resource': default_to_resource_strict, }], 'l3extOut': [{ @@ -1140,10 +1181,10 @@ def bgp_as_id_converter(object_dict, otype, helper, to_aim=True): # vzRsFiltAtt__In, vzRsFiltAtt__Out # fvRsProv__Ext, fvRsCons__Ext resource_map.update({ - 'vzRsFiltAtt__In': [{'resource': resource.ContractSubject, - 'converter': vzInTerm_vzRsFiltAtt_converter}], - 'vzRsFiltAtt__Out': [{'resource': resource.ContractSubject, - 'converter': vzOutTerm_vzRsFiltAtt_converter}], + 'vzRsFiltAtt__In': [{'resource': resource.ContractSubjInFilter, + 'converter': vzRsFiltAtt_in_converter}], + 'vzRsFiltAtt__Out': [{'resource': resource.ContractSubjOutFilter, + 'converter': vzRsFiltAtt_out_converter}], 'fvRsProv__Ext': [{'resource': resource.ExternalNetwork, 'converter': fvRsProv_Ext_converter, 'convert_pre_existing': True, diff --git a/aim/aim_lib/nat_strategy.py b/aim/aim_lib/nat_strategy.py index f94a1a2b..1a3ae463 100644 --- a/aim/aim_lib/nat_strategy.py +++ b/aim/aim_lib/nat_strategy.py @@ -479,8 +479,12 @@ def _get_nat_objects(self, ctx, l3out): subject = resource.ContractSubject( tenant_name=contract.tenant_name, contract_name=contract.name, - name='Allow', display_name='Allow', - bi_filters=[fltr.name]) + name='Allow', display_name='Allow') + subject_filter = resource.ContractSubjFilter( + tenant_name=contract.tenant_name, + contract_name=contract.name, + contract_subject_name='Allow', + filter_name=fltr.name) bd = self._get_nat_bd(ctx, l3out) bd.vrf_name = l3out.vrf_name ap, epg = self._get_nat_ap_epg(ctx, l3out) @@ -497,7 +501,7 @@ def _get_nat_objects(self, ctx, l3out): epg.consumed_contract_names = [contract.name] epg.vmm_domains = vm_doms epg.physical_domains = phy_doms - return [fltr, entry, contract, subject, bd, ap, epg] + return [fltr, entry, contract, subject, subject_filter, bd, ap, epg] def _select_domains(self, objs, vmm_domains=None, phys_domains=None): for obj in objs: diff --git a/aim/aim_manager.py b/aim/aim_manager.py index 478ff7c5..ce70bb24 100644 --- a/aim/aim_manager.py +++ b/aim/aim_manager.py @@ -64,6 +64,12 @@ class AimManager(object): api_res.FilterEntry, api_res.Contract, api_res.ContractSubject, + api_res.ContractSubjFilter, + api_res.ContractSubjInFilter, + api_res.ContractSubjOutFilter, + api_res.ContractSubjGraph, + api_res.ContractSubjInGraph, + api_res.ContractSubjOutGraph, api_status.AciStatus, api_status.AciFault, api_res.Endpoint, diff --git a/aim/aim_store.py b/aim/aim_store.py index 00e6ec93..282295f7 100644 --- a/aim/aim_store.py +++ b/aim/aim_store.py @@ -209,6 +209,18 @@ class SqlAlchemyStore(AimStore): api_res.FilterEntry: models.FilterEntry, api_res.Contract: models.Contract, api_res.ContractSubject: models.ContractSubject, + api_res.ContractSubjFilter: + models.ContractSubjFilter, + api_res.ContractSubjInFilter: + models.ContractSubjInFilter, + api_res.ContractSubjOutFilter: + models.ContractSubjOutFilter, + api_res.ContractSubjGraph: + models.ContractSubjGraph, + api_res.ContractSubjInGraph: + models.ContractSubjInGraph, + api_res.ContractSubjOutGraph: + models.ContractSubjOutGraph, api_status.AciStatus: status_model.Status, api_status.AciFault: status_model.Fault, api_res.Endpoint: models.Endpoint, diff --git a/aim/api/resource.py b/aim/api/resource.py index 6c3ab058..b2072868 100644 --- a/aim/api/resource.py +++ b/aim/api/resource.py @@ -671,6 +671,156 @@ def __init__(self, **kwargs): 'monitored': False}, **kwargs) +class ContractSubjInFilter(AciResourceBase): + """Resource representing a subject within a contract in ACI. + + Identity attributes: name of ACI tenant, name of contract and + name of subject. + """ + + identity_attributes = t.identity( + ('tenant_name', t.name), + ('contract_name', t.name), + ('contract_subject_name', t.name), + ('filter_name', t.name)) + other_attributes = t.other( + ('display_name', t.name), + ('action', t.enum('permit', 'deny')), + ('monitored', t.bool)) + + _aci_mo_name = 'vzRsFiltAtt__In' + _tree_parent = ContractSubject + + def __init__(self, **kwargs): + super(ContractSubjInFilter, self).__init__({'action': 'permit', + 'monitored': False}, + **kwargs) + + +class ContractSubjOutFilter(AciResourceBase): + """Resource representing a subject within a contract in ACI. + + Identity attributes: name of ACI tenant, name of contract and + name of subject. + """ + + identity_attributes = t.identity( + ('tenant_name', t.name), + ('contract_name', t.name), + ('contract_subject_name', t.name), + ('filter_name', t.name)) + other_attributes = t.other( + ('display_name', t.name), + ('action', t.enum('permit', 'deny')), + ('monitored', t.bool)) + + _aci_mo_name = 'vzRsFiltAtt__Out' + _tree_parent = ContractSubject + + def __init__(self, **kwargs): + super(ContractSubjOutFilter, self).__init__({'action': 'permit', + 'monitored': False}, + **kwargs) + + +class ContractSubjFilter(AciResourceBase): + """Resource representing a subject within a contract in ACI. + + Identity attributes: name of ACI tenant, name of contract and + name of subject. + """ + + identity_attributes = t.identity( + ('tenant_name', t.name), + ('contract_name', t.name), + ('contract_subject_name', t.name), + ('filter_name', t.name)) + other_attributes = t.other( + ('display_name', t.name), + ('action', t.enum('permit', 'deny')), + ('monitored', t.bool)) + + _aci_mo_name = 'vzRsSubjFiltAtt' + _tree_parent = ContractSubject + + def __init__(self, **kwargs): + super(ContractSubjFilter, self).__init__({'action': 'permit', + 'monitored': False}, + **kwargs) + + +class ContractSubjInGraph(AciResourceBase): + """Resource representing a subject within a contract in ACI. + + Identity attributes: name of ACI tenant, name of contract and + name of subject. + """ + + identity_attributes = t.identity( + ('tenant_name', t.name), + ('contract_name', t.name), + ('contract_subject_name', t.name)) + other_attributes = t.other( + ('graph_name', t.name), + ('display_name', t.name), + ('monitored', t.bool)) + + _aci_mo_name = 'vzRsInTermGraphAtt' + _tree_parent = ContractSubject + + def __init__(self, **kwargs): + super(ContractSubjInGraph, self).__init__({'monitored': False}, + **kwargs) + + +class ContractSubjOutGraph(AciResourceBase): + """Resource representing a subject within a contract in ACI. + + Identity attributes: name of ACI tenant, name of contract and + name of subject. + """ + + identity_attributes = t.identity( + ('tenant_name', t.name), + ('contract_name', t.name), + ('contract_subject_name', t.name)) + other_attributes = t.other( + ('graph_name', t.name), + ('display_name', t.name), + ('monitored', t.bool)) + + _aci_mo_name = 'vzRsOutTermGraphAtt' + _tree_parent = ContractSubject + + def __init__(self, **kwargs): + super(ContractSubjOutGraph, self).__init__({'monitored': False}, + **kwargs) + + +class ContractSubjGraph(AciResourceBase): + """Resource representing a subject within a contract in ACI. + + Identity attributes: name of ACI tenant, name of contract and + name of subject. + """ + + identity_attributes = t.identity( + ('tenant_name', t.name), + ('contract_name', t.name), + ('contract_subject_name', t.name)) + other_attributes = t.other( + ('graph_name', t.name), + ('display_name', t.name), + ('monitored', t.bool)) + + _aci_mo_name = 'vzRsSubjGraphAtt' + _tree_parent = ContractSubject + + def __init__(self, **kwargs): + super(ContractSubjGraph, self).__init__({'monitored': False}, + **kwargs) + + class Endpoint(ResourceBase): """Resource representing an endpoint. diff --git a/aim/db/migration/alembic_migrations/versions/185efc4806b2_contract_subject_filter.py b/aim/db/migration/alembic_migrations/versions/185efc4806b2_contract_subject_filter.py new file mode 100644 index 00000000..e07a786d --- /dev/null +++ b/aim/db/migration/alembic_migrations/versions/185efc4806b2_contract_subject_filter.py @@ -0,0 +1,159 @@ +# Copyright (c) 2017 Cisco Systems +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +""" + +Revision ID: 185efc4806b2 +Revises: 2f05b6baf008 +Create Date: 2021-02-23 12:04:35.098964 + +""" + +# revision identifiers, used by Alembic. +revision = '185efc4806b2' +down_revision = '2f05b6baf008' +branch_labels = None +depends_on = None + +from aim.common import utils +from aim.db.migration.data_migration import contract_subject_filter +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.create_table( + 'aim_contract_subject_filter_relation', + sa.Column('aim_id', sa.String(255), default=utils.generate_uuid), + sa.Column('epoch', sa.BigInteger(), nullable=False, + server_default='0'), + sa.Column('filter_name', sa.String(64), nullable=False), + sa.Column('contract_name', sa.String(64), nullable=False), + sa.Column('contract_subject_name', sa.String(64), nullable=False), + sa.Column('tenant_name', sa.String(64), nullable=False), + sa.Column('display_name', sa.String(256), nullable=False, default=''), + sa.Column('monitored', sa.Boolean, nullable=False, default=False), + sa.Column('action', sa.Enum('permit', 'deny'), default='permit'), + sa.PrimaryKeyConstraint('aim_id'), + sa.UniqueConstraint('tenant_name', 'contract_name', + 'contract_subject_name', + 'filter_name', + name='uniq_aim_contract_subject_filter_identity'), + sa.Index('idx_aim_contract_subject_filter_identity', + 'tenant_name', 'contract_name', 'contract_subject_name', + 'filter_name')) + op.create_table( + 'aim_contract_subject_in_filter_relation', + sa.Column('aim_id', sa.String(255), default=utils.generate_uuid), + sa.Column('epoch', sa.BigInteger(), nullable=False, + server_default='0'), + sa.Column('filter_name', sa.String(64), nullable=False), + sa.Column('contract_name', sa.String(64), nullable=False), + sa.Column('contract_subject_name', sa.String(64), nullable=False), + sa.Column('tenant_name', sa.String(64), nullable=False), + sa.Column('display_name', sa.String(256), nullable=False, default=''), + sa.Column('monitored', sa.Boolean, nullable=False, default=False), + sa.Column('action', sa.Enum('permit', 'deny'), default='permit'), + sa.PrimaryKeyConstraint('aim_id'), + sa.UniqueConstraint('tenant_name', 'contract_name', + 'contract_subject_name', + 'filter_name', + name='uniq_aim_contract_subject' + '_in_filter_identity'), + sa.Index('idx_aim_contract_subject_in_filter_identity', + 'tenant_name', 'contract_name', 'contract_subject_name', + 'filter_name')) + op.create_table( + 'aim_contract_subject_out_filter_relation', + sa.Column('aim_id', sa.String(255), default=utils.generate_uuid), + sa.Column('epoch', sa.BigInteger(), nullable=False, + server_default='0'), + sa.Column('filter_name', sa.String(64), nullable=False), + sa.Column('contract_name', sa.String(64), nullable=False), + sa.Column('contract_subject_name', sa.String(64), nullable=False), + sa.Column('tenant_name', sa.String(64), nullable=False), + sa.Column('display_name', sa.String(256), nullable=False, default=''), + sa.Column('monitored', sa.Boolean, nullable=False, default=False), + sa.Column('action', sa.Enum('permit', 'deny'), default='permit'), + sa.PrimaryKeyConstraint('aim_id'), + sa.UniqueConstraint('tenant_name', 'contract_name', + 'contract_subject_name', + 'filter_name', + name='uniq_aim_contract_subject' + '_out_filter_identity'), + sa.Index('idx_aim_contract_subject_out_filter_identity', + 'tenant_name', 'contract_name', 'contract_subject_name', + 'filter_name')) + op.create_table( + 'aim_contract_subject_graph_relation', + sa.Column('aim_id', sa.String(255), default=utils.generate_uuid), + sa.Column('epoch', sa.BigInteger(), nullable=False, + server_default='0'), + sa.Column('graph_name', sa.String(64)), + sa.Column('contract_name', sa.String(64), nullable=False), + sa.Column('contract_subject_name', sa.String(64), nullable=False), + sa.Column('tenant_name', sa.String(64), nullable=False), + sa.Column('display_name', sa.String(256), nullable=False, default=''), + sa.Column('monitored', sa.Boolean, nullable=False, default=False), + sa.PrimaryKeyConstraint('aim_id'), + sa.UniqueConstraint('tenant_name', 'contract_name', + 'contract_subject_name', + name='uniq_aim_contract_subject' + '_graph_identity'), + sa.Index('idx_aim_contract_subject_graph_identity', + 'tenant_name', 'contract_name', 'contract_subject_name')) + op.create_table( + 'aim_contract_subject_in_graph_relation', + sa.Column('aim_id', sa.String(255), default=utils.generate_uuid), + sa.Column('epoch', sa.BigInteger(), nullable=False, + server_default='0'), + sa.Column('graph_name', sa.String(64)), + sa.Column('contract_name', sa.String(64), nullable=False), + sa.Column('contract_subject_name', sa.String(64), nullable=False), + sa.Column('tenant_name', sa.String(64), nullable=False), + sa.Column('display_name', sa.String(256), nullable=False, default=''), + sa.Column('monitored', sa.Boolean, nullable=False, default=False), + sa.PrimaryKeyConstraint('aim_id'), + sa.UniqueConstraint('tenant_name', 'contract_name', + 'contract_subject_name', + name='uniq_aim_contract_subject' + '_in_graph_identity'), + sa.Index('idx_aim_contract_subject_in_graph_identity', + 'tenant_name', 'contract_name', 'contract_subject_name')) + op.create_table( + 'aim_contract_subject_out_graph_relation', + sa.Column('aim_id', sa.String(255), default=utils.generate_uuid), + sa.Column('epoch', sa.BigInteger(), nullable=False, + server_default='0'), + sa.Column('graph_name', sa.String(64)), + sa.Column('contract_name', sa.String(64), nullable=False), + sa.Column('contract_subject_name', sa.String(64), nullable=False), + sa.Column('tenant_name', sa.String(64), nullable=False), + sa.Column('display_name', sa.String(256), nullable=False, default=''), + sa.Column('monitored', sa.Boolean, nullable=False, default=False), + sa.PrimaryKeyConstraint('aim_id'), + sa.UniqueConstraint('tenant_name', 'contract_name', + 'contract_subject_name', + name='uniq_aim_contract_subject' + '_out_graph_identity'), + sa.Index('idx_aim_contract_subject_out_graph_identity', + 'tenant_name', 'contract_name', 'contract_subject_name')) + session = sa.orm.Session(bind=op.get_bind(), autocommit=True) + contract_subject_filter.migrate(session) + + +def downgrade(): + pass diff --git a/aim/db/migration/data_migration/contract_subject_filter.py b/aim/db/migration/data_migration/contract_subject_filter.py new file mode 100644 index 00000000..e5a1e2e0 --- /dev/null +++ b/aim/db/migration/data_migration/contract_subject_filter.py @@ -0,0 +1,205 @@ +# Copyright (c) 2018 Cisco Systems +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from aim.common import utils +import sqlalchemy as sa +from sqlalchemy.ext import declarative +from sqlalchemy import orm + +Base = declarative.declarative_base() + +ContractSubjFilter = sa.Table( + 'aim_contract_subject_filter_relation', sa.MetaData(), + sa.Column('aim_id', sa.String(255), + default=utils.generate_uuid, primary_key=True), + sa.Column('tenant_name', sa.String(64), primary_key=True), + sa.Column('contract_name', sa.String(64), primary_key=True), + sa.Column('contract_subject_name', sa.String(64), primary_key=True), + sa.Column('filter_name', sa.String(64), primary_key=True), + sa.Column('display_name', sa.String(64)), + sa.Column('monitored', sa.Boolean), + sa.Column('action', sa.Enum('permit', 'deny')) +) + + +ContractSubjInFilter = sa.Table( + 'aim_contract_subject_in_filter_relation', sa.MetaData(), + sa.Column('aim_id', sa.String(255), + default=utils.generate_uuid, primary_key=True), + sa.Column('tenant_name', sa.String(64), primary_key=True), + sa.Column('contract_name', sa.String(64), primary_key=True), + sa.Column('contract_subject_name', sa.String(64), primary_key=True), + sa.Column('filter_name', sa.String(64), primary_key=True), + sa.Column('display_name', sa.String(64)), + sa.Column('monitored', sa.Boolean), + sa.Column('action', sa.Enum('permit', 'deny')) +) + + +ContractSubjOutFilter = sa.Table( + 'aim_contract_subject_out_filter_relation', sa.MetaData(), + sa.Column('aim_id', sa.String(255), + default=utils.generate_uuid, primary_key=True), + sa.Column('tenant_name', sa.String(64), primary_key=True), + sa.Column('contract_name', sa.String(64), primary_key=True), + sa.Column('contract_subject_name', sa.String(64), primary_key=True), + sa.Column('filter_name', sa.String(64), primary_key=True), + sa.Column('display_name', sa.String(64)), + sa.Column('monitored', sa.Boolean), + sa.Column('action', sa.Enum('permit', 'deny')) +) + + +ContractSubjGraph = sa.Table( + 'aim_contract_subject_graph_relation', sa.MetaData(), + sa.Column('aim_id', sa.String(255), + default=utils.generate_uuid, primary_key=True), + sa.Column('tenant_name', sa.String(64), primary_key=True), + sa.Column('contract_name', sa.String(64), primary_key=True), + sa.Column('contract_subject_name', sa.String(64), primary_key=True), + sa.Column('graph_name', sa.String(64)), + sa.Column('display_name', sa.String(64)), + sa.Column('monitored', sa.Boolean) +) + + +ContractSubjInGraph = sa.Table( + 'aim_contract_subject_graph_relation', sa.MetaData(), + sa.Column('aim_id', sa.String(255), + default=utils.generate_uuid, primary_key=True), + sa.Column('tenant_name', sa.String(64), primary_key=True), + sa.Column('contract_name', sa.String(64), primary_key=True), + sa.Column('contract_subject_name', sa.String(64), primary_key=True), + sa.Column('graph_name', sa.String(64)), + sa.Column('display_name', sa.String(64)), + sa.Column('monitored', sa.Boolean) +) + + +ContractSubjOutGraph = sa.Table( + 'aim_contract_subject_graph_relation', sa.MetaData(), + sa.Column('aim_id', sa.String(255), + default=utils.generate_uuid, primary_key=True), + sa.Column('tenant_name', sa.String(64), primary_key=True), + sa.Column('contract_name', sa.String(64), primary_key=True), + sa.Column('contract_subject_name', sa.String(64), primary_key=True), + sa.Column('graph_name', sa.String(64)), + sa.Column('display_name', sa.String(64)), + sa.Column('monitored', sa.Boolean)) + + +class ContractSubjectFilter(Base): + __tablename__ = 'aim_contract_subject_filters' + subject_aim_id = sa.Column(sa.Integer, + sa.ForeignKey('aim_contract_subjects.aim_id'), + primary_key=True) + name = sa.Column(sa.String(64), primary_key=True) + direction = sa.Column(sa.Enum('bi', 'in', 'out'), primary_key=True) + + +class ContractSubject(Base): + __tablename__ = 'aim_contract_subjects' + aim_id = sa.Column(sa.Integer, primary_key=True) + tenant_name = sa.Column(sa.String(64), primary_key=True) + contract_name = sa.Column(sa.String(64), primary_key=True) + name = sa.Column(sa.String(64), primary_key=True) + service_graph_name = sa.Column(sa.String(64)) + in_service_graph_name = sa.Column(sa.String(64)) + out_service_graph_name = sa.Column(sa.String(64)) + filters = orm.relationship(ContractSubjectFilter, + backref='contract', + cascade='all, delete-orphan', + lazy='joined') + + +def migrate(session): + with session.begin(subtransactions=True): + migrations_in = [] + migrations_out = [] + migrations_bi = [] + migrations_graph = [] + migrations_in_graph = [] + migrations_out_graph = [] + contract_subjects = session.query(ContractSubject).all() + for subj in contract_subjects: + for flt in subj.filters: + if flt.direction == 'in': + migrations_in.append({'tenant_name': subj.tenant_name, + 'contract_name': subj.contract_name, + 'contract_subject_name': subj.name, + 'filter_name': flt.name, + 'display_name': '', + 'monitored': False, + 'action': 'permit'}) + if flt.direction == 'out': + migrations_out.append({'tenant_name': subj.tenant_name, + 'contract_name': subj.contract_name, + 'contract_subject_name': subj.name, + 'filter_name': flt.name, + 'display_name': '', + 'monitored': False, + 'action': 'permit'}) + if flt.direction == 'bi': + migrations_bi.append({'tenant_name': subj.tenant_name, + 'contract_name': subj.contract_name, + 'contract_subject_name': subj.name, + 'filter_name': flt.name, + 'display_name': '', + 'monitored': False, + 'action': 'permit'}) + if subj.service_graph_name: + migrations_graph.append({'tenant_name': subj.tenant_name, + 'contract_name': subj.contract_name, + 'contract_subject_name': subj.name, + 'graph_name': subj.service_graph_name, + 'display_name': '', + 'monitored': False}) + if subj.in_service_graph_name: + migrations_in_graph.append({'tenant_name': subj.tenant_name, + 'contract_name': + subj.contract_name, + 'contract_subject_name': subj.name, + 'graph_name': + subj.in_service_graph_name, + 'display_name': '', + 'monitored': False}) + if subj.service_graph_name: + migrations_out_graph.append({'tenant_name': subj.tenant_name, + 'contract_name': + subj.contract_name, + 'contract_subject_name': + subj.name, + 'graph_name': + subj.out_service_graph_name, + 'display_name': '', + 'monitored': False}) + if migrations_bi: + session.execute(ContractSubjFilter.insert().values( + migrations_bi)) + if migrations_in: + session.execute(ContractSubjInFilter.insert().values( + migrations_in)) + if migrations_out: + session.execute(ContractSubjOutFilter.insert().values( + migrations_out)) + if migrations_graph: + session.execute(ContractSubjOutFilter.insert().values( + migrations_graph)) + if migrations_in_graph: + session.execute(ContractSubjOutFilter.insert().values( + migrations_in_graph)) + if migrations_out_graph: + session.execute(ContractSubjOutFilter.insert().values( + migrations_out_graph)) diff --git a/aim/db/models.py b/aim/db/models.py index 4d35a871..a3ea2647 100644 --- a/aim/db/models.py +++ b/aim/db/models.py @@ -574,6 +574,114 @@ def to_attr(self, session): return res_attr +class ContractSubjFilter(model_base.Base, model_base.HasAimId, + model_base.HasTenantName, + model_base.HasDisplayName, + model_base.AttributeMixin, + model_base.IsMonitored): + """DB model for filters used by Contract Subject.""" + __tablename__ = 'aim_contract_subject_filter_relation' + __table_args__ = ( + model_base.uniq_column(__tablename__, 'tenant_name', 'contract_name', + 'contract_subject_name', + 'filter_name') + + model_base.to_tuple(model_base.Base.__table_args__)) + + contract_name = model_base.name_column(nullable=False) + contract_subject_name = model_base.name_column(nullable=False) + filter_name = sa.Column(sa.String(64), nullable=False) + action = sa.Column(sa.Enum('permit', 'deny')) + + +class ContractSubjInFilter(model_base.Base, model_base.HasAimId, + model_base.HasTenantName, + model_base.HasDisplayName, + model_base.AttributeMixin, + model_base.IsMonitored): + """DB model for in filters used by Contract Subject.""" + __tablename__ = 'aim_contract_subject_in_filter_relation' + __table_args__ = ( + model_base.uniq_column(__tablename__, 'tenant_name', 'contract_name', + 'contract_subject_name', + 'filter_name') + + model_base.to_tuple(model_base.Base.__table_args__)) + + contract_name = model_base.name_column(nullable=False) + contract_subject_name = model_base.name_column(nullable=False) + filter_name = sa.Column(sa.String(64), nullable=False) + action = sa.Column(sa.Enum('permit', 'deny')) + + +class ContractSubjOutFilter(model_base.Base, model_base.HasAimId, + model_base.HasTenantName, + model_base.HasDisplayName, + model_base.AttributeMixin, + model_base.IsMonitored): + """DB model for out filters used by Contract Subject.""" + __tablename__ = 'aim_contract_subject_out_filter_relation' + __table_args__ = ( + model_base.uniq_column(__tablename__, 'tenant_name', 'contract_name', + 'contract_subject_name', + 'filter_name') + + model_base.to_tuple(model_base.Base.__table_args__)) + + contract_name = model_base.name_column(nullable=False) + contract_subject_name = model_base.name_column(nullable=False) + filter_name = sa.Column(sa.String(64), nullable=False) + action = sa.Column(sa.Enum('permit', 'deny')) + + +class ContractSubjGraph(model_base.Base, model_base.HasAimId, + model_base.HasTenantName, + model_base.HasDisplayName, + model_base.AttributeMixin, + model_base.IsMonitored): + """DB model for filters used by Contract Subject.""" + __tablename__ = 'aim_contract_subject_graph_relation' + __table_args__ = ( + model_base.uniq_column(__tablename__, 'tenant_name', 'contract_name', + 'contract_subject_name') + + model_base.to_tuple(model_base.Base.__table_args__)) + + contract_name = model_base.name_column(nullable=False) + contract_subject_name = model_base.name_column(nullable=False) + graph_name = sa.Column(sa.String(64)) + + +class ContractSubjOutGraph(model_base.Base, model_base.HasAimId, + model_base.HasTenantName, + model_base.HasDisplayName, + model_base.AttributeMixin, + model_base.IsMonitored): + """DB model for filters used by Contract Subject.""" + __tablename__ = 'aim_contract_subject_out_graph_relation' + __table_args__ = ( + model_base.uniq_column(__tablename__, 'tenant_name', 'contract_name', + 'contract_subject_name') + + model_base.to_tuple(model_base.Base.__table_args__)) + + contract_name = model_base.name_column(nullable=False) + contract_subject_name = model_base.name_column(nullable=False) + graph_name = sa.Column(sa.String(64)) + + +class ContractSubjInGraph(model_base.Base, model_base.HasAimId, + model_base.HasTenantName, + model_base.HasDisplayName, + model_base.AttributeMixin, + model_base.IsMonitored): + """DB model for filters used by Contract Subject.""" + __tablename__ = 'aim_contract_subject_in_graph_relation' + __table_args__ = ( + model_base.uniq_column(__tablename__, 'tenant_name', 'contract_name', + 'contract_subject_name') + + model_base.to_tuple(model_base.Base.__table_args__)) + + contract_name = model_base.name_column(nullable=False) + contract_subject_name = model_base.name_column(nullable=False) + graph_name = sa.Column(sa.String(64)) + + class Endpoint(model_base.Base, model_base.HasDisplayName, model_base.AttributeMixin): """DB model for Endpoint.""" diff --git a/aim/tests/unit/agent/aid_universes/test_aci_universe.py b/aim/tests/unit/agent/aid_universes/test_aci_universe.py index b499386f..3617fad0 100644 --- a/aim/tests/unit/agent/aid_universes/test_aci_universe.py +++ b/aim/tests/unit/agent/aid_universes/test_aci_universe.py @@ -253,13 +253,16 @@ def test_get_resources(self): 'dn': 'uni/tn-t1/brc-c/subj-s/outtmnl'}}}, {'vzRsSubjFiltAtt': {'attributes': { 'dn': 'uni/tn-t1/brc-c/subj-s/rssubjFiltAtt-f', - 'tnVzFilterName': 'f'}}}, + 'tnVzFilterName': 'f', + 'action': 'permit'}}}, {'vzRsFiltAtt': {'attributes': { 'dn': 'uni/tn-t1/brc-c/subj-s/intmnl/rsfiltAtt-g', - 'tnVzFilterName': 'g'}}}, + 'tnVzFilterName': 'g', + 'action': 'permit'}}}, {'vzRsFiltAtt': {'attributes': { 'dn': 'uni/tn-t1/brc-c/subj-s/outtmnl/rsfiltAtt-h', - 'tnVzFilterName': 'h'}}}] + 'tnVzFilterName': 'h', + 'action': 'permit'}}}] self._add_data_to_tree(objs, self.backend_state) keys = [('fvTenant|t1', 'fvAp|a1', 'fvAEPg|test', 'faultInst|951'), ('fvTenant|test-tenant', 'fvBD|test'), diff --git a/aim/tests/unit/agent/aid_universes/test_aim_db_universe.py b/aim/tests/unit/agent/aid_universes/test_aim_db_universe.py index ecacd792..cba93de9 100644 --- a/aim/tests/unit/agent/aid_universes/test_aim_db_universe.py +++ b/aim/tests/unit/agent/aid_universes/test_aim_db_universe.py @@ -323,8 +323,8 @@ def test_push_resources(self): subj = resource.ContractSubject(tenant_name='t1', contract_name='c', name='s2') status = aim_mgr.get_status(self.ctx, subj) - self.assertEqual(3, len(status.faults)) - self.assertEqual(['F1111', 'F1112', 'F1113'], + self.assertEqual(2, len(status.faults)) + self.assertEqual(['F1111', 'F1112'], [f.fault_code for f in status.faults]) # delete filter faults diff --git a/aim/tests/unit/agent/aid_universes/test_converter.py b/aim/tests/unit/agent/aid_universes/test_converter.py index 8d841b81..44d5ef24 100644 --- a/aim/tests/unit/agent/aid_universes/test_converter.py +++ b/aim/tests/unit/agent/aid_universes/test_converter.py @@ -637,125 +637,240 @@ class TestAciToAimConverterContractSubject(TestAciToAimConverterBase, 'exceptions': {}, 'skip': ['inFilters', 'outFilters', 'biFilters', 'serviceGraphName', 'inServiceGraphName', - 'outServiceGraphName']}, + 'outServiceGraphName']}] + sample_input = [[get_example_aci_subject(nameAlias='alias')], + [{'vzSubj': {'attributes': { + 'dn': 'uni/tn-common/brc-prs1/subj-prs1', + 'revFltPorts': 'yes', 'nameAlias': 'prs1', + 'name': 'prs1', 'prio': 'unspecified', + 'targetDscp': 'unspecified', 'descr': '', + 'consMatchT': 'AtleastOne', + 'provMatchT': 'AtleastOne'}}}]] + sample_output = [ + resource.ContractSubject(tenant_name='t1', contract_name='c', name='s', + in_filters=[], + out_filters=[], + bi_filters=[], + service_graph_name='', + in_service_graph_name='', + out_service_graph_name='', + display_name='alias'), + resource.ContractSubject(tenant_name='common', contract_name='prs1', + name='prs1', + display_name='prs1')] + + +class TestAciToAimConverterContractSubjFilter(TestAciToAimConverterBase, + base.TestAimDBBase): + resource_type = resource.ContractSubjFilter + reverse_map_output = [ {'resource': 'vzRsSubjFiltAtt', 'exceptions': {}, - 'converter': converter.vzRsSubjFiltAtt_converter}, - {'resource': 'vzRsSubjGraphAtt', - 'exceptions': {'service_graph_name': {'other': 'tnVnsAbsGraphName', - 'skip_if_empty': True}}, - 'to_resource': converter.default_to_resource_strict}, - {'resource': 'vzRsFiltAtt', - 'exceptions': {}, - 'converter': converter.vzInTerm_vzRsFiltAtt_converter}, + 'converter': converter.vzRsSubjFiltAtt_converter}] + sample_input = [[{"vzRsSubjFiltAtt": { + "attributes": {"action": "permit", + "dn": "uni/tn-common/brc-prs1/" + "subj-prs1/rssubjFiltAtt-f1", + "tnVzFilterName": "f1"}}}], + [{"vzRsSubjFiltAtt": { + "attributes": {"action": "deny", + "dn": "uni/tn-common/brc-prs1/" + "subj-prs1/rssubjFiltAtt-f2", + "tnVzFilterName": "f2"}}}]] + sample_output = [ + resource.ContractSubjFilter(tenant_name='common', + contract_name='prs1', + contract_subject_name='prs1', + filter_name='f1', + action='permit'), + resource.ContractSubjFilter(tenant_name='common', + contract_name='prs1', + contract_subject_name='prs1', + filter_name='f2', + action='deny')] + + +class TestAciToAimConverterContractSubjInFilter(TestAciToAimConverterBase, + base.TestAimDBBase): + resource_type = resource.ContractSubjInFilter + reverse_map_output = [ {'resource': 'vzRsFiltAtt', 'exceptions': {}, - 'converter': converter.vzOutTerm_vzRsFiltAtt_converter}, + 'converter': converter.vzRsFiltAtt_in_converter}, {'resource': 'vzInTerm', 'exceptions': {}, - 'skip': ['displayName'], - 'to_resource': converter.to_resource_filter_container}, - {'resource': 'vzOutTerm', - 'exceptions': {}, - 'skip': ['displayName'], - 'to_resource': converter.to_resource_filter_container}, - {'resource': 'vzRsInTermGraphAtt', - 'exceptions': {'in_service_graph_name': - {'other': 'tnVnsAbsGraphName', - 'skip_if_empty': True}}, - 'to_resource': converter.default_to_resource_strict}, - {'resource': 'vzRsOutTermGraphAtt', - 'exceptions': {'out_service_graph_name': - {'other': 'tnVnsAbsGraphName', - 'skip_if_empty': True}}, - 'to_resource': converter.default_to_resource_strict}] - sample_input = [[get_example_aci_subject(nameAlias='alias'), - _aci_obj('vzRsSubjFiltAtt', - dn='uni/tn-t1/brc-c/subj-s/rssubjFiltAtt-f1', - tnVzFilterName='f1'), - _aci_obj('vzRsSubjFiltAtt', - dn='uni/tn-t1/brc-c/subj-s/rssubjFiltAtt-f2', - tnVzFilterName='f2'), - _aci_obj('vzRsSubjGraphAtt', - dn='uni/tn-t1/brc-c/subj-s/rsSubjGraphAtt', - tnVnsAbsGraphName='g1'), - _aci_obj('vzInTerm', - dn='uni/tn-t1/brc-c/subj-s/intmnl'), - _aci_obj('vzOutTerm', - dn='uni/tn-t1/brc-c/subj-s/outtmnl'), - _aci_obj('vzRsFiltAtt', - dn='uni/tn-t1/brc-c/subj-s/intmnl/rsfiltAtt-i1', - tnVzFilterName='i1'), - _aci_obj('vzRsFiltAtt', - dn='uni/tn-t1/brc-c/subj-s/intmnl/rsfiltAtt-i2', - tnVzFilterName='i2'), - _aci_obj('vzRsFiltAtt', - dn='uni/tn-t1/brc-c/subj-s/outtmnl/rsfiltAtt-o1', - tnVzFilterName='o1'), - _aci_obj('vzRsFiltAtt', - dn='uni/tn-t1/brc-c/subj-s/outtmnl/rsfiltAtt-o2', - tnVzFilterName='o2'), - _aci_obj('vzRsInTermGraphAtt', - dn='uni/tn-t1/brc-c/subj-s/intmnl/' - 'rsInTermGraphAtt', - tnVnsAbsGraphName='g2'), - _aci_obj('vzRsOutTermGraphAtt', - dn='uni/tn-t1/brc-c/subj-s/outtmnl/' - 'rsOutTermGraphAtt', - tnVnsAbsGraphName='g3'), ], - [{'vzSubj': { - 'attributes': { - 'dn': 'uni/tn-common/brc-prs1/subj-prs1', - 'revFltPorts': 'yes', 'nameAlias': 'prs1', - 'name': 'prs1', 'prio': 'unspecified', - 'targetDscp': 'unspecified', 'descr': '', - 'consMatchT': 'AtleastOne', - 'provMatchT': 'AtleastOne'}}}, - {'vzRsFiltAtt': { - 'attributes': { - 'dn': 'uni/tn-common/brc-prs1/subj-prs1/intmnl' - '/rsfiltAtt-reverse-pr1', 'directives': '', - 'tnVzFilterName': 'reverse-pr1'}}}, - {'vzRsFiltAtt': { - 'attributes': { - 'dn': 'uni/tn-common/brc-prs1/subj-prs1/intmnl' - '/rsfiltAtt-pr1', - 'directives': '', 'tnVzFilterName': 'pr1'}}}, - {'vzRsFiltAtt': { - 'attributes': { - 'dn': 'uni/tn-common/brc-prs1/subj-prs1/outtmnl' - '/rsfiltAtt-pr1', 'directives': '', - 'tnVzFilterName': 'pr1'}}}, - {'vzRsFiltAtt': { - 'attributes': { - 'dn': 'uni/tn-common/brc-prs1/subj-prs1/outtmnl' - '/rsfiltAtt-reverse-pr1', 'directives': '', - 'tnVzFilterName': 'reverse-pr1'}}}, + 'converter': converter.vzterm_converter}] + sample_input = [[{'vzRsFiltAtt': { + 'attributes': {'dn': 'uni/tn-common/brc-prs1/' + 'subj-prs1/intmnl/rsfiltAtt-reverse-pr1', + 'tnVzFilterName': 'reverse-pr1', + 'action': 'deny'}}}, {'vzInTerm': { 'attributes': { 'dn': 'uni/tn-common/brc-prs1/subj-prs1/intmnl', 'nameAlias': '', 'name': '', 'descr': '', 'targetDscp': 'unspecified', - 'prio': 'unspecified'}}}, + 'prio': 'unspecified'}}}], + [{'vzRsFiltAtt': { + 'attributes': { + 'dn': 'uni/tn-common/brc-prs2/subj-prs2/intmnl/' + 'rsfiltAtt-reverse-pr2', + 'tnVzFilterName': 'reverse-pr2', + 'action': 'permit'}}}, + {'vzInTerm': { + 'attributes': { + 'dn': 'uni/tn-common/brc-prs2/subj-prs2/intmnl', + 'nameAlias': '', 'name': '', 'descr': '', + 'targetDscp': 'unspecified', + 'prio': 'unspecified'}}}]] + sample_output = [ + resource.ContractSubjInFilter(tenant_name='common', + contract_name='prs1', + contract_subject_name='prs1', + filter_name='reverse-pr1', + action='deny'), + resource.ContractSubjInFilter(tenant_name='common', + contract_name='prs2', + contract_subject_name='prs2', + filter_name='reverse-pr2', + action='permit')] + + +class TestAciToAimConverterContractSubjOutFilter(TestAciToAimConverterBase, + base.TestAimDBBase): + resource_type = resource.ContractSubjOutFilter + reverse_map_output = [ + {'resource': 'vzRsFiltAtt', + 'exceptions': {}, + 'converter': converter.vzRsFiltAtt_out_converter}, + {'resource': 'vzOutTerm', + 'exceptions': {}, + 'converter': converter.vzterm_converter}] + sample_input = [[{'vzRsFiltAtt': { + 'attributes': {'dn': 'uni/tn-common/brc-prs1' + '/subj-prs1/outtmnl/' + 'rsfiltAtt-reverse-pr1', + 'tnVzFilterName': 'reverse-pr1', + 'action': 'deny'}}}, {'vzOutTerm': { 'attributes': { 'dn': 'uni/tn-common/brc-prs1/subj-prs1/outtmnl', 'nameAlias': '', 'name': '', 'descr': '', 'targetDscp': 'unspecified', + 'prio': 'unspecified'}}}], + [{'vzRsFiltAtt': { + 'attributes': { + 'dn': 'uni/tn-common/brc-prs2/subj-prs2/outtmnl/' + 'rsfiltAtt-reverse-pr2', + 'tnVzFilterName': 'reverse-pr2', + 'action': 'permit'}}}, + {'vzOutTerm': { + 'attributes': { + 'dn': 'uni/tn-common/brc-prs2/subj-prs2/outtmnl', + 'nameAlias': '', 'name': '', 'descr': '', + 'targetDscp': 'unspecified', 'prio': 'unspecified'}}}]] sample_output = [ - resource.ContractSubject(tenant_name='t1', contract_name='c', name='s', - in_filters=['i1', 'i2'], - out_filters=['o1', 'o2'], - bi_filters=['f1', 'f2'], - service_graph_name='g1', - in_service_graph_name='g2', - out_service_graph_name='g3', - display_name='alias'), - resource.ContractSubject(tenant_name='common', contract_name='prs1', - name='prs1', display_name='prs1', - in_filters=['pr1', 'reverse-pr1'], - out_filters=['pr1', 'reverse-pr1'])] + resource.ContractSubjOutFilter(tenant_name='common', + contract_name='prs1', + contract_subject_name='prs1', + filter_name='reverse-pr1', + action='deny'), + resource.ContractSubjOutFilter(tenant_name='common', + contract_name='prs2', + contract_subject_name='prs2', + filter_name='reverse-pr2', + action='permit')] + + +class TestAciToAimConverterContractSubjInGraph(TestAciToAimConverterBase, + base.TestAimDBBase): + resource_type = resource.ContractSubjInGraph + reverse_map_output = [ + {'resource': 'vzRsInTermGraphAtt', + 'exceptions': {'graph_name': {'other': 'tnVnsAbsGraphName'}}, + 'to_resource': converter.default_to_resource_strict}, + {'resource': 'vzInTerm', + 'exceptions': {}, + 'converter': converter.vzterm_converter}] + sample_input = [[_aci_obj('vzRsInTermGraphAtt', + dn='uni/tn-t1/brc-c/subj-s/intmnl/' + 'rsInTermGraphAtt', + tnVnsAbsGraphName='g1'), + _aci_obj('vzInTerm', + dn='uni/tn-t1/brc-c/subj-s/intmnl')], + [_aci_obj('vzRsInTermGraphAtt', + dn='uni/tn-t1/brc-c/subj-s1/intmnl/' + 'rsInTermGraphAtt', + tnVnsAbsGraphName='g2'), + _aci_obj('vzInTerm', + dn='uni/tn-t1/brc-c/subj-s1/intmnl')]] + sample_output = [ + resource.ContractSubjInGraph(tenant_name='t1', + contract_name='c', + contract_subject_name='s', + graph_name='g1'), + resource.ContractSubjInGraph(tenant_name='t1', + contract_name='c', + contract_subject_name='s1', + graph_name='g2')] + + +class TestAciToAimConverterContractSubjOutGraph(TestAciToAimConverterBase, + base.TestAimDBBase): + resource_type = resource.ContractSubjOutGraph + reverse_map_output = [ + {'resource': 'vzRsOutTermGraphAtt', + 'exceptions': {'graph_name': {'other': 'tnVnsAbsGraphName'}}, + 'to_resource': converter.default_to_resource_strict}, + {'resource': 'vzOutTerm', + 'exceptions': {}, + 'converter': converter.vzterm_converter}] + sample_input = [[_aci_obj('vzRsOutTermGraphAtt', + dn='uni/tn-t1/brc-c/subj-s/outtmnl/' + 'rsOutTermGraphAtt', + tnVnsAbsGraphName='g1'), + _aci_obj('vzOutTerm', + dn='uni/tn-t1/brc-c/subj-s/outtmnl')], + [_aci_obj('vzRsOutTermGraphAtt', + dn='uni/tn-t1/brc-c/subj-s1/outtmnl/' + 'rsOutTermGraphAtt', + tnVnsAbsGraphName='g2'), + _aci_obj('vzOutTerm', + dn='uni/tn-t1/brc-c/subj-s1/outtmnl')]] + sample_output = [ + resource.ContractSubjOutGraph(tenant_name='t1', + contract_name='c', + contract_subject_name='s', + graph_name='g1'), + resource.ContractSubjOutGraph(tenant_name='t1', + contract_name='c', + contract_subject_name='s1', + graph_name='g2')] + + +class TestAciToAimConverterContractSubjGraph(TestAciToAimConverterBase, + base.TestAimDBBase): + resource_type = resource.ContractSubjGraph + reverse_map_output = [ + {'resource': 'vzRsSubjGraphAtt', + 'exceptions': {'graph_name': {'other': 'tnVnsAbsGraphName'}}, + 'to_resource': converter.default_to_resource_strict}] + sample_input = [[_aci_obj('vzRsSubjGraphAtt', + dn='uni/tn-t1/brc-c/subj-s/rsSubjGraphAtt', + tnVnsAbsGraphName='g1')], + [_aci_obj('vzRsSubjGraphAtt', + dn='uni/tn-t1/brc-c/subj-s1/rsSubjGraphAtt', + tnVnsAbsGraphName='g2')]] + sample_output = [ + resource.ContractSubjGraph(tenant_name='t1', + contract_name='c', + contract_subject_name='s', + graph_name='g1'), + resource.ContractSubjGraph(tenant_name='t1', + contract_name='c', + contract_subject_name='s1', + graph_name='g2')] class TestAciToAimConverterFault(TestAciToAimConverterBase, @@ -2643,52 +2758,120 @@ def get_example_aim_contract_subject(**kwargs): class TestAimToAciConverterContractSubject(TestAimToAciConverterBase, base.TestAimDBBase): sample_input = [ - get_example_aim_contract_subject(in_filters=['i1', 'i2'], - out_filters=['o1', 'o2'], - bi_filters=['f1', 'f2'], - service_graph_name='g1', - in_service_graph_name='g2', + get_example_aim_contract_subject(service_graph_name='', + in_service_graph_name='', display_name='alias'), get_example_aim_contract_subject(name='s2', - out_service_graph_name='g3')] + out_service_graph_name='')] sample_output = [ [_aci_obj('vzSubj', dn='uni/tn-test-tenant/brc-c1/subj-s1', - nameAlias='alias'), - _aci_obj('vzRsSubjFiltAtt', + nameAlias='alias')], + [_aci_obj('vzSubj', dn='uni/tn-test-tenant/brc-c1/subj-s2', + nameAlias="")]] + + +def get_example_aim_contract_subject_filter(direction, **kwargs): + if direction == 'bi': + example = resource.ContractSubjFilter(tenant_name='test-tenant', + contract_name='c1', + contract_subject_name='s1', + filter_name='f1') + if direction == 'in': + example = resource.ContractSubjInFilter(tenant_name='test-tenant', + contract_name='c1', + contract_subject_name='s1', + filter_name='f1') + if direction == 'out': + example = resource.ContractSubjOutFilter(tenant_name='test-tenant', + contract_name='c1', + contract_subject_name='s1', + filter_name='f1') + example.__dict__.update(kwargs) + return example + + +class TestAimToAciConverterContractSubjectFilter( + TestAimToAciConverterBase, + base.TestAimDBBase): + sample_input = [ + get_example_aim_contract_subject_filter(direction='bi', + action='permit'), + get_example_aim_contract_subject_filter(filter_name='f2', + direction='bi', + action='deny'), + get_example_aim_contract_subject_filter(filter_name='i1', + direction='in', + action='permit'), + get_example_aim_contract_subject_filter(filter_name='o1', + direction='out', + action='deny')] + sample_output = [ + [_aci_obj('vzRsSubjFiltAtt', dn='uni/tn-test-tenant/brc-c1/subj-s1/rssubjFiltAtt-f1', - tnVzFilterName='f1'), - _aci_obj('vzRsSubjFiltAtt', + tnVzFilterName='f1', + action='permit')], + [_aci_obj('vzRsSubjFiltAtt', dn='uni/tn-test-tenant/brc-c1/subj-s1/rssubjFiltAtt-f2', - tnVzFilterName='f2'), - _aci_obj('vzRsSubjGraphAtt', - dn='uni/tn-test-tenant/brc-c1/subj-s1/rsSubjGraphAtt', - tnVnsAbsGraphName='g1'), + tnVzFilterName='f2', + action='deny')], + [_aci_obj('vzInTerm', + dn='uni/tn-test-tenant/brc-c1/subj-s1/intmnl'), _aci_obj('vzRsFiltAtt__In', dn='uni/tn-test-tenant/brc-c1/subj-s1/intmnl/rsfiltAtt-i1', - tnVzFilterName='i1'), - _aci_obj('vzRsFiltAtt__In', - dn='uni/tn-test-tenant/brc-c1/subj-s1/intmnl/rsfiltAtt-i2', - tnVzFilterName='i2'), - _aci_obj('vzRsFiltAtt__Out', + tnVzFilterName='i1', + action='permit')], + [_aci_obj('vzRsFiltAtt__Out', dn='uni/tn-test-tenant/brc-c1/subj-s1/outtmnl/rsfiltAtt-o1', - tnVzFilterName='o1'), - _aci_obj('vzRsFiltAtt__Out', - dn='uni/tn-test-tenant/brc-c1/subj-s1/outtmnl/rsfiltAtt-o2', - tnVzFilterName='o2'), - _aci_obj('vzInTerm', - dn='uni/tn-test-tenant/brc-c1/subj-s1/intmnl'), + tnVzFilterName='o1', + action='deny'), _aci_obj('vzOutTerm', - dn='uni/tn-test-tenant/brc-c1/subj-s1/outtmnl'), + dn='uni/tn-test-tenant/brc-c1/subj-s1/outtmnl')]] + + +def get_example_aim_contract_subject_graph(direction, **kwargs): + if direction == 'bi': + example = resource.ContractSubjGraph(tenant_name='test-tenant', + contract_name='c1', + contract_subject_name='s1', + graph_name='g1') + if direction == 'in': + example = resource.ContractSubjInGraph(tenant_name='test-tenant', + contract_name='c1', + contract_subject_name='s1', + graph_name='g1') + if direction == 'out': + example = resource.ContractSubjOutGraph(tenant_name='test-tenant', + contract_name='c1', + contract_subject_name='s1', + graph_name='g1') + example.__dict__.update(kwargs) + return example + + +class TestAimToAciConverterContractSubjectGraphs(TestAimToAciConverterBase, + base.TestAimDBBase): + sample_input = [ + get_example_aim_contract_subject_graph(direction='bi', + graph_name='g1'), + get_example_aim_contract_subject_graph(direction='in', + graph_name='g2'), + get_example_aim_contract_subject_graph( + direction='out', graph_name='g3')] + + sample_output = [ + [_aci_obj('vzRsSubjGraphAtt', + dn='uni/tn-test-tenant/brc-c1/subj-s1/rsSubjGraphAtt', + tnVnsAbsGraphName='g1')], + [_aci_obj('vzInTerm', + dn='uni/tn-test-tenant/brc-c1/subj-s1/intmnl'), _aci_obj('vzRsInTermGraphAtt', dn='uni/tn-test-tenant/brc-c1/subj-s1/intmnl/' 'rsInTermGraphAtt', tnVnsAbsGraphName='g2')], - [_aci_obj('vzSubj', dn='uni/tn-test-tenant/brc-c1/subj-s2', - nameAlias=""), - _aci_obj('vzOutTerm', - dn='uni/tn-test-tenant/brc-c1/subj-s2/outtmnl'), + [_aci_obj('vzOutTerm', + dn='uni/tn-test-tenant/brc-c1/subj-s1/outtmnl'), _aci_obj('vzRsOutTermGraphAtt', - dn='uni/tn-test-tenant/brc-c1/subj-s2/outtmnl/' + dn='uni/tn-test-tenant/brc-c1/subj-s1/outtmnl/' 'rsOutTermGraphAtt', tnVnsAbsGraphName='g3')]] diff --git a/aim/tests/unit/agent/test_agent.py b/aim/tests/unit/agent/test_agent.py index 8353f458..644c15c3 100644 --- a/aim/tests/unit/agent/test_agent.py +++ b/aim/tests/unit/agent/test_agent.py @@ -1364,11 +1364,17 @@ def test_create_delete(self): name='rtr_fb8f33cf-fe9c-48a9-a7b2-aa35ac63f189') sub = resource.ContractSubject( tenant_name=tenant_name, contract_name=ctr.name, - name='route', bi_filters=['noirolab_AnyFilter']) + name='route') + subflt = resource.ContractSubjFilter( + tenant_name=tenant_name, contract_name=ctr.name, + contract_subject_name=sub.name, + filter_name='noirolab_AnyFilter') with self.ctx.store.begin(subtransactions=True): self.aim_manager.create(self.ctx, ctr) self.aim_manager.create(self.ctx, sub) + self.aim_manager.create(self.ctx, subflt) with self.ctx.store.begin(subtransactions=True): + self.aim_manager.delete(self.ctx, subflt) self.aim_manager.delete(self.ctx, sub) self.aim_manager.delete(self.ctx, ctr) desired_config.observe(self.ctx) diff --git a/aim/tests/unit/aim_lib/test_nat_strategy.py b/aim/tests/unit/aim_lib/test_nat_strategy.py index 086bba6f..996da003 100644 --- a/aim/tests/unit/aim_lib/test_nat_strategy.py +++ b/aim/tests/unit/aim_lib/test_nat_strategy.py @@ -93,8 +93,10 @@ def _get_l3out_objects(self, l3out_name=None, l3out_display_name=None, a_res.Contract(tenant_name='t1', name=name, display_name=d_name), a_res.ContractSubject(tenant_name='t1', contract_name=name, - name='Allow', display_name='Allow', - bi_filters=[name]), + name='Allow', display_name='Allow'), + a_res.ContractSubjFilter(tenant_name='t1', contract_name=name, + contract_subject_name='Allow', + filter_name=name), a_res.BridgeDomain(tenant_name='t1', name=name, display_name=d_name, vrf_name=nat_vrf_name or name, diff --git a/aim/tests/unit/test_aim_manager.py b/aim/tests/unit/test_aim_manager.py index cc015814..e43a1475 100644 --- a/aim/tests/unit/test_aim_manager.py +++ b/aim/tests/unit/test_aim_manager.py @@ -1014,16 +1014,11 @@ class TestContractSubjectMixin(object): test_required_attributes = {'tenant_name': 'tenant1', 'contract_name': 'contract1', 'name': 'subject1', - 'in_filters': ['f1', 'f2'], - 'out_filters': ['f2', 'f3'], - 'bi_filters': ['f1', 'f3', 'f4'], 'service_graph_name': 'g1', 'in_service_graph_name': 'g2', 'out_service_graph_name': 'g3'} test_search_attributes = {'name': 'subject1'} - test_update_attributes = {'in_filters': ['f1', 'f2', 'f3'], - 'out_filters': [], - 'service_graph_name': 'g11', + test_update_attributes = {'service_graph_name': 'g11', 'in_service_graph_name': 'g21', 'out_service_graph_name': 'g31'} test_default_values = {'in_filters': [], @@ -1036,6 +1031,153 @@ class TestContractSubjectMixin(object): res_command = 'contract-subject' +class TestContractSubjFilterMixin(object): + resource_class = resource.ContractSubjFilter + resource_root_type = resource.Tenant._aci_mo_name + prereq_objects = [ + resource.Tenant(name='tenant1'), + resource.Contract(tenant_name='tenant1', name='contract1'), + resource.ContractSubject(tenant_name='tenant1', + contract_name='contract1', + name='subject1')] + test_identity_attributes = {'tenant_name': 'tenant1', + 'contract_name': 'contract1', + 'contract_subject_name': 'subject1', + 'filter_name': 'filter1'} + test_required_attributes = {'tenant_name': 'tenant1', + 'contract_name': 'contract1', + 'contract_subject_name': 'subject1', + 'filter_name': 'filter1', + 'action': 'permit'} + test_search_attributes = {'filter_name': 'filter1'} + test_update_attributes = {'action': 'deny'} + test_default_values = {'action': 'permit'} + test_dn = \ + 'uni/tn-tenant1/brc-contract1/subj-subject1/rssubjFiltAtt-filter1' + res_command = 'contract-subj-filter' + + +class TestContractSubjInFilterMixin(object): + resource_class = resource.ContractSubjInFilter + resource_root_type = resource.Tenant._aci_mo_name + prereq_objects = [ + resource.Tenant(name='tenant1'), + resource.Contract(tenant_name='tenant1', name='contract1'), + resource.ContractSubject(tenant_name='tenant1', + contract_name='contract1', + name='subject1')] + test_identity_attributes = {'tenant_name': 'tenant1', + 'contract_name': 'contract1', + 'contract_subject_name': 'subject1', + 'filter_name': 'filter1'} + test_required_attributes = {'tenant_name': 'tenant1', + 'contract_name': 'contract1', + 'contract_subject_name': 'subject1', + 'filter_name': 'filter1', + 'action': 'permit'} + test_search_attributes = {'filter_name': 'filter1'} + test_update_attributes = {'action': 'deny'} + test_default_values = {'action': 'permit'} + test_dn = \ + 'uni/tn-tenant1/brc-contract1/subj-subject1/intmnl/rsFiltAtt-filter1' + res_command = 'contract-subj-in-filter' + + +class TestContractSubjOutFilterMixin(object): + resource_class = resource.ContractSubjOutFilter + resource_root_type = resource.Tenant._aci_mo_name + prereq_objects = [ + resource.Tenant(name='tenant1'), + resource.Contract(tenant_name='tenant1', name='contract1'), + resource.ContractSubject(tenant_name='tenant1', + contract_name='contract1', + name='subject1')] + test_identity_attributes = {'tenant_name': 'tenant1', + 'contract_name': 'contract1', + 'contract_subject_name': 'subject1', + 'filter_name': 'filter1'} + test_required_attributes = {'tenant_name': 'tenant1', + 'contract_name': 'contract1', + 'contract_subject_name': 'subject1', + 'filter_name': 'filter1', + 'action': 'permit'} + test_search_attributes = {'filter_name': 'filter1'} + test_update_attributes = {'action': 'deny'} + test_default_values = {'action': 'permit'} + test_dn = \ + 'uni/tn-tenant1/brc-contract1/subj-subject1/outtmnl/rsFiltAtt-filter1' + res_command = 'contract-subj-out-filter' + + +class TestContractSubjGraphMixin(object): + resource_class = resource.ContractSubjGraph + resource_root_type = resource.Tenant._aci_mo_name + prereq_objects = [ + resource.Tenant(name='tenant1'), + resource.Contract(tenant_name='tenant1', name='contract1'), + resource.ContractSubject(tenant_name='tenant1', + contract_name='contract1', + name='subject1')] + test_identity_attributes = {'tenant_name': 'tenant1', + 'contract_name': 'contract1', + 'contract_subject_name': 'subject1'} + test_required_attributes = {'tenant_name': 'tenant1', + 'contract_name': 'contract1', + 'contract_subject_name': 'subject1', + 'graph_name': 'graph1'} + test_search_attributes = {'graph_name': 'graph1'} + test_update_attributes = {'graph_name': 'graph2'} + test_dn = \ + 'uni/tn-tenant1/brc-contract1/subj-subject1/rsSubjGraphAtt' + res_command = 'contract-subj-graph' + + +class TestContractSubjInGraphMixin(object): + resource_class = resource.ContractSubjInGraph + resource_root_type = resource.Tenant._aci_mo_name + prereq_objects = [ + resource.Tenant(name='tenant1'), + resource.Contract(tenant_name='tenant1', name='contract1'), + resource.ContractSubject(tenant_name='tenant1', + contract_name='contract1', + name='subject1')] + test_identity_attributes = {'tenant_name': 'tenant1', + 'contract_name': 'contract1', + 'contract_subject_name': 'subject1'} + test_required_attributes = {'tenant_name': 'tenant1', + 'contract_name': 'contract1', + 'contract_subject_name': 'subject1', + 'graph_name': 'graph1'} + test_search_attributes = {'graph_name': 'graph1'} + test_update_attributes = {'graph_name': 'graph2'} + test_dn = \ + 'uni/tn-tenant1/brc-contract1/subj-subject1/intmnl/rsInTermGraphAtt' + res_command = 'contract-subj-in-graph' + + +class TestContractSubjOutGraphMixin(object): + resource_class = resource.ContractSubjOutGraph + resource_root_type = resource.Tenant._aci_mo_name + prereq_objects = [ + resource.Tenant(name='tenant1'), + resource.Contract(tenant_name='tenant1', name='contract1'), + resource.ContractSubject(tenant_name='tenant1', + contract_name='contract1', + name='subject1')] + test_identity_attributes = {'tenant_name': 'tenant1', + 'contract_name': 'contract1', + 'contract_subject_name': 'subject1'} + test_required_attributes = {'tenant_name': 'tenant1', + 'contract_name': 'contract1', + 'contract_subject_name': 'subject1', + 'graph_name': 'graph1'} + test_search_attributes = {'graph_name': 'graph1'} + test_update_attributes = {'graph_name': 'graph2'} + test_dn = \ + 'uni/tn-tenant1/brc-contract1/subj-subject1/outtmnl/rsOutTermGraphAtt' + res_command = 'contract-subj-out-graph' + + class TestEndpointMixin(object): resource_class = resource.Endpoint prereq_objects = [ @@ -2529,6 +2671,36 @@ class TestContractSubject(TestContractSubjectMixin, TestAciResourceOpsBase, pass +class TestContractSubjFilter(TestContractSubjFilterMixin, + TestAciResourceOpsBase, base.TestAimDBBase): + pass + + +class TestContractSubjInFilter(TestContractSubjFilterMixin, + TestAciResourceOpsBase, base.TestAimDBBase): + pass + + +class TestContractSubjOutFilter(TestContractSubjFilterMixin, + TestAciResourceOpsBase, base.TestAimDBBase): + pass + + +class TestContractSubjGraph(TestContractSubjFilterMixin, + TestAciResourceOpsBase, base.TestAimDBBase): + pass + + +class TestContractSubjInGraph(TestContractSubjFilterMixin, + TestAciResourceOpsBase, base.TestAimDBBase): + pass + + +class TestContractSubjOutGraph(TestContractSubjFilterMixin, + TestAciResourceOpsBase, base.TestAimDBBase): + pass + + class TestEndpoint(TestEndpointMixin, TestResourceOpsBase, base.TestAimDBBase): pass diff --git a/aim/tests/unit/test_hashtree_db_listener.py b/aim/tests/unit/test_hashtree_db_listener.py index a0f2c4c6..360ca053 100644 --- a/aim/tests/unit/test_hashtree_db_listener.py +++ b/aim/tests/unit/test_hashtree_db_listener.py @@ -345,11 +345,25 @@ def test_subject_related_objects(self): self.ctx, aim_res.Contract(tenant_name='common', name='c-name')) subj = aim_res.ContractSubject( **{'contract_name': 'c-name', - 'out_filters': ['pr_1', 'reverse-pr_1', 'pr_2', 'reverse-pr_2'], 'name': 's-name', - 'tenant_name': 'common', 'monitored': False, 'bi_filters': [], - 'in_filters': ['pr_1', 'reverse-pr_1', 'pr_2', 'reverse-pr_2']}) + 'tenant_name': 'common', 'monitored': False}) subj = self.mgr.create(self.ctx, subj) + + subj_flt = aim_res.ContractSubjOutFilter( + **{'contract_name': 'c-name', + 'contract_subject_name': 's-name', + 'tenant_name': 'common', + 'monitored': False, + 'filter_name': 'pr_1'}) + subj_flt = self.mgr.create(self.ctx, subj_flt) + + subj_flt1 = aim_res.ContractSubjInFilter( + **{'contract_name': 'c-name', + 'contract_subject_name': 's-name', + 'tenant_name': 'common', + 'monitored': False, + 'filter_name': 'pr_1'}) + subj_flt1 = self.mgr.create(self.ctx, subj_flt1) cfg_tree = self.tt_mgr.get(self.ctx, 'tn-common', tree=tree_manager.CONFIG_TREE) # verify pr_1 and its reverse are in the tree @@ -358,22 +372,17 @@ def test_subject_related_objects(self): "vzOutTerm|outtmnl", "vzRsFiltAtt|pr_1")) rev_pr_1 = cfg_tree.find( ("fvTenant|common", "vzBrCP|c-name", "vzSubj|s-name", - "vzOutTerm|outtmnl", "vzRsFiltAtt|reverse-pr_1")) + "vzInTerm|intmnl", "vzRsFiltAtt|pr_1")) self.assertIsNotNone(pr_1) self.assertIsNotNone(rev_pr_1) - self.mgr.update(self.ctx, subj, out_filters=['pr_2', 'reverse-pr_2'], - in_filters=['pr_2', 'reverse-pr_2']) + self.mgr.update(self.ctx, subj_flt1, action='deny') cfg_tree = self.tt_mgr.get(self.ctx, 'tn-common', tree=tree_manager.CONFIG_TREE) - pr_1 = cfg_tree.find( - ("fvTenant|common", "vzBrCP|c-name", "vzSubj|s-name", - "vzOutTerm|outtmnl", "vzRsFiltAtt|pr_1")) rev_pr_1 = cfg_tree.find( ("fvTenant|common", "vzBrCP|c-name", "vzSubj|s-name", - "vzOutTerm|outtmnl", "vzRsFiltAtt|reverse-pr_1")) - self.assertIsNone(pr_1) - self.assertIsNone(rev_pr_1) + "vzInTerm|intmnl", "vzRsFiltAtt|pr_1")) + self.assertIsNotNone(rev_pr_1) def test_delete_all_trees(self): self.mgr.create(self.ctx, aim_res.Tenant(name='common')) diff --git a/aim/tests/unit/test_structured_hash_tree.py b/aim/tests/unit/test_structured_hash_tree.py index 8e3dc950..f6a447f3 100644 --- a/aim/tests/unit/test_structured_hash_tree.py +++ b/aim/tests/unit/test_structured_hash_tree.py @@ -925,23 +925,35 @@ def test_update_1(self): exp_tree = tree.StructuredHashTree() subj = resource.ContractSubject(tenant_name='t1', contract_name='c1', - name='s1', in_filters=['i1'], - out_filters=['o1'], bi_filters=['f1']) - self.maker.update(htree, [subj]) + name='s1') + subj_flt = resource.ContractSubjFilter( + tenant_name='t1', contract_name='c1', + contract_subject_name='s1', filter_name='f1', + direction='bi') + subj_flt_in = resource.ContractSubjInFilter( + tenant_name='t1', contract_name='c1', + contract_subject_name='s1', filter_name='i1') + subj_flt_out = resource.ContractSubjOutFilter( + tenant_name='t1', contract_name='c1', contract_subject_name='s1', + filter_name='o1') + self.maker.update(htree, [subj, subj_flt, subj_flt_in, subj_flt_out]) exp_tree = exp_tree.add(('fvTenant|t1', 'vzBrCP|c1', 'vzSubj|s1'), nameAlias='') exp_tree = exp_tree.add( ('fvTenant|t1', 'vzBrCP|c1', 'vzSubj|s1', 'vzInTerm|intmnl', 'vzRsFiltAtt|i1'), - tnVzFilterName='i1') + **{'tnVzFilterName': 'i1', + 'action': 'permit'}) exp_tree = exp_tree.add( ('fvTenant|t1', 'vzBrCP|c1', 'vzSubj|s1', 'vzOutTerm|outtmnl', 'vzRsFiltAtt|o1'), - tnVzFilterName='o1') + **{'tnVzFilterName': 'o1', + 'action': 'permit'}) exp_tree = exp_tree.add( ('fvTenant|t1', 'vzBrCP|c1', 'vzSubj|s1', 'vzRsSubjFiltAtt|f1'), - tnVzFilterName='f1') + **{'tnVzFilterName': 'f1', + 'action': 'permit'}) exp_tree = exp_tree.add( ('fvTenant|t1', 'vzBrCP|c1', 'vzSubj|s1', 'vzInTerm|intmnl')) exp_tree = exp_tree.add( diff --git a/aim/tests/unit/tools/cli/test_manager.py b/aim/tests/unit/tools/cli/test_manager.py index 356d8fd5..6c6c6059 100644 --- a/aim/tests/unit/tools/cli/test_manager.py +++ b/aim/tests/unit/tools/cli/test_manager.py @@ -692,6 +692,48 @@ class TestContractSubject(test_aim_manager.TestContractSubjectMixin, pass +class TestContractSubjFilter( + test_aim_manager.TestContractSubjFilterMixin, + TestManagerResourceOpsBase, + base.TestShell): + pass + + +class TestContractSubjInFilter( + test_aim_manager.TestContractSubjInFilterMixin, + TestManagerResourceOpsBase, + base.TestShell): + pass + + +class TestContractSubjOutFilter( + test_aim_manager.TestContractSubjOutFilterMixin, + TestManagerResourceOpsBase, + base.TestShell): + pass + + +class TestContractSubjGraph( + test_aim_manager.TestContractSubjGraphMixin, + TestManagerResourceOpsBase, + base.TestShell): + pass + + +class TestContractSubjInGraph( + test_aim_manager.TestContractSubjInGraphMixin, + TestManagerResourceOpsBase, + base.TestShell): + pass + + +class TestContractSubjOutGraph( + test_aim_manager.TestContractSubjOutGraphMixin, + TestManagerResourceOpsBase, + base.TestShell): + pass + + class TestEndpoint(test_aim_manager.TestEndpointMixin, TestManagerResourceOpsBase, base.TestShell):