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