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