Publishing of ofoverlay augmentations, fixed port-name augmentation bug
[groupbasedpolicy.git] / renderers / ofoverlay / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / EndpointManager.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.renderer.ofoverlay;
10
11 import java.util.Collection;
12 import java.util.Collections;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Map.Entry;
17 import java.util.Set;
18 import java.util.concurrent.ConcurrentHashMap;
19 import java.util.concurrent.ConcurrentMap;
20 import java.util.concurrent.CopyOnWriteArrayList;
21 import java.util.concurrent.ExecutionException;
22 import java.util.concurrent.ScheduledExecutorService;
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.WriteTransaction;
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.controller.sal.binding.api.RpcProviderRegistry;
31 import org.opendaylight.groupbasedpolicy.endpoint.EndpointRpcRegistry;
32 import org.opendaylight.groupbasedpolicy.endpoint.EpKey;
33 import org.opendaylight.groupbasedpolicy.endpoint.EpRendererAugmentation;
34 import org.opendaylight.groupbasedpolicy.resolver.EgKey;
35 import org.opendaylight.groupbasedpolicy.util.SetUtils;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Name;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.Endpoints;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.RegisterEndpointInput;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.RegisterL3PrefixEndpointInput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3Builder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3PrefixBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayConfig.LearningMode;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContextBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContextInput;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
57 import org.opendaylight.yangtools.concepts.ListenerRegistration;
58 import org.opendaylight.yangtools.yang.binding.DataObject;
59 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62
63 import com.google.common.base.Function;
64 import com.google.common.base.Optional;
65 import com.google.common.base.Predicate;
66 import com.google.common.collect.Collections2;
67 import com.google.common.collect.Sets;
68
69 /**
70  * Keep track of endpoints on the system. Maintain an index of endpoints and
71  * their locations for renderering. The endpoint manager will maintain
72  * appropriate indexes only for switches that are attached to the current
73  * controller node.
74  *
75  * In order to render the policy, we need to be able to efficiently enumerate
76  * all endpoints on a particular switch and also all the switches containing
77  * each particular endpoint group
78  *
79  * @author readams
80  */
81 public class EndpointManager implements AutoCloseable, DataChangeListener
82 {
83     private static final Logger LOG =
84             LoggerFactory.getLogger(EndpointManager.class);
85     private final static InstanceIdentifier<Nodes> nodesIid = InstanceIdentifier
86             .builder(Nodes.class).build();
87     private final static InstanceIdentifier<Node> nodeIid = InstanceIdentifier
88             .builder(Nodes.class).child(Node.class).build();
89     private ListenerRegistration<DataChangeListener> nodesReg;
90
91     private static final InstanceIdentifier<Endpoint> endpointsIid =
92             InstanceIdentifier.builder(Endpoints.class)
93                     .child(Endpoint.class).build();
94     final ListenerRegistration<DataChangeListener> listenerReg;
95
96     private final ConcurrentHashMap<EpKey, Endpoint> endpoints =
97             new ConcurrentHashMap<>();
98     private final ConcurrentHashMap<NodeId, ConcurrentMap<EgKey, Set<EpKey>>> endpointsByGroupByNode =
99             new ConcurrentHashMap<>();
100     private final ConcurrentHashMap<NodeId, Set<EpKey>> endpointsByNode =
101             new ConcurrentHashMap<>();
102
103     private final ConcurrentHashMap<EgKey, Set<EpKey>> endpointsByGroup =
104             new ConcurrentHashMap<>();
105
106     private List<EndpointListener> listeners = new CopyOnWriteArrayList<>();
107
108     final private OfEndpointAug endpointRpcAug = new OfEndpointAug();
109
110     final private ScheduledExecutorService executor;
111
112     final private DataBroker dataProvider;
113
114     public EndpointManager(DataBroker dataProvider,
115             RpcProviderRegistry rpcRegistry,
116             ScheduledExecutorService executor,
117             SwitchManager switchManager) {
118         this.executor = executor;
119         this.dataProvider = dataProvider;
120         EndpointRpcRegistry.register(dataProvider, rpcRegistry, executor, endpointRpcAug);
121         if (dataProvider != null) {
122             listenerReg = dataProvider
123                     .registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
124                             endpointsIid,
125                             this,
126                             DataChangeScope.ONE);
127             nodesReg = dataProvider.registerDataChangeListener(
128                     LogicalDatastoreType.OPERATIONAL, nodeIid,
129                     new NodesListener(), DataChangeScope.SUBTREE);
130
131         } else
132             listenerReg = null;
133
134         LOG.debug("Initialized OFOverlay endpoint manager");
135     }
136
137     // ***************
138     // EndpointManager
139     // ***************
140
141     /**
142      * Add a {@link EndpointListener} to get notifications of switch events
143      *
144      * @param listener
145      *            the {@link EndpointListener} to add
146      */
147     public void registerListener(EndpointListener listener) {
148         listeners.add(listener);
149     }
150
151     /**
152      * Get a collection of endpoints attached to a particular switch
153      *
154      * @param nodeId
155      *            the nodeId of the switch to get endpoints for
156      * @return a collection of {@link Endpoint} objects.
157      */
158     public synchronized Set<EgKey> getGroupsForNode(NodeId nodeId) {
159         Map<EgKey, Set<EpKey>> nodeEps = endpointsByGroupByNode.get(nodeId);
160         if (nodeEps == null)
161             return Collections.emptySet();
162         return Collections.unmodifiableSet(nodeEps.keySet());
163     }
164
165     /**
166      * Get the set of nodes
167      *
168      * @param egKey
169      *            the egKey of the endpointgroup to get nodes for
170      * @return a collection of {@link NodeId} objects.
171      */
172     public synchronized Set<NodeId> getNodesForGroup(final EgKey egKey) {
173         return Collections.unmodifiableSet(Sets.filter(endpointsByGroupByNode.keySet(),
174                 new Predicate<NodeId>() {
175                     @Override
176                     public boolean apply(NodeId input) {
177                         Map<EgKey, Set<EpKey>> nodeEps =
178                                 endpointsByGroupByNode.get(input);
179                         return (nodeEps != null &&
180                         nodeEps.containsKey(egKey));
181                     }
182
183                 }));
184     }
185
186     /**
187      * Get the endpoints in a particular group on a particular node
188      *
189      * @param nodeId
190      *            the node ID to look up
191      * @param eg
192      *            the group to look up
193      * @return the endpoints
194      */
195     public synchronized Collection<Endpoint> getEndpointsForNode(NodeId nodeId, EgKey eg) {
196         // TODO: alagalah Create method findEndpointsByNode() that uses
197         // datastore
198
199         Map<EgKey, Set<EpKey>> nodeEps = endpointsByGroupByNode.get(nodeId);
200         if (nodeEps == null)
201             return Collections.emptyList();
202         Collection<EpKey> ebn = nodeEps.get(eg);
203         if (ebn == null)
204             return Collections.emptyList();
205         return Collections.unmodifiableCollection(Collections2
206                 .transform(ebn,
207                         indexTransform));
208     }
209
210     /**
211      * Get the endpoints on a particular node
212      *
213      * @param nodeId
214      *            the node ID to look up
215      * @return the endpoints
216      */
217     public synchronized Collection<Endpoint> getEndpointsForNode(final NodeId nodeId) {
218         // TODO: alagalah Create method findEndpointsByNode() that uses
219         // datastore. See commented code below.
220
221         Collection<Endpoint> epsByNode = Collections.emptyList();
222         // Blocking for test.
223         // // Predicate for filtering only the endpoints we need for this nodeID
224         // //TODO: This pulls from datastore. Will be more performant to update
225         // // endpointByNode in updateEndpoint.
226         // Predicate<Endpoint> predicate = new Predicate<Endpoint>() {
227         // @Override
228         // public boolean apply(Endpoint ep) {
229         // return
230         // ep.getAugmentation(OfOverlayContext.class).getNodeId().getValue().equals(nodeId.getValue());
231         // }
232         // };
233         //
234         // Optional<Endpoints> epResult;
235         // final InstanceIdentifier<Endpoints> endpointsIid =
236         // InstanceIdentifier.builder(Endpoints.class).build();
237         // try {
238         // epResult =
239         // dataProvider.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL,
240         // endpointsIid).get();
241         // if(epResult.isPresent()) {
242         // Endpoints endpoints = epResult.get();
243         // epsByNode =
244         // Collections2.filter((Collection<Endpoint>)endpoints.getEndpoint(),predicate);
245         // }
246         // } catch (InterruptedException | ExecutionException e) {
247         // LOG.error("Caught exception in getEPsForNode");
248         // }
249         Collection<EpKey> ebn = endpointsByNode.get(nodeId);
250         if (ebn == null)
251             return Collections.emptyList();
252         return Collections.unmodifiableCollection(Collections2
253                 .transform(ebn,
254                         indexTransform));
255
256     }
257
258     /**
259      * Get the endpoint object for the given key
260      *
261      * @param epKey
262      *            the key
263      * @return the {@link Endpoint} corresponding to the key
264      */
265     public Endpoint getEndpoint(EpKey epKey) {
266         return endpoints.get(epKey);
267     }
268
269     /**
270      * Set the learning mode to the specified value
271      *
272      * @param learningMode
273      *            the learning mode to set
274      */
275     public void setLearningMode(LearningMode learningMode) {
276         // No-op for now
277     }
278
279     /**
280      * Get a collection of endpoints in a particular endpoint group
281      *
282      * @param nodeId
283      *            the nodeId of the switch to get endpoints for
284      * @return a collection of {@link Endpoint} objects.
285      */
286     public synchronized Collection<Endpoint> getEndpointsForGroup(EgKey eg) {
287         Collection<EpKey> ebg = endpointsByGroup.get(eg);
288         if (ebg == null)
289             return Collections.emptyList();
290         return Collections2.transform(ebg, indexTransform);
291     }
292
293     /**
294      * Get the effective list of conditions that apply to a particular endpoint.
295      * This could include additional conditions over the condition labels
296      * directly represented in the endpoint object
297      *
298      * @param endpoint
299      *            the {@link Endpoint} to resolve
300      * @return the list of {@link ConditionName}
301      */
302     public List<ConditionName> getCondsForEndpoint(Endpoint endpoint) {
303         // TODO Be alagalah From Helium: consider group conditions as well. Also
304         // need to notify
305         // endpoint updated if the endpoint group conditions change
306         if (endpoint.getCondition() != null)
307             return endpoint.getCondition();
308         else
309             return Collections.emptyList();
310     }
311
312     // ************************
313     // Endpoint Augmentation
314     // ************************
315     private class OfEndpointAug implements EpRendererAugmentation {
316
317         @Override
318         public void buildEndpointAugmentation(EndpointBuilder eb,
319                 RegisterEndpointInput input) {
320             // In order to support both the port-name and the data-path
321             // information, allow
322             // an EP to register without the augmentations, and resolve later.
323             OfOverlayContextBuilder ictx = checkAugmentation(input);
324             if (ictx != null) {
325                 eb.addAugmentation(OfOverlayContext.class, ictx.build());
326             }
327         }
328
329         @Override
330         public void buildEndpointL3Augmentation(EndpointL3Builder eb,
331                 RegisterEndpointInput input) {
332         }
333
334         @Override
335         public void buildL3PrefixEndpointAugmentation(EndpointL3PrefixBuilder eb, RegisterL3PrefixEndpointInput input) {
336             // TODO Auto-generated method stub
337
338         }
339
340         private OfOverlayContextBuilder checkAugmentation(RegisterEndpointInput input) {
341             OfOverlayContextInput ictx = input.getAugmentation(OfOverlayContextInput.class);
342             if (ictx == null) {
343                 return null;
344             }
345
346             OfOverlayContextBuilder ictxBuilder = new OfOverlayContextBuilder(ictx);
347             if (ictx.getPortName() != null && ictx.getNodeId() != null && ictx.getNodeConnectorId() != null) {
348                 return ictxBuilder;
349             }
350
351             /*
352              * In the case where they've provided just the port name,
353              * go see if we can find the NodeId and NodeConnectorId
354              * from inventory.
355              */
356             if (ictx.getPortName() != null) {
357                 NodeInfo augmentation = fetchAugmentation(ictx.getPortName().getValue());
358                 if (augmentation != null) {
359                     ictxBuilder.setNodeId(augmentation.getNode().getId());
360                     ictxBuilder.setNodeConnectorId(augmentation.getNodeConnector().getId());
361                 }
362             }
363             return ictxBuilder;
364         }
365
366         private NodeInfo fetchAugmentation(String portName) {
367             NodeInfo nodeInfo = null;
368
369             if (dataProvider != null) {
370
371                 Optional<Nodes> result;
372                 try {
373                     result = dataProvider
374                             .newReadOnlyTransaction().read(
375                                     LogicalDatastoreType.OPERATIONAL, nodesIid).get();
376                     if (result.isPresent()) {
377                         Nodes nodes = result.get();
378                         for (Node node : nodes.getNode()) {
379                             if (node.getNodeConnector() != null) {
380                                 boolean found = false;
381                                 for (NodeConnector nc : node.getNodeConnector()) {
382                                     FlowCapableNodeConnector fcnc = nc
383                                             .getAugmentation(FlowCapableNodeConnector.class);
384                                     if (fcnc.getName().equals(portName)) {
385                                         nodeInfo = new NodeInfo();
386                                         nodeInfo.setNode(node);
387                                         nodeInfo.setNodeConnector(nc);
388                                         found = true;
389                                         break;
390                                     }
391                                 }
392                                 if (found)
393                                     break;
394                             }
395                         }
396                     }
397                 } catch (InterruptedException | ExecutionException e) {
398                     LOG.error("Caught exception in fetchAugmentation portName", e);
399                 }
400
401             }
402             return nodeInfo;
403         }
404     }
405
406     // *************
407     // AutoCloseable
408     // *************
409
410     @Override
411     public void close() throws Exception {
412         if (listenerReg != null)
413             listenerReg.close();
414         EndpointRpcRegistry.unregister(endpointRpcAug);
415     }
416
417     // ******************
418     // DataChangeListener
419     // ******************
420
421     @Override
422     public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
423         for (DataObject dao : change.getCreatedData().values()) {
424             if (dao instanceof Endpoint)
425                 updateEndpoint(null, (Endpoint) dao);
426         }
427         for (InstanceIdentifier<?> iid : change.getRemovedPaths()) {
428             DataObject old = change.getOriginalData().get(iid);
429             if (old != null && old instanceof Endpoint)
430                 updateEndpoint((Endpoint) old, null);
431         }
432         Map<InstanceIdentifier<?>, DataObject> d = change.getUpdatedData();
433         for (Entry<InstanceIdentifier<?>, DataObject> entry : d.entrySet()) {
434             if (!(entry.getValue() instanceof Endpoint))
435                 continue;
436             DataObject old = change.getOriginalData().get(entry.getKey());
437             Endpoint oldEp = null;
438             if (old != null && old instanceof Endpoint)
439                 oldEp = (Endpoint) old;
440             updateEndpoint(oldEp, (Endpoint) entry.getValue());
441         }
442     }
443
444     // TODO: alagalah Investigate using the internal project listener structure
445     // for this. ie Endpoint should listen to
446     // SwitchManager updates and update the EP maps accordingly (update
447     // Endpoint). Removal should include the overloaded
448     // method updateEndpoint(Node node)
449     private class NodesListener implements DataChangeListener {
450         @Override
451         public void onDataChanged(
452                 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
453             for (DataObject dao : change.getCreatedData().values()) {
454                 if (!(dao instanceof Node))
455                     continue;
456                 Node node = (Node) dao;
457                 if (node.getNodeConnector() != null) {
458                     updateEndpoint(node);
459                 }
460             }
461             for (DataObject dao : change.getUpdatedData().values()) {
462                 if (!(dao instanceof Node))
463                     continue;
464                 Node node = (Node) dao;
465                 if (node.getNodeConnector() != null) {
466                     updateEndpoint(node);
467                 }
468             }
469         }
470     }
471
472     // TODO Li alagalah move this near to other updateEndpoint()
473     private void updateEndpoint(Node node) {
474         final InstanceIdentifier<Endpoints> endpointsIid = InstanceIdentifier.builder(Endpoints.class).build();
475
476         Optional<Endpoints> epResult;
477         EpKey epKey = null;
478         for (NodeConnector nc : node.getNodeConnector()) {
479             FlowCapableNodeConnector fcnc = nc
480                     .getAugmentation(FlowCapableNodeConnector.class);
481             try {
482                 epResult = dataProvider.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL, endpointsIid)
483                         .get();
484                 if (epResult.isPresent()) {
485                     Endpoints endpoints = epResult.get();
486                     if (endpoints.getEndpoint() != null) {
487                         Boolean isEmpty = true;
488                         for (Endpoint ep : endpoints.getEndpoint()) {
489                             // 2. Search for portname
490                             OfOverlayContext currentAugmentation = ep.getAugmentation(OfOverlayContext.class);
491                             if (currentAugmentation.getPortName() != null && fcnc.getName() != null
492                                     && currentAugmentation.getPortName().getValue().equals(fcnc.getName())) {
493                                 NodeId nodeId;
494                                 NodeConnectorId nodeConnectorId;
495                                 Name name;
496                                 try {
497                                     nodeId = currentAugmentation.getNodeId();
498                                     nodeConnectorId = currentAugmentation.getNodeConnectorId();
499                                     name = currentAugmentation.getPortName();
500                                 } catch (Exception e) {
501                                     nodeId = null;
502                                     nodeConnectorId = null;
503                                     name = null;
504                                 }
505                                 Boolean process = false;
506                                 if (nodeId == null && nodeConnectorId == null) {
507                                     LOG.debug("ep NodeID and NC ID Both null");
508                                     process = true;
509                                 }
510                                 if (nodeId != null && nodeConnectorId != null) {
511                                     if (!(nodeConnectorId.getValue().equals(nc.getId().getValue()))) {
512                                         LOG.debug("ep NodeID and NC ID Both NOT null but epNCID !=nodeNCID");
513                                         process = true;
514                                     }
515                                 }
516                                 if (process) {
517                                     WriteTransaction tx = dataProvider.newWriteOnlyTransaction();
518                                     // 3. Update endpoint
519                                     EndpointBuilder epBuilder = new EndpointBuilder(ep);
520                                     OfOverlayContextBuilder ofOverlayAugmentation = new OfOverlayContextBuilder();
521                                     ofOverlayAugmentation.setNodeId(node.getId());
522                                     ofOverlayAugmentation.setNodeConnectorId(nc.getId());
523                                     ofOverlayAugmentation.setPortName(name);
524                                     epBuilder.addAugmentation(OfOverlayContext.class, ofOverlayAugmentation.build());
525                                     epBuilder.setL3Address(ep.getL3Address());
526                                     InstanceIdentifier<Endpoint> iidEp = InstanceIdentifier.builder(Endpoints.class)
527                                             .child(Endpoint.class, ep.getKey()).build();
528                                     tx.put(LogicalDatastoreType.OPERATIONAL, iidEp, epBuilder.build());
529                                     tx.submit().get();
530                                     epKey = new EpKey(ep.getKey().getL2Context(), ep.getKey().getMacAddress());
531                                     notifyEndpointUpdated(epKey);
532                                     LOG.debug("Values:");
533                                     LOG.debug("node: Node ID:" + node.getId().getValue());
534                                     LOG.debug("node: NodeConnectorID: " + nc.getId().getValue());
535                                     if (nodeId != null && nodeConnectorId != null) {
536                                         LOG.debug("ep: nodeID:" + nodeId.getValue());
537                                         LOG.debug("ep: nodeConnectorID:" + nodeConnectorId.getValue());
538                                     }
539                                     isEmpty = false;
540                                 }
541                             }
542                         }
543                     }
544                 }
545             } catch (InterruptedException | ExecutionException e) {
546                 LOG.error("Exception in UpdateEndpoint", e);
547             }
548         }
549     }
550
551     // **************
552     // Implementation
553     // **************
554
555     private void notifyEndpointUpdated(EpKey epKey) {
556         for (EndpointListener l : listeners) {
557             l.endpointUpdated(epKey);
558         }
559     }
560
561     private void notifyNodeEndpointUpdated(NodeId nodeId, EpKey epKey) {
562         for (EndpointListener l : listeners) {
563             l.nodeEndpointUpdated(nodeId, epKey);
564         }
565     }
566
567     private void notifyGroupEndpointUpdated(EgKey egKey, EpKey epKey) {
568         for (EndpointListener l : listeners) {
569             l.groupEndpointUpdated(egKey, epKey);
570         }
571     }
572
573     private Function<EpKey, Endpoint> indexTransform =
574             new Function<EpKey, Endpoint>() {
575                 @Override
576                 public Endpoint apply(EpKey input) {
577                     return endpoints.get(input);
578                 }
579             };
580
581     private boolean validEp(Endpoint endpoint) {
582         return (endpoint != null && endpoint.getTenant() != null &&
583                 (endpoint.getEndpointGroup() != null || endpoint.getEndpointGroups() != null) &&
584                 endpoint.getL2Context() != null && endpoint.getMacAddress() != null);
585     }
586
587     private NodeId getLocation(Endpoint endpoint) {
588         if (!validEp(endpoint))
589             return null;
590         OfOverlayContext context =
591                 endpoint.getAugmentation(OfOverlayContext.class);
592         if (context != null)
593             return context.getNodeId();
594
595         return null;
596     }
597
598     private EpKey getEpKey(Endpoint endpoint) {
599         if (!validEp(endpoint))
600             return null;
601         return new EpKey(endpoint.getL2Context(), endpoint.getMacAddress());
602     }
603
604     public EgKey getEgKey(Endpoint endpoint) {
605         if (!validEp(endpoint))
606             return null;
607         return new EgKey(endpoint.getTenant(), endpoint.getEndpointGroup());
608     }
609
610     public Set<EgKey> getEgKeysForEndpoint(Endpoint ep) {
611         Set<EgKey> egKeys = new HashSet<EgKey>();
612
613         if (ep.getEndpointGroup() != null) {
614             egKeys.add(new EgKey(ep.getTenant(), ep.getEndpointGroup()));
615         }
616         if (ep.getEndpointGroups() != null) {
617             for (EndpointGroupId epgId : ep.getEndpointGroups()) {
618                 egKeys.add(new EgKey(ep.getTenant(), epgId));
619             }
620         }
621         return egKeys;
622     }
623
624     private Set<EpKey> getEpNGSet(NodeId location, EgKey eg) {
625         ConcurrentMap<EgKey, Set<EpKey>> map = endpointsByGroupByNode.get(location);
626         if (map == null) {
627             map = new ConcurrentHashMap<>();
628             ConcurrentMap<EgKey, Set<EpKey>> old =
629                     endpointsByGroupByNode.putIfAbsent(location, map);
630             if (old != null)
631                 map = old;
632         }
633         return SetUtils.getNestedSet(eg, map);
634     }
635
636     private static final ConcurrentMap<EgKey, Set<EpKey>> EMPTY_MAP =
637             new ConcurrentHashMap<>();
638
639     private Set<EpKey> getEpGSet(EgKey eg) {
640         return SetUtils.getNestedSet(eg, endpointsByGroup);
641     }
642
643     /**
644      * Update the endpoint indexes. Set newEp to null to remove.
645      */
646     protected synchronized void updateEndpoint(Endpoint oldEp, Endpoint newEp) {
647         // TODO Be alagalah From Helium only keep track of endpoints that are
648         // attached
649         // to switches that are actually connected to us
650
651         // TODO Li alagalah: This needs a major clean up and refactor. For now
652         // it works.
653         NodeId oldLoc = getLocation(oldEp);
654         NodeId newLoc = getLocation(newEp);
655         // EgKey oldEgKey = getEgKey(oldEp);
656         EpKey oldEpKey = getEpKey(oldEp);
657         EpKey newEpKey = getEpKey(newEp);
658
659         boolean notifyOldLoc = false;
660         boolean notifyNewLoc = false;
661         boolean notifyOldEg = false;
662         boolean notifyNewEg = false;
663
664         // When newLoc and oldLoc are null there is nothing to do
665         if (!(newLoc == null && oldLoc == null)) {
666
667             Set<EndpointGroupId> newEpgIds = new HashSet<EndpointGroupId>();
668             TenantId tenantId = null;
669             if (newEp != null) {
670                 if (newEp.getEndpointGroups() != null) {
671                     newEpgIds.addAll(newEp.getEndpointGroups());
672                 }
673                 if (newEp.getEndpointGroup() != null) {
674                     newEpgIds.add(newEp.getEndpointGroup());
675                 }
676                 tenantId = newEp.getTenant();
677             }
678
679             Set<EndpointGroupId> oldEpgIds = new HashSet<EndpointGroupId>();
680             if (oldEp != null) {
681                 if (oldEp.getEndpointGroups() != null) {
682                     oldEpgIds.addAll(oldEp.getEndpointGroups());
683                 }
684                 if (oldEp.getEndpointGroup() != null) {
685                     oldEpgIds.add(oldEp.getEndpointGroup());
686                 }
687             }
688
689             /*
690              * maintainIndex(endpointsByNode,oldEp,newEp) Maintain following
691              * maps endpoints - <EpKey, Endpoint> endpointsByGroupByNode -
692              * <NodeId, ConcurrentMap<EgKey, Set<EpKey>>> endpointsByNode -
693              * <NodeId,Set<EpKey>> endpointsByGroup ConcurrentHashMap<EgKey,
694              * Set<EpKey>>
695              */
696
697             // Maintain "endpoints" map
698             if (newEp != null) {
699                 endpoints.put(newEpKey, newEp);
700             } else {
701                 endpoints.remove(oldEpKey);
702             }
703
704             /*
705              * New endpoint with location information
706              */
707             if (oldEp == null && newEp != null && newLoc != null) {
708                 // Update endpointsByNode
709                 if (endpointsByNode.get(newLoc) == null) {
710                     // TODO: alagalah cleaner way with checking epsNode
711                     // then do this.
712                     Set<EpKey> epsNode = new HashSet<EpKey>();
713                     epsNode.add(newEpKey);
714                     endpointsByNode.put(newLoc, epsNode);
715                 } else {
716                     Set<EpKey> epsNode = endpointsByNode.get(newLoc);
717                     epsNode.add(newEpKey);
718                 }
719                 // Update endpointsByGroupByNode and endpointsByGroup
720                 for (EndpointGroupId newEpgId : newEpgIds) {
721                     // endpointsByGroupByNode
722                     EgKey newEgKey = new EgKey(tenantId, newEpgId);
723                     Set<EpKey> eps = getEpNGSet(newLoc, newEgKey);
724                     eps.add(newEpKey);
725                     // endpointsByGroup
726                     Set<EpKey> geps = endpointsByGroup.get(newEgKey);
727                     if (geps == null) {
728                         geps = new HashSet<>();
729                     }
730                     geps.add(newEpKey);
731                     endpointsByGroup.put(newEgKey, geps);
732                     LOG.debug("Endpoint {} added to node {}", newEpKey, newLoc);
733
734                 }
735
736                 notifyNewLoc = true;
737                 notifyNewEg = true;
738             }
739
740             /*
741              * Removed endpoint
742              */
743             if (oldEp != null && newEp == null) {
744                 // Update endpointsByNode
745                 Set<EpKey> epsNode = endpointsByNode.get(oldLoc);
746                 if (epsNode != null) {
747                     epsNode.remove(oldEpKey);
748                     if (epsNode.isEmpty())
749                         endpointsByNode.remove(oldLoc);
750                 }
751                 // Update endpointsByGroupByNode
752                 // Update endpointsByGroup
753                 // Get map of EPGs and their Endpoints for Node
754                 ConcurrentMap<EgKey, Set<EpKey>> map =
755                         endpointsByGroupByNode.get(oldLoc);
756                 // For each EPG in the removed endpoint...
757                 for (EndpointGroupId oldEpgId : newEpgIds) {
758                     EgKey oldEgKey = new EgKey(oldEp.getTenant(), oldEpgId);
759                     // Get list of endpoints for EPG
760                     Set<EpKey> eps = map.get(oldEgKey);
761                     // Remove the endpoint from the map
762                     if (eps != null) {
763                         eps.remove(oldEpKey);
764                         if (eps.isEmpty())
765                             map.remove(oldEgKey, Collections.emptySet());
766                     }
767                     // endpointsByGroup
768                     Set<EpKey> geps = endpointsByGroup.get(oldEgKey);
769                     if (geps != null) {
770                         geps.remove(oldEpKey);
771                         if (geps.isEmpty())
772                             endpointsByGroup.remove(oldEgKey);
773                     }
774                 }
775                 // If map is empty, no more EPGs on this node, remove node from
776                 // map
777                 if (map.isEmpty())
778                     endpointsByGroupByNode.remove(oldLoc, EMPTY_MAP);
779                 notifyOldLoc = true;
780                 notifyOldEg = true;
781             }
782
783             /*
784              * Moved endpoint (from node to node or from NULL to node)
785              */
786             if ((oldEp != null && newEp != null && oldEpKey != null && newEpKey != null) &&
787                     (oldEpKey.toString().equals(newEpKey.toString()))) {
788                 // old and new Endpoints have same key. (same endpoint)
789
790                 /*
791                  * Remove old endpoint if moved.
792                  */
793                 if (oldLoc != null && !(oldLoc.getValue().equals(newLoc.getValue()))) {
794                     // This is an endpoint that has moved, remove from old node
795                     Set<EpKey> epsNode = endpointsByNode.get(oldLoc);
796                     if (epsNode != null) {
797                         epsNode.remove(oldEpKey);
798                         if (epsNode.isEmpty())
799                             endpointsByNode.remove(oldLoc);
800                     }
801                     // Update endpointsByGroupByNode
802                     // Get map of EPGs and their Endpoints for Node
803                     ConcurrentMap<EgKey, Set<EpKey>> map =
804                             endpointsByGroupByNode.get(oldLoc);
805                     // For each EPG in the removed endpoint...
806                     for (EndpointGroupId oldEpgId : oldEpgIds) {
807                         EgKey oldEgKey = new EgKey(oldEp.getTenant(), oldEpgId);
808                         // Get list of endpoints for EPG
809                         Set<EpKey> eps = map.get(oldEgKey);
810                         // Remove the endpoint from the map
811                         if (eps != null) {
812                             eps.remove(oldEpKey);
813                             if (eps.isEmpty())
814                                 map.remove(oldEgKey, Collections.emptySet());
815                         }
816                         // endpointsByGroup
817                         Set<EpKey> geps = endpointsByGroup.get(oldEgKey);
818                         if (geps != null)
819                         {
820                             geps.remove(oldEpKey);
821                             if (geps.isEmpty())
822                                 endpointsByGroup.remove(oldEgKey);
823                         }
824                     }
825                     // If map is empty, no more EPGs on this node, remove node
826                     // from map
827                     if (map.isEmpty())
828                         endpointsByGroupByNode.remove(oldLoc, EMPTY_MAP);
829                     notifyOldLoc = true;
830                     notifyOldEg = true;
831                 }
832
833                 /*
834                  * Add new endpoint
835                  */
836                 // Update endpointsByNode
837                 if (endpointsByNode.get(newLoc) == null) {
838                     Set<EpKey> newEpsNode = new HashSet<EpKey>();
839                     newEpsNode.add(newEpKey);
840                     endpointsByNode.put(newLoc, newEpsNode);
841                 } else {
842                     Set<EpKey> newEpsNode = endpointsByNode.get(newLoc);
843                     newEpsNode.add(newEpKey);
844                 }
845                 notifyNewLoc = true;
846
847                 // Update endpointsByGroupByNode
848                 // Update endpointsByGroup
849                 for (EndpointGroupId newEpgId : newEpgIds) {
850                     EgKey newEgKey = new EgKey(tenantId, newEpgId);
851                     Set<EpKey> eps = getEpNGSet(newLoc, newEgKey);
852                     eps.add(newEpKey);
853                     // endpointsByGroup
854                     Set<EpKey> geps = endpointsByGroup.get(newEgKey);
855                     if (geps == null) {
856                         geps = new HashSet<>();
857                     }
858                     geps.add(newEpKey);
859                     endpointsByGroup.put(newEgKey, geps);
860                     notifyNewEg = true;
861
862                     LOG.debug("Endpoint {} added to node {}", newEpKey, newLoc);
863                 }
864
865             }
866
867             if (newEp != null)
868                 notifyEndpointUpdated(newEpKey);
869             else
870                 notifyEndpointUpdated(oldEpKey);
871
872             // TODO alagalah NEXt: ensure right notification flags are set.
873             if (notifyOldLoc)
874                 notifyNodeEndpointUpdated(oldLoc, oldEpKey);
875             if (notifyNewLoc)
876                 notifyNodeEndpointUpdated(newLoc, newEpKey);
877             if (notifyOldEg)
878                 for (EndpointGroupId oldEpgId : oldEpgIds) {
879                     EgKey oldEgKey = new EgKey(oldEp.getTenant(), oldEpgId);
880                     notifyGroupEndpointUpdated(oldEgKey, oldEpKey);
881                 }
882             if (notifyNewEg)
883                 for (EndpointGroupId newEpgId : newEpgIds) {
884                     EgKey newEgKey = new EgKey(newEp.getTenant(), newEpgId);
885                     notifyGroupEndpointUpdated(newEgKey, newEpKey);
886                 }
887
888         }
889     }
890
891     // A wrapper class around node, nodeConnector info so we can pass a final
892     // object inside OnSuccess anonymous inner class
893     private static class NodeInfo {
894         NodeConnector nodeConnector;
895         Node node;
896
897         private NodeInfo() {
898
899         }
900
901         private NodeInfo(NodeConnector nc, Node node) {
902             this.nodeConnector = nc;
903             this.node = node;
904         }
905
906         private Node getNode() {
907             return this.node;
908         }
909
910         private NodeConnector getNodeConnector() {
911             return this.nodeConnector;
912         }
913
914         public void setNodeConnector(NodeConnector nodeConnector) {
915             this.nodeConnector = nodeConnector;
916         }
917
918         public void setNode(Node node) {
919             this.node = node;
920         }
921     }
922
923 }