255e90a5e002f948c0169ad51aad11f9fb62e544
[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.tenants.Tenant;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Policy;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.SubjectFeatureInstances;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstance;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ClassifierInstance;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.ResolvedPolicies;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.ResolvedPoliciesBuilder;
48 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 import com.google.common.annotations.VisibleForTesting;
53 import com.google.common.base.Optional;
54 import com.google.common.collect.HashMultimap;
55 import com.google.common.collect.HashMultiset;
56 import com.google.common.collect.Multimaps;
57 import com.google.common.collect.Multiset;
58 import com.google.common.collect.SetMultimap;
59 import com.google.common.collect.Table;
60
61 /**
62  * The policy resolver is a utility for renderers to help in resolving
63  * group-based policy into a form that is easier to apply to the actual network.
64  * For any pair of endpoint groups, there is a set of rules that could apply to
65  * the endpoints on that group based on the policy configuration. The exact list
66  * of rules that apply to a given pair of endpoints depends on the conditions
67  * that are active on the endpoints.
68  * We need to be able to query against this policy model, enumerate the relevant
69  * classes of traffic and endpoints, and notify renderers when there are changes
70  * to policy as it applies to active sets of endpoints and endpoint groups.
71  * The policy resolver will maintain the necessary state for all tenants in its
72  * control domain, which is the set of tenants for which policy listeners have
73  * been registered.
74  */
75 public class PolicyResolver implements PolicyValidatorRegistry, AutoCloseable {
76
77     private static final Logger LOG = LoggerFactory.getLogger(PolicyResolver.class);
78
79     private final DataBroker dataProvider;
80
81     private final FollowedTenantListener followedTenantListener;
82
83     protected final ConcurrentMap<TenantId, IndexedTenant> resolvedTenants;
84
85     protected final Multiset<TenantId> subscribersPerTenant = HashMultiset.create();
86
87     private PolicyChangeListener tenantChangeListener;
88
89     /*
90      * Store validators for ActionDefinitions from Renderers
91      *
92      */
93     private SetMultimap<ActionDefinitionId, Validator<ActionInstance>> actionInstanceValidatorsByDefinition =
94             Multimaps.synchronizedSetMultimap(HashMultimap.<ActionDefinitionId, Validator<ActionInstance>>create());
95     private SetMultimap<ClassifierDefinitionId, Validator<ClassifierInstance>> classifierInstanceValidatorsByDefinition =
96             Multimaps
97                 .synchronizedSetMultimap(HashMultimap.<ClassifierDefinitionId, Validator<ClassifierInstance>>create());
98
99     public PolicyResolver(DataBroker dataProvider) {
100         this.dataProvider = dataProvider;
101         followedTenantListener = new FollowedTenantListener(dataProvider, this);
102         resolvedTenants = new ConcurrentHashMap<>();
103         tenantChangeListener =
104                 new PolicyChangeListener(dataProvider, new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
105                         InstanceIdentifier.builder(Tenants.class).child(Tenant.class).build()));
106         LOG.debug("Initialized renderer common policy resolver");
107     }
108
109     // *************
110     // AutoCloseable
111     // *************
112     @Override
113     public void close() throws Exception {
114         if (tenantChangeListener != null) {
115             tenantChangeListener.close();
116         }
117         if (followedTenantListener != null) {
118             followedTenantListener.close();
119         }
120     }
121
122     // *************************
123     // PolicyResolutionValidatorRegistrar
124     // *************************
125
126     @Override
127     public void register(ActionDefinitionId actionDefinitionId, Validator<ActionInstance> validator) {
128         actionInstanceValidatorsByDefinition.put(actionDefinitionId, validator);
129     }
130
131     @Override
132     public void unregister(ActionDefinitionId actionDefinitionId, Validator<ActionInstance> validator) {
133         actionInstanceValidatorsByDefinition.remove(actionDefinitionId, validator);
134     }
135
136     @Override
137     public void register(ClassifierDefinitionId classifierDefinitionId, Validator<ClassifierInstance> validator) {
138         classifierInstanceValidatorsByDefinition.put(classifierDefinitionId, validator);
139     }
140
141     @Override
142     public void unregister(ClassifierDefinitionId classifierDefinitionId, Validator<ClassifierInstance> validator) {
143         classifierInstanceValidatorsByDefinition.remove(classifierDefinitionId, validator);
144     }
145
146     /**
147      * Subscribe the resolver to updates related to a particular tenant.
148      *
149      * @param tenantId the tenant ID to subscribe to
150      */
151     protected void subscribeTenant(TenantId tenantId) {
152         synchronized (subscribersPerTenant) {
153             if (subscribersPerTenant.count(tenantId) == 0) {
154                 ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction();
155                 Optional<Tenant> potentialTenant = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
156                         IidFactory.tenantIid(tenantId), rTx);
157                 if (potentialTenant.isPresent()) {
158                     updateTenant(tenantId, potentialTenant.get());
159                 }
160                 rTx.close();
161             }
162             subscribersPerTenant.add(tenantId);
163         }
164     }
165
166     /**
167      * Unsubscribe the resolver from updates related to a particular tenant.
168      *
169      * @param tenantId the tenant ID to unsubscribe from
170      */
171     protected void unsubscribeTenant(TenantId tenantId) {
172         synchronized (subscribersPerTenant) {
173             subscribersPerTenant.remove(tenantId);
174             if (subscribersPerTenant.count(tenantId) == 0) {
175                 // nobody is interested in the tenant - can be removed from OPER and resolved policy
176                 updateTenant(tenantId, null);
177             }
178         }
179     }
180
181     @VisibleForTesting
182     void updateTenant(final TenantId tenantId, final Tenant unresolvedTenant) {
183         if (dataProvider == null) {
184             return;
185         }
186
187         if (unresolvedTenant == null) {
188             LOG.info("Tenant {} not found in CONF; check&delete from OPER", tenantId);
189             resolvedTenants.remove(tenantId);
190             ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
191             DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL, IidFactory.tenantIid(tenantId), rwTx);
192             updateResolvedPolicy(rwTx);
193             if (DataStoreHelper.submitToDs(rwTx)) {
194                 LOG.debug("Removed resolved tenant {} and wrote resolved policies to Datastore.", tenantId.getValue());
195             } else {
196                 LOG.error("Failed to remove resolved tenant {} and to write resolved policies to Datastore.",
197                         tenantId.getValue());
198             }
199         } else {
200             LOG.debug("Resolving of tenant inheritance and policy triggered by a change in tenant {}", tenantId);
201             Tenant resolvedTenant = InheritanceUtils.resolveTenant(unresolvedTenant);
202             if (isPolicyValid(resolvedTenant.getPolicy())) {
203                 // Update the policy cache and notify listeners
204                 resolvedTenants.put(tenantId, new IndexedTenant(resolvedTenant));
205                 WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
206                 wTx.put(LogicalDatastoreType.OPERATIONAL, IidFactory.tenantIid(tenantId), resolvedTenant, true);
207                 updateResolvedPolicy(wTx);
208                 if (DataStoreHelper.submitToDs(wTx)) {
209                     LOG.debug("Wrote resolved tenant {} and resolved policies to Datastore.", tenantId.getValue());
210                 } else {
211                     LOG.error("Failed to write resolved tenant {} and resolved policies to Datastore.",
212                             tenantId.getValue());
213                 }
214             }
215         }
216     }
217
218     private void updateResolvedPolicy(WriteTransaction wTx) {
219         if (dataProvider == null) {
220             LOG.error("Couldn't Write Resolved Tenants Policy Info to Datastore because dataProvider is NULL");
221             return;
222         }
223         Set<IndexedTenant> indexedTenants = getIndexedTenants(resolvedTenants.values());
224         Table<EgKey, EgKey, org.opendaylight.groupbasedpolicy.dto.Policy> policyMap =
225                 PolicyResolverUtils.resolvePolicy(indexedTenants);
226         ResolvedPolicies resolvedPolicies =
227                 new ResolvedPoliciesBuilder().setResolvedPolicy(PolicyInfoUtils.buildResolvedPolicy(policyMap, resolvedTenants)).build();
228
229         wTx.put(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(ResolvedPolicies.class).build(),
230                 resolvedPolicies, true);
231     }
232
233     private Set<IndexedTenant> getIndexedTenants(Collection<IndexedTenant> tenantCtxs) {
234         Set<IndexedTenant> result = new HashSet<>();
235         for (IndexedTenant tenant : tenantCtxs) {
236             if (tenant != null) {
237                 result.add(tenant);
238             }
239         }
240         return result;
241     }
242
243     private boolean isPolicyValid(Policy policy) {
244         if (policy != null && policy.getSubjectFeatureInstances() != null) {
245             SubjectFeatureInstances subjectFeatureInstances = policy.getSubjectFeatureInstances();
246             if (actionInstancesAreValid(subjectFeatureInstances.getActionInstance())
247                     && classifierInstancesAreValid(subjectFeatureInstances.getClassifierInstance())) {
248                 return true;
249             }
250         }
251         return false;
252     }
253
254     /**
255      * Validation of action instances.
256      *
257      * @param actionInstances list of instances to validate
258      * @return true if instances are valid or if <code>actionInstances</code>
259      *         is <code>null</code>, Otherwise returns false.
260      */
261     private boolean actionInstancesAreValid(List<ActionInstance> actionInstances) {
262         if (actionInstances == null) {
263             return true;
264         }
265         for (ActionInstance actionInstance : actionInstances) {
266             Set<Validator<ActionInstance>> actionInstanceValidators =
267                     actionInstanceValidatorsByDefinition.get(actionInstance.getActionDefinitionId());
268             for (Validator<ActionInstance> actionInstanceValidator : actionInstanceValidators) {
269                 ValidationResult validationResult = actionInstanceValidator.validate(actionInstance);
270                 if (!validationResult.isValid()) {
271                     LOG.error("ActionInstance {} is not valid! {}", actionInstance.getName().getValue(),
272                             validationResult.getMessage());
273                     return false;
274                 }
275             }
276         }
277         return true;
278     }
279
280     /**
281      * Validation of classifier instances.
282      *
283      * @param classifierInstances list of instances to validate
284      * @return true if instances are valid or if <code>classifierInstances</code>
285      *         is <code>null</code>, Otherwise returns false.
286      */
287     private boolean classifierInstancesAreValid(List<ClassifierInstance> classifierInstances) {
288         if (classifierInstances == null) {
289             return true;
290         }
291         for (ClassifierInstance classifierInstance : classifierInstances) {
292             Set<Validator<ClassifierInstance>> classifierInstanceValidators =
293                     classifierInstanceValidatorsByDefinition.get(classifierInstance.getClassifierDefinitionId());
294             for (Validator<ClassifierInstance> classifierInstanceValidator : classifierInstanceValidators) {
295                 ValidationResult validationResult = classifierInstanceValidator.validate(classifierInstance);
296                 if (!validationResult.isValid()) {
297                     LOG.error("ClassifierInstance {} is not valid! {}", classifierInstance.getName().getValue(),
298                             validationResult.getMessage());
299                     return false;
300                 }
301             }
302         }
303         return true;
304     }
305
306     @Immutable
307     private class PolicyChangeListener extends DataTreeChangeHandler<Tenant> {
308
309         protected PolicyChangeListener(DataBroker dataProvider, DataTreeIdentifier<Tenant> pointOfInterest) {
310             super(dataProvider, pointOfInterest);
311         }
312
313         @Override
314         protected void onWrite(DataObjectModification<Tenant> rootNode, InstanceIdentifier<Tenant> rootIdentifier) {
315             Tenant tenantAfter = rootNode.getDataAfter();
316             synchronized (subscribersPerTenant) {
317                 if (subscribersPerTenant.contains(tenantAfter.getId())) {
318                     updateTenant(tenantAfter.getId(), tenantAfter);
319                 }
320             }
321         }
322
323         @Override
324         protected void onDelete(DataObjectModification<Tenant> rootNode, InstanceIdentifier<Tenant> rootIdentifier) {
325             TenantId tenantId = rootIdentifier.firstKeyOf(Tenant.class).getId();
326             synchronized (subscribersPerTenant) {
327                 if (subscribersPerTenant.contains(tenantId)) {
328                     updateTenant(tenantId, null);
329                 }
330             }
331         }
332
333         @Override
334         protected void onSubtreeModified(DataObjectModification<Tenant> rootNode,
335                 InstanceIdentifier<Tenant> rootIdentifier) {
336             Tenant tenantAfter = rootNode.getDataAfter();
337             synchronized (subscribersPerTenant) {
338                 if (subscribersPerTenant.contains(tenantAfter.getId())) {
339                     updateTenant(tenantAfter.getId(), tenantAfter);
340                 }
341             }
342         }
343
344     }
345
346 }