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