Bug 4988: OF statistics & REST client
[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.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;
53
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;
62
63 /**
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
75  * been registered.
76  */
77 public class PolicyResolver implements PolicyValidatorRegistry, AutoCloseable {
78
79     private static final Logger LOG = LoggerFactory.getLogger(PolicyResolver.class);
80
81     private final DataBroker dataProvider;
82
83     private final FollowedTenantListener followedTenantListener;
84
85     protected final ConcurrentMap<TenantId, IndexedTenant> resolvedTenants;
86
87     protected final Multiset<TenantId> subscribersPerTenant = HashMultiset.create();
88
89     private PolicyChangeListener tenantChangeListener;
90
91     /*
92      * Store validators for ActionDefinitions from Renderers
93      *
94      */
95     private SetMultimap<ActionDefinitionId, Validator<ActionInstance>> actionInstanceValidatorsByDefinition =
96             Multimaps.synchronizedSetMultimap(HashMultimap.<ActionDefinitionId, Validator<ActionInstance>>create());
97     private SetMultimap<ClassifierDefinitionId, Validator<ClassifierInstance>> classifierInstanceValidatorsByDefinition =
98             Multimaps
99                 .synchronizedSetMultimap(HashMultimap.<ClassifierDefinitionId, Validator<ClassifierInstance>>create());
100
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");
109     }
110
111     // *************
112     // AutoCloseable
113     // *************
114     @Override
115     public void close() throws Exception {
116         if (tenantChangeListener != null) {
117             tenantChangeListener.close();
118         }
119         if (followedTenantListener != null) {
120             followedTenantListener.close();
121         }
122     }
123
124     // *************************
125     // PolicyResolutionValidatorRegistrar
126     // *************************
127
128     @Override
129     public void register(ActionDefinitionId actionDefinitionId, Validator<ActionInstance> validator) {
130         actionInstanceValidatorsByDefinition.put(actionDefinitionId, validator);
131     }
132
133     @Override
134     public void unregister(ActionDefinitionId actionDefinitionId, Validator<ActionInstance> validator) {
135         actionInstanceValidatorsByDefinition.remove(actionDefinitionId, validator);
136     }
137
138     @Override
139     public void register(ClassifierDefinitionId classifierDefinitionId, Validator<ClassifierInstance> validator) {
140         classifierInstanceValidatorsByDefinition.put(classifierDefinitionId, validator);
141     }
142
143     @Override
144     public void unregister(ClassifierDefinitionId classifierDefinitionId, Validator<ClassifierInstance> validator) {
145         classifierInstanceValidatorsByDefinition.remove(classifierDefinitionId, validator);
146     }
147
148     /**
149      * Subscribe the resolver to updates related to a particular tenant.
150      *
151      * @param tenantId the tenant ID to subscribe to
152      */
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());
161                 }
162                 rTx.close();
163             }
164             subscribersPerTenant.add(tenantId);
165         }
166     }
167
168     /**
169      * Unsubscribe the resolver from updates related to a particular tenant.
170      *
171      * @param tenantId the tenant ID to unsubscribe from
172      */
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);
179             }
180         }
181     }
182
183     @VisibleForTesting
184     void updateTenant(final TenantId tenantId, final Tenant unresolvedTenant) {
185         if (dataProvider == null) {
186             return;
187         }
188
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());
197             } else {
198                 LOG.error("Failed to remove resolved tenant {} and to write resolved policies to Datastore.",
199                         tenantId.getValue());
200             }
201         } else {
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());
212                 } else {
213                     LOG.error("Failed to write resolved tenant {} and resolved policies to Datastore.",
214                             tenantId.getValue());
215                 }
216             }
217         }
218     }
219
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");
223             return;
224         }
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();
230
231         wTx.put(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(ResolvedPolicies.class).build(),
232                 resolvedPolicies, true);
233     }
234
235     private Set<IndexedTenant> getIndexedTenants(Collection<IndexedTenant> tenantCtxs) {
236         Set<IndexedTenant> result = new HashSet<>();
237         for (IndexedTenant tenant : tenantCtxs) {
238             if (tenant != null) {
239                 result.add(tenant);
240             }
241         }
242         return result;
243     }
244
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())) {
250                 return true;
251             }
252         }
253         return false;
254     }
255
256     /**
257      * Validation of action instances.
258      *
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.
262      */
263     private boolean actionInstancesAreValid(List<ActionInstance> actionInstances) {
264         if (actionInstances == null) {
265             return true;
266         }
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());
275                     return false;
276                 }
277             }
278         }
279         return true;
280     }
281
282     /**
283      * Validation of classifier instances.
284      *
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.
288      */
289     private boolean classifierInstancesAreValid(List<ClassifierInstance> classifierInstances) {
290         if (classifierInstances == null) {
291             return true;
292         }
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());
301                     return false;
302                 }
303             }
304         }
305         return true;
306     }
307
308     @Immutable
309     private class PolicyChangeListener extends DataTreeChangeHandler<Tenant> {
310
311         protected PolicyChangeListener(DataBroker dataProvider, DataTreeIdentifier<Tenant> pointOfInterest) {
312             super(dataProvider, pointOfInterest);
313         }
314
315         @Override
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()
325                             .getContract()
326                             .get(0)
327                             .getSubject()
328                             .get(0)
329                             .getRule()
330                             .get(0)
331                             .getClassifierRef();
332                     cref.get(0).getInstanceName();
333                     tenantAfter.getPolicy().getSubjectFeatureInstances().getClassifierInstance().get(0).getName();
334                     tenantAfter.getPolicy().getSubjectFeatureInstances().getClassifierInstance().get(0).getParameterValue()
335                             .get(0);
336                 }
337             }
338         }
339
340         @Override
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);
346                 }
347             }
348         }
349
350         @Override
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);
357                 }
358             }
359         }
360
361     }
362
363 }