/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-
package org.opendaylight.groupbasedpolicy.resolver;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.concurrent.Immutable;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContractId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubjectName;
+import org.opendaylight.groupbasedpolicy.api.PolicyValidatorRegistry;
+import org.opendaylight.groupbasedpolicy.api.ValidationResult;
+import org.opendaylight.groupbasedpolicy.api.Validator;
+import org.opendaylight.groupbasedpolicy.dto.EgKey;
+import org.opendaylight.groupbasedpolicy.dto.IndexedTenant;
+import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
+import org.opendaylight.groupbasedpolicy.util.DataTreeChangeHandler;
+import org.opendaylight.groupbasedpolicy.util.IidFactory;
+import org.opendaylight.groupbasedpolicy.util.InheritanceUtils;
+import org.opendaylight.groupbasedpolicy.util.PolicyInfoUtils;
+import org.opendaylight.groupbasedpolicy.util.PolicyResolverUtils;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionDefinitionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierDefinitionId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.ConsumerSelectionRelator;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Matcher.MatchType;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.ProviderSelectionRelator;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRefBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.condition.matchers.ConditionMatcher;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.conditions.Condition;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.target.selector.QualityMatcher;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Tenants;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Contract;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Clause;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Subject;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Target;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.GroupIdentificationConstraints;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.group.identification.constraints.GroupRequirementConstraintCase;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.group.identification.constraints.group.requirement.constraint._case.RequirementMatcher;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.group.identification.constraints.GroupCapabilityConstraintCase;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.group.identification.constraints.group.capability.constraint._case.CapabilityMatcher;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.subject.Rule;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.subject.RuleBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ConsumerNamedSelector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ConsumerTargetSelector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderNamedSelector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderTargetSelector;
-import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Policy;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.SubjectFeatureInstances;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstance;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ClassifierInstance;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.ResolvedPolicies;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.ResolvedPoliciesBuilder;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.HashBasedTable;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Ordering;
-import com.google.common.collect.Sets;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.Multiset;
+import com.google.common.collect.SetMultimap;
import com.google.common.collect.Table;
-import com.google.common.collect.Table.Cell;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
/**
* The policy resolver is a utility for renderers to help in resolving
* group-based policy into a form that is easier to apply to the actual network.
- *
- * <p>For any pair of endpoint groups, there is a set of rules that could apply
- * to the endpoints on that group based on the policy configuration. The exact
- * list of rules that apply to a given pair of endpoints depends on the
- * conditions that are active on the endpoints.
- *
- * <p>We need to be able to query against this policy model, enumerate the
- * relevant classes of traffic and endpoints, and notify renderers when there
- * are changes to policy as it applies to active sets of endpoints and
- * endpoint groups.
- *
- * <p>The policy resolver will maintain the necessary state for all tenants
- * in its control domain, which is the set of tenants for which
- * policy listeners have been registered.
- *
- * @author readams
+ * For any pair of endpoint groups, there is a set of rules that could apply to
+ * the endpoints on that group based on the policy configuration. The exact list
+ * of rules that apply to a given pair of endpoints depends on the conditions
+ * that are active on the endpoints.
+ * We need to be able to query against this policy model, enumerate the relevant
+ * classes of traffic and endpoints, and notify renderers when there are changes
+ * to policy as it applies to active sets of endpoints and endpoint groups.
+ * The policy resolver will maintain the necessary state for all tenants in its
+ * control domain, which is the set of tenants for which policy listeners have
+ * been registered.
*/
-public class PolicyResolver implements AutoCloseable {
+public class PolicyResolver implements PolicyValidatorRegistry, AutoCloseable {
+
private static final Logger LOG = LoggerFactory.getLogger(PolicyResolver.class);
private final DataBroker dataProvider;
- private final ScheduledExecutorService executor;
- /**
- * Keep track of the current relevant policy scopes.
- */
- protected CopyOnWriteArrayList<PolicyScope> policyListenerScopes;
+ private final FollowedTenantListener followedTenantListener;
- protected ConcurrentMap<TenantId, TenantContext> resolvedTenants;
+ protected final ConcurrentMap<TenantId, IndexedTenant> resolvedTenants;
- /**
- * Store a policy object for each endpoint group pair. The table
- * is stored with the key as (consumer, provider). Two endpoints could
- * appear in both roles at the same time, in which case both policies would
- * apply.
+ protected final Multiset<TenantId> subscribersPerTenant = HashMultiset.create();
+
+ private PolicyChangeListener tenantChangeListener;
+
+ /*
+ * Store validators for ActionDefinitions from Renderers
+ *
*/
- AtomicReference<PolicyInfo> policy = new AtomicReference<>();
+ private SetMultimap<ActionDefinitionId, Validator<ActionInstance>> actionInstanceValidatorsByDefinition =
+ Multimaps.synchronizedSetMultimap(HashMultimap.<ActionDefinitionId, Validator<ActionInstance>>create());
+ private SetMultimap<ClassifierDefinitionId, Validator<ClassifierInstance>> classifierInstanceValidatorsByDefinition =
+ Multimaps
+ .synchronizedSetMultimap(HashMultimap.<ClassifierDefinitionId, Validator<ClassifierInstance>>create());
- public PolicyResolver(DataBroker dataProvider,
- ScheduledExecutorService executor) {
- super();
+ public PolicyResolver(DataBroker dataProvider) {
this.dataProvider = dataProvider;
- this.executor = executor;
- policyListenerScopes = new CopyOnWriteArrayList<>();
+ followedTenantListener = new FollowedTenantListener(dataProvider, this);
resolvedTenants = new ConcurrentHashMap<>();
+ tenantChangeListener =
+ new PolicyChangeListener(dataProvider, new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
+ InstanceIdentifier.builder(Tenants.class).child(Tenant.class).build()));
LOG.debug("Initialized renderer common policy resolver");
}
// *************
// AutoCloseable
// *************
-
@Override
public void close() throws Exception {
- for (TenantContext ctx : resolvedTenants.values()) {
- if (ctx.registration != null)
- ctx.registration.close();
+ if (tenantChangeListener != null) {
+ tenantChangeListener.close();
+ }
+ if (followedTenantListener != null) {
+ followedTenantListener.close();
}
}
// *************************
- // PolicyResolver public API
+ // PolicyResolutionValidatorRegistrar
// *************************
- /**
- * Get a snapshot of the current policy
- * @return the {@link PolicyInfo} object representing an immutable
- * snapshot of the policy state
- */
- public PolicyInfo getCurrentPolicy() {
- return policy.get();
- }
-
- /**
- * Get the normalized tenant for the given ID
- * @param tenant the tenant ID
- * @return the {@link Tenant}
- */
- public IndexedTenant getTenant(TenantId tenant) {
- TenantContext tc = resolvedTenants.get(tenant);
- if (tc == null) return null;
- return tc.tenant.get();
+ @Override
+ public void register(ActionDefinitionId actionDefinitionId, Validator<ActionInstance> validator) {
+ actionInstanceValidatorsByDefinition.put(actionDefinitionId, validator);
}
- /**
- * Register a listener to receive update events.
- * @param listener the {@link PolicyListener} object to receive the update
- * events
- */
- public PolicyScope registerListener(PolicyListener listener) {
- PolicyScope ps = new PolicyScope(this, listener);
- policyListenerScopes.add(ps);
-
- return ps;
+ @Override
+ public void unregister(ActionDefinitionId actionDefinitionId, Validator<ActionInstance> validator) {
+ actionInstanceValidatorsByDefinition.remove(actionDefinitionId, validator);
}
- /**
- * Remove the listener registered for the given {@link PolicyScope}.
- * @param scope the scope to remove
- * @see PolicyResolver#registerListener(PolicyListener)
- */
- public void removeListener(PolicyScope scope) {
- policyListenerScopes.remove(scope);
+ @Override
+ public void register(ClassifierDefinitionId classifierDefinitionId, Validator<ClassifierInstance> validator) {
+ classifierInstanceValidatorsByDefinition.put(classifierDefinitionId, validator);
}
- // **************
- // Implementation
- // **************
-
- /**
- * Atomically update the active policy and notify policy listeners
- * of relevant changes
- * @param policyMap the new policy to set
- * @param egConditions the map of endpoint groups to relevant condition sets
- * @return the set of groups with updated policy
- */
- protected Set<EgKey> updatePolicy(Table<EgKey, EgKey, Policy> policyMap,
- Map<EgKey, Set<ConditionSet>> egConditions,
- List<PolicyScope> policyListenerScopes) {
- PolicyInfo newPolicy = new PolicyInfo(policyMap, egConditions);
- PolicyInfo oldPolicy = policy.getAndSet(newPolicy);
-
- HashSet<EgKey> notifySet = new HashSet<>();
-
- for (Cell<EgKey, EgKey, Policy> cell : newPolicy.getPolicyMap().cellSet()) {
- Policy newp = cell.getValue();
- Policy oldp = null;
- if (oldPolicy != null)
- oldp = oldPolicy.getPolicyMap().get(cell.getRowKey(),
- cell.getColumnKey());
- if (oldp == null || !newp.equals(oldp)) {
- notifySet.add(cell.getRowKey());
- notifySet.add(cell.getColumnKey());
- }
- }
- if (oldPolicy != null) {
- for (Cell<EgKey, EgKey, Policy> cell : oldPolicy.getPolicyMap().cellSet()) {
- if (!newPolicy.getPolicyMap().contains(cell.getRowKey(),
- cell.getColumnKey())) {
- notifySet.add(cell.getRowKey());
- notifySet.add(cell.getColumnKey());
- }
- }
- }
- return notifySet;
- }
-
- /**
- * Notify the policy listeners about a set of updated groups
- */
- private void notifyListeners(Set<EgKey> updatedGroups) {
- for (final PolicyScope scope : policyListenerScopes) {
- Set<EgKey> filtered =
- Sets.filter(updatedGroups, new Predicate<EgKey>() {
- @Override
- public boolean apply(EgKey input) {
- return scope.contains(input.getTenantId(),
- input.getEgId());
- }
- });
- if (!filtered.isEmpty()) {
- scope.getListener().policyUpdated(filtered);
- }
- }
+ @Override
+ public void unregister(ClassifierDefinitionId classifierDefinitionId, Validator<ClassifierInstance> validator) {
+ classifierInstanceValidatorsByDefinition.remove(classifierDefinitionId, validator);
}
/**
- * Subscribe the resolver to updates related to a particular tenant
- * Make sure that this can't be called concurrently with subscribe
+ * Subscribe the resolver to updates related to a particular tenant.
+ *
* @param tenantId the tenant ID to subscribe to
*/
protected void subscribeTenant(TenantId tenantId) {
- if (!resolvedTenants.containsKey(tenantId))
- updateTenant(tenantId);
- }
-
- /**
- * Unsubscribe the resolver from updates related to a particular tenant
- * Make sure that this can't be called concurrently with subscribe
- * @param tenantId the tenant ID to subscribe to
- */
- protected void unsubscribeTenant(TenantId tenantId) {
- TenantContext context = resolvedTenants.get(tenantId);
- if (context != null) {
- resolvedTenants.remove(tenantId);
- context.registration.close();
- }
- }
-
- private void updateTenant(final TenantId tenantId) {
- if (dataProvider == null) return;
-
- TenantContext context = resolvedTenants.get(tenantId);
- if (context == null) {
- ListenerRegistration<DataChangeListener> registration = null;
- registration = dataProvider
- .registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
- TenantUtils.tenantIid(tenantId),
- new PolicyChangeListener(tenantId),
- DataChangeScope.SUBTREE);
-
- context = new TenantContext(registration);
- TenantContext oldContext =
- resolvedTenants.putIfAbsent(tenantId, context);
- if (oldContext != null) {
- // already registered in a different thread; just use the other
- // context
- registration.close();
- context = oldContext;
- } else {
- LOG.info("Added tenant {} to policy scope", tenantId);
- }
- }
-
- // Resolve the new tenant and update atomically
- final AtomicReference<IndexedTenant> tenantRef = context.tenant;
- final IndexedTenant ot = tenantRef.get();
- ReadOnlyTransaction transaction =
- dataProvider.newReadOnlyTransaction();
- final InstanceIdentifier<Tenant> tiid = TenantUtils.tenantIid(tenantId);
- ListenableFuture<Optional<Tenant>> unresolved;
-
- unresolved = transaction.read(LogicalDatastoreType.CONFIGURATION, tiid);
-
- Futures.addCallback(unresolved, new FutureCallback<Optional<Tenant>>() {
- @Override
- public void onSuccess(Optional<Tenant> result) {
- if (!result.isPresent()) {
- LOG.warn("Tenant {} not found", tenantId);
- }
-
- Tenant t = InheritanceUtils.resolveTenant((Tenant)result.get());
- IndexedTenant it = new IndexedTenant(t);
- if (!tenantRef.compareAndSet(ot, it)) {
- // concurrent update of tenant policy. Retry
- updateTenant(tenantId);
- } else {
- // Update the policy cache and notify listeners
- WriteTransaction wt = dataProvider.newWriteOnlyTransaction();
- wt.put(LogicalDatastoreType.OPERATIONAL, tiid, t, true);
- wt.submit();
- updatePolicy();
+ synchronized (subscribersPerTenant) {
+ if (subscribersPerTenant.count(tenantId) == 0) {
+ ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction();
+ Optional<Tenant> potentialTenant = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
+ IidFactory.tenantIid(tenantId), rTx);
+ if (potentialTenant.isPresent()) {
+ updateTenant(tenantId, potentialTenant.get());
}
+ rTx.close();
}
-
- @Override
- public void onFailure(Throwable t) {
- LOG.error("Count not get tenant {}", tenantId, t);
- }
- }, executor);
- }
-
- protected void updatePolicy() {
- try {
- Map<EgKey, Set<ConditionSet>> egConditions = new HashMap<>();
- Table<EgKey, EgKey, Policy> policyMap =
- resolvePolicy(resolvedTenants.values(),
- egConditions);
- Set<EgKey> updatedGroups =
- updatePolicy(policyMap,
- egConditions,
- policyListenerScopes);
-
- notifyListeners(updatedGroups);
- } catch (Exception e) {
- LOG.error("Failed to update policy", e);
+ subscribersPerTenant.add(tenantId);
}
}
-
- /**
- * Resolve the policy in three phases:
- * (1) select contracts that in scope based on contract selectors.
- * (2) select subjects that are in scope for each contract based on
- * matchers in clauses
- * (3) resolve the set of in-scope contracts into a list of subjects that
- * apply for each pair of endpoint groups and the conditions that can
- * apply for for each endpoint in those groups.
- */
- protected Table<EgKey, EgKey, Policy>
- resolvePolicy(Collection<TenantContext> tenants,
- Map<EgKey, Set<ConditionSet>> egConditions) {
- // select contracts that apply for the given tenant
- Table<EgKey, EgKey, List<ContractMatch>> contractMatches =
- selectContracts(tenants);
-
- // select subjects for the matching contracts and resolve the policy
- // for endpoint group pairs. This does phase (2) and (3) as one step
- return selectSubjects(contractMatches, egConditions);
- }
/**
- * Choose the contracts that are in scope for each pair of endpoint
- * groups, then perform subject selection for the pair
+ * Unsubscribe the resolver from updates related to a particular tenant.
+ *
+ * @param tenantId the tenant ID to unsubscribe from
*/
- protected Table<EgKey, EgKey, List<ContractMatch>>
- selectContracts(Collection<TenantContext> tenants) {
- Table<TenantId, ContractId,
- List<ConsumerContractMatch>> consumerMatches =
- HashBasedTable.create();
- Table<EgKey, EgKey, List<ContractMatch>> contractMatches =
- HashBasedTable.create();
-
- for (TenantContext tenant : tenants) {
- IndexedTenant t = tenant.tenant.get();
- if (t == null) continue;
- selectContracts(consumerMatches,
- contractMatches,
- t.getTenant());
- }
- return contractMatches;
- }
- protected void selectContracts(Table<TenantId,
- ContractId,
- List<ConsumerContractMatch>> consumerMatches,
- Table<EgKey, EgKey,
- List<ContractMatch>> contractMatches,
- Tenant tenant) {
- // For each endpoint group, match consumer selectors
- // against contracts to get a set of matching consumer selectors
- if (tenant.getEndpointGroup() == null) return;
- for (EndpointGroup group : tenant.getEndpointGroup()) {
- List<ConsumerContractMatch> r =
- matchConsumerContracts(tenant, group);
- for (ConsumerContractMatch ccm : r) {
- List<ConsumerContractMatch> cms =
- consumerMatches.get(tenant.getId(),
- ccm.contract.getId());
- if (cms == null) {
- cms = new ArrayList<>();
- consumerMatches.put(tenant.getId(),
- ccm.contract.getId(), cms);
- }
- cms.add(ccm);
- }
- }
-
- // Match provider selectors, and check each match for a corresponding
- // consumer selector match.
- for (EndpointGroup group : tenant.getEndpointGroup()) {
- List<ContractMatch> matches =
- matchProviderContracts(tenant, group, consumerMatches);
- for (ContractMatch cm : matches) {
- EgKey consumerKey = new EgKey(cm.consumerTenant.getId(),
- cm.consumer.getId());
- EgKey providerKey = new EgKey(cm.providerTenant.getId(),
- cm.provider.getId());
- List<ContractMatch> egPairMatches =
- contractMatches.get(consumerKey, providerKey);
- if (egPairMatches == null) {
- egPairMatches = new ArrayList<>();
- contractMatches.put(consumerKey, providerKey,
- egPairMatches);
- }
-
- egPairMatches.add(cm);
+ protected void unsubscribeTenant(TenantId tenantId) {
+ synchronized (subscribersPerTenant) {
+ subscribersPerTenant.remove(tenantId);
+ if (subscribersPerTenant.count(tenantId) == 0) {
+ // nobody is interested in the tenant - can be removed from OPER and resolved policy
+ updateTenant(tenantId, null);
}
}
}
- private boolean clauseMatches(Clause clause, ContractMatch match) {
- if (clause.getConsumerMatchers() != null) {
- GroupIdentificationConstraints groupIdentificationConstraintsConsumer = clause.getConsumerMatchers()
- .getGroupIdentificationConstraints();
- if (groupIdentificationConstraintsConsumer instanceof GroupRequirementConstraintCase) {
- List<RequirementMatcher> reqMatchers = ((GroupRequirementConstraintCase) groupIdentificationConstraintsConsumer)
- .getRequirementMatcher();
- if (reqMatchers != null) {
- for (RequirementMatcher reqMatcher : reqMatchers) {
- if (!MatcherUtils.applyReqMatcher(reqMatcher,
- match.consumerRelator)) {
- return false;
- }
- }
- }
- }
+ @VisibleForTesting
+ void updateTenant(final TenantId tenantId, final Tenant unresolvedTenant) {
+ if (dataProvider == null) {
+ return;
}
- if (clause.getProviderMatchers() != null) {
- org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.GroupIdentificationConstraints groupIdentificationConstraintsProvider = clause
- .getProviderMatchers().getGroupIdentificationConstraints();
- if (groupIdentificationConstraintsProvider instanceof GroupCapabilityConstraintCase) {
- List<CapabilityMatcher> capMatchers = ((GroupCapabilityConstraintCase) groupIdentificationConstraintsProvider)
- .getCapabilityMatcher();
-
- if (capMatchers != null) {
- for (CapabilityMatcher capMatcher : capMatchers) {
- if (!MatcherUtils.applyCapMatcher(capMatcher,
- match.providerRelator)) {
- return false;
- }
- }
- }
- }
- }
- return true;
- }
- private ConditionSet buildConditionSet(List<ConditionMatcher> condMatchers) {
- if (condMatchers == null) return ConditionSet.EMPTY;
-
- ImmutableSet.Builder<ConditionName> allb = ImmutableSet.builder();
- ImmutableSet.Builder<ConditionName> noneb = ImmutableSet.builder();
- ImmutableSet.Builder<Set<ConditionName>> anyb =
- ImmutableSet.builder();
- for (ConditionMatcher condMatcher : condMatchers) {
- if (condMatcher.getCondition() == null)
- continue;
- MatchType type = condMatcher.getMatchType();
- if (type == null) type = MatchType.All;
- if (type.equals(MatchType.Any)) {
- ImmutableSet.Builder<ConditionName> a =
- ImmutableSet.builder();
- for (Condition c : condMatcher.getCondition()) {
- a.add(c.getName());
- }
- anyb.add(a.build());
+ if (unresolvedTenant == null) {
+ LOG.info("Tenant {} not found in CONF; check&delete from OPER", tenantId);
+ resolvedTenants.remove(tenantId);
+ ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
+ DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL, IidFactory.tenantIid(tenantId), rwTx);
+ updateResolvedPolicy(rwTx);
+ if (DataStoreHelper.submitToDs(rwTx)) {
+ LOG.debug("Removed resolved tenant {} and wrote resolved policies to Datastore.", tenantId.getValue());
} else {
- for (Condition c : condMatcher.getCondition()) {
- switch (type) {
- case Any:
- break;
- case None:
- noneb.add(c.getName());
- break;
- case All:
- default:
- allb.add(c.getName());
- break;
- }
+ LOG.error("Failed to remove resolved tenant {} and to write resolved policies to Datastore.",
+ tenantId.getValue());
+ }
+ } else {
+ LOG.debug("Resolving of tenant inheritance and policy triggered by a change in tenant {}", tenantId);
+ Tenant resolvedTenant = InheritanceUtils.resolveTenant(unresolvedTenant);
+ if (isPolicyValid(resolvedTenant.getPolicy())) {
+ // Update the policy cache and notify listeners
+ resolvedTenants.put(tenantId, new IndexedTenant(resolvedTenant));
+ WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
+ wTx.put(LogicalDatastoreType.OPERATIONAL, IidFactory.tenantIid(tenantId), resolvedTenant, true);
+ updateResolvedPolicy(wTx);
+ if (DataStoreHelper.submitToDs(wTx)) {
+ LOG.debug("Wrote resolved tenant {} and resolved policies to Datastore.", tenantId.getValue());
+ } else {
+ LOG.error("Failed to write resolved tenant {} and resolved policies to Datastore.",
+ tenantId.getValue());
}
}
}
- return new ConditionSet(allb.build(), noneb.build(), anyb.build());
}
- private ConditionSet buildConsConditionSet(Clause clause) {
- if (clause.getConsumerMatchers() != null) {
- List<ConditionMatcher> condMatchers =
- clause.getConsumerMatchers().getConditionMatcher();
- return buildConditionSet(condMatchers);
+ private void updateResolvedPolicy(WriteTransaction wTx) {
+ if (dataProvider == null) {
+ LOG.error("Couldn't Write Resolved Tenants Policy Info to Datastore because dataProvider is NULL");
+ return;
}
- return ConditionSet.EMPTY;
+ Set<IndexedTenant> indexedTenants = getIndexedTenants(resolvedTenants.values());
+ Table<EgKey, EgKey, org.opendaylight.groupbasedpolicy.dto.Policy> policyMap =
+ PolicyResolverUtils.resolvePolicy(indexedTenants);
+ ResolvedPolicies resolvedPolicies =
+ new ResolvedPoliciesBuilder().setResolvedPolicy(PolicyInfoUtils.buildResolvedPolicy(policyMap, resolvedTenants)).build();
+
+ wTx.put(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(ResolvedPolicies.class).build(),
+ resolvedPolicies, true);
}
- private ConditionSet buildProvConditionSet(Clause clause) {
- if (clause.getProviderMatchers() != null) {
- List<ConditionMatcher> condMatchers =
- clause.getProviderMatchers().getConditionMatcher();
- return buildConditionSet(condMatchers);
- }
- return ConditionSet.EMPTY;
- }
-
- private Policy resolvePolicy(Tenant contractTenant,
- Contract contract,
- boolean reverse,
- Policy merge,
- Table<ConditionSet, ConditionSet,
- List<Subject>> subjectMap) {
- Table<ConditionSet, ConditionSet, List<RuleGroup>> ruleMap =
- HashBasedTable.create();
- if (merge != null) {
- ruleMap.putAll(merge.ruleMap);
- }
- for (Cell<ConditionSet, ConditionSet, List<Subject>> entry :
- subjectMap.cellSet()) {
- List<RuleGroup> rules = new ArrayList<>();
- ConditionSet rowKey = entry.getRowKey();
- ConditionSet columnKey = entry.getColumnKey();
- if (reverse) {
- rowKey = columnKey;
- columnKey = entry.getRowKey();
- }
- List<RuleGroup> oldrules =
- ruleMap.get(rowKey, columnKey);
- if (oldrules != null) {
- rules.addAll(oldrules);
+ private Set<IndexedTenant> getIndexedTenants(Collection<IndexedTenant> tenantCtxs) {
+ Set<IndexedTenant> result = new HashSet<>();
+ for (IndexedTenant tenant : tenantCtxs) {
+ if (tenant != null) {
+ result.add(tenant);
}
- for (Subject s : entry.getValue()) {
- if (s.getRule() == null) continue;
- List<Rule> srules;
- if (reverse)
- srules = reverseRules(s.getRule());
- else
- srules = Ordering
- .from(TenantUtils.RULE_COMPARATOR)
- .immutableSortedCopy(s.getRule());
-
- RuleGroup rg = new RuleGroup(srules, s.getOrder(),
- contractTenant, contract,
- s.getName());
- rules.add(rg);
- }
- Collections.sort(rules);
- ruleMap.put(rowKey, columnKey,
- Collections.unmodifiableList(rules));
}
- return new Policy(ruleMap);
+ return result;
}
-
- private List<Rule> reverseRules(List<Rule> rules) {
- ArrayList<Rule> nrules = new ArrayList<>();
- for (Rule input : rules) {
- if (input.getClassifierRef() == null ||
- input.getClassifierRef().size() == 0) {
- nrules.add(input);
- continue;
- }
- List<ClassifierRef> classifiers = new ArrayList<>();
- for (ClassifierRef clr : input.getClassifierRef()) {
- Direction nd = Direction.Bidirectional;
- if (clr.getDirection() != null) {
- switch (clr.getDirection()) {
- case In:
- nd = Direction.Out;
- break;
- case Out:
- nd = Direction.In;
- break;
- case Bidirectional:
- default:
- nd = Direction.Bidirectional;
- }
- }
- classifiers.add(new ClassifierRefBuilder(clr)
- .setDirection(nd).build());
+ private boolean isPolicyValid(Policy policy) {
+ if (policy != null && policy.getSubjectFeatureInstances() != null) {
+ SubjectFeatureInstances subjectFeatureInstances = policy.getSubjectFeatureInstances();
+ if (actionInstancesAreValid(subjectFeatureInstances.getActionInstance())
+ && classifierInstancesAreValid(subjectFeatureInstances.getClassifierInstance())) {
+ return true;
}
- nrules.add(new RuleBuilder(input)
- .setClassifierRef(Collections.unmodifiableList(classifiers))
- .build());
}
- Collections.sort(nrules, TenantUtils.RULE_COMPARATOR);
- return Collections.unmodifiableList(nrules);
+ return false;
}
/**
- * Get the "natural" direction for the policy for the given pair of
- * endpoint groups.
- * @param one The first endpoint group
- * @param two The second endpoint group
- * @return true if the order should be reversed in the index
+ * Validation of action instances.
+ *
+ * @param actionInstances list of instances to validate
+ * @return true if instances are valid or if <code>actionInstances</code>
+ * is <code>null</code>, Otherwise returns false.
*/
- protected static boolean shouldReverse(EgKey one, EgKey two) {
- if (one.compareTo(two) < 0) {
+ private boolean actionInstancesAreValid(List<ActionInstance> actionInstances) {
+ if (actionInstances == null) {
return true;
}
- return false;
- }
-
- private void addConditionSet(EgKey eg, ConditionSet cs,
- Map<EgKey, Set<ConditionSet>> egConditions) {
- if (egConditions == null) return;
- Set<ConditionSet> cset = egConditions.get(eg);
- if (cset == null) {
- egConditions.put(eg, cset = new HashSet<>());
- }
- cset.add(cs);
- }
-
- /**
- * Choose the set of subjects that in scope for each possible set of
- * endpoint conditions
- */
- protected Table<EgKey, EgKey, Policy>
- selectSubjects(Table<EgKey, EgKey,
- List<ContractMatch>> contractMatches,
- Map<EgKey, Set<ConditionSet>> egConditions) {
- // Note that it's possible to further simplify the resulting policy
- // in the case of things like repeated rules, condition sets that
- // cover other condition sets, etc. This would be a good thing to do
- // at some point
- Table<EgKey, EgKey, Policy> policy = HashBasedTable.create();
-
- for (List<ContractMatch> matches : contractMatches.values()) {
- for (ContractMatch match : matches) {
- List<Clause> clauses = match.contract.getClause();
- if (clauses == null) continue;
-
- List<Subject> subjectList = match.contract.getSubject();
- if (subjectList == null) continue;
-
- EgKey ckey = new EgKey(match.consumerTenant.getId(),
- match.consumer.getId());
- EgKey pkey = new EgKey(match.providerTenant.getId(),
- match.provider.getId());
- EgKey one = ckey;
- EgKey two = pkey;
- boolean reverse = shouldReverse(ckey, pkey);
- if (reverse) {
- one = pkey;
- two = ckey;
+ for (ActionInstance actionInstance : actionInstances) {
+ Set<Validator<ActionInstance>> actionInstanceValidators =
+ actionInstanceValidatorsByDefinition.get(actionInstance.getActionDefinitionId());
+ for (Validator<ActionInstance> actionInstanceValidator : actionInstanceValidators) {
+ ValidationResult validationResult = actionInstanceValidator.validate(actionInstance);
+ if (!validationResult.isValid()) {
+ LOG.error("ActionInstance {} is not valid! {}", actionInstance.getName().getValue(),
+ validationResult.getMessage());
+ return false;
}
- Policy existing = policy.get(one, two);
-
- HashMap<SubjectName, Subject> subjects = new HashMap<>();
- for (Subject s : subjectList) {
- subjects.put(s.getName(), s);
- }
-
- Table<ConditionSet, ConditionSet, List<Subject>> subjectMap =
- HashBasedTable.create();
-
- for (Clause clause : clauses) {
- if (clause.getSubjectRefs() != null &&
- clauseMatches(clause, match)) {
- ConditionSet consCSet = buildConsConditionSet(clause);
- addConditionSet(ckey, consCSet, egConditions);
- ConditionSet provCSet = buildProvConditionSet(clause);
- addConditionSet(pkey, provCSet, egConditions);
- List<Subject> clauseSubjects =
- subjectMap.get(consCSet, provCSet);
- if (clauseSubjects == null) {
- clauseSubjects = new ArrayList<>();
- subjectMap.put(consCSet, provCSet, clauseSubjects);
- }
- for (SubjectName sn : clause.getSubjectRefs()) {
- Subject s = subjects.get(sn);
- if (s != null) clauseSubjects.add(s);
- }
- }
- }
-
- policy.put(one, two,
- resolvePolicy(match.contractTenant,
- match.contract,
- reverse,
- existing,
- subjectMap));
}
}
-
- return policy;
+ return true;
}
- private List<ConsumerContractMatch> matchConsumerContracts(Tenant tenant,
- EndpointGroup consumer) {
- List<ConsumerContractMatch> matches = new ArrayList<>();
- if (consumer.getConsumerNamedSelector() != null) {
- for (ConsumerNamedSelector cns : consumer.getConsumerNamedSelector()) {
- if (cns.getContract() == null) continue;
- for (ContractId contractId : cns.getContract()) {
- Contract contract =
- TenantUtils.findContract(tenant, contractId);
- if (contract == null) continue;
- matches.add(new ConsumerContractMatch(tenant, contract,
- tenant, consumer,
- cns));
- }
- }
+ /**
+ * Validation of classifier instances.
+ *
+ * @param classifierInstances list of instances to validate
+ * @return true if instances are valid or if <code>classifierInstances</code>
+ * is <code>null</code>, Otherwise returns false.
+ */
+ private boolean classifierInstancesAreValid(List<ClassifierInstance> classifierInstances) {
+ if (classifierInstances == null) {
+ return true;
}
- if (consumer.getConsumerTargetSelector() != null) {
- for (ConsumerTargetSelector cts : consumer.getConsumerTargetSelector()) {
- if (tenant.getContract() == null) continue;
- for (Contract contract : tenant.getContract()) {
- if (contract.getTarget() == null) continue;
- for (Target t : contract.getTarget()) {
- boolean match = true;
- if (cts.getQualityMatcher() != null) {
- for (QualityMatcher m : cts.getQualityMatcher()) {
- if (!MatcherUtils.applyQualityMatcher(m, t)) {
- match = false;
- break;
- }
- }
- }
- if (match) {
- matches.add(new ConsumerContractMatch(tenant,
- contract,
- tenant,
- consumer,
- cts));
- }
- }
+ for (ClassifierInstance classifierInstance : classifierInstances) {
+ Set<Validator<ClassifierInstance>> classifierInstanceValidators =
+ classifierInstanceValidatorsByDefinition.get(classifierInstance.getClassifierDefinitionId());
+ for (Validator<ClassifierInstance> classifierInstanceValidator : classifierInstanceValidators) {
+ ValidationResult validationResult = classifierInstanceValidator.validate(classifierInstance);
+ if (!validationResult.isValid()) {
+ LOG.error("ClassifierInstance {} is not valid! {}", classifierInstance.getName().getValue(),
+ validationResult.getMessage());
+ return false;
}
}
}
- // TODO match selectors also against contract references
-// for (ConsumerTargetSelector cts : consumer.getConsumerTargetSelector()) {
-// if (tenant.getContractRef() == null) continue;
-// for (ContractRef c : tenant.getContractRef()) {
-//
-// }
-// }
- return matches;
+ return true;
}
- private void amendContractMatches(List<ContractMatch> matches,
- List<ConsumerContractMatch> cMatches,
- Tenant tenant, EndpointGroup provider,
- ProviderSelectionRelator relator) {
- if (cMatches == null) return;
- for (ConsumerContractMatch cMatch : cMatches) {
- matches.add(new ContractMatch(cMatch, tenant, provider, relator));
+ @Immutable
+ private class PolicyChangeListener extends DataTreeChangeHandler<Tenant> {
+
+ protected PolicyChangeListener(DataBroker dataProvider, DataTreeIdentifier<Tenant> pointOfInterest) {
+ super(dataProvider, pointOfInterest);
}
- }
- private List<ContractMatch>
- matchProviderContracts(Tenant tenant, EndpointGroup provider,
- Table<TenantId,
- ContractId,
- List<ConsumerContractMatch>> consumerMatches) {
- List<ContractMatch> matches = new ArrayList<>();
- if (provider.getProviderNamedSelector() != null) {
- for (ProviderNamedSelector pns : provider.getProviderNamedSelector()) {
- if (pns.getContract() == null) continue;
- for (ContractId contractId : pns.getContract()) {
- Contract c = TenantUtils.findContract(tenant, contractId);
- if (c == null) continue;
- List<ConsumerContractMatch> cMatches =
- consumerMatches.get(tenant.getId(), c.getId());
- amendContractMatches(matches, cMatches, tenant, provider, pns);
+ @Override
+ protected void onWrite(DataObjectModification<Tenant> rootNode, InstanceIdentifier<Tenant> rootIdentifier) {
+ Tenant tenantAfter = rootNode.getDataAfter();
+ synchronized (subscribersPerTenant) {
+ if (subscribersPerTenant.contains(tenantAfter.getId())) {
+ updateTenant(tenantAfter.getId(), tenantAfter);
}
}
}
- if (provider.getProviderTargetSelector() != null) {
- for (ProviderTargetSelector pts : provider.getProviderTargetSelector()) {
- if (tenant.getContract() == null) continue;
- for (Contract c : tenant.getContract()) {
- if (c.getTarget() == null) continue;
- for (Target t : c.getTarget()) {
- boolean match = true;
- if (pts.getQualityMatcher() != null) {
- for (QualityMatcher m : pts.getQualityMatcher()) {
- if (!MatcherUtils.applyQualityMatcher(m, t)) {
- match = false;
- break;
- }
- }
- }
- if (match) {
- List<ConsumerContractMatch> cMatches =
- consumerMatches.get(tenant.getId(),
- c.getId());
- amendContractMatches(matches, cMatches, tenant,
- provider, pts);
-
- }
- }
+
+ @Override
+ protected void onDelete(DataObjectModification<Tenant> rootNode, InstanceIdentifier<Tenant> rootIdentifier) {
+ TenantId tenantId = rootIdentifier.firstKeyOf(Tenant.class).getId();
+ synchronized (subscribersPerTenant) {
+ if (subscribersPerTenant.contains(tenantId)) {
+ updateTenant(tenantId, null);
}
}
}
- return matches;
- }
-
- protected static class TenantContext {
- ListenerRegistration<DataChangeListener> registration;
-
- AtomicReference<IndexedTenant> tenant = new AtomicReference<>();
-
- public TenantContext(ListenerRegistration<DataChangeListener> registration) {
- super();
- this.registration = registration;
- }
- }
-
- /**
- * Represents a selected contract made by endpoint groups matching it
- * using selection relators. This is the result of the contract selection
- * phase.
- * @author readams
- *
- */
- @Immutable
- protected static class ContractMatch extends ConsumerContractMatch {
- /**
- * The tenant ID of the provider endpoint group
- */
- final Tenant providerTenant;
-
- /**
- * The provider endpoint group
- */
- final EndpointGroup provider;
-
- /**
- * The provider selection relator that was used to match the contract
- */
- final ProviderSelectionRelator providerRelator;
-
- public ContractMatch(ConsumerContractMatch consumerMatch,
- Tenant providerTenant, EndpointGroup provider,
- ProviderSelectionRelator providerRelator) {
- super(consumerMatch.contractTenant,
- consumerMatch.contract,
- consumerMatch.consumerTenant,
- consumerMatch.consumer,
- consumerMatch.consumerRelator);
- this.providerTenant = providerTenant;
- this.provider = provider;
- this.providerRelator = providerRelator;
- }
- }
-
- @Immutable
- private static class ConsumerContractMatch {
- /**
- * The tenant of the matching contract
- */
- final Tenant contractTenant;
-
- /**
- * The matching contract
- */
- final Contract contract;
-
- /**
- * The tenant for the endpoint group
- */
- final Tenant consumerTenant;
-
- /**
- * The consumer endpoint group
- */
- final EndpointGroup consumer;
-
- /**
- * The consumer selection relator that was used to match the contract
- */
- final ConsumerSelectionRelator consumerRelator;
-
-
- public ConsumerContractMatch(Tenant contractTenant,
- Contract contract,
- Tenant consumerTenant,
- EndpointGroup consumer,
- ConsumerSelectionRelator consumerRelator) {
- super();
- this.contractTenant = contractTenant;
- this.contract = contract;
- this.consumerTenant = consumerTenant;
- this.consumer = consumer;
- this.consumerRelator = consumerRelator;
- }
- }
-
- @Immutable
- private class PolicyChangeListener implements DataChangeListener {
- final TenantId tenantId;
-
- public PolicyChangeListener(TenantId tenantId) {
- super();
- this.tenantId = tenantId;
- }
@Override
- public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> arg0) {
- updateTenant(tenantId);
+ protected void onSubtreeModified(DataObjectModification<Tenant> rootNode,
+ InstanceIdentifier<Tenant> rootIdentifier) {
+ Tenant tenantAfter = rootNode.getDataAfter();
+ synchronized (subscribersPerTenant) {
+ if (subscribersPerTenant.contains(tenantAfter.getId())) {
+ updateTenant(tenantAfter.getId(), tenantAfter);
+ }
+ }
}
}
+
}