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