gbp-validate: Tenant and resource level scoping.

Change-Id: Ia25071474c02954516da09b9c3f935b36fcac82b
This commit is contained in:
ansao-aci
2021-02-08 15:01:06 +05:30
parent f1e8610498
commit fa41913b94
8 changed files with 390 additions and 25 deletions

View File

@@ -6347,7 +6347,6 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
network_id=mapping.network_id)
def validate_aim_mapping(self, mgr):
# Register all AIM resource types used by mapping.
mgr.register_aim_resource_class(aim_infra.HostDomainMappingV2)
mgr.register_aim_resource_class(aim_resource.ApplicationProfile)
mgr.register_aim_resource_class(aim_resource.BridgeDomain)
@@ -6376,8 +6375,17 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
mgr.register_aim_resource_class(aim_resource.InfraAccBundleGroup)
# Copy common Tenant from actual to expected AIM store.
mgr.tenant_ids = []
project_dict = self.project_details_cache.project_details
for project_id in project_dict.keys():
tenant_name = project_dict[project_id][0]
if tenant_name in mgr.tenants:
mgr.tenat_ids.append(project_id)
for tenant in mgr.aim_mgr.find(
mgr.actual_aim_ctx, aim_resource.Tenant, name=COMMON_TENANT_NAME):
mgr.actual_aim_ctx,
aim_resource.Tenant,
name=COMMON_TENANT_NAME):
mgr.aim_mgr.create(mgr.expected_aim_ctx, tenant)
# Copy AIM resources that are managed via aimctl from actual
@@ -6395,7 +6403,8 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
aim_resource.L3Outside,
aim_resource.VRF]:
for resource in mgr.actual_aim_resources(resource_class):
if resource.monitored:
if (resource.monitored and
mgr._should_validate_tenant(resource.tenant_name)):
mgr.aim_mgr.create(mgr.expected_aim_ctx, resource)
# Register DB tables to be validated.
@@ -6412,10 +6421,11 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
# manager since this will be needed for both Neutron and GBP
# resources.
mgr._expected_projects = set()
self._validate_static_resources(mgr)
self._validate_address_scopes(mgr)
router_dbs, ext_net_routers = self._validate_routers(mgr)
self._validate_networks(mgr, router_dbs, ext_net_routers)
self._validate_routers(mgr)
self._validate_networks(mgr)
self._validate_security_groups(mgr)
self._validate_ports(mgr)
self._validate_subnetpools(mgr)
@@ -6427,6 +6437,8 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
# test execution, where they are called repeatedly.
def _validate_static_resources(self, mgr):
if not mgr._should_validate_tenant(COMMON_TENANT_NAME):
return
self._ensure_common_tenant(mgr.expected_aim_ctx)
self._ensure_unrouted_vrf(mgr.expected_aim_ctx)
self._ensure_any_filter(mgr.expected_aim_ctx)
@@ -6434,11 +6446,22 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
mgr.expected_aim_ctx)
def _validate_address_scopes(self, mgr):
if not mgr._should_validate_neutron_resource("address_scope"):
return
owned_scopes_by_vrf = defaultdict(list)
scopes_db = None
query = BAKERY(lambda s: s.query(
as_db.AddressScope))
for scope_db in query(mgr.actual_session):
if mgr.tenant_scope:
query += lambda q: q.filter(
as_db.AddressScope.project_id.in_(
sa.bindparam('project_ids', expanding=True)))
scopes_db = query(mgr.actual_session).params(
project_ids=list(mgr.tenant_ids))
else:
scopes_db = query(mgr.actual_session)
for scope_db in scopes_db:
self._expect_project(mgr, scope_db.project_id)
mapping = scope_db.aim_mapping
if mapping:
@@ -6464,12 +6487,22 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
mgr.expected_aim_ctx, vrf, scopes)
def _validate_routers(self, mgr):
if not mgr._should_validate_neutron_resource("router"):
return
router_dbs = {}
ext_net_routers = defaultdict(list)
query = BAKERY(lambda s: s.query(
l3_db.Router))
for router_db in query(mgr.actual_session):
if mgr.tenant_scope:
query += lambda q: q.filter(
l3_db.Router.project_id.in_(
sa.bindparam('project_ids', expanding=True)))
rtr_dbs = query(mgr.actual_session).params(
project_ids=list(mgr.tenant_ids))
else:
rtr_dbs = query(mgr.actual_session)
for router_db in rtr_dbs:
self._expect_project(mgr, router_db.project_id)
router_dbs[router_db.id] = router_db
if router_db.gw_port_id:
@@ -6497,13 +6530,38 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
return router_dbs, ext_net_routers
def _validate_networks(self, mgr, router_dbs, ext_net_routers):
def _get_routers(self, mgr):
router_dbs = {}
ext_net_routers = defaultdict(list)
query = BAKERY(lambda s: s.query(
l3_db.Router))
for router_db in query(mgr.actual_session):
router_dbs[router_db.id] = router_db
if router_db.gw_port_id:
ext_net_routers[router_db.gw_port.network_id].append(
router_db.id)
return router_dbs, ext_net_routers
def _validate_networks(self, mgr):
if not mgr._should_validate_neutron_resource("network"):
return
router_dbs, ext_net_routers = self._get_routers(mgr)
net_dbs = {}
query = BAKERY(lambda s: s.query(
models_v2.Network))
query += lambda q: q.options(
orm.joinedload('segments'))
net_dbs = {net_db.id: net_db for net_db in query(mgr.actual_session)}
if mgr.tenant_scope:
query += lambda q: q.filter(
models_v2.Network.project_id.in_(
sa.bindparam('project_ids', expanding=True)))
net_db_dicts = query(mgr.actual_session).params(
project_ids=list(mgr.tenant_ids))
net_dbs = {net_db.id: net_db for net_db in net_db_dicts}
else:
net_dbs = {net_db.id: net_db
for net_db in query(mgr.actual_session)}
router_ext_prov, router_ext_cons = self._get_router_ext_contracts(mgr)
routed_nets = self._get_router_interface_info(mgr)
network_vrfs, router_vrfs = self._determine_vrfs(
@@ -7025,6 +7083,8 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
return bd, epg, vrf
def _validate_security_groups(self, mgr):
if not mgr._should_validate_neutron_resource("security_group"):
return
sg_ips = defaultdict(set)
query = BAKERY(lambda s: s.query(
@@ -7043,14 +7103,23 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
# lazy='subquery' to lazy='dynamic'. If there is any way to
# override this dynamic loading with eager loading for a
# specific query, we may want to do so.
sg_dbs = None
query = BAKERY(lambda s: s.query(
sg_models.SecurityGroup))
for sg_db in query(mgr.actual_session):
if mgr.tenant_scope:
query += lambda q: q.filter(
sg_models.SecurityGroup.tenant_id.in_(
sa.bindparam('project_ids', expanding=True)))
sg_dbs = query(mgr.actual_session).params(
project_ids=list(mgr.tenant_ids))
else:
sg_dbs = query(mgr.actual_session)
for sg_db in sg_dbs:
# Ignore anonymous SGs, which seem to be a Neutron bug.
if sg_db.tenant_id:
self._expect_project(mgr, sg_db.project_id)
tenant_name = self.name_mapper.project(
mgr.expected_session, sg_db.tenant_id)
self._expect_project(mgr, sg_db.project_id)
sg = aim_resource.SecurityGroup(
tenant_name=tenant_name, name=sg_db.id,
display_name=aim_utils.sanitize_display_name(sg_db.name))
@@ -7091,10 +7160,16 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
mgr.expect_aim_resource(sg_rule)
def _validate_ports(self, mgr):
if not mgr._should_validate_neutron_resource("port"):
return
query = BAKERY(lambda s: s.query(
models_v2.Port.project_id))
query += lambda q: q.distinct()
for project_id, in query(mgr.actual_session):
tenant_name = self.name_mapper.project(
mgr.expected_session, project_id)
if not mgr._should_validate_tenant(tenant_name):
continue
self._expect_project(mgr, project_id)
query = BAKERY(lambda s: s.query(
models_v2.Port))
@@ -7124,17 +7199,29 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
mgr.expect_aim_resource(resource)
def _validate_subnetpools(self, mgr):
if not mgr._should_validate_neutron_resource("subnet_pool"):
return
query = BAKERY(lambda s: s.query(
models_v2.SubnetPool.project_id))
query += lambda q: q.distinct()
for project_id, in query(mgr.actual_session):
tenant_name = self.name_mapper.project(
mgr.expected_session, project_id)
if not mgr._should_validate_tenant(tenant_name):
continue
self._expect_project(mgr, project_id)
def _validate_floatingips(self, mgr):
if not mgr._should_validate_neutron_resource("floatingip"):
return
query = BAKERY(lambda s: s.query(
l3_db.FloatingIP.project_id))
query += lambda q: q.distinct()
for project_id, in query(mgr.actual_session):
tenant_name = self.name_mapper.project(
mgr.expected_session, project_id)
if not mgr._should_validate_tenant(tenant_name):
continue
self._expect_project(mgr, project_id)
def _validate_port_bindings(self, mgr):

View File

@@ -151,9 +151,9 @@ class AIMMappingDriver(nrd.CommonNeutronBase, aim_rpc.AIMMappingRPCMixin):
def start_rpc_listeners(self):
return []
def validate_state(self, repair):
def validate_state(self, repair, resources, tenants):
mgr = aim_validation.ValidationManager()
return mgr.validate(repair)
return mgr.validate(repair, resources, tenants)
@property
def aim_mech_driver(self):

View File

@@ -16,6 +16,7 @@
from contextlib import contextmanager
import copy
from aim.aim_lib.db import model as aim_lib_model
from aim import aim_store
from aim.api import resource as aim_resource
from aim import context as aim_context
@@ -24,11 +25,14 @@ from neutron_lib.plugins import directory
from oslo_log import log
from gbpservice.neutron.db import api as db_api
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import db
from gbpservice.neutron.services.grouppolicy import (
group_policy_driver_api as api)
LOG = log.getLogger(__name__)
COMMON_TENANT_NAME = 'common'
class InternalValidationError(Exception):
pass
@@ -54,11 +58,65 @@ class ValidationManager(object):
if driver:
self.sfcd = driver.obj
def validate(self, repair=False):
def validate(self, repair=False, resources=None, tenants=None):
self.output("Validating deployment, repair: %s" % repair)
self.result = api.VALIDATION_PASSED
self.repair = repair
self.neutron_resources = resources if resources else []
self.tenants = tenants if tenants else []
self.resource_scope = True if self.neutron_resources else False
self.tenant_scope = True if self.tenants else False
self.neutron_to_aim_mapping = {
"router": [
aim_resource.Contract,
aim_resource.ContractSubject,
aim_resource.Tenant,
aim_resource.ApplicationProfile],
"security_group": [
aim_resource.SecurityGroup,
aim_resource.SecurityGroupRule,
aim_resource.SecurityGroupSubject,
aim_resource.Tenant,
aim_resource.ApplicationProfile],
"network": [
aim_resource.BridgeDomain,
aim_resource.ApplicationProfile,
aim_resource.EndpointGroup,
aim_resource.ExternalNetwork,
aim_resource.ExternalSubnet,
db.NetworkMapping,
aim_resource.L3Outside,
aim_resource.Subnet,
aim_resource.VRF,
aim_resource.Tenant,
aim_resource.ApplicationProfile,
aim_lib_model.CloneL3Out],
"port": [
aim_resource.InfraAccBundleGroup,
aim_resource.SpanVsourceGroup,
aim_resource.SpanVdestGroup,
aim_resource.SpanVsource,
aim_resource.SpanVdest,
aim_resource.SpanVepgSummary,
aim_resource.SpanSpanlbl,
aim_resource.Tenant,
aim_resource.ApplicationProfile],
"subnetpool": [
aim_resource.Tenant,
aim_resource.ApplicationProfile],
"floatingip": [
aim_resource.Tenant,
aim_resource.ApplicationProfile],
"address_scope": [
aim_resource.VRF,
db.AddressScopeMapping,
aim_resource.Tenant,
aim_resource.ApplicationProfile],
}
self.aim_resources = set()
for resource in self.neutron_resources:
self.aim_resources.update(self.neutron_to_aim_mapping[resource])
# REVISIT: Validate configuration.
@@ -147,7 +205,8 @@ class ValidationManager(object):
self._actual_aim_resources[resource_class] = {
tuple(resource.identity): resource
for resource in self.aim_mgr.find(
self.actual_aim_ctx, resource_class)}
self.actual_aim_ctx, resource_class)
if self._should_register_resource(resource)}
def expect_aim_resource(self, resource, replace=False, remove=False):
expected_resources = self._expected_aim_resources[resource.__class__]
@@ -189,6 +248,8 @@ class ValidationManager(object):
return list(self._actual_aim_resources[resource_class].values())
def register_db_instance_class(self, instance_class, primary_keys):
if self.aim_resources and instance_class not in self.aim_resources:
return
self._expected_db_instances.setdefault(instance_class, {})
self._db_instance_primary_keys[instance_class] = primary_keys
@@ -240,9 +301,14 @@ class ValidationManager(object):
for resource_class in self._expected_aim_resources.keys():
self._validate_aim_resource_class(resource_class)
def _should_validate_neutron_resource(self, resource):
if self.neutron_resources:
return True if resource in self.neutron_resources else False
else:
return True
def _validate_aim_resource_class(self, resource_class):
expected_resources = self._expected_aim_resources[resource_class]
for actual_resource in self.actual_aim_resources(resource_class):
key = tuple(actual_resource.identity)
expected_resource = expected_resources.pop(key, None)
@@ -252,11 +318,57 @@ class ValidationManager(object):
for expected_resource in expected_resources.values():
self._handle_missing_aim_resource(expected_resource)
def _should_validate_unexpected_resource(self, resource):
resource_class = resource.__class__
if self.tenant_scope:
if resource_class == aim_resource.Tenant:
if (resource.name != COMMON_TENANT_NAME and
resource.name in self.tenants):
return True
else:
return False
else:
if not hasattr(resource, 'tenant_name'):
return True
return True if resource.tenant_name in self.tenants else False
else:
if self.resource_scope:
return False
return True
def _should_validate_unexpected_db_instance(self, instance):
instance_class = instance.__class__
if self.tenant_scope:
instance_tenant = None
if instance_class == aim_lib_model.CloneL3Out:
instance_tenant = instance.tenant_name
elif instance_class == db.AddressScopeMapping:
instance_tenant = instance.vrf_tenant_name
elif instance_class == db.NetworkMapping:
if hasattr(instance, 'l3out_tenant_name'):
instance_tenant = instance.l3out_tenant_name
else:
instance_tenant = instance.epg_tenant_name
if instance_tenant:
return True if instance_tenant in self.tenants else False
else:
return True
return True
def _validate_actual_aim_resource(self, actual_resource,
expected_resource):
if not expected_resource:
# Some infra resources do not have the monitored
# attribute, but are treated as if they are monitored.
# During resource scoped run of validation routine
# it will report tenant and ap of some other resource
# as unexpected, although they may be expected for other
# resource. We can only know for sure from unscoped validationn
# or from tenant scoped validation of reported tenant/ap.
# Therefore ignore them during recource scoped run.
if (not self._should_validate_unexpected_resource(
expected_resource)):
return
if not getattr(actual_resource, 'monitored', True):
self._handle_unexpected_aim_resource(actual_resource)
else:
@@ -268,6 +380,29 @@ class ValidationManager(object):
self._handle_incorrect_aim_resource(
expected_resource, actual_resource)
def _should_validate_tenant(self, tenant):
if tenant == COMMON_TENANT_NAME:
return True
if self.tenants:
return True if tenant in self.tenants else False
return True
def _should_register_resource(self, resource):
resource_class = resource.__class__
if (resource_class == aim_resource.Tenant and
self.tenants and resource.name != COMMON_TENANT_NAME and
resource.name not in self.tenants):
return False
if (not hasattr(resource, 'tenant_name') or
resource.tenant_name == COMMON_TENANT_NAME):
return True
if (self.aim_resources and
resource_class not in self.aim_resources):
return False
if self.tenants:
return True if resource.tenant_name in self.tenants else False
return True
def _handle_unexpected_aim_resource(self, actual_resource):
if self.should_repair(
"unexpected %(type)s: %(actual)r" %
@@ -316,6 +451,9 @@ class ValidationManager(object):
key = tuple([getattr(actual_instance, k) for k in primary_keys])
expected_instance = expected_instances.pop(key, None)
if not expected_instance:
if (not self._should_validate_unexpected_db_instance(
actual_instance)):
return
self._handle_unexpected_db_instance(actual_instance)
else:
if not self._is_db_instance_correct(

View File

@@ -1400,7 +1400,7 @@ class PolicyDriver(object):
"""
pass
def validate_state(self, repair):
def validate_state(self, repair, resources, tenants):
"""Validate persistent state managed by the driver.
:param repair: Repair invalid state if True.

View File

@@ -71,8 +71,9 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin):
def start_rpc_listeners(self):
return self.policy_driver_manager.start_rpc_listeners()
def validate_state(self, repair):
return self.policy_driver_manager.validate_state(repair)
def validate_state(self, repair, resources, tenants):
return self.policy_driver_manager.validate_state(repair,
resources, tenants)
@property
def servicechain_plugin(self):

View File

@@ -495,10 +495,10 @@ class PolicyDriverManager(stevedore.named.NamedExtensionManager):
def start_rpc_listeners(self):
return self._call_on_drivers("start_rpc_listeners")
def validate_state(self, repair):
def validate_state(self, repair, resources, tenants):
result = api.VALIDATION_PASSED
for driver in self.ordered_policy_drivers:
this_result = driver.obj.validate_state(repair)
this_result = driver.obj.validate_state(repair, resources, tenants)
if this_result not in api.VALIDATION_RESULT_PRECEDENCE:
LOG.error("Policy driver %(name)s validate_state returned "
"unrecognized result: %(result)s",

View File

@@ -45,6 +45,11 @@ class AimValidationTestMixin(object):
# Validate should pass.
self.assertEqual(api.VALIDATION_PASSED, self.av_mgr.validate())
def _validate_scoped(self, resources=None, tenants=None):
# Validate should pass.
self.assertEqual(api.VALIDATION_PASSED,
self.av_mgr.validate(False, resources, tenants))
def _validate_repair_validate(self):
# Validate should fail.
self.assertEqual(
@@ -57,6 +62,27 @@ class AimValidationTestMixin(object):
# Validate should pass.
self.assertEqual(api.VALIDATION_PASSED, self.av_mgr.validate())
def _validate_repair_validate_scoped(self, resources, tenants):
# Validate should fail.
self.assertEqual(
api.VALIDATION_FAILED_REPAIRABLE,
self.av_mgr.validate(False, resources, tenants))
# Repair.
self.assertEqual(
api.VALIDATION_REPAIRED,
self.av_mgr.validate(True, resources, tenants))
# Validate should pass.
self.assertEqual(api.VALIDATION_PASSED,
self.av_mgr.validate(False, resources, tenants))
def _validate_repairable_scoped(self, resources, tenants):
# Validate should fail.
self.assertEqual(
api.VALIDATION_FAILED_REPAIRABLE,
self.av_mgr.validate(False, resources, tenants))
def _validate_unrepairable(self):
# Repair should fail.
self.assertEqual(
@@ -757,6 +783,7 @@ class TestNeutronMapping(AimValidationTestCase):
(self.db_session.query(aim_lib_model.CloneL3Out).
filter_by(tenant_name=tenant_name, name=l3out_name).
delete())
self._validate_repairable_scoped(["network"], None)
self._validate_repair_validate()
# Corrupt the CloneL3Out record and test.
@@ -1068,6 +1095,116 @@ class TestNeutronMapping(AimValidationTestCase):
self.aim_mgr.delete(self.aim_ctx, source_groups[0])
self._validate_repair_validate()
def test_network_scope(self):
kwargs = {'apic:extra_provided_contracts': ['ep1', 'ep2'],
'apic:extra_consumed_contracts': ['ec1', 'ec2'],
'apic:epg_contract_masters': [{'app_profile_name': 'ap1',
'name': 'ec3'},
{'app_profile_name': 'ap2',
'name': 'ec4'}]}
net_resp = self._make_network(
self.fmt, 'net1', True, arg_list=tuple(kwargs.keys()), **kwargs)
net = net_resp['network']
net_id = net['id']
self._validate()
self._validate_scoped(["router"], None)
self._validate_scoped(["security_group"], None)
# Test AIM resources.
bd_dn = net['apic:distinguished_names']['BridgeDomain']
epg_dn = net['apic:distinguished_names']['EndpointGroup']
# Delete the network's mapping record and test.
(self.db_session.query(db.NetworkMapping).
filter_by(network_id=net_id).
delete())
# delete BridgeDomain.
bd = aim_resource.BridgeDomain.from_dn(bd_dn)
self.aim_mgr.delete(self.aim_ctx, bd)
# delete EndpointGroup.
epg = aim_resource.EndpointGroup.from_dn(epg_dn)
self.aim_mgr.delete(self.aim_ctx, epg)
# self._validate_scoped(["router"], None)
self._validate_repair_validate_scoped(["network"], None)
def test_tenant_scope(self):
# setting scope to security group but
# should validate common tenant resources
tenant = aim_resource.Tenant(name='common')
self.aim_mgr.delete(self.aim_ctx, tenant)
self._validate_repair_validate_scoped(["security_group"], None)
net_resp1 = self._make_network(
self.fmt, 'net1', True, tenant_id='tenant_1')
net1 = net_resp1['network']
bd_dn1 = net1['apic:distinguished_names']['BridgeDomain']
epg_dn1 = net1['apic:distinguished_names']['EndpointGroup']
bd1 = aim_resource.BridgeDomain.from_dn(bd_dn1)
self.aim_mgr.delete(self.aim_ctx, bd1)
# delete EndpointGroup.
epg1 = aim_resource.EndpointGroup.from_dn(epg_dn1)
self.aim_mgr.delete(self.aim_ctx, epg1)
net_resp2 = self._make_network(
self.fmt, 'net2', True, tenant_id='tenant_2')
net2 = net_resp2['network']
bd_dn2 = net2['apic:distinguished_names']['BridgeDomain']
epg_dn2 = net2['apic:distinguished_names']['EndpointGroup']
bd2 = aim_resource.BridgeDomain.from_dn(bd_dn2)
self.aim_mgr.delete(self.aim_ctx, bd2)
# delete EndpointGroup.
epg2 = aim_resource.EndpointGroup.from_dn(epg_dn2)
self.aim_mgr.delete(self.aim_ctx, epg2)
self._validate_repair_validate_scoped(None, ['prj_tenant_1'])
self._validate_repair_validate_scoped(None, ['prj_tenant_2'])
def test_security_group_scope(self):
sg = self._make_security_group(
self.fmt, 'sg1', 'security group 1')['security_group']
rule1 = self._build_security_group_rule(
sg['id'], 'ingress', 'tcp', '22', '23')
rules = {'security_group_rules': [rule1['security_group_rule']]}
sg_rule = self._make_security_group_rule(
self.fmt, rules)['security_group_rules'][0]
# Test the AIM SecurityGroup.
tenant_name = self.driver.aim_mech_driver.name_mapper.project(
None, sg['project_id'])
sg_name = sg['id']
aim_sg = aim_resource.SecurityGroup(
name=sg_name, tenant_name=tenant_name)
self._test_aim_resource(aim_sg)
self.aim_mgr.delete(self.aim_ctx, aim_sg)
# Test the AIM SecurityGroupSubject.
aim_subject = aim_resource.SecurityGroupSubject(
name='default', security_group_name=sg_name,
tenant_name=tenant_name)
self._test_aim_resource(aim_subject)
self.aim_mgr.delete(self.aim_ctx, aim_subject)
# Test the AIM SecurityGroupRule.
aim_rule = aim_resource.SecurityGroupRule(
name=sg_rule['id'],
security_group_subject_name='default',
security_group_name=sg_name,
tenant_name=tenant_name)
self._test_aim_resource(aim_rule)
self.aim_mgr.delete(self.aim_ctx, aim_rule)
aim_tenant = aim_resource.Tenant(name=tenant_name)
self._test_aim_resource(aim_tenant)
self.aim_mgr.delete(self.aim_ctx, aim_tenant)
self._validate_repair_validate_scoped(None, [tenant_name])
class TestGbpMapping(AimValidationTestCase):

View File

@@ -28,7 +28,9 @@ from gbpservice.neutron.services.grouppolicy import (
# the CLI options must be registered before the GBP service plugin and
# the configured policy drivers can be loaded.
cli_opts = [
cfg.BoolOpt('repair', default=False, help='Enable repair of invalid state.')
cfg.BoolOpt('repair', default=False, help='Enable repair of invalid state.'),
cfg.ListOpt('resources', default=[], help='List of resources to be reconciled'),
cfg.ListOpt('tenants', default=[], help='List of tenants to be reconciled')
]
@@ -52,7 +54,7 @@ def main():
if not gbp_plugin:
sys.exit("GBP service plugin not configured.")
result = gbp_plugin.validate_state(cfg.CONF.repair)
result = gbp_plugin.validate_state(cfg.CONF.repair, cfg.CONF.resources, cfg.CONF.tenants)
if result in [api.VALIDATION_FAILED_REPAIRABLE,
api.VALIDATION_FAILED_UNREPAIRABLE,
api.VALIDATION_FAILED_WITH_EXCEPTION]: