Onward!
[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.Collections;
13 import java.util.HashMap;
14 import java.util.List;
15 import java.util.Set;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.ConcurrentMap;
18 import java.util.concurrent.CopyOnWriteArrayList;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.concurrent.atomic.AtomicReference;
21
22 import javax.annotation.concurrent.Immutable;
23
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
26 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
27 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
28 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.groupbasedpolicy.resolver.PolicyCache.ConditionSet;
31 import org.opendaylight.groupbasedpolicy.resolver.PolicyCache.Policy;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContractId;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubjectName;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.ConsumerSelectionRelator;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Matcher.MatchType;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.ProviderSelectionRelator;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.condition.matchers.ConditionMatcher;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.conditions.Condition;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.target.selector.QualityMatcher;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Contract;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Clause;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Subject;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Target;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.RequirementMatcher;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.CapabilityMatcher;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.subject.Rule;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ConsumerNamedSelector;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ConsumerTargetSelector;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderNamedSelector;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderTargetSelector;
56 import org.opendaylight.yangtools.concepts.ListenerRegistration;
57 import org.opendaylight.yangtools.yang.binding.DataObject;
58 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 import com.google.common.base.Optional;
63 import com.google.common.base.Predicate;
64 import com.google.common.collect.HashBasedTable;
65 import com.google.common.collect.ImmutableSet;
66 import com.google.common.collect.Ordering;
67 import com.google.common.collect.Sets;
68 import com.google.common.collect.Table;
69 import com.google.common.collect.Table.Cell;
70 import com.google.common.util.concurrent.FutureCallback;
71 import com.google.common.util.concurrent.Futures;
72 import com.google.common.util.concurrent.ListenableFuture;
73
74 /**
75  * The policy resolver is a utility for renderers to help in resolving
76  * group-based policy into a form that is easier to apply to the actual network.
77  * 
78  * <p>For any pair of endpoint groups, there is a set of rules that could apply 
79  * to the endpoints on that group based on the policy configuration.  The exact
80  * list of rules that apply to a given pair of endpoints depends on the 
81  * conditions that are active on the endpoints.
82  * 
83  * In a more formal sense: Let there be endpoint groups G_n, and for each G_n a 
84  * set of conditions C_n that can apply to endpoints in G_n.  Further, let S be 
85  * the set of lists of rules defined in the policy.  Our policy can be 
86  * represented as a function F: (G_n, 2^C_n, G_m, 2^C_m) -> S, where 2^C_n 
87  * represents the power set of C_n. In other words, we want to map all the 
88  * possible tuples of pairs of endpoints along with their active conditions 
89  * onto the right list of rules to apply.
90  * 
91  * <p>We need to be able to query against this policy model, enumerate the 
92  * relevant classes of traffic and endpoints, and notify renderers when there
93  * are changes to policy as it applies to active sets of endpoints and 
94  * endpoint groups.
95  * 
96  * <p>The policy resolver will maintain the necessary state for all tenants
97  * in its control domain, which is the set of tenants for which 
98  * policy listeners have been registered.
99  * 
100  * @author readams
101  */
102 public class PolicyResolver implements AutoCloseable {
103     private static final Logger LOG = LoggerFactory.getLogger(PolicyResolver.class);
104
105     private final DataBroker dataProvider;
106     private final ScheduledExecutorService executor;
107     
108     /**
109      *  Keep track of the current relevant policy scopes.
110      */
111     private CopyOnWriteArrayList<PolicyScope> policyListenerScopes;
112     
113     private ConcurrentMap<TenantId, TenantContext> resolvedTenants;
114     
115     private PolicyCache policyCache = new PolicyCache();
116     
117     public PolicyResolver(DataBroker dataProvider,
118                           ScheduledExecutorService executor) {
119         super();
120         this.dataProvider = dataProvider;
121         this.executor = executor;
122         policyListenerScopes = new CopyOnWriteArrayList<>();
123         resolvedTenants = new ConcurrentHashMap<>();
124         
125         LOG.debug("Initialized renderer common policy resolver");
126     }
127
128     // *************
129     // AutoCloseable
130     // *************
131
132     @Override
133     public void close() throws Exception {
134         for (TenantContext ctx : resolvedTenants.values()) {
135             if (ctx.registration != null)
136                 ctx.registration.close();
137         }
138     }
139
140     // *************************
141     // PolicyResolver public API
142     // *************************
143
144     /**
145      * Get the policy that currently applies to a pair of endpoints. 
146      * with the specified groups and conditions.  The first endpoint acts as
147      * the consumer and the second endpoint acts as the provider, so to get 
148      * all policy related to this pair of endpoints you must call this
149      * function twice: once for each possible order of endpoints.
150      * 
151      * @param ep1Tenant the tenant ID for the first endpoint 
152      * @param ep1Group the endpoint group for the first endpoint 
153      * @param ep1Conds The conditions that apply to the first endpoint
154      * @param ep2Tenant the tenant ID for the second endpoint
155      * @param ep2Group the endpoint group for the second endpoint 
156      * @param ep2Conds The conditions that apply to the second endpoint.
157      * @return a list of {@link RuleGroup} that apply to the endpoints.
158      * Cannot be null, but may be an empty list of rulegroups
159      */
160     public List<RuleGroup> getPolicy(TenantId ep1Tenant,
161                                      EndpointGroupId ep1Group, 
162                                      ConditionSet ep1Conds,
163                                      TenantId ep2Tenant,
164                                      EndpointGroupId ep2Group, 
165                                      ConditionSet ep2Conds) {
166         return policyCache.getPolicy(ep1Tenant, ep1Group, ep1Conds, 
167                                      ep2Tenant, ep2Group, ep2Conds);
168     }
169
170     /**
171      * Register a listener to receive update events.
172      * @param listener the {@link PolicyListener} object to receive the update
173      * events
174      */
175     public PolicyScope registerListener(PolicyListener listener) {
176         PolicyScope ps = new PolicyScope(this, listener);
177         policyListenerScopes.add(ps);
178         
179         return ps;
180     }
181     
182     /**
183      * Remove the listener registered for the given {@link PolicyScope}.
184      * @param scope the scope to remove
185      * @see PolicyResolver#registerListener(PolicyListener)
186      */
187     public void removeListener(PolicyScope scope) {
188         policyListenerScopes.remove(scope);        
189     }
190
191     // **************
192     // Implementation
193     // **************
194     
195     /**
196      * Notify the policy listeners about a set of updated consumers
197      */
198     private void notifyListeners(Set<EgKey> updatedConsumers) {
199         for (final PolicyScope scope : policyListenerScopes) {
200             Set<EgKey> filtered = 
201                     Sets.filter(updatedConsumers, new Predicate<EgKey>() {
202                         @Override
203                         public boolean apply(EgKey input) {
204                             return scope.contains(input.getTenantId(), 
205                                                   input.getEgId());
206                         }
207                     });
208             if (!filtered.isEmpty()) {
209                 scope.getListener().policyUpdated(filtered);
210             }
211         }
212     }
213     
214     /**
215      * Subscribe the resolver to updates related to a particular tenant
216      * Make sure that this can't be called concurrently with subscribe
217      * @param tenantId the tenant ID to subscribe to
218      */
219     protected void subscribeTenant(TenantId tenantId) {
220         if (!resolvedTenants.containsKey(tenantId))
221             updateTenant(tenantId);
222     }
223
224     /**
225      * Unsubscribe the resolver from updates related to a particular tenant
226      * Make sure that this can't be called concurrently with subscribe
227      * @param tenantId the tenant ID to subscribe to
228      */
229     protected void unsubscribeTenant(TenantId tenantId) {
230         TenantContext context = resolvedTenants.get(tenantId);
231         if (context != null) {
232             resolvedTenants.remove(tenantId);
233             context.registration.close();
234         }
235     }
236
237     private void updateTenant(final TenantId tenantId) {
238         TenantContext context = resolvedTenants.get(tenantId);
239         if (context == null) {
240             ListenerRegistration<DataChangeListener> registration = 
241                     dataProvider.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
242                                                             TenantUtils.tenantIid(tenantId), 
243                                                             new PolicyChangeListener(tenantId),
244                                                             DataChangeScope.SUBTREE);
245
246             context = new TenantContext(tenantId, registration);
247             TenantContext oldContext = 
248                     resolvedTenants.putIfAbsent(tenantId, context);
249             if (oldContext != null) {
250                 // already registered in a different thread; just use the other
251                 // context
252                 registration.close();
253                 context = oldContext;
254             }
255         }
256
257         // Resolve the new tenant and update atomically
258         final AtomicReference<Tenant> tenantRef = context.tenant;
259         final Tenant ot = tenantRef.get();
260         ReadOnlyTransaction transaction = 
261                 dataProvider.newReadOnlyTransaction();
262         InstanceIdentifier<Tenant> tiid = TenantUtils.tenantIid(tenantId);
263         ListenableFuture<Optional<DataObject>> unresolved;
264
265         unresolved = transaction.read(LogicalDatastoreType.CONFIGURATION, tiid);
266         
267         Futures.addCallback(unresolved, new FutureCallback<Optional<DataObject>>() {
268             @Override
269             public void onSuccess(Optional<DataObject> result) {
270                 if (!result.isPresent()) return;
271
272                 Tenant t = InheritanceUtils.resolveTenant((Tenant)result.get());
273                 if (!tenantRef.compareAndSet(ot, t)) {
274                     // concurrent update of tenant policy.  Retry
275                     updateTenant(tenantId);
276                 } else {
277                     // Update the policy cache and notify listeners
278                     Table<EgKey, EgKey, Policy> policy = resolvePolicy(t);        
279                     Set<EgKey> updatedConsumers = 
280                             policyCache.updatePolicy(policy, policyListenerScopes);
281
282                     notifyListeners(updatedConsumers);
283                 }
284             }
285
286             @Override
287             public void onFailure(Throwable t) {
288                 LOG.error("Count not get tenant {}", tenantId, t);
289             }
290         }, executor);
291     }
292     
293     
294     /**
295      * Resolve the policy in three phases:
296      * (1) select contracts that in scope based on contract selectors. 
297      * (2) select subjects that are in scope for each contract based on
298      * matchers in clauses
299      * (3) resolve the set of in-scope contracts into a list of subjects that
300      * apply for each pair of endpoint groups and the conditions that can 
301      * apply for for each endpoint in those groups.
302      */
303     protected Table<EgKey, EgKey, Policy> resolvePolicy(Tenant t) {
304         // select contracts that apply for the given tenant
305         Table<EgKey, EgKey, List<ContractMatch>> contractMatches =
306                 selectContracts(t);
307         
308         // select subjects for the matching contracts and resolve the policy
309         // for endpoint group pairs.  This does phase (2) and (3) as one step
310         return selectSubjects(contractMatches);
311     }
312     
313     /**
314      * Choose the contracts that are in scope for each pair of endpoint
315      * groups, then perform subject selection for the pair
316      */
317     protected Table<EgKey, EgKey, List<ContractMatch>> 
318         selectContracts(Tenant tenant) {
319         // For each endpoint group, match consumer selectors 
320         // against contracts to get a set of matching consumer selectors
321         Table<TenantId, ContractId, List<ConsumerContractMatch>> consumerMatches = 
322                 HashBasedTable.create();
323         if (tenant.getEndpointGroup() == null) return HashBasedTable.create();
324         for (EndpointGroup group : tenant.getEndpointGroup()) {
325             List<ConsumerContractMatch> r = 
326                     matchConsumerContracts(tenant, group);
327             for (ConsumerContractMatch ccm : r) {
328                 List<ConsumerContractMatch> cms = 
329                         consumerMatches.get(tenant.getId(), 
330                                             ccm.contract.getId());
331                 if (cms == null) {
332                     cms = new ArrayList<>();
333                     consumerMatches.put(tenant.getId(), 
334                                         ccm.contract.getId(), cms);
335                 }
336                 cms.add(ccm);
337             }
338         }
339         
340         // Match provider selectors, and check each match for a corresponding
341         // consumer selector match.
342         Table<EgKey, EgKey, List<ContractMatch>> contractMatches = 
343                 HashBasedTable.create();
344         for (EndpointGroup group : tenant.getEndpointGroup()) {
345             List<ContractMatch> matches = 
346                     matchProviderContracts(tenant, group, consumerMatches);
347             for (ContractMatch cm : matches) {
348                 EgKey consumerKey = new EgKey(cm.consumerTenant.getId(), 
349                                               cm.consumer.getId());
350                 EgKey providerKey = new EgKey(cm.providerTenant.getId(), 
351                                               cm.provider.getId());
352                 List<ContractMatch> egPairMatches =
353                         contractMatches.get(consumerKey, providerKey);
354                 if (egPairMatches == null) {
355                     egPairMatches = new ArrayList<>();
356                     contractMatches.put(consumerKey, providerKey,
357                                         egPairMatches);
358                 }
359
360                 egPairMatches.add(cm);
361             }
362         }
363         return contractMatches;
364     }
365     
366     private boolean clauseMatches(Clause clause, ContractMatch match) {
367         if (clause.getConsumerMatchers() != null) {
368             List<RequirementMatcher> reqMatchers = 
369                     clause.getConsumerMatchers().getRequirementMatcher();
370             if (reqMatchers != null) {
371                 for (RequirementMatcher reqMatcher : reqMatchers) {
372                     if (!MatcherUtils.applyReqMatcher(reqMatcher, 
373                                                       match.consumerRelator)) {
374                         return false;
375                     }
376                 }
377             }
378         }
379         if (clause.getProviderMatchers() != null) {
380             List<CapabilityMatcher> capMatchers = 
381                     clause.getProviderMatchers().getCapabilityMatcher();
382             if (capMatchers != null) {
383                 for (CapabilityMatcher capMatcher : capMatchers) {
384                     if (!MatcherUtils.applyCapMatcher(capMatcher, 
385                                                       match.providerRelator)) {
386                         return false;
387                     }
388                 }
389             }
390         }
391         return true;
392     }
393
394     private ConditionSet buildConditionSet(List<ConditionMatcher> condMatchers) {
395         if (condMatchers == null) return ConditionSet.EMPTY;
396
397         ImmutableSet.Builder<ConditionName> allb = ImmutableSet.builder();
398         ImmutableSet.Builder<ConditionName> noneb = ImmutableSet.builder();
399         ImmutableSet.Builder<Set<ConditionName>> anyb = 
400                 ImmutableSet.builder();
401         for (ConditionMatcher condMatcher : condMatchers) {
402             if (condMatcher.getCondition() == null)
403                 continue;
404             MatchType type = condMatcher.getMatchType();
405             if (type == null) type = MatchType.All;
406             if (type.equals(MatchType.Any)) {
407                 ImmutableSet.Builder<ConditionName> a = 
408                         ImmutableSet.builder();
409                 for (Condition c : condMatcher.getCondition()) {
410                     a.add(c.getName());
411                 }
412                 anyb.add(a.build());
413             } else { 
414                 for (Condition c : condMatcher.getCondition()) {
415                     switch (type) {
416                     case Any:
417                         break;
418                     case None:
419                         noneb.add(c.getName());
420                         break;
421                     case All:
422                     default:
423                         allb.add(c.getName());
424                         break;
425                     }
426                 }
427             }
428         }
429         return new ConditionSet(allb.build(), noneb.build(), anyb.build());
430     }
431     
432     private ConditionSet buildConsConditionSet(Clause clause) {
433         if (clause.getConsumerMatchers() != null) {
434             List<ConditionMatcher> condMatchers =
435                     clause.getConsumerMatchers().getConditionMatcher();
436             return buildConditionSet(condMatchers);
437         }
438         return ConditionSet.EMPTY;
439     }
440
441     private ConditionSet buildProvConditionSet(Clause clause) {
442         if (clause.getProviderMatchers() != null) {
443             List<ConditionMatcher> condMatchers =
444                     clause.getProviderMatchers().getConditionMatcher();
445             return buildConditionSet(condMatchers);
446         }
447         return ConditionSet.EMPTY;
448     }
449     
450     private Policy resolvePolicy(Tenant contractTenant,
451                                  Contract contract,
452                                  Policy merge,
453                                  Table<ConditionSet, ConditionSet, List<Subject>> subjectMap) {
454         Table<ConditionSet, ConditionSet, List<RuleGroup>> ruleMap = 
455                 HashBasedTable.create();
456         if (merge != null) {
457             ruleMap.putAll(merge.ruleMap);
458         }
459         for (Cell<ConditionSet, ConditionSet, List<Subject>> entry : 
460                 subjectMap.cellSet()) {
461             List<RuleGroup> rules = new ArrayList<>();
462             List<RuleGroup> oldrules = 
463                     ruleMap.get(entry.getRowKey(), entry.getColumnKey());
464             if (oldrules != null) {
465                 rules.addAll(oldrules);
466             }
467             for (Subject s : entry.getValue()) {
468                 if (s.getRule() == null) continue;
469                 List<Rule> srules = Ordering
470                         .from(TenantUtils.RULE_COMPARATOR)
471                         .immutableSortedCopy(s.getRule());
472                 RuleGroup rg = new RuleGroup(srules, s.getOrder(),
473                                              contractTenant, contract,
474                                              s.getName());
475                 rules.add(rg);
476             }
477             Collections.sort(rules);
478             ruleMap.put(entry.getRowKey(), entry.getColumnKey(), 
479                         Collections.unmodifiableList(rules));
480         }
481         return new Policy(ruleMap);
482     }
483     
484     /**
485      * Choose the set of subjects that in scope for each possible set of 
486      * endpoint conditions
487      */
488     protected Table<EgKey, EgKey, Policy> 
489             selectSubjects(Table<EgKey, EgKey, 
490                                  List<ContractMatch>> contractMatches) {
491         // Note that it's possible to further simplify the resulting policy
492         // in the case of things like repeated rules, condition sets that
493         // cover other condition sets, etc.  This would be a good thing to do
494         // at some point
495         Table<EgKey, EgKey, Policy> policy = HashBasedTable.create();
496
497         for (List<ContractMatch> matches : contractMatches.values()) {
498             for (ContractMatch match : matches) {
499                 List<Clause> clauses = match.contract.getClause();
500                 if (clauses == null) continue;
501
502                 List<Subject> subjectList = match.contract.getSubject();
503                 if (subjectList == null) continue;
504                 
505                 EgKey ckey = new EgKey(match.consumerTenant.getId(),
506                                        match.consumer.getId());
507                 EgKey pkey = new EgKey(match.providerTenant.getId(),
508                                        match.provider.getId());
509                 Policy existing = policy.get(ckey, pkey);
510                 boolean alreadyMatched = false;
511                 if (existing != null) {
512                     for (List<RuleGroup> rgl : existing.ruleMap.values()) {
513                         for (RuleGroup rg : rgl) {
514                             if (rg.relatedContract == match.contract) {
515                                 alreadyMatched = true;
516                                 break;
517                             }
518                         }
519                         if (alreadyMatched) break;
520                     }
521                     if (alreadyMatched) continue;
522                 }
523                 
524                 HashMap<SubjectName, Subject> subjects = new HashMap<>();
525                 for (Subject s : subjectList) {
526                     subjects.put(s.getName(), s);
527                 }
528                 
529                 Table<ConditionSet, ConditionSet, List<Subject>> subjectMap = 
530                         HashBasedTable.create();
531                 
532                 for (Clause clause : clauses) {
533                     if (clause.getSubjectRefs() != null &&
534                         clauseMatches(clause, match)) {
535                         ConditionSet consCSet = buildConsConditionSet(clause);
536                         ConditionSet provCSet = buildProvConditionSet(clause);
537                         List<Subject> clauseSubjects = 
538                                 subjectMap.get(consCSet, provCSet);
539                         if (clauseSubjects == null) {
540                             clauseSubjects = new ArrayList<>();
541                             subjectMap.put(consCSet, provCSet, clauseSubjects);
542                         }
543                         for (SubjectName sn : clause.getSubjectRefs()) {
544                             Subject s = subjects.get(sn);
545                             if (s != null) clauseSubjects.add(s);
546                         }
547                     }
548                 }
549
550                 policy.put(ckey, pkey, 
551                            resolvePolicy(match.contractTenant, 
552                                          match.contract,
553                                          existing, 
554                                          subjectMap));
555             }
556         }
557         
558         return policy;
559     }
560     
561     private List<ConsumerContractMatch> matchConsumerContracts(Tenant tenant,
562                                                                EndpointGroup consumer) {
563         List<ConsumerContractMatch> matches = new ArrayList<>();
564         if (consumer.getConsumerNamedSelector() != null) {
565             for (ConsumerNamedSelector cns : consumer.getConsumerNamedSelector()) {
566                 if (cns.getContract() == null) continue;
567                 for (ContractId contractId : cns.getContract()) {
568                     Contract contract = 
569                             TenantUtils.findContract(tenant, contractId);
570                     if (contract == null) continue;
571                     matches.add(new ConsumerContractMatch(tenant, contract, 
572                                                           tenant, consumer, 
573                                                           cns));
574                 }
575             }
576         }
577         if (consumer.getConsumerTargetSelector() != null) {
578             for (ConsumerTargetSelector cts : consumer.getConsumerTargetSelector()) {
579                 if (tenant.getContract() == null) continue;
580                 for (Contract contract : tenant.getContract()) {
581                     if (contract.getTarget() == null) continue;
582                     for (Target t : contract.getTarget()) {
583                         boolean match = true;
584                         if (cts.getQualityMatcher() != null) {
585                             for (QualityMatcher m : cts.getQualityMatcher()) {
586                                 if (!MatcherUtils.applyQualityMatcher(m, t)) {
587                                     match = false;
588                                     break;
589                                 }
590                             }
591                         }
592                         if (match) {
593                             matches.add(new ConsumerContractMatch(tenant, 
594                                                                   contract, 
595                                                                   tenant, 
596                                                                   consumer, 
597                                                                   cts));
598                         }
599                     }
600                 }
601             }
602         }
603         // TODO match selectors also against contract references
604 //        for (ConsumerTargetSelector cts : consumer.getConsumerTargetSelector()) {
605 //            if (tenant.getContractRef() == null) continue;
606 //            for (ContractRef c : tenant.getContractRef()) {
607 //                
608 //            }
609 //        }
610         return matches;
611     }
612
613     private void amendContractMatches(List<ContractMatch> matches,
614                                       List<ConsumerContractMatch> cMatches,
615                                       Tenant tenant, EndpointGroup provider, 
616                                       ProviderSelectionRelator relator) {
617         if (cMatches == null) return;
618         for (ConsumerContractMatch cMatch : cMatches) {
619             matches.add(new ContractMatch(cMatch, tenant, provider, relator));
620         }
621     }
622     
623     private List<ContractMatch> 
624         matchProviderContracts(Tenant tenant, EndpointGroup provider,
625                                Table<TenantId, 
626                                      ContractId, 
627                                      List<ConsumerContractMatch>> consumerMatches) {
628         List<ContractMatch> matches = new ArrayList<>();
629         if (provider.getProviderNamedSelector() != null) {
630             for (ProviderNamedSelector pns : provider.getProviderNamedSelector()) {
631                 if (pns.getContract() == null) continue;
632                 for (ContractId contractId : pns.getContract()) {
633                     Contract c = TenantUtils.findContract(tenant, contractId);
634                     if (c == null) continue;
635                     List<ConsumerContractMatch> cMatches = 
636                             consumerMatches.get(tenant.getId(), c.getId());
637                     amendContractMatches(matches, cMatches, tenant, provider, pns);
638                 }
639             }
640         }
641         if (provider.getProviderTargetSelector() != null) {
642             for (ProviderTargetSelector pts : provider.getProviderTargetSelector()) {
643                 if (tenant.getContract() == null) continue;
644                 for (Contract c : tenant.getContract()) {
645                     if (c.getTarget() == null) continue;
646                     for (Target t : c.getTarget()) {
647                         boolean match = true;
648                         if (pts.getQualityMatcher() != null) {
649                             for (QualityMatcher m : pts.getQualityMatcher()) {
650                                 if (!MatcherUtils.applyQualityMatcher(m, t)) {
651                                     match = false;
652                                     break;
653                                 }
654                             }
655                         }
656                         if (match) {
657                             List<ConsumerContractMatch> cMatches = 
658                                     consumerMatches.get(tenant.getId(), 
659                                                         c.getId());
660                             amendContractMatches(matches, cMatches, tenant, 
661                                                  provider, pts);
662
663                         }
664                     }
665                 }
666             }
667         }
668         return matches;
669     }
670
671     private static class TenantContext {
672         //TenantId tenantId;
673         ListenerRegistration<DataChangeListener> registration;
674
675         AtomicReference<Tenant> tenant = new AtomicReference<Tenant>();
676         
677         public TenantContext(TenantId tenantId,
678                              ListenerRegistration<DataChangeListener> registration) {
679             super();
680             //this.tenantId = tenantId;
681             this.registration = registration;
682         }
683     }
684     
685     /**
686      * Represents a selected contract made by endpoint groups matching it
687      * using selection relators.  This is the result of the contract selection
688      * phase.
689      * @author readams
690      *
691      */
692     @Immutable
693     protected static class ContractMatch extends ConsumerContractMatch {
694         /**
695          * The tenant ID of the provider endpoint group
696          */
697         final Tenant providerTenant;
698         
699         /**
700          * The provider endpoint group
701          */
702         final EndpointGroup provider;
703         
704         /**
705          * The provider selection relator that was used to match the contract
706          */
707         final ProviderSelectionRelator providerRelator;
708
709         public ContractMatch(ConsumerContractMatch consumerMatch,
710                              Tenant providerTenant, EndpointGroup provider,
711                              ProviderSelectionRelator providerRelator) {
712             super(consumerMatch.contractTenant, 
713                   consumerMatch.contract, 
714                   consumerMatch.consumerTenant,
715                   consumerMatch.consumer, 
716                   consumerMatch.consumerRelator);
717             this.providerTenant = providerTenant;
718             this.provider = provider;
719             this.providerRelator = providerRelator;
720         }
721     }
722
723     @Immutable
724     private static class ConsumerContractMatch {
725         /**
726          * The tenant of the matching contract
727          */
728         final Tenant contractTenant;
729         
730         /**
731          * The matching contract
732          */
733         final Contract contract;
734
735         /**
736          * The tenant for the endpoint group
737          */
738         final Tenant consumerTenant;
739         
740         /**
741          * The consumer endpoint group
742          */
743         final EndpointGroup consumer;
744         
745         /**
746          * The consumer selection relator that was used to match the contract
747          */
748         final ConsumerSelectionRelator consumerRelator;
749         
750
751         public ConsumerContractMatch(Tenant contractTenant,
752                                      Contract contract,
753                                      Tenant consumerTenant,
754                                      EndpointGroup consumer,
755                                      ConsumerSelectionRelator consumerRelator) {
756             super();
757             this.contractTenant = contractTenant;
758             this.contract = contract;
759             this.consumerTenant = consumerTenant;
760             this.consumer = consumer;
761             this.consumerRelator = consumerRelator;
762         }
763     }
764
765     @Immutable
766     private class PolicyChangeListener implements DataChangeListener {
767         final TenantId tenantId;
768         
769         public PolicyChangeListener(TenantId tenantId) {
770             super();
771             this.tenantId = tenantId;
772         }
773
774         @Override
775         public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> arg0) {
776             updateTenant(tenantId);            
777         }
778         
779     }
780
781 }