Add INFO.yaml for GBP
[groupbasedpolicy.git] / groupbasedpolicy / src / main / java / org / opendaylight / groupbasedpolicy / resolver / PolicyResolver.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
3  *
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
7  */
8 package org.opendaylight.groupbasedpolicy.resolver;
9
10 import java.util.Collection;
11 import java.util.HashSet;
12 import java.util.List;
13 import java.util.Set;
14 import java.util.concurrent.ConcurrentHashMap;
15 import java.util.concurrent.ConcurrentMap;
16
17 import javax.annotation.concurrent.Immutable;
18
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.ReadWriteTransaction;
23 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
24 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
25 import org.opendaylight.groupbasedpolicy.api.PolicyValidatorRegistry;
26 import org.opendaylight.groupbasedpolicy.api.ValidationResult;
27 import org.opendaylight.groupbasedpolicy.api.Validator;
28 import org.opendaylight.groupbasedpolicy.dto.EgKey;
29 import org.opendaylight.groupbasedpolicy.dto.IndexedTenant;
30 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
31 import org.opendaylight.groupbasedpolicy.util.DataTreeChangeHandler;
32 import org.opendaylight.groupbasedpolicy.util.IidFactory;
33 import org.opendaylight.groupbasedpolicy.util.InheritanceUtils;
34 import org.opendaylight.groupbasedpolicy.util.PolicyInfoUtils;
35 import org.opendaylight.groupbasedpolicy.util.PolicyResolverUtils;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionDefinitionId;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierDefinitionId;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Tenants;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Policy;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.SubjectFeatureInstances;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstance;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ClassifierInstance;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.ResolvedPolicies;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.ResolvedPoliciesBuilder;
47 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 import com.google.common.annotations.VisibleForTesting;
52 import com.google.common.collect.HashMultimap;
53 import com.google.common.collect.Multimaps;
54 import com.google.common.collect.SetMultimap;
55 import com.google.common.collect.Table;
56
57 /**
58  * The policy resolver is a utility for renderers to help in resolving
59  * group-based policy into a form that is easier to apply to the actual network.
60  * For any pair of endpoint groups, there is a set of rules that could apply to
61  * the endpoints on that group based on the policy configuration. The exact list
62  * of rules that apply to a given pair of endpoints depends on the conditions
63  * that are active on the endpoints.
64  * We need to be able to query against this policy model, enumerate the relevant
65  * classes of traffic and endpoints, and notify renderers when there are changes
66  * to policy as it applies to active sets of endpoints and endpoint groups.
67  * The policy resolver will maintain the necessary state for all tenants in its
68  * control domain, which is the set of tenants for which policy listeners have
69  * been registered.
70  */
71 public class PolicyResolver implements PolicyValidatorRegistry, AutoCloseable {
72
73     private static final Logger LOG = LoggerFactory.getLogger(PolicyResolver.class);
74
75     private final DataBroker dataProvider;
76
77     protected final ConcurrentMap<TenantId, IndexedTenant> resolvedTenants;
78
79     private PolicyChangeListener tenantChangeListener;
80
81     /*
82      * Store validators for ActionDefinitions from Renderers
83      *
84      */
85     private SetMultimap<ActionDefinitionId, Validator<ActionInstance>> actionInstanceValidatorsByDefinition =
86             Multimaps.synchronizedSetMultimap(HashMultimap.<ActionDefinitionId, Validator<ActionInstance>>create());
87     private SetMultimap<ClassifierDefinitionId, Validator<ClassifierInstance>> classifierInstanceValidatorsByDefinition =
88             Multimaps
89                 .synchronizedSetMultimap(HashMultimap.<ClassifierDefinitionId, Validator<ClassifierInstance>>create());
90
91     public PolicyResolver(DataBroker dataProvider) {
92         this.dataProvider = dataProvider;
93         resolvedTenants = new ConcurrentHashMap<>();
94         tenantChangeListener =
95                 new PolicyChangeListener(dataProvider, new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
96                         InstanceIdentifier.builder(Tenants.class).child(Tenant.class).build()));
97         LOG.debug("Initialized renderer common policy resolver");
98     }
99
100     // *************
101     // AutoCloseable
102     // *************
103     @Override
104     public void close() {
105         if (tenantChangeListener != null) {
106             tenantChangeListener.close();
107         }
108     }
109
110     // *************************
111     // PolicyResolutionValidatorRegistrar
112     // *************************
113
114     @Override
115     public void register(ActionDefinitionId actionDefinitionId, Validator<ActionInstance> validator) {
116         actionInstanceValidatorsByDefinition.put(actionDefinitionId, validator);
117     }
118
119     @Override
120     public void unregister(ActionDefinitionId actionDefinitionId, Validator<ActionInstance> validator) {
121         actionInstanceValidatorsByDefinition.remove(actionDefinitionId, validator);
122     }
123
124     @Override
125     public void register(ClassifierDefinitionId classifierDefinitionId, Validator<ClassifierInstance> validator) {
126         classifierInstanceValidatorsByDefinition.put(classifierDefinitionId, validator);
127     }
128
129     @Override
130     public void unregister(ClassifierDefinitionId classifierDefinitionId, Validator<ClassifierInstance> validator) {
131         classifierInstanceValidatorsByDefinition.remove(classifierDefinitionId, validator);
132     }
133
134     @VisibleForTesting
135     void updateTenant(final TenantId tenantId, final Tenant unresolvedTenant) {
136         if (dataProvider == null) {
137             LOG.error("Tenant {} will not be resolved because because dataProvider is NULL", tenantId.getValue());
138             return;
139         }
140
141         if (unresolvedTenant == null) {
142             LOG.info("Tenant {} not found in CONF; check&delete from OPER", tenantId);
143             resolvedTenants.remove(tenantId);
144             ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
145             DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL, IidFactory.tenantIid(tenantId), rwTx);
146             updateResolvedPolicy(rwTx);
147             if (DataStoreHelper.submitToDs(rwTx)) {
148                 LOG.debug("Removed resolved tenant {} and wrote resolved policies to Datastore.", tenantId.getValue());
149             } else {
150                 LOG.error("Failed to remove resolved tenant {} and to write resolved policies to Datastore.",
151                         tenantId.getValue());
152             }
153         } else {
154             LOG.debug("Resolving of tenant inheritance and policy triggered by a change in tenant {}", tenantId);
155             Tenant resolvedTenant = InheritanceUtils.resolveTenant(unresolvedTenant);
156             if (isPolicyValid(resolvedTenant.getPolicy())) {
157                 // Update the policy cache and notify listeners
158                 resolvedTenants.put(tenantId, new IndexedTenant(resolvedTenant));
159                 WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
160                 wTx.put(LogicalDatastoreType.OPERATIONAL, IidFactory.tenantIid(tenantId), resolvedTenant, true);
161                 updateResolvedPolicy(wTx);
162                 if (DataStoreHelper.submitToDs(wTx)) {
163                     LOG.debug("Wrote resolved tenant {} and resolved policies to Datastore.", tenantId.getValue());
164                 } else {
165                     LOG.error("Failed to write resolved tenant {} and resolved policies to Datastore.",
166                             tenantId.getValue());
167                 }
168             }
169         }
170     }
171
172     private void updateResolvedPolicy(WriteTransaction wTx) {
173         Set<IndexedTenant> indexedTenants = getIndexedTenants(resolvedTenants.values());
174         Table<EgKey, EgKey, org.opendaylight.groupbasedpolicy.dto.Policy> policyMap =
175                 PolicyResolverUtils.resolvePolicy(indexedTenants);
176         ResolvedPolicies resolvedPolicies =
177                 new ResolvedPoliciesBuilder().setResolvedPolicy(PolicyInfoUtils.buildResolvedPolicy(policyMap, resolvedTenants)).build();
178
179         wTx.put(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(ResolvedPolicies.class).build(),
180                 resolvedPolicies, true);
181     }
182
183     private Set<IndexedTenant> getIndexedTenants(Collection<IndexedTenant> tenantCtxs) {
184         Set<IndexedTenant> result = new HashSet<>();
185         for (IndexedTenant tenant : tenantCtxs) {
186             if (tenant != null) {
187                 result.add(tenant);
188             }
189         }
190         return result;
191     }
192
193     @VisibleForTesting
194     boolean isPolicyValid(Policy policy) {
195         if (policy != null && policy.getSubjectFeatureInstances() != null) {
196             SubjectFeatureInstances subjectFeatureInstances = policy.getSubjectFeatureInstances();
197             if (actionInstancesAreValid(subjectFeatureInstances.getActionInstance())
198                     && classifierInstancesAreValid(subjectFeatureInstances.getClassifierInstance())) {
199                 return true;
200             }
201         }
202         return false;
203     }
204
205     /**
206      * Validation of action instances.
207      *
208      * @param actionInstances list of instances to validate
209      * @return true if instances are valid or if <code>actionInstances</code>
210      *         is <code>null</code>, Otherwise returns false.
211      */
212     private boolean actionInstancesAreValid(List<ActionInstance> actionInstances) {
213         if (actionInstances == null) {
214             return true;
215         }
216         for (ActionInstance actionInstance : actionInstances) {
217             Set<Validator<ActionInstance>> actionInstanceValidators =
218                     actionInstanceValidatorsByDefinition.get(actionInstance.getActionDefinitionId());
219             for (Validator<ActionInstance> actionInstanceValidator : actionInstanceValidators) {
220                 ValidationResult validationResult = actionInstanceValidator.validate(actionInstance);
221                 if (!validationResult.isValid()) {
222                     LOG.error("ActionInstance {} is not valid! {}", actionInstance.getName().getValue(),
223                             validationResult.getMessage());
224                     return false;
225                 }
226             }
227         }
228         return true;
229     }
230
231     /**
232      * Validation of classifier instances.
233      *
234      * @param classifierInstances list of instances to validate
235      * @return true if instances are valid or if <code>classifierInstances</code>
236      *         is <code>null</code>, Otherwise returns false.
237      */
238     private boolean classifierInstancesAreValid(List<ClassifierInstance> classifierInstances) {
239         if (classifierInstances == null) {
240             return true;
241         }
242         for (ClassifierInstance classifierInstance : classifierInstances) {
243             Set<Validator<ClassifierInstance>> classifierInstanceValidators =
244                     classifierInstanceValidatorsByDefinition.get(classifierInstance.getClassifierDefinitionId());
245             for (Validator<ClassifierInstance> classifierInstanceValidator : classifierInstanceValidators) {
246                 ValidationResult validationResult = classifierInstanceValidator.validate(classifierInstance);
247                 if (!validationResult.isValid()) {
248                     LOG.error("ClassifierInstance {} is not valid! {}", classifierInstance.getName().getValue(),
249                             validationResult.getMessage());
250                     return false;
251                 }
252             }
253         }
254         return true;
255     }
256
257     @Immutable
258     private class PolicyChangeListener extends DataTreeChangeHandler<Tenant> {
259
260         protected PolicyChangeListener(DataBroker dataProvider, DataTreeIdentifier<Tenant> pointOfInterest) {
261             super(dataProvider);
262             registerDataTreeChangeListener(pointOfInterest);
263         }
264
265         @Override
266         protected void onWrite(DataObjectModification<Tenant> rootNode, InstanceIdentifier<Tenant> rootIdentifier) {
267             Tenant tenantAfter = rootNode.getDataAfter();
268             updateTenant(tenantAfter.getId(), tenantAfter);
269         }
270
271         @Override
272         protected void onDelete(DataObjectModification<Tenant> rootNode, InstanceIdentifier<Tenant> rootIdentifier) {
273             TenantId tenantId = rootIdentifier.firstKeyOf(Tenant.class).getId();
274             updateTenant(tenantId, null);
275         }
276
277         @Override
278         protected void onSubtreeModified(DataObjectModification<Tenant> rootNode,
279                 InstanceIdentifier<Tenant> rootIdentifier) {
280             Tenant tenantAfter = rootNode.getDataAfter();
281             updateTenant(tenantAfter.getId(), tenantAfter);
282         }
283
284     }
285
286 }