Adding multiple EPG capability to EPs and IntraEPG policy
[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
9 package org.opendaylight.groupbasedpolicy.resolver;
10
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Set;
19 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.concurrent.ConcurrentMap;
21 import java.util.concurrent.CopyOnWriteArrayList;
22 import java.util.concurrent.ScheduledExecutorService;
23 import java.util.concurrent.atomic.AtomicReference;
24
25 import javax.annotation.concurrent.Immutable;
26
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
29 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
30 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
31 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
32 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
33 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContractId;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubjectName;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.ConsumerSelectionRelator;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Matcher.MatchType;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.ProviderSelectionRelator;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRef;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRefBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.condition.matchers.ConditionMatcher;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.conditions.Condition;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.target.selector.QualityMatcher;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Contract;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Clause;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Subject;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Target;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.GroupIdentificationConstraints;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.group.identification.constraints.GroupRequirementConstraintCase;
55 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;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.group.identification.constraints.GroupCapabilityConstraintCase;
57 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;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.subject.Rule;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.subject.RuleBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ConsumerNamedSelector;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ConsumerTargetSelector;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderNamedSelector;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderTargetSelector;
64 import org.opendaylight.yangtools.concepts.ListenerRegistration;
65 import org.opendaylight.yangtools.yang.binding.DataObject;
66 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69
70 import com.google.common.base.Optional;
71 import com.google.common.base.Predicate;
72 import com.google.common.collect.HashBasedTable;
73 import com.google.common.collect.ImmutableSet;
74 import com.google.common.collect.Ordering;
75 import com.google.common.collect.Sets;
76 import com.google.common.collect.Table;
77 import com.google.common.collect.Table.Cell;
78 import com.google.common.util.concurrent.FutureCallback;
79 import com.google.common.util.concurrent.Futures;
80 import com.google.common.util.concurrent.ListenableFuture;
81
82 /**
83  * The policy resolver is a utility for renderers to help in resolving
84  * group-based policy into a form that is easier to apply to the actual network.
85  *
86  * For any pair of endpoint groups, there is a set of rules that could apply to
87  * the endpoints on that group based on the policy configuration. The exact list
88  * of rules that apply to a given pair of endpoints depends on the conditions
89  * that are active on the endpoints.
90  *
91  * We need to be able to query against this policy model, enumerate the relevant
92  * classes of traffic and endpoints, and notify renderers when there are changes
93  * to policy as it applies to active sets of endpoints and endpoint groups.
94  *
95  * The policy resolver will maintain the necessary state for all tenants in its
96  * control domain, which is the set of tenants for which policy listeners have
97  * been registered.
98  *
99  */
100 public class PolicyResolver implements AutoCloseable {
101     private static final Logger LOG = LoggerFactory.getLogger(PolicyResolver.class);
102
103     private final DataBroker dataProvider;
104     private final ScheduledExecutorService executor;
105
106     /**
107      * Keep track of the current relevant policy scopes.
108      */
109     protected CopyOnWriteArrayList<PolicyScope> policyListenerScopes;
110
111     protected ConcurrentMap<TenantId, TenantContext> resolvedTenants;
112
113     /**
114      * Store a policy object for each endpoint group pair. The table is stored
115      * with the key as (consumer, provider). Two endpoints could appear in both
116      * roles at the same time, in which case both policies would apply.
117      */
118     AtomicReference<PolicyInfo> policy = new AtomicReference<>();
119
120     public PolicyResolver(DataBroker dataProvider,
121             ScheduledExecutorService executor) {
122         super();
123         this.dataProvider = dataProvider;
124         this.executor = executor;
125         policyListenerScopes = new CopyOnWriteArrayList<>();
126         resolvedTenants = new ConcurrentHashMap<>();
127         LOG.debug("Initialized renderer common policy resolver");
128     }
129
130     // *************
131     // AutoCloseable
132     // *************
133
134     @Override
135     public void close() throws Exception {
136         for (TenantContext ctx : resolvedTenants.values()) {
137             if (ctx.registration != null)
138                 ctx.registration.close();
139         }
140     }
141
142     // *************************
143     // PolicyResolver public API
144     // *************************
145
146     /**
147      * Get a snapshot of the current policy
148      *
149      * @return the {@link PolicyInfo} object representing an immutable snapshot
150      *         of the policy state
151      */
152     public PolicyInfo getCurrentPolicy() {
153         return policy.get();
154     }
155
156     /**
157      * Get the normalized tenant for the given ID
158      *
159      * @param tenant
160      *            the tenant ID
161      * @return the {@link Tenant}
162      */
163     public IndexedTenant getTenant(TenantId tenant) {
164         TenantContext tc = resolvedTenants.get(tenant);
165         if (tc == null)
166             return null;
167         return tc.tenant.get();
168     }
169
170     /**
171      * Register a listener to receive update events.
172      *
173      * @param listener
174      *            the {@link PolicyListener} object to receive the update events
175      */
176     public PolicyScope registerListener(PolicyListener listener) {
177         PolicyScope ps = new PolicyScope(this, listener);
178         policyListenerScopes.add(ps);
179
180         return ps;
181     }
182
183     /**
184      * Remove the listener registered for the given {@link PolicyScope}.
185      *
186      * @param scope
187      *            the scope to remove
188      * @see PolicyResolver#registerListener(PolicyListener)
189      */
190     public void removeListener(PolicyScope scope) {
191         policyListenerScopes.remove(scope);
192     }
193
194     // **************
195     // Implementation
196     // **************
197
198     /**
199      * Atomically update the active policy and notify policy listeners of
200      * relevant changes
201      *
202      * @param policyMap
203      *            the new policy to set
204      * @param egConditions
205      *            the map of endpoint groups to relevant condition sets
206      * @return the set of groups with updated policy
207      */
208     protected Set<EgKey> updatePolicy(Table<EgKey, EgKey, Policy> policyMap,
209             Map<EgKey, Set<ConditionSet>> egConditions,
210             List<PolicyScope> policyListenerScopes) {
211         PolicyInfo newPolicy = new PolicyInfo(policyMap, egConditions);
212         PolicyInfo oldPolicy = policy.getAndSet(newPolicy);
213
214         HashSet<EgKey> notifySet = new HashSet<>();
215
216         for (Cell<EgKey, EgKey, Policy> cell : newPolicy.getPolicyMap().cellSet()) {
217             Policy newp = cell.getValue();
218             Policy oldp = null;
219             if (oldPolicy != null)
220                 oldp = oldPolicy.getPolicyMap().get(cell.getRowKey(),
221                         cell.getColumnKey());
222             if (oldp == null || !newp.equals(oldp)) {
223                 notifySet.add(cell.getRowKey());
224                 notifySet.add(cell.getColumnKey());
225             }
226         }
227         if (oldPolicy != null) {
228             for (Cell<EgKey, EgKey, Policy> cell : oldPolicy.getPolicyMap().cellSet()) {
229                 if (!newPolicy.getPolicyMap().contains(cell.getRowKey(),
230                         cell.getColumnKey())) {
231                     notifySet.add(cell.getRowKey());
232                     notifySet.add(cell.getColumnKey());
233                 }
234             }
235         }
236         return notifySet;
237     }
238
239     /**
240      * Notify the policy listeners about a set of updated groups
241      */
242     private void notifyListeners(Set<EgKey> updatedGroups) {
243         for (final PolicyScope scope : policyListenerScopes) {
244             Set<EgKey> filtered =
245                     Sets.filter(updatedGroups, new Predicate<EgKey>() {
246                         @Override
247                         public boolean apply(EgKey input) {
248                             return scope.contains(input.getTenantId(),
249                                     input.getEgId());
250                         }
251                     });
252             if (!filtered.isEmpty()) {
253                 scope.getListener().policyUpdated(filtered);
254             }
255         }
256     }
257
258     /**
259      * Subscribe the resolver to updates related to a particular tenant Make
260      * sure that this can't be called concurrently with subscribe
261      *
262      * @param tenantId
263      *            the tenant ID to subscribe to
264      */
265     protected void subscribeTenant(TenantId tenantId) {
266         if (!resolvedTenants.containsKey(tenantId))
267             updateTenant(tenantId);
268     }
269
270     /**
271      * Unsubscribe the resolver from updates related to a particular tenant Make
272      * sure that this can't be called concurrently with subscribe
273      *
274      * @param tenantId
275      *            the tenant ID to subscribe to
276      */
277     protected void unsubscribeTenant(TenantId tenantId) {
278         TenantContext context = resolvedTenants.get(tenantId);
279         if (context != null) {
280             resolvedTenants.remove(tenantId);
281             context.registration.close();
282         }
283     }
284
285     private void updateTenant(final TenantId tenantId) {
286         if (dataProvider == null)
287             return;
288
289         TenantContext context = resolvedTenants.get(tenantId);
290         if (context == null) {
291             ListenerRegistration<DataChangeListener> registration = null;
292             registration = dataProvider
293                     .registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
294                             TenantUtils.tenantIid(tenantId),
295                             new PolicyChangeListener(tenantId),
296                             DataChangeScope.SUBTREE);
297
298             context = new TenantContext(registration);
299             TenantContext oldContext =
300                     resolvedTenants.putIfAbsent(tenantId, context);
301             if (oldContext != null) {
302                 // already registered in a different thread; just use the other
303                 // context
304                 registration.close();
305                 context = oldContext;
306             } else {
307                 LOG.info("Added tenant {} to policy scope", tenantId);
308             }
309         }
310
311         // Resolve the new tenant and update atomically
312         final AtomicReference<IndexedTenant> tenantRef = context.tenant;
313         final IndexedTenant ot = tenantRef.get();
314         ReadOnlyTransaction transaction =
315                 dataProvider.newReadOnlyTransaction();
316         final InstanceIdentifier<Tenant> tiid = TenantUtils.tenantIid(tenantId);
317         ListenableFuture<Optional<Tenant>> unresolved;
318
319         unresolved = transaction.read(LogicalDatastoreType.CONFIGURATION, tiid);
320
321         Futures.addCallback(unresolved, new FutureCallback<Optional<Tenant>>() {
322             @Override
323             public void onSuccess(Optional<Tenant> result) {
324                 if (!result.isPresent()) {
325                     LOG.warn("Tenant {} not found", tenantId);
326                 }
327
328                 Tenant t = InheritanceUtils.resolveTenant(result.get());
329                 IndexedTenant it = new IndexedTenant(t);
330                 if (!tenantRef.compareAndSet(ot, it)) {
331                     // concurrent update of tenant policy. Retry
332                     updateTenant(tenantId);
333                 } else {
334                     // Update the policy cache and notify listeners
335                     WriteTransaction wt = dataProvider.newWriteOnlyTransaction();
336                     wt.put(LogicalDatastoreType.OPERATIONAL, tiid, t, true);
337                     wt.submit();
338                     updatePolicy();
339                 }
340             }
341
342             @Override
343             public void onFailure(Throwable t) {
344                 LOG.error("Count not get tenant {}", tenantId, t);
345             }
346         }, executor);
347     }
348
349     protected void updatePolicy() {
350         try {
351             Map<EgKey, Set<ConditionSet>> egConditions = new HashMap<>();
352             Table<EgKey, EgKey, Policy> policyMap =
353                     resolvePolicy(resolvedTenants.values(),
354                             egConditions);
355             Set<EgKey> updatedGroups =
356                     updatePolicy(policyMap,
357                             egConditions,
358                             policyListenerScopes);
359
360             notifyListeners(updatedGroups);
361         } catch (Exception e) {
362             LOG.error("Failed to update policy", e);
363         }
364     }
365
366     /**
367      * Resolve the policy in three phases: (1) select contracts that in scope
368      * based on contract selectors. (2) select subjects that are in scope for
369      * each contract based on matchers in clauses (3) resolve the set of
370      * in-scope contracts into a list of subjects that apply for each pair of
371      * endpoint groups and the conditions that can apply for for each endpoint
372      * in those groups.
373      */
374     protected Table<EgKey, EgKey, Policy>
375             resolvePolicy(Collection<TenantContext> tenants,
376                     Map<EgKey, Set<ConditionSet>> egConditions) {
377         // select contracts that apply for the given tenant
378         Table<EgKey, EgKey, List<ContractMatch>> contractMatches =
379                 selectContracts(tenants);
380
381         // select subjects for the matching contracts and resolve the policy
382         // for endpoint group pairs. This does phase (2) and (3) as one step
383         return selectSubjects(contractMatches, egConditions);
384     }
385
386     /**
387      * Choose the contracts that are in scope for each pair of endpoint groups,
388      * then perform subject selection for the pair
389      */
390     protected Table<EgKey, EgKey, List<ContractMatch>>
391             selectContracts(Collection<TenantContext> tenants) {
392         Table<TenantId, ContractId, List<ConsumerContractMatch>> consumerMatches =
393                 HashBasedTable.create();
394         Table<EgKey, EgKey, List<ContractMatch>> contractMatches =
395                 HashBasedTable.create();
396
397         for (TenantContext tenant : tenants) {
398             IndexedTenant t = tenant.tenant.get();
399             if (t == null)
400                 continue;
401             selectContracts(consumerMatches,
402                     contractMatches,
403                     t.getTenant());
404         }
405         return contractMatches;
406     }
407
408     protected void selectContracts(Table<TenantId,
409             ContractId,
410             List<ConsumerContractMatch>> consumerMatches,
411             Table<EgKey, EgKey,
412             List<ContractMatch>> contractMatches,
413             Tenant tenant) {
414         // For each endpoint group, match consumer selectors
415         // against contracts to get a set of matching consumer selectors
416         if (tenant.getEndpointGroup() == null)
417             return;
418         for (EndpointGroup group : tenant.getEndpointGroup()) {
419             List<ConsumerContractMatch> r =
420                     matchConsumerContracts(tenant, group);
421             for (ConsumerContractMatch ccm : r) {
422                 List<ConsumerContractMatch> cms =
423                         consumerMatches.get(tenant.getId(),
424                                 ccm.contract.getId());
425                 if (cms == null) {
426                     cms = new ArrayList<>();
427                     consumerMatches.put(tenant.getId(),
428                             ccm.contract.getId(), cms);
429                 }
430                 cms.add(ccm);
431             }
432         }
433
434         // Match provider selectors, and check each match for a corresponding
435         // consumer selector match.
436         for (EndpointGroup group : tenant.getEndpointGroup()) {
437             List<ContractMatch> matches =
438                     matchProviderContracts(tenant, group, consumerMatches);
439             for (ContractMatch cm : matches) {
440                 EgKey consumerKey = new EgKey(cm.consumerTenant.getId(),
441                         cm.consumer.getId());
442                 EgKey providerKey = new EgKey(cm.providerTenant.getId(),
443                         cm.provider.getId());
444                 List<ContractMatch> egPairMatches =
445                         contractMatches.get(consumerKey, providerKey);
446                 if (egPairMatches == null) {
447                     egPairMatches = new ArrayList<>();
448                     contractMatches.put(consumerKey, providerKey,
449                             egPairMatches);
450                 }
451
452                 egPairMatches.add(cm);
453             }
454         }
455     }
456
457     private boolean clauseMatches(Clause clause, ContractMatch match) {
458         if (clause.getConsumerMatchers() != null) {
459             GroupIdentificationConstraints groupIdentificationConstraintsConsumer = clause.getConsumerMatchers()
460                     .getGroupIdentificationConstraints();
461             if (groupIdentificationConstraintsConsumer instanceof GroupRequirementConstraintCase) {
462                 List<RequirementMatcher> reqMatchers = ((GroupRequirementConstraintCase) groupIdentificationConstraintsConsumer)
463                         .getRequirementMatcher();
464                 if (reqMatchers != null) {
465                     for (RequirementMatcher reqMatcher : reqMatchers) {
466                         if (!MatcherUtils.applyReqMatcher(reqMatcher,
467                                 match.consumerRelator)) {
468                             return false;
469                         }
470                     }
471                 }
472             }
473         }
474         if (clause.getProviderMatchers() != null) {
475             org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.GroupIdentificationConstraints groupIdentificationConstraintsProvider = clause
476                     .getProviderMatchers().getGroupIdentificationConstraints();
477             if (groupIdentificationConstraintsProvider instanceof GroupCapabilityConstraintCase) {
478                 List<CapabilityMatcher> capMatchers = ((GroupCapabilityConstraintCase) groupIdentificationConstraintsProvider)
479                         .getCapabilityMatcher();
480
481                 if (capMatchers != null) {
482                     for (CapabilityMatcher capMatcher : capMatchers) {
483                         if (!MatcherUtils.applyCapMatcher(capMatcher,
484                                 match.providerRelator)) {
485                             return false;
486                         }
487                     }
488                 }
489             }
490         }
491         return true;
492     }
493
494     private ConditionSet buildConditionSet(List<ConditionMatcher> condMatchers) {
495         if (condMatchers == null)
496             return ConditionSet.EMPTY;
497
498         ImmutableSet.Builder<ConditionName> allb = ImmutableSet.builder();
499         ImmutableSet.Builder<ConditionName> noneb = ImmutableSet.builder();
500         ImmutableSet.Builder<Set<ConditionName>> anyb =
501                 ImmutableSet.builder();
502         for (ConditionMatcher condMatcher : condMatchers) {
503             if (condMatcher.getCondition() == null)
504                 continue;
505             MatchType type = condMatcher.getMatchType();
506             if (type == null)
507                 type = MatchType.All;
508             if (type.equals(MatchType.Any)) {
509                 ImmutableSet.Builder<ConditionName> a =
510                         ImmutableSet.builder();
511                 for (Condition c : condMatcher.getCondition()) {
512                     a.add(c.getName());
513                 }
514                 anyb.add(a.build());
515             } else {
516                 for (Condition c : condMatcher.getCondition()) {
517                     switch (type) {
518                     case Any:
519                         break;
520                     case None:
521                         noneb.add(c.getName());
522                         break;
523                     case All:
524                     default:
525                         allb.add(c.getName());
526                         break;
527                     }
528                 }
529             }
530         }
531         return new ConditionSet(allb.build(), noneb.build(), anyb.build());
532     }
533
534     private ConditionSet buildConsConditionSet(Clause clause) {
535         if (clause.getConsumerMatchers() != null) {
536             List<ConditionMatcher> condMatchers =
537                     clause.getConsumerMatchers().getConditionMatcher();
538             return buildConditionSet(condMatchers);
539         }
540         return ConditionSet.EMPTY;
541     }
542
543     private ConditionSet buildProvConditionSet(Clause clause) {
544         if (clause.getProviderMatchers() != null) {
545             List<ConditionMatcher> condMatchers =
546                     clause.getProviderMatchers().getConditionMatcher();
547             return buildConditionSet(condMatchers);
548         }
549         return ConditionSet.EMPTY;
550     }
551
552     private Policy resolvePolicy(Tenant contractTenant,
553             Contract contract,
554             boolean reverse,
555             Policy merge,
556             Table<ConditionSet, ConditionSet,
557             List<Subject>> subjectMap) {
558         Table<ConditionSet, ConditionSet, List<RuleGroup>> ruleMap =
559                 HashBasedTable.create();
560         if (merge != null) {
561             ruleMap.putAll(merge.ruleMap);
562         }
563         for (Cell<ConditionSet, ConditionSet, List<Subject>> entry : subjectMap.cellSet()) {
564             List<RuleGroup> rules = new ArrayList<>();
565             ConditionSet rowKey = entry.getRowKey();
566             ConditionSet columnKey = entry.getColumnKey();
567             if (reverse) {
568                 rowKey = columnKey;
569                 columnKey = entry.getRowKey();
570             }
571             List<RuleGroup> oldrules =
572                     ruleMap.get(rowKey, columnKey);
573             if (oldrules != null) {
574                 rules.addAll(oldrules);
575             }
576             for (Subject s : entry.getValue()) {
577                 if (s.getRule() == null)
578                     continue;
579                 List<Rule> srules;
580                 if (reverse)
581                     srules = reverseRules(s.getRule());
582                 else
583                     srules = Ordering
584                             .from(TenantUtils.RULE_COMPARATOR)
585                             .immutableSortedCopy(s.getRule());
586
587                 RuleGroup rg = new RuleGroup(srules, s.getOrder(),
588                         contractTenant, contract,
589                         s.getName());
590                 rules.add(rg);
591             }
592             Collections.sort(rules);
593             ruleMap.put(rowKey, columnKey,
594                     Collections.unmodifiableList(rules));
595         }
596         return new Policy(ruleMap);
597     }
598
599     private List<Rule> reverseRules(List<Rule> rules) {
600         ArrayList<Rule> nrules = new ArrayList<>();
601         for (Rule input : rules) {
602             if (input.getClassifierRef() == null ||
603                     input.getClassifierRef().size() == 0) {
604                 nrules.add(input);
605                 continue;
606             }
607
608             List<ClassifierRef> classifiers = new ArrayList<>();
609             for (ClassifierRef clr : input.getClassifierRef()) {
610                 Direction nd = Direction.Bidirectional;
611                 if (clr.getDirection() != null) {
612                     switch (clr.getDirection()) {
613                     case In:
614                         nd = Direction.Out;
615                         break;
616                     case Out:
617                         nd = Direction.In;
618                         break;
619                     case Bidirectional:
620                     default:
621                         nd = Direction.Bidirectional;
622                     }
623                 }
624                 classifiers.add(new ClassifierRefBuilder(clr)
625                         .setDirection(nd).build());
626             }
627             nrules.add(new RuleBuilder(input)
628                     .setClassifierRef(Collections.unmodifiableList(classifiers))
629                     .build());
630         }
631         Collections.sort(nrules, TenantUtils.RULE_COMPARATOR);
632         return Collections.unmodifiableList(nrules);
633     }
634
635     /**
636      * Get the "natural" direction for the policy for the given pair of endpoint
637      * groups.
638      *
639      * @param one
640      *            The first endpoint group
641      * @param two
642      *            The second endpoint group
643      * @return true if the order should be reversed in the index
644      */
645     protected static boolean shouldReverse(EgKey one, EgKey two) {
646         if (one.compareTo(two) < 0) {
647             return true;
648         }
649         return false;
650     }
651
652     private void addConditionSet(EgKey eg, ConditionSet cs,
653             Map<EgKey, Set<ConditionSet>> egConditions) {
654         if (egConditions == null)
655             return;
656         Set<ConditionSet> cset = egConditions.get(eg);
657         if (cset == null) {
658             egConditions.put(eg, cset = new HashSet<>());
659         }
660         cset.add(cs);
661     }
662
663     /**
664      * Choose the set of subjects that in scope for each possible set of
665      * endpoint conditions
666      */
667     protected Table<EgKey, EgKey, Policy>
668             selectSubjects(Table<EgKey, EgKey,
669                     List<ContractMatch>> contractMatches,
670                     Map<EgKey, Set<ConditionSet>> egConditions) {
671         // TODO: Note that it's possible to further simplify the resulting
672         // policy
673         // in the case of things like repeated rules, condition sets that
674         // cover other condition sets, etc. This would be a good thing to do
675         // at some point
676         Table<EgKey, EgKey, Policy> policy = HashBasedTable.create();
677
678         for (List<ContractMatch> matches : contractMatches.values()) {
679             for (ContractMatch match : matches) {
680                 List<Clause> clauses = match.contract.getClause();
681                 if (clauses == null)
682                     continue;
683
684                 List<Subject> subjectList = match.contract.getSubject();
685                 if (subjectList == null)
686                     continue;
687
688                 EgKey ckey = new EgKey(match.consumerTenant.getId(),
689                         match.consumer.getId());
690                 EgKey pkey = new EgKey(match.providerTenant.getId(),
691                         match.provider.getId());
692                 EgKey one = ckey;
693                 EgKey two = pkey;
694                 boolean reverse = shouldReverse(ckey, pkey);
695                 if (reverse) {
696                     one = pkey;
697                     two = ckey;
698                 }
699                 Policy existing = policy.get(one, two);
700
701                 HashMap<SubjectName, Subject> subjects = new HashMap<>();
702                 for (Subject s : subjectList) {
703                     subjects.put(s.getName(), s);
704                 }
705
706                 Table<ConditionSet, ConditionSet, List<Subject>> subjectMap =
707                         HashBasedTable.create();
708
709                 for (Clause clause : clauses) {
710                     if (clause.getSubjectRefs() != null &&
711                             clauseMatches(clause, match)) {
712                         ConditionSet consCSet = buildConsConditionSet(clause);
713                         addConditionSet(ckey, consCSet, egConditions);
714                         ConditionSet provCSet = buildProvConditionSet(clause);
715                         addConditionSet(pkey, provCSet, egConditions);
716                         List<Subject> clauseSubjects =
717                                 subjectMap.get(consCSet, provCSet);
718                         if (clauseSubjects == null) {
719                             clauseSubjects = new ArrayList<>();
720                             subjectMap.put(consCSet, provCSet, clauseSubjects);
721                         }
722                         for (SubjectName sn : clause.getSubjectRefs()) {
723                             Subject s = subjects.get(sn);
724                             if (s != null)
725                                 clauseSubjects.add(s);
726                         }
727                     }
728                 }
729
730                 policy.put(one, two,
731                         resolvePolicy(match.contractTenant,
732                                 match.contract,
733                                 reverse,
734                                 existing,
735                                 subjectMap));
736             }
737         }
738
739         return policy;
740     }
741
742     private List<ConsumerContractMatch> matchConsumerContracts(Tenant tenant,
743             EndpointGroup consumer) {
744         List<ConsumerContractMatch> matches = new ArrayList<>();
745         if (consumer.getConsumerNamedSelector() != null) {
746             for (ConsumerNamedSelector cns : consumer.getConsumerNamedSelector()) {
747                 if (cns.getContract() == null)
748                     continue;
749                 for (ContractId contractId : cns.getContract()) {
750                     Contract contract =
751                             TenantUtils.findContract(tenant, contractId);
752                     if (contract == null)
753                         continue;
754                     matches.add(new ConsumerContractMatch(tenant, contract,
755                             tenant, consumer,
756                             cns));
757                 }
758             }
759         }
760         if (consumer.getConsumerTargetSelector() != null) {
761             for (ConsumerTargetSelector cts : consumer.getConsumerTargetSelector()) {
762                 if (tenant.getContract() == null)
763                     continue;
764                 for (Contract contract : tenant.getContract()) {
765                     if (contract.getTarget() == null)
766                         continue;
767                     for (Target t : contract.getTarget()) {
768                         boolean match = true;
769                         if (cts.getQualityMatcher() != null) {
770                             for (QualityMatcher m : cts.getQualityMatcher()) {
771                                 if (!MatcherUtils.applyQualityMatcher(m, t)) {
772                                     match = false;
773                                     break;
774                                 }
775                             }
776                         }
777                         if (match) {
778                             matches.add(new ConsumerContractMatch(tenant,
779                                     contract,
780                                     tenant,
781                                     consumer,
782                                     cts));
783                         }
784                     }
785                 }
786             }
787         }
788         // TODO match selectors also against contract references
789         // for (ConsumerTargetSelector cts :
790         // consumer.getConsumerTargetSelector()) {
791         // if (tenant.getContractRef() == null) continue;
792         // for (ContractRef c : tenant.getContractRef()) {
793         //
794         // }
795         // }
796         return matches;
797     }
798
799     private void amendContractMatches(List<ContractMatch> matches,
800             List<ConsumerContractMatch> cMatches,
801             Tenant tenant, EndpointGroup provider,
802             ProviderSelectionRelator relator) {
803         if (cMatches == null)
804             return;
805         for (ConsumerContractMatch cMatch : cMatches) {
806             matches.add(new ContractMatch(cMatch, tenant, provider, relator));
807         }
808     }
809
810     private List<ContractMatch>
811             matchProviderContracts(Tenant tenant, EndpointGroup provider,
812                     Table<TenantId,
813                     ContractId,
814                     List<ConsumerContractMatch>> consumerMatches) {
815         List<ContractMatch> matches = new ArrayList<>();
816         if (provider.getProviderNamedSelector() != null) {
817             for (ProviderNamedSelector pns : provider.getProviderNamedSelector()) {
818                 if (pns.getContract() == null)
819                     continue;
820                 for (ContractId contractId : pns.getContract()) {
821                     Contract c = TenantUtils.findContract(tenant, contractId);
822                     if (c == null)
823                         continue;
824                     List<ConsumerContractMatch> cMatches =
825                             consumerMatches.get(tenant.getId(), c.getId());
826                     amendContractMatches(matches, cMatches, tenant, provider, pns);
827                 }
828             }
829         }
830         if (provider.getProviderTargetSelector() != null) {
831             for (ProviderTargetSelector pts : provider.getProviderTargetSelector()) {
832                 if (tenant.getContract() == null)
833                     continue;
834                 for (Contract c : tenant.getContract()) {
835                     if (c.getTarget() == null)
836                         continue;
837                     for (Target t : c.getTarget()) {
838                         boolean match = true;
839                         if (pts.getQualityMatcher() != null) {
840                             for (QualityMatcher m : pts.getQualityMatcher()) {
841                                 if (!MatcherUtils.applyQualityMatcher(m, t)) {
842                                     match = false;
843                                     break;
844                                 }
845                             }
846                         }
847                         if (match) {
848                             List<ConsumerContractMatch> cMatches =
849                                     consumerMatches.get(tenant.getId(),
850                                             c.getId());
851                             amendContractMatches(matches, cMatches, tenant,
852                                     provider, pts);
853
854                         }
855                     }
856                 }
857             }
858         }
859         return matches;
860     }
861
862     protected static class TenantContext {
863         ListenerRegistration<DataChangeListener> registration;
864
865         AtomicReference<IndexedTenant> tenant = new AtomicReference<>();
866
867         public TenantContext(ListenerRegistration<DataChangeListener> registration) {
868             super();
869             this.registration = registration;
870         }
871     }
872
873     /**
874      * Represents a selected contract made by endpoint groups matching it using
875      * selection relators. This is the result of the contract selection phase.
876      *
877      * @author readams
878      *
879      */
880     @Immutable
881     protected static class ContractMatch extends ConsumerContractMatch {
882         /**
883          * The tenant ID of the provider endpoint group
884          */
885         final Tenant providerTenant;
886
887         /**
888          * The provider endpoint group
889          */
890         final EndpointGroup provider;
891
892         /**
893          * The provider selection relator that was used to match the contract
894          */
895         final ProviderSelectionRelator providerRelator;
896
897         public ContractMatch(ConsumerContractMatch consumerMatch,
898                 Tenant providerTenant, EndpointGroup provider,
899                 ProviderSelectionRelator providerRelator) {
900             super(consumerMatch.contractTenant,
901                     consumerMatch.contract,
902                     consumerMatch.consumerTenant,
903                     consumerMatch.consumer,
904                     consumerMatch.consumerRelator);
905             this.providerTenant = providerTenant;
906             this.provider = provider;
907             this.providerRelator = providerRelator;
908         }
909     }
910
911     @Immutable
912     private static class ConsumerContractMatch {
913         /**
914          * The tenant of the matching contract
915          */
916         final Tenant contractTenant;
917
918         /**
919          * The matching contract
920          */
921         final Contract contract;
922
923         /**
924          * The tenant for the endpoint group
925          */
926         final Tenant consumerTenant;
927
928         /**
929          * The consumer endpoint group
930          */
931         final EndpointGroup consumer;
932
933         /**
934          * The consumer selection relator that was used to match the contract
935          */
936         final ConsumerSelectionRelator consumerRelator;
937
938         public ConsumerContractMatch(Tenant contractTenant,
939                 Contract contract,
940                 Tenant consumerTenant,
941                 EndpointGroup consumer,
942                 ConsumerSelectionRelator consumerRelator) {
943             super();
944             this.contractTenant = contractTenant;
945             this.contract = contract;
946             this.consumerTenant = consumerTenant;
947             this.consumer = consumer;
948             this.consumerRelator = consumerRelator;
949         }
950     }
951
952     @Immutable
953     private class PolicyChangeListener implements DataChangeListener {
954         final TenantId tenantId;
955
956         public PolicyChangeListener(TenantId tenantId) {
957             super();
958             this.tenantId = tenantId;
959         }
960
961         @Override
962         public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> arg0) {
963             updateTenant(tenantId);
964         }
965
966     }
967 }