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