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