gbp-validate: Tenant and resource level scoping.
Change-Id: Ia25071474c02954516da09b9c3f935b36fcac82b
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -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]:
|
||||
|
||||
Reference in New Issue
Block a user