2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.groupbasedpolicy.resolver;
10 import java.util.Collection;
11 import java.util.HashSet;
12 import java.util.List;
14 import java.util.concurrent.ConcurrentHashMap;
15 import java.util.concurrent.ConcurrentMap;
17 import javax.annotation.concurrent.Immutable;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
21 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
22 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
23 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
24 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.groupbasedpolicy.api.PolicyValidatorRegistry;
27 import org.opendaylight.groupbasedpolicy.api.ValidationResult;
28 import org.opendaylight.groupbasedpolicy.api.Validator;
29 import org.opendaylight.groupbasedpolicy.dto.EgKey;
30 import org.opendaylight.groupbasedpolicy.dto.IndexedTenant;
31 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
32 import org.opendaylight.groupbasedpolicy.util.DataTreeChangeHandler;
33 import org.opendaylight.groupbasedpolicy.util.IidFactory;
34 import org.opendaylight.groupbasedpolicy.util.InheritanceUtils;
35 import org.opendaylight.groupbasedpolicy.util.PolicyInfoUtils;
36 import org.opendaylight.groupbasedpolicy.util.PolicyResolverUtils;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionDefinitionId;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierDefinitionId;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Tenants;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRef;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Policy;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.SubjectFeatureInstances;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstance;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ClassifierInstance;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ClassifierInstanceBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.ResolvedPolicies;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.ResolvedPoliciesBuilder;
50 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
54 import com.google.common.annotations.VisibleForTesting;
55 import com.google.common.base.Optional;
56 import com.google.common.collect.HashMultimap;
57 import com.google.common.collect.HashMultiset;
58 import com.google.common.collect.Multimaps;
59 import com.google.common.collect.Multiset;
60 import com.google.common.collect.SetMultimap;
61 import com.google.common.collect.Table;
64 * The policy resolver is a utility for renderers to help in resolving
65 * group-based policy into a form that is easier to apply to the actual network.
66 * For any pair of endpoint groups, there is a set of rules that could apply to
67 * the endpoints on that group based on the policy configuration. The exact list
68 * of rules that apply to a given pair of endpoints depends on the conditions
69 * that are active on the endpoints.
70 * We need to be able to query against this policy model, enumerate the relevant
71 * classes of traffic and endpoints, and notify renderers when there are changes
72 * to policy as it applies to active sets of endpoints and endpoint groups.
73 * The policy resolver will maintain the necessary state for all tenants in its
74 * control domain, which is the set of tenants for which policy listeners have
77 public class PolicyResolver implements PolicyValidatorRegistry, AutoCloseable {
79 private static final Logger LOG = LoggerFactory.getLogger(PolicyResolver.class);
81 private final DataBroker dataProvider;
83 private final FollowedTenantListener followedTenantListener;
85 protected final ConcurrentMap<TenantId, IndexedTenant> resolvedTenants;
87 protected final Multiset<TenantId> subscribersPerTenant = HashMultiset.create();
89 private PolicyChangeListener tenantChangeListener;
92 * Store validators for ActionDefinitions from Renderers
95 private SetMultimap<ActionDefinitionId, Validator<ActionInstance>> actionInstanceValidatorsByDefinition =
96 Multimaps.synchronizedSetMultimap(HashMultimap.<ActionDefinitionId, Validator<ActionInstance>>create());
97 private SetMultimap<ClassifierDefinitionId, Validator<ClassifierInstance>> classifierInstanceValidatorsByDefinition =
99 .synchronizedSetMultimap(HashMultimap.<ClassifierDefinitionId, Validator<ClassifierInstance>>create());
101 public PolicyResolver(DataBroker dataProvider) {
102 this.dataProvider = dataProvider;
103 followedTenantListener = new FollowedTenantListener(dataProvider, this);
104 resolvedTenants = new ConcurrentHashMap<>();
105 tenantChangeListener =
106 new PolicyChangeListener(dataProvider, new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
107 InstanceIdentifier.builder(Tenants.class).child(Tenant.class).build()));
108 LOG.debug("Initialized renderer common policy resolver");
115 public void close() throws Exception {
116 if (tenantChangeListener != null) {
117 tenantChangeListener.close();
119 if (followedTenantListener != null) {
120 followedTenantListener.close();
124 // *************************
125 // PolicyResolutionValidatorRegistrar
126 // *************************
129 public void register(ActionDefinitionId actionDefinitionId, Validator<ActionInstance> validator) {
130 actionInstanceValidatorsByDefinition.put(actionDefinitionId, validator);
134 public void unregister(ActionDefinitionId actionDefinitionId, Validator<ActionInstance> validator) {
135 actionInstanceValidatorsByDefinition.remove(actionDefinitionId, validator);
139 public void register(ClassifierDefinitionId classifierDefinitionId, Validator<ClassifierInstance> validator) {
140 classifierInstanceValidatorsByDefinition.put(classifierDefinitionId, validator);
144 public void unregister(ClassifierDefinitionId classifierDefinitionId, Validator<ClassifierInstance> validator) {
145 classifierInstanceValidatorsByDefinition.remove(classifierDefinitionId, validator);
149 * Subscribe the resolver to updates related to a particular tenant.
151 * @param tenantId the tenant ID to subscribe to
153 protected void subscribeTenant(TenantId tenantId) {
154 synchronized (subscribersPerTenant) {
155 if (subscribersPerTenant.count(tenantId) == 0) {
156 ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction();
157 Optional<Tenant> potentialTenant = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
158 IidFactory.tenantIid(tenantId), rTx);
159 if (potentialTenant.isPresent()) {
160 updateTenant(tenantId, potentialTenant.get());
164 subscribersPerTenant.add(tenantId);
169 * Unsubscribe the resolver from updates related to a particular tenant.
171 * @param tenantId the tenant ID to unsubscribe from
173 protected void unsubscribeTenant(TenantId tenantId) {
174 synchronized (subscribersPerTenant) {
175 subscribersPerTenant.remove(tenantId);
176 if (subscribersPerTenant.count(tenantId) == 0) {
177 // nobody is interested in the tenant - can be removed from OPER and resolved policy
178 updateTenant(tenantId, null);
184 void updateTenant(final TenantId tenantId, final Tenant unresolvedTenant) {
185 if (dataProvider == null) {
189 if (unresolvedTenant == null) {
190 LOG.info("Tenant {} not found in CONF; check&delete from OPER", tenantId);
191 resolvedTenants.remove(tenantId);
192 ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
193 DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL, IidFactory.tenantIid(tenantId), rwTx);
194 updateResolvedPolicy(rwTx);
195 if (DataStoreHelper.submitToDs(rwTx)) {
196 LOG.debug("Removed resolved tenant {} and wrote resolved policies to Datastore.", tenantId.getValue());
198 LOG.error("Failed to remove resolved tenant {} and to write resolved policies to Datastore.",
199 tenantId.getValue());
202 LOG.debug("Resolving of tenant inheritance and policy triggered by a change in tenant {}", tenantId);
203 Tenant resolvedTenant = InheritanceUtils.resolveTenant(unresolvedTenant);
204 if (isPolicyValid(resolvedTenant.getPolicy())) {
205 // Update the policy cache and notify listeners
206 resolvedTenants.put(tenantId, new IndexedTenant(resolvedTenant));
207 WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
208 wTx.put(LogicalDatastoreType.OPERATIONAL, IidFactory.tenantIid(tenantId), resolvedTenant, true);
209 updateResolvedPolicy(wTx);
210 if (DataStoreHelper.submitToDs(wTx)) {
211 LOG.debug("Wrote resolved tenant {} and resolved policies to Datastore.", tenantId.getValue());
213 LOG.error("Failed to write resolved tenant {} and resolved policies to Datastore.",
214 tenantId.getValue());
220 private void updateResolvedPolicy(WriteTransaction wTx) {
221 if (dataProvider == null) {
222 LOG.error("Couldn't Write Resolved Tenants Policy Info to Datastore because dataProvider is NULL");
225 Set<IndexedTenant> indexedTenants = getIndexedTenants(resolvedTenants.values());
226 Table<EgKey, EgKey, org.opendaylight.groupbasedpolicy.dto.Policy> policyMap =
227 PolicyResolverUtils.resolvePolicy(indexedTenants);
228 ResolvedPolicies resolvedPolicies =
229 new ResolvedPoliciesBuilder().setResolvedPolicy(PolicyInfoUtils.buildResolvedPolicy(policyMap, resolvedTenants)).build();
231 wTx.put(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(ResolvedPolicies.class).build(),
232 resolvedPolicies, true);
235 private Set<IndexedTenant> getIndexedTenants(Collection<IndexedTenant> tenantCtxs) {
236 Set<IndexedTenant> result = new HashSet<>();
237 for (IndexedTenant tenant : tenantCtxs) {
238 if (tenant != null) {
245 private boolean isPolicyValid(Policy policy) {
246 if (policy != null && policy.getSubjectFeatureInstances() != null) {
247 SubjectFeatureInstances subjectFeatureInstances = policy.getSubjectFeatureInstances();
248 if (actionInstancesAreValid(subjectFeatureInstances.getActionInstance())
249 && classifierInstancesAreValid(subjectFeatureInstances.getClassifierInstance())) {
257 * Validation of action instances.
259 * @param actionInstances list of instances to validate
260 * @return true if instances are valid or if <code>actionInstances</code>
261 * is <code>null</code>, Otherwise returns false.
263 private boolean actionInstancesAreValid(List<ActionInstance> actionInstances) {
264 if (actionInstances == null) {
267 for (ActionInstance actionInstance : actionInstances) {
268 Set<Validator<ActionInstance>> actionInstanceValidators =
269 actionInstanceValidatorsByDefinition.get(actionInstance.getActionDefinitionId());
270 for (Validator<ActionInstance> actionInstanceValidator : actionInstanceValidators) {
271 ValidationResult validationResult = actionInstanceValidator.validate(actionInstance);
272 if (!validationResult.isValid()) {
273 LOG.error("ActionInstance {} is not valid! {}", actionInstance.getName().getValue(),
274 validationResult.getMessage());
283 * Validation of classifier instances.
285 * @param classifierInstances list of instances to validate
286 * @return true if instances are valid or if <code>classifierInstances</code>
287 * is <code>null</code>, Otherwise returns false.
289 private boolean classifierInstancesAreValid(List<ClassifierInstance> classifierInstances) {
290 if (classifierInstances == null) {
293 for (ClassifierInstance classifierInstance : classifierInstances) {
294 Set<Validator<ClassifierInstance>> classifierInstanceValidators =
295 classifierInstanceValidatorsByDefinition.get(classifierInstance.getClassifierDefinitionId());
296 for (Validator<ClassifierInstance> classifierInstanceValidator : classifierInstanceValidators) {
297 ValidationResult validationResult = classifierInstanceValidator.validate(classifierInstance);
298 if (!validationResult.isValid()) {
299 LOG.error("ClassifierInstance {} is not valid! {}", classifierInstance.getName().getValue(),
300 validationResult.getMessage());
309 private class PolicyChangeListener extends DataTreeChangeHandler<Tenant> {
311 protected PolicyChangeListener(DataBroker dataProvider, DataTreeIdentifier<Tenant> pointOfInterest) {
312 super(dataProvider, pointOfInterest);
316 protected void onWrite(DataObjectModification<Tenant> rootNode, InstanceIdentifier<Tenant> rootIdentifier) {
317 Tenant tenantAfter = rootNode.getDataAfter();
318 synchronized (subscribersPerTenant) {
319 if (subscribersPerTenant.contains(tenantAfter.getId())) {
320 updateTenant(tenantAfter.getId(), tenantAfter);
321 tenantAfter.getPolicy().getContract().get(0).getId();
322 tenantAfter.getPolicy().getContract().get(0).getSubject().get(0).getName();
323 tenantAfter.getPolicy().getContract().get(0).getSubject().get(0).getRule().get(0).getName();
324 List<ClassifierRef> cref = tenantAfter.getPolicy()
332 cref.get(0).getInstanceName();
333 tenantAfter.getPolicy().getSubjectFeatureInstances().getClassifierInstance().get(0).getName();
334 tenantAfter.getPolicy().getSubjectFeatureInstances().getClassifierInstance().get(0).getParameterValue()
341 protected void onDelete(DataObjectModification<Tenant> rootNode, InstanceIdentifier<Tenant> rootIdentifier) {
342 TenantId tenantId = rootIdentifier.firstKeyOf(Tenant.class).getId();
343 synchronized (subscribersPerTenant) {
344 if (subscribersPerTenant.contains(tenantId)) {
345 updateTenant(tenantId, null);
351 protected void onSubtreeModified(DataObjectModification<Tenant> rootNode,
352 InstanceIdentifier<Tenant> rootIdentifier) {
353 Tenant tenantAfter = rootNode.getDataAfter();
354 synchronized (subscribersPerTenant) {
355 if (subscribersPerTenant.contains(tenantAfter.getId())) {
356 updateTenant(tenantAfter.getId(), tenantAfter);