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