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