Fixed bug with groupRef and meterRef in group and meter models.
[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#").append(num).append(" : ");
700             for (String switchName : hierarchy) {
701                 buf.append(switchName).append("/");
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(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             if (stopping) {
920                 return;
921             }
922             ARPPending arphost;
923             /* This routine runs every 4 seconds */
924             logger.trace("Number of Entries in ARP Pending/Failed Lists: ARPPendingList = {}, failedARPReqList = {}",
925                     ARPPendingList.size(), failedARPReqList.size());
926             for (Entry<InetAddress, ARPPending> entry : ARPPendingList.entrySet()) {
927                 arphost = entry.getValue();
928
929                 if (hostsDB.containsKey(arphost.getHostIP())) {
930                     // this host is already learned, shouldn't be in
931                     // ARPPendingList
932                     // Remove it and continue
933                     logger.warn("Learned Host {} found in ARPPendingList", arphost.getHostIP());
934                     ARPPendingList.remove(entry.getKey());
935                     continue;
936                 }
937                 if (arphost.getSent_count() < hostRetryCount) {
938                     /*
939                      * No reply has been received of first ARP Req, send the
940                      * next one. Before sending the ARP, check if ARPHandler is
941                      * available or not
942                      */
943                     if (hostFinder == null) {
944                         logger.warn("ARPHandler Services are not available for Outstanding ARPs");
945                         continue;
946                     }
947                     hostFinder.find(arphost.getHostIP());
948                     arphost.sent_count++;
949                     logger.debug("ARP Sent from ARPPending List, IP: {}", arphost.getHostIP().getHostAddress());
950                 } else if (arphost.getSent_count() >= hostRetryCount) {
951                     /*
952                      * ARP requests have been sent without receiving a reply,
953                      * remove this from the pending list
954                      */
955                     ARPPendingList.remove(entry.getKey());
956                     logger.debug("ARP reply not received after multiple attempts, removing from Pending List IP: {}",
957                             arphost.getHostIP().getHostAddress());
958                     /*
959                      * Add this host to a different list which will be processed
960                      * on link up events
961                      */
962                     logger.debug("Adding the host to FailedARPReqList IP: {}", arphost.getHostIP().getHostAddress());
963                     failedARPReqList.put(entry.getKey(), arphost);
964
965                 } else {
966                     logger.error("Inavlid arp_sent count for entry: {}", entry);
967                 }
968             }
969         }
970     }
971
972     private class ARPRefreshHandler extends TimerTask {
973         @Override
974         public void run() {
975             if (stopping) {
976                 return;
977             }
978             if ((clusterContainerService != null) && !clusterContainerService.amICoordinator()) {
979                 return;
980             }
981             if (!hostRefresh) {
982                 /*
983                  * The host probe procedure is turned off
984                  */
985                 return;
986             }
987             if (hostsDB == null) {
988                 /* hostsDB is not allocated yet */
989                 logger.error("ARPRefreshHandler(): hostsDB is not allocated yet:");
990                 return;
991             }
992             for (Entry<InetAddress, HostNodeConnector> entry : hostsDB.entrySet()) {
993                 HostNodeConnector host = entry.getValue();
994                 if (host.isStaticHost()) {
995                     /* this host was learned via API3, don't age it out */
996                     continue;
997                 }
998
999                 short arp_cntdown = host.getArpSendCountDown();
1000                 arp_cntdown--;
1001                 if (arp_cntdown > hostRetryCount) {
1002                     host.setArpSendCountDown(arp_cntdown);
1003                 } else if (arp_cntdown <= 0) {
1004                     /*
1005                      * No ARP Reply received in last 2 minutes, remove this host
1006                      * and inform applications
1007                      */
1008                     removeKnownHost(entry.getKey());
1009                     notifyHostLearnedOrRemoved(host, false);
1010                 } else if (arp_cntdown <= hostRetryCount) {
1011                     /*
1012                      * Use the services of arphandler to check if host is still
1013                      * there
1014                      */
1015                     if (logger.isTraceEnabled()) {
1016                         logger.trace(
1017                                 "ARP Probing ({}) for {}({})",
1018                                 new Object[] { arp_cntdown, host.getNetworkAddress().getHostAddress(),
1019                                         HexEncode.bytesToHexString(host.getDataLayerAddressBytes()) });
1020                     }
1021                     host.setArpSendCountDown(arp_cntdown);
1022                     if (hostFinder == null) {
1023                         /*
1024                          * If hostfinder is not available, then can't send the
1025                          * probe. However, continue the age out the hosts since
1026                          * we don't know if the host is indeed out there or not.
1027                          */
1028                         logger.trace("ARPHandler is not avaialable, can't send the probe");
1029                         continue;
1030                     }
1031                     hostFinder.probe(host);
1032                 }
1033             }
1034         }
1035     }
1036
1037     /**
1038      * Inform the controller IP to MAC binding of a host and its connectivity to
1039      * an openflow switch in terms of Node, port, and VLAN.
1040      *
1041      * @param networkAddr
1042      *            IP address of the host
1043      * @param dataLayer
1044      *            Address MAC address of the host
1045      * @param nc
1046      *            NodeConnector to which host is connected
1047      * @param port
1048      *            Port of the switch to which host is connected
1049      * @param vlan
1050      *            Vlan of which this host is member of
1051      *
1052      * @return Status The status object as described in {@code Status}
1053      *         indicating the result of this action.
1054      */
1055
1056     protected Status addStaticHostReq(InetAddress networkAddr, byte[] dataLayerAddress, NodeConnector nc, short vlan) {
1057         if (dataLayerAddress.length != NetUtils.MACAddrLengthInBytes) {
1058             return new Status(StatusCode.BADREQUEST, "Invalid MAC address");
1059         }
1060
1061         if (nc == null) {
1062             return new Status(StatusCode.BADREQUEST, "Invalid NodeConnector");
1063         }
1064         HostNodeConnector host = null;
1065         try {
1066             host = new HostNodeConnector(dataLayerAddress, networkAddr, nc, vlan);
1067             if (hostExists(host)) {
1068                 // This host is already learned either via ARP or through a
1069                 // northbound request
1070                 HostNodeConnector transHost = hostsDB.get(networkAddr);
1071                 transHost.setStaticHost(true);
1072                 return new Status(StatusCode.SUCCESS);
1073             }
1074
1075             if (hostsDB.get(networkAddr) != null) {
1076                 // There is already a host with this IP address (but behind
1077                 // a different (switch, port, vlan) tuple. Return an error
1078                 return new Status(StatusCode.CONFLICT, "Host with this IP already exists.");
1079             }
1080             host.setStaticHost(true);
1081             /*
1082              * Check if the nc is an ISL port
1083              */
1084             if (topologyManager != null) {
1085                 if (topologyManager.isInternal(nc)) {
1086                     return new Status(StatusCode.BADREQUEST, "Cannot add host on ISL port");
1087                 }
1088             }
1089             /*
1090              * Before adding host, Check if the switch and the port have already
1091              * come up
1092              */
1093             if (switchManager.isNodeConnectorEnabled(nc)) {
1094                 learnNewHost(host);
1095                 processPendingARPReqs(networkAddr);
1096                 notifyHostLearnedOrRemoved(host, true);
1097             } else {
1098                 inactiveStaticHosts.put(nc, host);
1099                 logger.debug("Switch or switchport is not up, adding host {} to inactive list",
1100                         networkAddr.getHostName());
1101             }
1102             return new Status(StatusCode.SUCCESS);
1103         } catch (ConstructionException e) {
1104             logger.error("", e);
1105             return new Status(StatusCode.INTERNALERROR, "Host could not be created");
1106         }
1107
1108     }
1109
1110     /**
1111      * Update the controller IP to MAC binding of a host and its connectivity to
1112      * an openflow switch in terms of switch id, switch port, and VLAN.
1113      *
1114      * @param networkAddr
1115      *            IP address of the host
1116      * @param dataLayer
1117      *            Address MAC address of the host
1118      * @param nc
1119      *            NodeConnector to which host is connected
1120      * @param port
1121      *            Port of the switch to which host is connected
1122      * @param vlan
1123      *            Vlan of which this host is member of
1124      *
1125      * @return Status The status object as described in {@code Status}
1126      *         indicating the result of this action.
1127      */
1128     public Status updateHostReq(InetAddress networkAddr, byte[] dataLayerAddress, NodeConnector nc, short vlan) {
1129         HostNodeConnector tobeUpdatedHost;
1130         HostNodeConnector host = null;
1131
1132         if (dataLayerAddress.length != NetUtils.MACAddrLengthInBytes) {
1133             return new Status(StatusCode.BADREQUEST, "Invalid MAC address");
1134         }
1135
1136         if (nc == null) {
1137             return new Status(StatusCode.BADREQUEST, "Invalid NodeConnector");
1138         }
1139
1140         try {
1141             host = new HostNodeConnector(dataLayerAddress, networkAddr, nc, vlan);
1142             if (hostExists(host)) {
1143                 return new Status(StatusCode.BADREQUEST, "Host already exists");
1144             }
1145
1146             if ((tobeUpdatedHost = hostsDB.get(networkAddr)) != null) {
1147                 if (hostsDB.replace(networkAddr, tobeUpdatedHost, host)) {
1148                     logger.debug("Host replaced from hostsDB. Old host: {} New Host: {}", tobeUpdatedHost, host);
1149                     notifyHostLearnedOrRemoved(tobeUpdatedHost, false);
1150                     notifyHostLearnedOrRemoved(host, true);
1151                     return new Status(StatusCode.SUCCESS);
1152                 } else {
1153                     logger.error("Static host replacement failed from hostsDB, Replaced Host: {}, New Host: {}",
1154                             tobeUpdatedHost, host);
1155                     return new Status(StatusCode.INTERNALERROR,
1156                             "Host Replacement Failed due to presence of another host with same IP");
1157                 }
1158             }
1159
1160             // Check if the host exists in inactive hosts database
1161             if ((tobeUpdatedHost = inactiveStaticHosts.get(nc)) != null) {
1162                 if (inactiveStaticHosts.replace(nc, tobeUpdatedHost, host)) {
1163                     logger.debug("Host replaced from inactive hostsDB. Old host: {} New Host: {}", tobeUpdatedHost,
1164                             host);
1165                     return new Status(StatusCode.SUCCESS);
1166                 } else {
1167                     logger.error("Static host replacement failed, Replaced Host: {}, New Host: {}", tobeUpdatedHost,
1168                             host);
1169                     return new Status(StatusCode.INTERNALERROR,
1170                             "Host Replacement Failed due to presence of another host with same IP");
1171                 }
1172             }
1173
1174             // Host doesn't exist
1175             return new Status(StatusCode.BADREQUEST, "Host doesn't exists, can't update");
1176         } catch (ConstructionException e) {
1177             logger.error("", e);
1178             return new Status(StatusCode.INTERNALERROR, "host object creation failure");
1179         }
1180     }
1181
1182     /**
1183      * Remove from the controller IP to MAC binding of a host and its
1184      * connectivity to an openflow switch
1185      *
1186      * @param networkAddr
1187      *            IP address of the host
1188      *
1189      * @return boolean true if the host was removed successfully, false
1190      *         otherwise
1191      */
1192
1193     public Status removeStaticHostReq(InetAddress networkAddress) {
1194         // Check if host is in active hosts database
1195         HostNodeConnector host = getHostFromOnActiveDB(networkAddress);
1196         if (host != null) {
1197             // Validation check
1198             if (!host.isStaticHost()) {
1199                 return new Status(StatusCode.FORBIDDEN, "Host " + networkAddress.getHostName() + " is not static");
1200             }
1201             // Remove and notify
1202             notifyHostLearnedOrRemoved(host, false);
1203             removeKnownHost(networkAddress);
1204             return new Status(StatusCode.SUCCESS, null);
1205         }
1206
1207         // Check if host is in inactive hosts database
1208         Entry<NodeConnector, HostNodeConnector> entry = getHostFromInactiveDB(networkAddress);
1209         if (entry != null) {
1210             host = entry.getValue();
1211             // Validation check
1212             if (!host.isStaticHost()) {
1213                 return new Status(StatusCode.FORBIDDEN, "Host " + networkAddress.getHostName() + " is not static");
1214             }
1215             this.removeHostFromInactiveDB(networkAddress);
1216             return new Status(StatusCode.SUCCESS, null);
1217         }
1218
1219         // Host is neither in active nor inactive hosts database
1220         return new Status(StatusCode.NOTFOUND, "Host does not exist");
1221     }
1222
1223     @Override
1224     public void modeChangeNotify(Node node, boolean proactive) {
1225         logger.debug("Set Switch {} Mode to {}", node.getID(), proactive);
1226     }
1227
1228     @Override
1229     public void notifyNode(Node node, UpdateType type, Map<String, Property> propMap) {
1230         if (node == null) {
1231             return;
1232         }
1233
1234         switch (type) {
1235         case REMOVED:
1236             logger.debug("Received removed node {}", node);
1237             for (Entry<InetAddress, HostNodeConnector> entry : hostsDB.entrySet()) {
1238                 HostNodeConnector host = entry.getValue();
1239                 if (host.getnodeconnectorNode().equals(node)) {
1240                     logger.debug("Node: {} is down, remove from Hosts_DB", node);
1241                     removeKnownHost(entry.getKey());
1242                     notifyHostLearnedOrRemoved(host, false);
1243                 }
1244             }
1245             break;
1246         default:
1247             break;
1248         }
1249     }
1250
1251     @Override
1252     public void notifyNodeConnector(NodeConnector nodeConnector, UpdateType type, Map<String, Property> propMap) {
1253         if (nodeConnector == null) {
1254             return;
1255         }
1256
1257         boolean up = false;
1258         switch (type) {
1259         case ADDED:
1260             up = true;
1261             break;
1262         case REMOVED:
1263             break;
1264         case CHANGED:
1265             State state = (State) propMap.get(State.StatePropName);
1266             if ((state != null) && (state.getValue() == State.EDGE_UP)) {
1267                 up = true;
1268             }
1269             break;
1270         default:
1271             return;
1272         }
1273
1274         if (up) {
1275             handleNodeConnectorStatusUp(nodeConnector);
1276         } else {
1277             handleNodeConnectorStatusDown(nodeConnector);
1278         }
1279     }
1280
1281     @Override
1282     public Status addStaticHost(String networkAddress, String dataLayerAddress, NodeConnector nc, String vlan) {
1283         try {
1284             InetAddress ip = InetAddress.getByName(networkAddress);
1285             short vl = 0;
1286             if (vlan != null && !vlan.isEmpty()) {
1287                 vl = Short.decode(vlan);
1288                 if (vl < 1 || vl > 4095) {
1289                     return new Status(StatusCode.BADREQUEST, "Host vlan out of range [1 - 4095]");
1290                 }
1291             }
1292
1293             return addStaticHostReq(ip, HexEncode.bytesFromHexString(dataLayerAddress), nc, vl);
1294
1295         } catch (UnknownHostException e) {
1296             logger.debug("Invalid host IP specified when adding static host", e);
1297             return new Status(StatusCode.BADREQUEST, "Invalid Host IP Address");
1298         } catch (NumberFormatException nfe) {
1299             logger.debug("Invalid host vlan or MAC specified when adding static host", nfe);
1300             return new Status(StatusCode.BADREQUEST, "Invalid Host vLan/MAC");
1301         }
1302     }
1303
1304     @Override
1305     public Status removeStaticHost(String networkAddress) {
1306         try {
1307             InetAddress address = InetAddress.getByName(networkAddress);
1308             return removeStaticHostReq(address);
1309         } catch (UnknownHostException e) {
1310             logger.debug("Invalid IP Address when trying to remove host", e);
1311             return new Status(StatusCode.BADREQUEST, "Invalid IP Address when trying to remove host");
1312         }
1313     }
1314
1315     private void handleNodeConnectorStatusUp(NodeConnector nodeConnector) {
1316         ARPPending arphost;
1317         HostNodeConnector host = null;
1318
1319         logger.trace("handleNodeConnectorStatusUp {}", nodeConnector);
1320
1321         for (Entry<InetAddress, ARPPending> entry : failedARPReqList.entrySet()) {
1322             arphost = entry.getValue();
1323             logger.trace("Sending the ARP from FailedARPReqList fors IP: {}", arphost.getHostIP().getHostAddress());
1324             if (hostFinder == null) {
1325                 logger.warn("ARPHandler is not available at interface  up");
1326                 logger.warn("Since this event is missed, host(s) connected to interface {} may not be discovered",
1327                         nodeConnector);
1328                 continue;
1329             }
1330
1331             // Send a broadcast ARP only on the interface which just came up.
1332             // Use hostFinder's "probe" method
1333             try {
1334                 byte[] dataLayerAddress = NetUtils.getBroadcastMACAddr();
1335                 host = new HostNodeConnector(dataLayerAddress, arphost.getHostIP(), nodeConnector, (short) 0);
1336                 hostFinder.probe(host);
1337             } catch (ConstructionException e) {
1338                 logger.debug("HostNodeConnector couldn't be created for Host: {}, NodeConnector: {}",
1339                         arphost.getHostIP(), nodeConnector);
1340                 logger.error("", e);
1341             }
1342         }
1343
1344         host = inactiveStaticHosts.get(nodeConnector);
1345         if (host != null) {
1346             inactiveStaticHosts.remove(nodeConnector);
1347             learnNewHost(host);
1348             processPendingARPReqs(host.getNetworkAddress());
1349             notifyHostLearnedOrRemoved(host, true);
1350         }
1351     }
1352
1353     private void handleNodeConnectorStatusDown(NodeConnector nodeConnector) {
1354         logger.trace("handleNodeConnectorStatusDown {}", nodeConnector);
1355
1356         for (Entry<InetAddress, HostNodeConnector> entry : hostsDB.entrySet()) {
1357             HostNodeConnector host = entry.getValue();
1358             if (host.getnodeConnector().equals(nodeConnector)) {
1359                 logger.debug(" NodeConnector: {} is down, remove from Hosts_DB", nodeConnector);
1360                 removeKnownHost(entry.getKey());
1361                 notifyHostLearnedOrRemoved(host, false);
1362             }
1363         }
1364     }
1365
1366     void setClusterContainerService(IClusterContainerServices s) {
1367         logger.debug("Cluster Service set");
1368         this.clusterContainerService = s;
1369     }
1370
1371     void unsetClusterContainerService(IClusterContainerServices s) {
1372         if (this.clusterContainerService == s) {
1373             logger.debug("Cluster Service removed!");
1374             this.clusterContainerService = null;
1375         }
1376     }
1377
1378     void setSwitchManager(ISwitchManager s) {
1379         logger.debug("SwitchManager set");
1380         this.switchManager = s;
1381     }
1382
1383     void unsetSwitchManager(ISwitchManager s) {
1384         if (this.switchManager == s) {
1385             logger.debug("SwitchManager removed!");
1386             this.switchManager = null;
1387         }
1388     }
1389
1390     public String getContainerName() {
1391         if (containerName == null) {
1392             return GlobalConstants.DEFAULT.toString();
1393         }
1394         return containerName;
1395     }
1396
1397     /**
1398      * Function called by the dependency manager when all the required
1399      * dependencies are satisfied
1400      *
1401      */
1402     void init(Component c) {
1403         Dictionary<?, ?> props = c.getServiceProperties();
1404         if (props != null) {
1405             this.containerName = (String) props.get("containerName");
1406             logger.debug("Running containerName: {}", this.containerName);
1407         } else {
1408             // In the Global instance case the containerName is empty
1409             this.containerName = "";
1410         }
1411         startUp();
1412     }
1413
1414     /**
1415      * Function called by the dependency manager when at least one dependency
1416      * become unsatisfied or when the component is shutting down because for
1417      * example bundle is being stopped.
1418      *
1419      */
1420     void destroy() {
1421     }
1422
1423     /**
1424      * Function called by dependency manager after "init ()" is called and after
1425      * the services provided by the class are registered in the service registry
1426      *
1427      */
1428     void start() {
1429         registerWithOSGIConsole();
1430     }
1431
1432     /**
1433      * Function called by the dependency manager before the services exported by
1434      * the component are unregistered, this will be followed by a "destroy ()"
1435      * calls
1436      *
1437      */
1438     void stop() {
1439     }
1440
1441     void stopping() {
1442         stopping = true;
1443         arpRefreshTimer.cancel();
1444         timer.cancel();
1445         executor.shutdownNow();
1446     }
1447
1448     @Override
1449     public void edgeOverUtilized(Edge edge) {
1450
1451     }
1452
1453     @Override
1454     public void edgeUtilBackToNormal(Edge edge) {
1455
1456     }
1457
1458     @Override
1459     public void entryCreated(InetAddress key, String cacheName, boolean originLocal) {
1460         if (originLocal) {
1461             return;
1462         }
1463         processPendingARPReqs(key);
1464     }
1465
1466     @Override
1467     public void entryUpdated(InetAddress key, HostNodeConnector new_value, String cacheName, boolean originLocal) {
1468     }
1469
1470     @Override
1471     public void entryDeleted(InetAddress key, String cacheName, boolean originLocal) {
1472     }
1473
1474     private void registerWithOSGIConsole() {
1475         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
1476         bundleContext.registerService(CommandProvider.class.getName(), this, null);
1477     }
1478
1479     @Override
1480     public String getHelp() {
1481         return null;
1482     }
1483
1484     public void _dumpPendingARPReqList(CommandInterpreter ci) {
1485         ARPPending arphost;
1486         for (Entry<InetAddress, ARPPending> entry : ARPPendingList.entrySet()) {
1487             arphost = entry.getValue();
1488             ci.println(arphost.getHostIP().toString());
1489         }
1490     }
1491
1492     public void _dumpFailedARPReqList(CommandInterpreter ci) {
1493         ARPPending arphost;
1494         for (Entry<InetAddress, ARPPending> entry : failedARPReqList.entrySet()) {
1495             arphost = entry.getValue();
1496             ci.println(arphost.getHostIP().toString());
1497         }
1498     }
1499 }