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

©2013 OpenDaylight, A Linux Foundation Collaborative Project. All Rights Reserved.
OpenDaylight is a registered trademark of The OpenDaylight Project, Inc.
Linux Foundation and OpenDaylight are registered trademarks of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.