Merge "When ports are added to a container, construct its topology immediately based...
[controller.git] / opendaylight / hosttracker / implementation / src / main / java / org / opendaylight / controller / hosttracker / internal / HostTracker.java
1 /*
2  * Copyright (c) 2013 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.controller.hosttracker.internal;
10
11 import java.net.InetAddress;
12 import java.net.UnknownHostException;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.Dictionary;
16 import java.util.EnumSet;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.Set;
22 import java.util.Timer;
23 import java.util.TimerTask;
24 import java.util.concurrent.Callable;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.ConcurrentMap;
27 import java.util.concurrent.ExecutorService;
28 import java.util.concurrent.Executors;
29 import java.util.concurrent.Future;
30
31 import org.apache.felix.dm.Component;
32 import org.eclipse.osgi.framework.console.CommandInterpreter;
33 import org.eclipse.osgi.framework.console.CommandProvider;
34 import org.opendaylight.controller.clustering.services.CacheConfigException;
35 import org.opendaylight.controller.clustering.services.CacheExistException;
36 import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
37 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
38 import org.opendaylight.controller.clustering.services.IClusterServices;
39 import org.opendaylight.controller.hosttracker.IfHostListener;
40 import org.opendaylight.controller.hosttracker.IfIptoHost;
41 import org.opendaylight.controller.hosttracker.IfNewHostNotify;
42 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
43 import org.opendaylight.controller.hosttracker.hostAware.IHostFinder;
44 import org.opendaylight.controller.sal.core.ConstructionException;
45 import org.opendaylight.controller.sal.core.Edge;
46 import org.opendaylight.controller.sal.core.Host;
47 import org.opendaylight.controller.sal.core.Node;
48 import org.opendaylight.controller.sal.core.NodeConnector;
49 import org.opendaylight.controller.sal.core.Property;
50 import org.opendaylight.controller.sal.core.State;
51 import org.opendaylight.controller.sal.core.Tier;
52 import org.opendaylight.controller.sal.core.UpdateType;
53 import org.opendaylight.controller.sal.packet.address.DataLinkAddress;
54 import org.opendaylight.controller.sal.packet.address.EthernetAddress;
55 import org.opendaylight.controller.sal.topology.TopoEdgeUpdate;
56 import org.opendaylight.controller.sal.utils.GlobalConstants;
57 import org.opendaylight.controller.sal.utils.HexEncode;
58 import org.opendaylight.controller.sal.utils.NetUtils;
59 import org.opendaylight.controller.sal.utils.NodeCreator;
60 import org.opendaylight.controller.sal.utils.Status;
61 import org.opendaylight.controller.sal.utils.StatusCode;
62 import org.opendaylight.controller.switchmanager.IInventoryListener;
63 import org.opendaylight.controller.switchmanager.ISwitchManager;
64 import org.opendaylight.controller.switchmanager.ISwitchManagerAware;
65 import org.opendaylight.controller.switchmanager.Subnet;
66 import org.opendaylight.controller.topologymanager.ITopologyManager;
67 import org.opendaylight.controller.topologymanager.ITopologyManagerAware;
68 import org.osgi.framework.BundleContext;
69 import org.osgi.framework.FrameworkUtil;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72
73 /**
74  * @file HostTracker.java This class tracks the location of IP Hosts as to which
75  *       Switch, Port, VLAN, they are connected to, as well as their MAC
76  *       address. This is done dynamically as well as statically. The dynamic
77  *       mechanism consists of listening to ARP messages as well sending ARP
78  *       requests. Static mechanism consists of Northbound APIs to add or remove
79  *       the hosts from the local database. ARP aging is also implemented to age
80  *       out dynamically learned hosts. Interface methods are provided for other
81  *       applications to 1. Query the local database for a single host 2. Get a
82  *       list of all hosts 3. Get notification if a host is learned/added or
83  *       removed the database
84  */
85
86 public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAware, IInventoryListener,
87         ITopologyManagerAware, ICacheUpdateAware<InetAddress, HostNodeConnector>, CommandProvider {
88     static final String ACTIVE_HOST_CACHE = "hosttracker.ActiveHosts";
89     static final String INACTIVE_HOST_CACHE = "hosttracker.InactiveHosts";
90     private static final Logger logger = LoggerFactory.getLogger(HostTracker.class);
91     protected IHostFinder hostFinder;
92     protected ConcurrentMap<InetAddress, HostNodeConnector> hostsDB;
93     /*
94      * Following is a list of hosts which have been requested by NB APIs to be
95      * added, but either the switch or the port is not sup, so they will be
96      * added here until both come up
97      */
98     private ConcurrentMap<NodeConnector, HostNodeConnector> inactiveStaticHosts;
99     private final Set<IfNewHostNotify> newHostNotify = Collections.synchronizedSet(new HashSet<IfNewHostNotify>());
100
101     private ITopologyManager topologyManager;
102     protected IClusterContainerServices clusterContainerService = null;
103     protected ISwitchManager switchManager = null;
104     private Timer timer;
105     private Timer arpRefreshTimer;
106     private String containerName = null;
107     private ExecutorService executor;
108     protected boolean stopping;
109     private static class ARPPending {
110         protected InetAddress hostIP;
111         protected short sent_count;
112         protected HostTrackerCallable hostTrackerCallable;
113
114         public InetAddress getHostIP() {
115             return hostIP;
116         }
117
118         public short getSent_count() {
119             return sent_count;
120         }
121
122         public HostTrackerCallable getHostTrackerCallable() {
123             return hostTrackerCallable;
124         }
125
126         public void setHostIP(InetAddress networkAddr) {
127             this.hostIP = networkAddr;
128         }
129
130         public void setSent_count(short count) {
131             this.sent_count = count;
132         }
133
134         public void setHostTrackerCallable(HostTrackerCallable callable) {
135             hostTrackerCallable = callable;
136         }
137     }
138
139     // This list contains the hosts for which ARP requests are being sent
140     // periodically
141     ConcurrentMap<InetAddress, ARPPending> ARPPendingList;
142     /*
143      * This list below contains the hosts which were initially in ARPPendingList
144      * above, but ARP response didn't come from there hosts after multiple
145      * attempts over 8 seconds. The assumption is that the response didn't come
146      * back due to one of the following possibilities: 1. The L3 interface
147      * wasn't created for this host in the controller. This would cause
148      * arphandler not to know where to send the ARP 2. The host facing port is
149      * down 3. The IP host doesn't exist or is not responding to ARP requests
150      *
151      * Conditions 1 and 2 above can be recovered if ARP is sent when the
152      * relevant L3 interface is added or the port facing host comes up. Whenever
153      * L3 interface is added or host facing port comes up, ARP will be sent to
154      * hosts in this list.
155      *
156      * We can't recover from condition 3 above
157      */
158     ConcurrentMap<InetAddress, ARPPending> failedARPReqList;
159
160     public HostTracker() {
161     }
162
163     private void startUp() {
164         nonClusterObjectCreate();
165         allocateCache();
166         retrieveCache();
167         stopping = false;
168
169         timer = new Timer();
170         timer.schedule(new OutStandingARPHandler(), 4000, 4000);
171         executor = Executors.newFixedThreadPool(2);
172         /* ARP Refresh Timer to go off every 5 seconds to implement ARP aging */
173         arpRefreshTimer = new Timer();
174         arpRefreshTimer.schedule(new ARPRefreshHandler(), 5000, 5000);
175         logger.debug("startUp: Caches created, timers started");
176     }
177
178     private void allocateCache() {
179         if (this.clusterContainerService == null) {
180             logger.error("un-initialized clusterContainerService, can't create cache");
181             return;
182         }
183         logger.debug("Creating Cache for HostTracker");
184         try {
185             this.clusterContainerService.createCache(ACTIVE_HOST_CACHE,
186                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
187             this.clusterContainerService.createCache(INACTIVE_HOST_CACHE,
188                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
189         } catch (CacheConfigException cce) {
190             logger.error("Cache couldn't be created for HostTracker -  check cache mode");
191         } catch (CacheExistException cce) {
192             logger.error("Cache for HostTracker already exists, destroy and recreate");
193         }
194         logger.debug("Cache successfully created for HostTracker");
195     }
196
197     @SuppressWarnings({ "unchecked" })
198     private void retrieveCache() {
199         if (this.clusterContainerService == null) {
200             logger.error("un-initialized clusterContainerService, can't retrieve cache");
201             return;
202         }
203         logger.debug("Retrieving cache for HostTrackerAH");
204         hostsDB = (ConcurrentMap<InetAddress, HostNodeConnector>) this.clusterContainerService
205                 .getCache(ACTIVE_HOST_CACHE);
206         if (hostsDB == null) {
207             logger.error("Cache couldn't be retrieved for HostTracker");
208         }
209         logger.debug("Cache was successfully retrieved for HostTracker");
210         logger.debug("Retrieving cache for HostTrackerIH");
211         inactiveStaticHosts = (ConcurrentMap<NodeConnector, HostNodeConnector>) this.clusterContainerService
212                 .getCache(INACTIVE_HOST_CACHE);
213         if (inactiveStaticHosts == null) {
214             logger.error("Cache couldn't be retrieved for HostTrackerIH");
215         }
216         logger.debug("Cache was successfully retrieved for HostTrackerIH");
217     }
218
219     public void nonClusterObjectCreate() {
220         hostsDB = new ConcurrentHashMap<InetAddress, HostNodeConnector>();
221         inactiveStaticHosts = new ConcurrentHashMap<NodeConnector, HostNodeConnector>();
222         ARPPendingList = new ConcurrentHashMap<InetAddress, ARPPending>();
223         failedARPReqList = new ConcurrentHashMap<InetAddress, ARPPending>();
224     }
225
226     public void shutDown() {
227     }
228
229     public void setnewHostNotify(IfNewHostNotify obj) {
230         this.newHostNotify.add(obj);
231     }
232
233     public void unsetnewHostNotify(IfNewHostNotify obj) {
234         this.newHostNotify.remove(obj);
235     }
236
237     public void setArpHandler(IHostFinder hostFinder) {
238         this.hostFinder = hostFinder;
239     }
240
241     public void unsetArpHandler(IHostFinder hostFinder) {
242         if (this.hostFinder == hostFinder) {
243             logger.debug("Arp Handler Service removed!");
244             this.hostFinder = null;
245         }
246     }
247
248     public void setTopologyManager(ITopologyManager s) {
249         this.topologyManager = s;
250     }
251
252     public void unsetTopologyManager(ITopologyManager s) {
253         if (this.topologyManager == s) {
254             logger.debug("Topology Manager Service removed!");
255             this.topologyManager = null;
256         }
257     }
258
259     private boolean hostExists(HostNodeConnector host) {
260         HostNodeConnector lhost = hostsDB.get(host.getNetworkAddress());
261         return host.equals(lhost);
262     }
263
264     private HostNodeConnector getHostFromOnActiveDB(InetAddress networkAddress) {
265         return hostsDB.get(networkAddress);
266     }
267
268     private Entry<NodeConnector, HostNodeConnector> getHostFromInactiveDB(InetAddress networkAddress) {
269         for (Entry<NodeConnector, HostNodeConnector> entry : inactiveStaticHosts.entrySet()) {
270             if (entry.getValue().equalsByIP(networkAddress)) {
271                 logger.debug("getHostFromInactiveDB(): Inactive Host found for IP:{} ", networkAddress.getHostAddress());
272                 return entry;
273             }
274         }
275         logger.debug("getHostFromInactiveDB() Inactive Host Not found for IP: {}", networkAddress.getHostAddress());
276         return null;
277     }
278
279     private void removeHostFromInactiveDB(InetAddress networkAddress) {
280         NodeConnector nodeConnector = null;
281         for (Entry<NodeConnector, HostNodeConnector> entry : inactiveStaticHosts.entrySet()) {
282             if (entry.getValue().equalsByIP(networkAddress)) {
283                 nodeConnector = entry.getKey();
284                 break;
285             }
286         }
287         if (nodeConnector != null) {
288             inactiveStaticHosts.remove(nodeConnector);
289             logger.debug("removeHostFromInactiveDB(): Host Removed for IP: {}", networkAddress.getHostAddress());
290             return;
291         }
292         logger.debug("removeHostFromInactiveDB(): Host Not found for IP: {}", networkAddress.getHostAddress());
293     }
294
295     protected boolean hostMoved(HostNodeConnector host) {
296         if (hostQuery(host.getNetworkAddress()) != null) {
297             return true;
298         }
299         return false;
300     }
301
302     @Override
303     public HostNodeConnector hostQuery(InetAddress networkAddress) {
304         return hostsDB.get(networkAddress);
305     }
306
307     @Override
308     public Future<HostNodeConnector> discoverHost(InetAddress networkAddress) {
309         if (executor == null) {
310             logger.error("discoverHost: Null executor");
311             return null;
312         }
313         Callable<HostNodeConnector> worker = new HostTrackerCallable(this, networkAddress);
314         Future<HostNodeConnector> submit = executor.submit(worker);
315         return submit;
316     }
317
318     @Override
319     public HostNodeConnector hostFind(InetAddress networkAddress) {
320         /*
321          * Sometimes at boot with containers configured in the startup we hit
322          * this path (from TIF) when hostFinder has not been set yet Caller
323          * already handles the null return
324          */
325
326         if (hostFinder == null) {
327             logger.debug("Exiting hostFind, null hostFinder");
328             return null;
329         }
330
331         HostNodeConnector host = hostQuery(networkAddress);
332         if (host != null) {
333             logger.debug("hostFind(): Host found for IP: {}", networkAddress.getHostAddress());
334             return host;
335         }
336
337         /* Add this host to ARPPending List for any potential retries */
338
339         addToARPPendingList(networkAddress);
340         logger.debug("hostFind(): Host Not Found for IP: {}, Inititated Host Discovery ...",
341                 networkAddress.getHostAddress());
342
343         /* host is not found, initiate a discovery */
344
345         hostFinder.find(networkAddress);
346         return null;
347     }
348
349     @Override
350     public Set<HostNodeConnector> getAllHosts() {
351         Set<HostNodeConnector> allHosts = new HashSet<HostNodeConnector>();
352         for (Entry<InetAddress, HostNodeConnector> entry : hostsDB.entrySet()) {
353             HostNodeConnector host = entry.getValue();
354             allHosts.add(host);
355         }
356         logger.debug("Exiting getAllHosts, Found {} Hosts", allHosts.size());
357         return allHosts;
358     }
359
360     @Override
361     public Set<HostNodeConnector> getActiveStaticHosts() {
362         Set<HostNodeConnector> list = new HashSet<HostNodeConnector>();
363         for (Entry<InetAddress, HostNodeConnector> entry : hostsDB.entrySet()) {
364             HostNodeConnector host = entry.getValue();
365             if (host.isStaticHost()) {
366                 list.add(host);
367             }
368         }
369         logger.debug("getActiveStaticHosts(): Found {} Hosts", list.size());
370         return list;
371     }
372
373     @Override
374     public Set<HostNodeConnector> getInactiveStaticHosts() {
375         Set<HostNodeConnector> list = new HashSet<HostNodeConnector>();
376         for (Entry<NodeConnector, HostNodeConnector> entry : inactiveStaticHosts.entrySet()) {
377             list.add(entry.getValue());
378         }
379         logger.debug("getInactiveStaticHosts(): Found {} Hosts", list.size());
380         return list;
381     }
382
383     private void addToARPPendingList(InetAddress networkAddr) {
384         ARPPending arphost = new ARPPending();
385
386         arphost.setHostIP(networkAddr);
387         arphost.setSent_count((short) 1);
388         ARPPendingList.put(networkAddr, arphost);
389         logger.debug("Host Added to ARPPending List, IP: {}", networkAddr);
390     }
391
392     public void setCallableOnPendingARP(InetAddress networkAddr, HostTrackerCallable callable) {
393         ARPPending arphost;
394         for (Entry<InetAddress, ARPPending> entry : ARPPendingList.entrySet()) {
395             arphost = entry.getValue();
396             if (arphost.getHostIP().equals(networkAddr)) {
397                 arphost.setHostTrackerCallable(callable);
398             }
399         }
400     }
401
402     private void processPendingARPReqs(InetAddress networkAddr) {
403         ARPPending arphost;
404
405         if ((arphost = ARPPendingList.remove(networkAddr)) != null) {
406             // Remove the arphost from ARPPendingList as it has been learned now
407             logger.debug("Host Removed from ARPPending List, IP: {}", networkAddr);
408             HostTrackerCallable htCallable = arphost.getHostTrackerCallable();
409             if (htCallable != null) {
410                 htCallable.wakeup();
411             }
412             return;
413         }
414
415         /*
416          * It could have been a host from the FailedARPReqList
417          */
418
419         if (failedARPReqList.containsKey(networkAddr)) {
420             failedARPReqList.remove(networkAddr);
421             logger.debug("Host Removed from FailedARPReqList List, IP: {}", networkAddr);
422         }
423     }
424
425     // Learn a new Host
426     private void learnNewHost(HostNodeConnector host) {
427         host.initArpSendCountDown();
428         HostNodeConnector rHost = hostsDB.putIfAbsent(host.getNetworkAddress(), host);
429         if (rHost != null) {
430             // Another host is already learned for this IP address, replace it
431             replaceHost(host.getNetworkAddress(), rHost, host);
432         } else {
433             logger.debug("New Host Learned: MAC: {}  IP: {}", HexEncode.bytesToHexString(host
434                     .getDataLayerAddressBytes()), host.getNetworkAddress().getHostAddress());
435         }
436     }
437
438     private void replaceHost(InetAddress networkAddr, HostNodeConnector removedHost, HostNodeConnector newHost) {
439         // Ignore ARP messages from internal nodes
440         NodeConnector newHostNc = newHost.getnodeConnector();
441         boolean newHostIsInternal = topologyManager.isInternal(newHostNc);
442         if (newHostIsInternal) {
443             return;
444         }
445
446         newHost.initArpSendCountDown();
447
448         if (hostsDB.replace(networkAddr, removedHost, newHost)) {
449             logger.debug("Host move occurred: Old Host IP:{}, New Host IP: {}", removedHost.getNetworkAddress()
450                     .getHostAddress(), newHost.getNetworkAddress().getHostAddress());
451             logger.debug("Old Host MAC: {}, New Host MAC: {}",
452                     HexEncode.bytesToHexString(removedHost.getDataLayerAddressBytes()),
453                     HexEncode.bytesToHexString(newHost.getDataLayerAddressBytes()));
454             // Display the Old and New HostNodeConnectors also
455             logger.debug("Old {}, New {}", removedHost, newHost);
456         } else {
457             /*
458              * Host replacement has failed, do the recovery
459              */
460             hostsDB.put(networkAddr, newHost);
461             logger.error("Host replacement failed. Overwrite the host. Repalced Host: {}, New Host: {}", removedHost,
462                     newHost);
463         }
464         notifyHostLearnedOrRemoved(removedHost, false);
465         notifyHostLearnedOrRemoved(newHost, true);
466         if (!newHost.isStaticHost()) {
467             processPendingARPReqs(networkAddr);
468         }
469     }
470
471     // Remove known Host
472     private void removeKnownHost(InetAddress key) {
473         HostNodeConnector host = hostsDB.get(key);
474         if (host != null) {
475             logger.debug("Removing Host: IP:{}", host.getNetworkAddress().getHostAddress());
476             hostsDB.remove(key);
477         } else {
478             logger.error("removeKnownHost(): Host for IP address {} not found in hostsDB", key.getHostAddress());
479         }
480     }
481
482     private class NotifyHostThread extends Thread {
483
484         private final HostNodeConnector host;
485
486         public NotifyHostThread(HostNodeConnector h) {
487             this.host = h;
488         }
489
490         @Override
491         public void run() {
492             HostNodeConnector removedHost = null;
493             InetAddress networkAddr = host.getNetworkAddress();
494
495             /* Check for Host Move case */
496             if (hostMoved(host)) {
497                 /*
498                  * Host has been moved from one location (switch,port, MAC, or
499                  * VLAN) to another. Replace the existing host and its previous
500                  * location parameters with new information, and notify the
501                  * applications listening to host move.
502                  */
503                 removedHost = hostsDB.get(networkAddr);
504                 if (removedHost != null) {
505                     replaceHost(networkAddr, removedHost, host);
506                     return;
507                 } else {
508                     logger.error("Host to be removed not found in hostsDB");
509                 }
510             }
511
512             // It is a new host
513             learnNewHost(host);
514
515             /* check if there is an outstanding request for this host */
516             processPendingARPReqs(networkAddr);
517             notifyHostLearnedOrRemoved(host, true);
518         }
519     }
520
521     @Override
522     public void hostListener(HostNodeConnector host) {
523
524         logger.debug("ARP received for Host: IP {}, MAC {}, {}", host.getNetworkAddress().getHostAddress(),
525                 HexEncode.bytesToHexString(host.getDataLayerAddressBytes()), host);
526         if (hostExists(host)) {
527             HostNodeConnector existinghost = hostsDB.get(host.getNetworkAddress());
528             existinghost.initArpSendCountDown();
529             // Update the host
530             hostsDB.put(host.getNetworkAddress(), existinghost);
531             return;
532         }
533         new NotifyHostThread(host).start();
534     }
535
536     // Notify whoever is interested that a new host was learned (dynamically or
537     // statically)
538     private void notifyHostLearnedOrRemoved(HostNodeConnector host, boolean add) {
539         // Update listeners if any
540         if (newHostNotify != null) {
541             logger.debug("Notifying Applications for Host {} Being {}", host.getNetworkAddress().getHostAddress(),
542                     add ? "Added" : "Deleted");
543             synchronized (this.newHostNotify) {
544                 for (IfNewHostNotify ta : newHostNotify) {
545                     try {
546                         if (add) {
547                             ta.notifyHTClient(host);
548                         } else {
549                             ta.notifyHTClientHostRemoved(host);
550                         }
551                     } catch (Exception e) {
552                         logger.error("Exception on callback", e);
553                     }
554                 }
555             }
556         } else {
557             logger.error("notifyHostLearnedOrRemoved(): New host notify is null");
558         }
559
560         // Topology update is for some reason outside of listeners registry
561         // logic
562         Node node = host.getnodeconnectorNode();
563         Host h = null;
564         NodeConnector p = host.getnodeConnector();
565         try {
566             DataLinkAddress dla = new EthernetAddress(host.getDataLayerAddressBytes());
567             h = new Host(dla, host.getNetworkAddress());
568         } catch (ConstructionException ce) {
569             p = null;
570             h = null;
571         }
572
573         if (topologyManager != null && p != null && h != null) {
574             logger.debug("Notifying Topology Manager for Host {} Being {}", h.getNetworkAddress().getHostAddress(),
575                     add ? "Added" : "Deleted");
576             if (add == true) {
577                 Tier tier = new Tier(1);
578                 switchManager.setNodeProp(node, tier);
579                 topologyManager.updateHostLink(p, h, UpdateType.ADDED, null);
580             } else {
581                 // No need to reset the tiering if no other hosts are currently
582                 // connected
583                 // If this switch was discovered to be an access switch, it
584                 // still is even if the host is down
585                 Tier tier = new Tier(0);
586                 switchManager.setNodeProp(node, tier);
587                 topologyManager.updateHostLink(p, h, UpdateType.REMOVED, null);
588             }
589         }
590     }
591
592     /**
593      * When a new Host is learnt by the hosttracker module, it places the
594      * directly connected Node in Tier-1 & using this function, updates the Tier
595      * value for all other Nodes in the network hierarchy.
596      *
597      * This is a recursive function and it takes care of updating the Tier value
598      * for all the connected and eligible Nodes.
599      *
600      * @param n
601      *            Node that represents one of the Vertex in the Topology Graph.
602      * @param currentTier
603      *            The Tier on which n belongs
604      */
605     @SuppressWarnings("unused")
606     private void updateSwitchTiers(Node n, int currentTier) {
607         Map<Node, Set<Edge>> ndlinks = topologyManager.getNodeEdges();
608         if (ndlinks == null) {
609             logger.debug("updateSwitchTiers(): ndlinks null for Node: {}, Tier:{}", n, currentTier);
610             return;
611         }
612         Set<Edge> links = ndlinks.get(n);
613         if (links == null) {
614             logger.debug("updateSwitchTiers(): links null for ndlinks:{}", ndlinks);
615             return;
616         }
617         ArrayList<Node> needsVisiting = new ArrayList<Node>();
618         for (Edge lt : links) {
619             if (!lt.getHeadNodeConnector().getType().equals(NodeConnector.NodeConnectorIDType.OPENFLOW)) {
620                 // We don't want to work on Node that are not openflow
621                 // for now
622                 continue;
623             }
624             Node dstNode = lt.getHeadNodeConnector().getNode();
625             if (switchNeedsTieringUpdate(dstNode, currentTier + 1)) {
626                 Tier t = new Tier(currentTier + 1);
627                 switchManager.setNodeProp(dstNode, t);
628                 needsVisiting.add(dstNode);
629             }
630         }
631
632         /*
633          * Due to the nature of the problem, having a separate loop for nodes
634          * that needs visiting provides a decent walk optimization.
635          */
636         for (Node node : needsVisiting) {
637             updateSwitchTiers(node, currentTier + 1);
638         }
639     }
640
641     /**
642      * Internal convenience routine to check the eligibility of a Switch for a
643      * Tier update. Any Node with Tier=0 or a Tier value that is greater than
644      * the new Tier Value is eligible for the update.
645      *
646      * @param n
647      *            Node for which the Tier update eligibility is checked
648      * @param tier
649      *            new Tier Value
650      * @return <code>true</code> if the Node is eligible for Tier Update
651      *         <code>false</code> otherwise
652      */
653
654     private boolean switchNeedsTieringUpdate(Node n, int tier) {
655         if (n == null) {
656             logger.error("switchNeedsTieringUpdate(): Null node for tier: {}", tier);
657             return false;
658         }
659         /*
660          * Node could have gone down
661          */
662         if (!switchManager.getNodes().contains(n)) {
663             return false;
664         }
665         // This is the case where Tier was never set for this node
666         Tier t = (Tier) switchManager.getNodeProp(n, Tier.TierPropName);
667         if (t == null) {
668             return true;
669         }
670         if (t.getValue() == 0) {
671             return true;
672         } else if (t.getValue() > tier) {
673             return true;
674         }
675         return false;
676     }
677
678     /**
679      * Internal convenience routine to clear all the Tier values to 0. This
680      * cleanup is performed during cases such as Topology Change where the
681      * existing Tier values might become incorrect
682      */
683     @SuppressWarnings("unused")
684     private void clearTiers() {
685         Set<Node> nodes = null;
686         if (switchManager == null) {
687             logger.error("clearTiers(): Null switchManager");
688             return;
689         }
690         nodes = switchManager.getNodes();
691
692         for (Node n : nodes) {
693             Tier t = new Tier(0);
694             switchManager.setNodeProp(n, t);
695         }
696     }
697
698     /**
699      * Internal convenience routine to print the hierarchies of switches.
700      */
701     @SuppressWarnings("unused")
702     private void logHierarchies(ArrayList<ArrayList<String>> hierarchies) {
703         String hierarchyString = null;
704         int num = 1;
705         for (ArrayList<String> hierarchy : hierarchies) {
706             StringBuffer buf = new StringBuffer();
707             buf.append("Hierarchy#" + num + " : ");
708             for (String switchName : hierarchy) {
709                 buf.append(switchName + "/");
710             }
711             logger.debug("{} -> {}", getContainerName(), buf);
712             num++;
713         }
714     }
715
716     /**
717      * getHostNetworkHierarchy is the Back-end routine for the North-Bound API
718      * that returns the Network Hierarchy for a given Host. This API is
719      * typically used by applications like Hadoop for Rack Awareness
720      * functionality.
721      *
722      * @param hostAddress
723      *            IP-Address of the host/node.
724      * @return Network Hierarchies represented by an Array of Array (of
725      *         Switch-Ids as String).
726      */
727     @Override
728     public List<List<String>> getHostNetworkHierarchy(InetAddress hostAddress) {
729         HostNodeConnector host = hostQuery(hostAddress);
730         if (host == null) {
731             return null;
732         }
733
734         List<List<String>> hierarchies = new ArrayList<List<String>>();
735         ArrayList<String> currHierarchy = new ArrayList<String>();
736         hierarchies.add(currHierarchy);
737
738         Node node = host.getnodeconnectorNode();
739         updateCurrentHierarchy(node, currHierarchy, hierarchies);
740         return hierarchies;
741     }
742
743     /**
744      * dpidToHostNameHack is a hack function for Cisco Live Hadoop Demo. Mininet
745      * is used as the network for Hadoop Demos & in order to give a meaningful
746      * rack-awareness switch names, the DPID is organized in ASCII Characters
747      * and retrieved as string.
748      *
749      * @param dpid
750      *            Switch DataPath Id
751      * @return Ascii String represented by the DPID.
752      */
753     private String dpidToHostNameHack(long dpid) {
754         String hex = Long.toHexString(dpid);
755
756         StringBuffer sb = new StringBuffer();
757         int result = 0;
758         for (int i = 0; i < hex.length(); i++) {
759             result = (int) ((dpid >> (i * 8)) & 0xff);
760             if (result == 0) {
761                 continue;
762             }
763             if (result < 0x30) {
764                 result += 0x40;
765             }
766             sb.append(String.format("%c", result));
767         }
768         return sb.reverse().toString();
769     }
770
771     /**
772      * A convenient recursive routine to obtain the Hierarchy of Switches.
773      *
774      * @param node
775      *            Current Node in the Recursive routine.
776      * @param currHierarchy
777      *            Array of Nodes that make this hierarchy on which the Current
778      *            Switch belong
779      * @param fullHierarchy
780      *            Array of multiple Hierarchies that represent a given host.
781      */
782     @SuppressWarnings("unchecked")
783     private void updateCurrentHierarchy(Node node, ArrayList<String> currHierarchy, List<List<String>> fullHierarchy) {
784         // currHierarchy.add(String.format("%x", currSw.getId()));
785         currHierarchy.add(dpidToHostNameHack((Long) node.getID()));
786         // Shallow copy as required
787         ArrayList<String> currHierarchyClone = (ArrayList<String>) currHierarchy.clone();
788
789         Map<Node, Set<Edge>> ndlinks = topologyManager.getNodeEdges();
790         if (ndlinks == null) {
791             logger.debug("updateCurrentHierarchy(): topologyManager returned null ndlinks for node: {}", node);
792             return;
793         }
794         Node n = NodeCreator.createOFNode((Long) node.getID());
795         Set<Edge> links = ndlinks.get(n);
796         if (links == null) {
797             logger.debug("updateCurrentHierarchy(): Null links for ndlinks");
798             return;
799         }
800         for (Edge lt : links) {
801             if (!lt.getHeadNodeConnector().getType().equals(NodeConnector.NodeConnectorIDType.OPENFLOW)) {
802                 // We don't want to work on Node that are not openflow
803                 // for now
804                 continue;
805             }
806             Node dstNode = lt.getHeadNodeConnector().getNode();
807
808             Tier nodeTier = (Tier) switchManager.getNodeProp(node, Tier.TierPropName);
809             /*
810              * If the host is directly attached to the src node, then the node
811              * should have been assigned the "Access" tier in
812              * notifyHostLearnedOrRemoved. If not, it would be assigned
813              * "Unknown" tier. Thus the tier of host attached node cannot be
814              * null. If the src node here, is the next node in the hierarchy of
815              * the nodes, then its tier cannot be null
816              */
817
818             Tier dstNodeTier = (Tier) switchManager.getNodeProp(dstNode, Tier.TierPropName);
819             /*
820              * Skip if the tier of the destination node is null
821              */
822             if (dstNodeTier == null) {
823                 continue;
824             }
825             if (dstNodeTier.getValue() > nodeTier.getValue()) {
826                 ArrayList<String> buildHierarchy = currHierarchy;
827                 if (currHierarchy.size() > currHierarchyClone.size()) {
828                     // Shallow copy as required
829                     buildHierarchy = (ArrayList<String>) currHierarchyClone.clone();
830                     fullHierarchy.add(buildHierarchy);
831                 }
832                 updateCurrentHierarchy(dstNode, buildHierarchy, fullHierarchy);
833             }
834         }
835     }
836
837     private void debugEdgeUpdate(Edge e, UpdateType type, Set<Property> props) {
838         Long srcNid = null;
839         Short srcPort = null;
840         Long dstNid = null;
841         Short dstPort = null;
842         boolean added = false;
843         String srcType = null;
844         String dstType = null;
845
846         if (e == null || type == null) {
847             logger.error("Edge or Update type are null!");
848             return;
849         } else {
850             srcType = e.getTailNodeConnector().getType();
851             dstType = e.getHeadNodeConnector().getType();
852
853             if (srcType.equals(NodeConnector.NodeConnectorIDType.PRODUCTION)) {
854                 logger.debug("Skip updates for {}", e);
855                 return;
856             }
857
858             if (!srcType.equals(NodeConnector.NodeConnectorIDType.OPENFLOW)) {
859                 logger.debug("For now we cannot handle updates for non-openflow nodes");
860                 return;
861             }
862
863             if (dstType.equals(NodeConnector.NodeConnectorIDType.PRODUCTION)) {
864                 logger.debug("Skip updates for {}", e);
865                 return;
866             }
867
868             if (!dstType.equals(NodeConnector.NodeConnectorIDType.OPENFLOW)) {
869                 logger.debug("For now we cannot handle updates for non-openflow nodes");
870                 return;
871             }
872
873             // At this point we know we got an openflow update, so
874             // lets fill everything accordingly.
875             srcNid = (Long) e.getTailNodeConnector().getNode().getID();
876             srcPort = (Short) e.getTailNodeConnector().getID();
877             dstNid = (Long) e.getHeadNodeConnector().getNode().getID();
878             dstPort = (Short) e.getHeadNodeConnector().getID();
879
880             // Now lets update the added flag
881             switch (type) {
882             case ADDED:
883             case CHANGED:
884                 added = true;
885                 break;
886             case REMOVED:
887                 added = false;
888             }
889         }
890
891         logger.debug("HostTracker Topology linkUpdate handling src:{}[port {}] dst:{}[port {}] added: {}",
892                 new Object[] { srcNid, srcPort, dstNid, dstPort, added });
893     }
894
895     @Override
896     public void edgeUpdate(List<TopoEdgeUpdate> topoedgeupdateList) {
897         if (logger.isDebugEnabled()) {
898             for (TopoEdgeUpdate topoEdgeUpdate : topoedgeupdateList) {
899                 Edge e = topoEdgeUpdate.getEdge();
900                 Set<Property> p = topoEdgeUpdate.getProperty();
901                 UpdateType type = topoEdgeUpdate.getUpdateType();
902
903                 debugEdgeUpdate(e, type, p);
904             }
905         }
906     }
907
908     @Override
909     public void subnetNotify(Subnet sub, boolean add) {
910         logger.debug("Received subnet notification: {}  add={}", sub, add);
911         if (add) {
912             for (Entry<InetAddress, ARPPending> entry : failedARPReqList.entrySet()) {
913                 ARPPending arphost;
914                 arphost = entry.getValue();
915                 if (hostFinder == null) {
916                     logger.warn("ARPHandler Services are not available on subnet addition");
917                     continue;
918                 }
919                 logger.debug("Sending the ARP from FailedARPReqList fors IP: {}", arphost.getHostIP().getHostAddress());
920                 hostFinder.find(arphost.getHostIP());
921             }
922         }
923     }
924
925     class OutStandingARPHandler extends TimerTask {
926         @Override
927         public void run() {
928             if (stopping) {
929                 return;
930             }
931             ARPPending arphost;
932             /* This routine runs every 4 seconds */
933             logger.trace("Number of Entries in ARP Pending/Failed Lists: ARPPendingList = {}, failedARPReqList = {}",
934                     ARPPendingList.size(), failedARPReqList.size());
935             for (Entry<InetAddress, ARPPending> entry : ARPPendingList.entrySet()) {
936                 arphost = entry.getValue();
937
938                 if (hostsDB.containsKey(arphost.getHostIP())) {
939                     // this host is already learned, shouldn't be in
940                     // ARPPendingList
941                     // Remove it and continue
942                     logger.warn("Learned Host {} found in ARPPendingList", arphost.getHostIP());
943                     ARPPendingList.remove(entry.getKey());
944                     continue;
945                 }
946                 if (arphost.getSent_count() < switchManager.getHostRetryCount()) {
947                     /*
948                      * No reply has been received of first ARP Req, send the
949                      * next one. Before sending the ARP, check if ARPHandler is
950                      * available or not
951                      */
952                     if (hostFinder == null) {
953                         logger.warn("ARPHandler Services are not available for Outstanding ARPs");
954                         continue;
955                     }
956                     hostFinder.find(arphost.getHostIP());
957                     arphost.sent_count++;
958                     logger.debug("ARP Sent from ARPPending List, IP: {}", arphost.getHostIP().getHostAddress());
959                 } else if (arphost.getSent_count() >= switchManager.getHostRetryCount()) {
960                     /*
961                      * ARP requests have been sent without receiving a reply,
962                      * remove this from the pending list
963                      */
964                     ARPPendingList.remove(entry.getKey());
965                     logger.debug("ARP reply not received after multiple attempts, removing from Pending List IP: {}",
966                             arphost.getHostIP().getHostAddress());
967                     /*
968                      * Add this host to a different list which will be processed
969                      * on link up events
970                      */
971                     logger.debug("Adding the host to FailedARPReqList IP: {}", arphost.getHostIP().getHostAddress());
972                     failedARPReqList.put(entry.getKey(), arphost);
973
974                 } else {
975                     logger.error("Inavlid arp_sent count for entry: {}", entry);
976                 }
977             }
978         }
979     }
980
981     private class ARPRefreshHandler extends TimerTask {
982         @Override
983         public void run() {
984             if (stopping) {
985                 return;
986             }
987             if ((clusterContainerService != null) && !clusterContainerService.amICoordinator()) {
988                 return;
989             }
990             if ((switchManager != null) && !switchManager.isHostRefreshEnabled()) {
991                 /*
992                  * The host probe procedure was disabled by CLI
993                  */
994                 return;
995             }
996             if (hostsDB == null) {
997                 /* hostsDB is not allocated yet */
998                 logger.error("ARPRefreshHandler(): hostsDB is not allocated yet:");
999                 return;
1000             }
1001             for (Entry<InetAddress, HostNodeConnector> entry : hostsDB.entrySet()) {
1002                 HostNodeConnector host = entry.getValue();
1003                 if (host.isStaticHost()) {
1004                     /* this host was learned via API3, don't age it out */
1005                     continue;
1006                 }
1007
1008                 short arp_cntdown = host.getArpSendCountDown();
1009                 arp_cntdown--;
1010                 if (arp_cntdown > switchManager.getHostRetryCount()) {
1011                     host.setArpSendCountDown(arp_cntdown);
1012                 } else if (arp_cntdown <= 0) {
1013                     /*
1014                      * No ARP Reply received in last 2 minutes, remove this host
1015                      * and inform applications
1016                      */
1017                     removeKnownHost(entry.getKey());
1018                     notifyHostLearnedOrRemoved(host, false);
1019                 } else if (arp_cntdown <= switchManager.getHostRetryCount()) {
1020                     /*
1021                      * Use the services of arphandler to check if host is still
1022                      * there
1023                      */
1024                     if (logger.isTraceEnabled()) {
1025                         logger.trace(
1026                                 "ARP Probing ({}) for {}({})",
1027                                 new Object[] { arp_cntdown, host.getNetworkAddress().getHostAddress(),
1028                                         HexEncode.bytesToHexString(host.getDataLayerAddressBytes()) });
1029                     }
1030                     host.setArpSendCountDown(arp_cntdown);
1031                     if (hostFinder == null) {
1032                         /*
1033                          * If hostfinder is not available, then can't send the
1034                          * probe. However, continue the age out the hosts since
1035                          * we don't know if the host is indeed out there or not.
1036                          */
1037                         logger.warn("ARPHandler is not avaialable, can't send the probe");
1038                         continue;
1039                     }
1040                     hostFinder.probe(host);
1041                 }
1042             }
1043         }
1044     }
1045
1046     /**
1047      * Inform the controller IP to MAC binding of a host and its connectivity to
1048      * an openflow switch in terms of Node, port, and VLAN.
1049      *
1050      * @param networkAddr
1051      *            IP address of the host
1052      * @param dataLayer
1053      *            Address MAC address of the host
1054      * @param nc
1055      *            NodeConnector to which host is connected
1056      * @param port
1057      *            Port of the switch to which host is connected
1058      * @param vlan
1059      *            Vlan of which this host is member of
1060      *
1061      * @return Status The status object as described in {@code Status}
1062      *         indicating the result of this action.
1063      */
1064
1065     public Status addStaticHostReq(InetAddress networkAddr, byte[] dataLayerAddress, NodeConnector nc, short vlan) {
1066         if (dataLayerAddress.length != NetUtils.MACAddrLengthInBytes) {
1067             return new Status(StatusCode.BADREQUEST, "Invalid MAC address");
1068         }
1069
1070         if (nc == null) {
1071             return new Status(StatusCode.BADREQUEST, "Invalid NodeConnector");
1072         }
1073         HostNodeConnector host = null;
1074         try {
1075             host = new HostNodeConnector(dataLayerAddress, networkAddr, nc, vlan);
1076             if (hostExists(host)) {
1077                 // This host is already learned either via ARP or through a
1078                 // northbound request
1079                 HostNodeConnector transHost = hostsDB.get(networkAddr);
1080                 transHost.setStaticHost(true);
1081                 return new Status(StatusCode.SUCCESS, null);
1082             }
1083
1084             if (hostsDB.get(networkAddr) != null) {
1085                 // There is already a host with this IP address (but behind
1086                 // a different (switch, port, vlan) tuple. Return an error
1087                 return new Status(StatusCode.CONFLICT, "Existing IP, Use PUT to update");
1088             }
1089             host.setStaticHost(true);
1090             /*
1091              * Check if the nc is an ISL port
1092              */
1093             if (topologyManager != null) {
1094                 if (topologyManager.isInternal(nc)) {
1095                     return new Status(StatusCode.BADREQUEST, "Cannot add host on ISL port");
1096                 }
1097             }
1098             /*
1099              * Before adding host, Check if the switch and the port have already
1100              * come up
1101              */
1102             if (switchManager.isNodeConnectorEnabled(nc)) {
1103                 learnNewHost(host);
1104                 processPendingARPReqs(networkAddr);
1105                 notifyHostLearnedOrRemoved(host, true);
1106             } else {
1107                 inactiveStaticHosts.put(nc, host);
1108                 logger.debug("Switch or switchport is not up, adding host {} to inactive list",
1109                         networkAddr.getHostName());
1110             }
1111             return new Status(StatusCode.SUCCESS, null);
1112         } catch (ConstructionException e) {
1113             logger.error("", e);
1114             return new Status(StatusCode.INTERNALERROR, "Host could not be created");
1115         }
1116
1117     }
1118
1119     /**
1120      * Update the controller IP to MAC binding of a host and its connectivity to
1121      * an openflow switch in terms of switch id, switch port, and VLAN.
1122      *
1123      * @param networkAddr
1124      *            IP address of the host
1125      * @param dataLayer
1126      *            Address MAC address of the host
1127      * @param nc
1128      *            NodeConnector to which host is connected
1129      * @param port
1130      *            Port of the switch to which host is connected
1131      * @param vlan
1132      *            Vlan of which this host is member of
1133      *
1134      * @return Status The status object as described in {@code Status}
1135      *         indicating the result of this action.
1136      */
1137     public Status updateHostReq(InetAddress networkAddr, byte[] dataLayerAddress, NodeConnector nc, short vlan) {
1138         HostNodeConnector tobeUpdatedHost;
1139         HostNodeConnector host = null;
1140
1141         if (dataLayerAddress.length != NetUtils.MACAddrLengthInBytes) {
1142             return new Status(StatusCode.BADREQUEST, "Invalid MAC address");
1143         }
1144
1145         if (nc == null) {
1146             return new Status(StatusCode.BADREQUEST, "Invalid NodeConnector");
1147         }
1148
1149         try {
1150             host = new HostNodeConnector(dataLayerAddress, networkAddr, nc, vlan);
1151             if (hostExists(host)) {
1152                 return new Status(StatusCode.BADREQUEST, "Host already exists");
1153             }
1154
1155             if ((tobeUpdatedHost = hostsDB.get(networkAddr)) != null) {
1156                 if (hostsDB.replace(networkAddr, tobeUpdatedHost, host)) {
1157                     logger.debug("Host replaced from hostsDB. Old host: {} New Host: {}", tobeUpdatedHost, host);
1158                     notifyHostLearnedOrRemoved(tobeUpdatedHost, false);
1159                     notifyHostLearnedOrRemoved(host, true);
1160                     return new Status(StatusCode.SUCCESS);
1161                 } else {
1162                     logger.error("Static host replacement failed from hostsDB, Replaced Host: {}, New Host: {}",
1163                             tobeUpdatedHost, host);
1164                     return new Status(StatusCode.INTERNALERROR,
1165                             "Host Replacement Failed due to presence of another host with same IP");
1166                 }
1167             }
1168
1169             // Check if the host exists in inactive hosts database
1170             if ((tobeUpdatedHost = inactiveStaticHosts.get(nc)) != null) {
1171                 if (inactiveStaticHosts.replace(nc, tobeUpdatedHost, host)) {
1172                     logger.debug("Host replaced from inactive hostsDB. Old host: {} New Host: {}", tobeUpdatedHost,
1173                             host);
1174                     return new Status(StatusCode.SUCCESS);
1175                 } else {
1176                     logger.error("Static host replacement failed, Replaced Host: {}, New Host: {}", tobeUpdatedHost,
1177                             host);
1178                     return new Status(StatusCode.INTERNALERROR,
1179                             "Host Replacement Failed due to presence of another host with same IP");
1180                 }
1181             }
1182
1183             // Host doesn't exist
1184             return new Status(StatusCode.BADREQUEST, "Host doesn't exists, can't update");
1185         } catch (ConstructionException e) {
1186             logger.error("", e);
1187             return new Status(StatusCode.INTERNALERROR, "host object creation failure");
1188         }
1189     }
1190
1191     /**
1192      * Remove from the controller IP to MAC binding of a host and its
1193      * connectivity to an openflow switch
1194      *
1195      * @param networkAddr
1196      *            IP address of the host
1197      *
1198      * @return boolean true if the host was removed successfully, false
1199      *         otherwise
1200      */
1201
1202     public Status removeStaticHostReq(InetAddress networkAddress) {
1203         // Check if host is in active hosts database
1204         HostNodeConnector host = getHostFromOnActiveDB(networkAddress);
1205         if (host != null) {
1206             // Validation check
1207             if (!host.isStaticHost()) {
1208                 return new Status(StatusCode.FORBIDDEN, "Host " + networkAddress.getHostName() + " is not static");
1209             }
1210             // Remove and notify
1211             notifyHostLearnedOrRemoved(host, false);
1212             removeKnownHost(networkAddress);
1213             return new Status(StatusCode.SUCCESS, null);
1214         }
1215
1216         // Check if host is in inactive hosts database
1217         Entry<NodeConnector, HostNodeConnector> entry = getHostFromInactiveDB(networkAddress);
1218         if (entry != null) {
1219             host = entry.getValue();
1220             // Validation check
1221             if (!host.isStaticHost()) {
1222                 return new Status(StatusCode.FORBIDDEN, "Host " + networkAddress.getHostName() + " is not static");
1223             }
1224             this.removeHostFromInactiveDB(networkAddress);
1225             return new Status(StatusCode.SUCCESS, null);
1226         }
1227
1228         // Host is neither in active nor inactive hosts database
1229         return new Status(StatusCode.NOTFOUND, "Host does not exist");
1230     }
1231
1232     @Override
1233     public void modeChangeNotify(Node node, boolean proactive) {
1234         logger.debug("Set Switch {} Mode to {}", node.getID(), proactive);
1235     }
1236
1237     @Override
1238     public void notifyNode(Node node, UpdateType type, Map<String, Property> propMap) {
1239         if (node == null) {
1240             return;
1241         }
1242
1243         switch (type) {
1244         case REMOVED:
1245             logger.debug("Received removed node {}", node);
1246             for (Entry<InetAddress, HostNodeConnector> entry : hostsDB.entrySet()) {
1247                 HostNodeConnector host = entry.getValue();
1248                 if (host.getnodeconnectorNode().equals(node)) {
1249                     logger.debug("Node: {} is down, remove from Hosts_DB", node);
1250                     removeKnownHost(entry.getKey());
1251                     notifyHostLearnedOrRemoved(host, false);
1252                 }
1253             }
1254             break;
1255         default:
1256             break;
1257         }
1258     }
1259
1260     @Override
1261     public void notifyNodeConnector(NodeConnector nodeConnector, UpdateType type, Map<String, Property> propMap) {
1262         if (nodeConnector == null) {
1263             return;
1264         }
1265
1266         boolean up = false;
1267         switch (type) {
1268         case ADDED:
1269             up = true;
1270             break;
1271         case REMOVED:
1272             break;
1273         case CHANGED:
1274             State state = (State) propMap.get(State.StatePropName);
1275             if ((state != null) && (state.getValue() == State.EDGE_UP)) {
1276                 up = true;
1277             }
1278             break;
1279         default:
1280             return;
1281         }
1282
1283         if (up) {
1284             handleNodeConnectorStatusUp(nodeConnector);
1285         } else {
1286             handleNodeConnectorStatusDown(nodeConnector);
1287         }
1288     }
1289
1290     @Override
1291     public Status addStaticHost(String networkAddress, String dataLayerAddress, NodeConnector nc, String vlan) {
1292         try {
1293             InetAddress ip = InetAddress.getByName(networkAddress);
1294             if (nc == null) {
1295                 return new Status(StatusCode.BADREQUEST, "Invalid NodeId");
1296             }
1297             return addStaticHostReq(ip, HexEncode.bytesFromHexString(dataLayerAddress), nc, Short.valueOf(vlan));
1298         } catch (UnknownHostException e) {
1299             logger.error("", e);
1300             return new Status(StatusCode.BADREQUEST, "Invalid Address");
1301         }
1302     }
1303
1304     @Override
1305     public Status removeStaticHost(String networkAddress) {
1306         InetAddress address;
1307         try {
1308             address = InetAddress.getByName(networkAddress);
1309             return removeStaticHostReq(address);
1310         } catch (UnknownHostException e) {
1311             logger.error("", e);
1312             return new Status(StatusCode.BADREQUEST, "Invalid Address");
1313         }
1314     }
1315
1316     private void handleNodeConnectorStatusUp(NodeConnector nodeConnector) {
1317         ARPPending arphost;
1318         HostNodeConnector host = null;
1319
1320         logger.debug("handleNodeConnectorStatusUp {}", nodeConnector);
1321
1322         for (Entry<InetAddress, ARPPending> entry : failedARPReqList.entrySet()) {
1323             arphost = entry.getValue();
1324             logger.debug("Sending the ARP from FailedARPReqList fors IP: {}", arphost.getHostIP().getHostAddress());
1325             if (hostFinder == null) {
1326                 logger.warn("ARPHandler is not available at interface  up");
1327                 logger.warn("Since this event is missed, host(s) connected to interface {} may not be discovered",
1328                         nodeConnector);
1329                 continue;
1330             }
1331
1332             // Send a broadcast ARP only on the interface which just came up.
1333             // Use hostFinder's "probe" method
1334             try {
1335                 byte[] dataLayerAddress = NetUtils.getBroadcastMACAddr();
1336                 host = new HostNodeConnector(dataLayerAddress, arphost.getHostIP(), nodeConnector, (short) 0);
1337                 hostFinder.probe(host);
1338             } catch (ConstructionException e) {
1339                 logger.debug("HostNodeConnector couldn't be created for Host: {}, NodeConnector: {}",
1340                         arphost.getHostIP(), nodeConnector);
1341                 logger.error("", e);
1342             }
1343             logger.debug("Done. handleNodeConnectorStatusUp {}", nodeConnector);
1344         }
1345
1346         host = inactiveStaticHosts.get(nodeConnector);
1347         if (host != null) {
1348             inactiveStaticHosts.remove(nodeConnector);
1349             learnNewHost(host);
1350             processPendingARPReqs(host.getNetworkAddress());
1351             notifyHostLearnedOrRemoved(host, true);
1352         }
1353     }
1354
1355     private void handleNodeConnectorStatusDown(NodeConnector nodeConnector) {
1356         logger.debug("handleNodeConnectorStatusDown {}", nodeConnector);
1357
1358         for (Entry<InetAddress, HostNodeConnector> entry : hostsDB.entrySet()) {
1359             HostNodeConnector host = entry.getValue();
1360             if (host.getnodeConnector().equals(nodeConnector)) {
1361                 logger.debug(" NodeConnector: {} is down, remove from Hosts_DB", nodeConnector);
1362                 removeKnownHost(entry.getKey());
1363                 notifyHostLearnedOrRemoved(host, false);
1364             }
1365         }
1366     }
1367
1368     void setClusterContainerService(IClusterContainerServices s) {
1369         logger.debug("Cluster Service set");
1370         this.clusterContainerService = s;
1371     }
1372
1373     void unsetClusterContainerService(IClusterContainerServices s) {
1374         if (this.clusterContainerService == s) {
1375             logger.debug("Cluster Service removed!");
1376             this.clusterContainerService = null;
1377         }
1378     }
1379
1380     void setSwitchManager(ISwitchManager s) {
1381         logger.debug("SwitchManager set");
1382         this.switchManager = s;
1383     }
1384
1385     void unsetSwitchManager(ISwitchManager s) {
1386         if (this.switchManager == s) {
1387             logger.debug("SwitchManager removed!");
1388             this.switchManager = null;
1389         }
1390     }
1391
1392     public String getContainerName() {
1393         if (containerName == null) {
1394             return GlobalConstants.DEFAULT.toString();
1395         }
1396         return containerName;
1397     }
1398
1399     /**
1400      * Function called by the dependency manager when all the required
1401      * dependencies are satisfied
1402      *
1403      */
1404     void init(Component c) {
1405         Dictionary<?, ?> props = c.getServiceProperties();
1406         if (props != null) {
1407             this.containerName = (String) props.get("containerName");
1408             logger.debug("Running containerName: {}", this.containerName);
1409         } else {
1410             // In the Global instance case the containerName is empty
1411             this.containerName = "";
1412         }
1413         startUp();
1414     }
1415
1416     /**
1417      * Function called by the dependency manager when at least one dependency
1418      * become unsatisfied or when the component is shutting down because for
1419      * example bundle is being stopped.
1420      *
1421      */
1422     void destroy() {
1423     }
1424
1425     /**
1426      * Function called by dependency manager after "init ()" is called and after
1427      * the services provided by the class are registered in the service registry
1428      *
1429      */
1430     void start() {
1431         registerWithOSGIConsole();
1432     }
1433
1434     /**
1435      * Function called by the dependency manager before the services exported by
1436      * the component are unregistered, this will be followed by a "destroy ()"
1437      * calls
1438      *
1439      */
1440     void stop() {
1441     }
1442
1443     void stopping() {
1444         stopping = true;
1445         arpRefreshTimer.cancel();
1446         timer.cancel();
1447         executor.shutdownNow();
1448     }
1449
1450     @Override
1451     public void edgeOverUtilized(Edge edge) {
1452
1453     }
1454
1455     @Override
1456     public void edgeUtilBackToNormal(Edge edge) {
1457
1458     }
1459
1460     @Override
1461     public void entryCreated(InetAddress key, String cacheName, boolean originLocal) {
1462         if (originLocal) {
1463             return;
1464         }
1465         processPendingARPReqs(key);
1466     }
1467
1468     @Override
1469     public void entryUpdated(InetAddress key, HostNodeConnector new_value, String cacheName, boolean originLocal) {
1470     }
1471
1472     @Override
1473     public void entryDeleted(InetAddress key, String cacheName, boolean originLocal) {
1474     }
1475
1476     private void registerWithOSGIConsole() {
1477         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
1478         bundleContext.registerService(CommandProvider.class.getName(), this, null);
1479     }
1480
1481     @Override
1482     public String getHelp() {
1483         return null;
1484     }
1485
1486     public void _dumpPendingARPReqList(CommandInterpreter ci) {
1487         ARPPending arphost;
1488         for (Entry<InetAddress, ARPPending> entry : ARPPendingList.entrySet()) {
1489             arphost = entry.getValue();
1490             ci.println(arphost.getHostIP().toString());
1491         }
1492     }
1493
1494     public void _dumpFailedARPReqList(CommandInterpreter ci) {
1495         ARPPending arphost;
1496         for (Entry<InetAddress, ARPPending> entry : failedARPReqList.entrySet()) {
1497             arphost = entry.getValue();
1498             ci.println(arphost.getHostIP().toString());
1499         }
1500     }
1501 }