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