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