X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fhosttracker%2Fimplementation%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fhosttracker%2Finternal%2FHostTracker.java;h=9cae4348b12121d7495cb06bec851987bb426b74;hp=d0f8bb5736bce562619f3ab71a5a2596d7a81678;hb=0e53568409974e90df1272860706d2f47bec5aa6;hpb=9e43cfabdc83df4c5db51ce6e22e0cecca12aa9a diff --git a/opendaylight/hosttracker/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/HostTracker.java b/opendaylight/hosttracker/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/HostTracker.java index d0f8bb5736..9cae4348b1 100644 --- a/opendaylight/hosttracker/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/HostTracker.java +++ b/opendaylight/hosttracker/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/HostTracker.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. * @@ -25,21 +24,28 @@ import java.util.TimerTask; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.apache.felix.dm.Component; -import org.apache.taglibs.standard.lang.jstl.DivideOperator; +import org.eclipse.osgi.framework.console.CommandInterpreter; +import org.eclipse.osgi.framework.console.CommandProvider; import org.opendaylight.controller.clustering.services.CacheConfigException; import org.opendaylight.controller.clustering.services.CacheExistException; +import org.opendaylight.controller.clustering.services.ICacheUpdateAware; import org.opendaylight.controller.clustering.services.IClusterContainerServices; import org.opendaylight.controller.clustering.services.IClusterServices; -import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector; -import org.opendaylight.controller.hosttracker.hostAware.IHostFinder; +import org.opendaylight.controller.hosttracker.HostIdFactory; +import org.opendaylight.controller.hosttracker.IHostId; +import org.opendaylight.controller.hosttracker.IPHostId; +import org.opendaylight.controller.hosttracker.IPMacHostId; import org.opendaylight.controller.hosttracker.IfHostListener; import org.opendaylight.controller.hosttracker.IfIptoHost; import org.opendaylight.controller.hosttracker.IfNewHostNotify; +import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector; +import org.opendaylight.controller.hosttracker.hostAware.IHostFinder; import org.opendaylight.controller.sal.core.ConstructionException; import org.opendaylight.controller.sal.core.Edge; import org.opendaylight.controller.sal.core.Host; @@ -51,8 +57,10 @@ import org.opendaylight.controller.sal.core.Tier; import org.opendaylight.controller.sal.core.UpdateType; import org.opendaylight.controller.sal.packet.address.DataLinkAddress; import org.opendaylight.controller.sal.packet.address.EthernetAddress; +import org.opendaylight.controller.sal.topology.TopoEdgeUpdate; import org.opendaylight.controller.sal.utils.GlobalConstants; import org.opendaylight.controller.sal.utils.HexEncode; +import org.opendaylight.controller.sal.utils.NetUtils; import org.opendaylight.controller.sal.utils.NodeCreator; import org.opendaylight.controller.sal.utils.Status; import org.opendaylight.controller.sal.utils.StatusCode; @@ -62,50 +70,70 @@ import org.opendaylight.controller.switchmanager.ISwitchManagerAware; import org.opendaylight.controller.switchmanager.Subnet; import org.opendaylight.controller.topologymanager.ITopologyManager; import org.opendaylight.controller.topologymanager.ITopologyManagerAware; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * @file HostTracker.java - * This class tracks the location of IP Hosts as to which Switch, Port, VLAN, they are - * connected to, as well as their MAC address. This is done dynamically as well as statically. - * The dynamic mechanism consists of listening to ARP messages as well sending ARP requests. - * Static mechanism consists of Northbound APIs to add or remove the hosts from the local - * database. ARP aging is also implemented to age out dynamically learned hosts. Interface - * methods are provided for other applications to - * 1. Query the local database for a single host - * 2. Get a list of all hosts - * 3. Get notification if a host is learned/added or removed the database + * @file HostTracker.java This class tracks the location of IP Hosts as to which + * Switch, Port, VLAN, they are connected to, as well as their MAC + * address. This is done dynamically as well as statically. The dynamic + * mechanism consists of listening to ARP messages as well sending ARP + * requests. Static mechanism consists of Northbound APIs to add or remove + * the hosts from the local database. ARP aging is also implemented to age + * out dynamically learned hosts. Interface methods are provided for other + * applications to 1. Query the local database for a single host 2. Get a + * list of all hosts 3. Get notification if a host is learned/added or + * removed the database + */ + +/*** + * + * HostTracker db key scheme implementation support. Support has been added for + * IP only or IP + MAC scheme as of now. User can use either of the schemes + * based on the configuration done in config.ini file. By default IP only key + * scheme is choosen. The attribute to be set in config.ini is + * hosttracker.keyscheme. It could have a value of 0 or 1 as of now. 0 is for IP + * only scheme. 1 is for IP + MAC scheme. + * + * */ -public class HostTracker implements IfIptoHost, IfHostListener, - ISwitchManagerAware, IInventoryListener, ITopologyManagerAware { - private static final Logger logger = LoggerFactory - .getLogger(HostTracker.class); - private IHostFinder hostFinder; - private ConcurrentMap hostsDB; - /* Following is a list of hosts which have been requested by NB APIs to be added, - * but either the switch or the port is not sup, so they will be added here until - * both come up +public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAware, IInventoryListener, + ITopologyManagerAware, ICacheUpdateAware, CommandProvider { + static final String ACTIVE_HOST_CACHE = "hosttracker.ActiveHosts"; + static final String INACTIVE_HOST_CACHE = "hosttracker.InactiveHosts"; + private static final Logger logger = LoggerFactory.getLogger(HostTracker.class); + protected final Set hostFinder = new CopyOnWriteArraySet();; + protected ConcurrentMap hostsDB; + /* + * Following is a list of hosts which have been requested by NB APIs to be + * added, but either the switch or the port is not sup, so they will be + * added here until both come up */ private ConcurrentMap inactiveStaticHosts; - private Set newHostNotify = Collections - .synchronizedSet(new HashSet()); + private final Set newHostNotify = Collections.synchronizedSet(new HashSet()); private ITopologyManager topologyManager; - private IClusterContainerServices clusterContainerService = null; - private ISwitchManager switchManager = null; + protected IClusterContainerServices clusterContainerService = null; + protected ISwitchManager switchManager = null; private Timer timer; - private Timer arp_refresh_timer; + private Timer arpRefreshTimer; private String containerName = null; + private ExecutorService executor; + protected boolean stopping; + private static boolean hostRefresh = true; + private static int hostRetryCount = 5; + private String keyScheme = null; private static class ARPPending { - protected InetAddress hostIP; + protected IHostId hostId; protected short sent_count; protected HostTrackerCallable hostTrackerCallable; - public InetAddress getHostIP() { - return hostIP; + public IHostId getHostId() { + return hostId; } public short getSent_count() { @@ -116,8 +144,8 @@ public class HostTracker implements IfIptoHost, IfHostListener, return hostTrackerCallable; } - public void setHostIP(InetAddress networkAddr) { - this.hostIP = networkAddr; + public void setHostId(IHostId id) { + this.hostId = id; } public void setSent_count(short count) { @@ -129,82 +157,80 @@ public class HostTracker implements IfIptoHost, IfHostListener, } } - //This list contains the hosts for which ARP requests are being sent periodically - private List ARPPendingList = new ArrayList(); + // This list contains the hosts for which ARP requests are being sent + // periodically + ConcurrentMap ARPPendingList; /* - * This list below contains the hosts which were initially in ARPPendingList above, - * but ARP response didn't come from there hosts after multiple attempts over 8 - * seconds. The assumption is that the response didn't come back due to one of the - * following possibilities: - * 1. The L3 interface wasn't created for this host in the controller. This would - * cause arphandler not to know where to send the ARP - * 2. The host facing port is down - * 3. The IP host doesn't exist or is not responding to ARP requests + * This list below contains the hosts which were initially in ARPPendingList + * above, but ARP response didn't come from there hosts after multiple + * attempts over 8 seconds. The assumption is that the response didn't come + * back due to one of the following possibilities: 1. The L3 interface + * wasn't created for this host in the controller. This would cause + * arphandler not to know where to send the ARP 2. The host facing port is + * down 3. The IP host doesn't exist or is not responding to ARP requests * - * Conditions 1 and 2 above can be recovered if ARP is sent when the relevant L3 - * interface is added or the port facing host comes up. Whenever L3 interface is - * added or host facing port comes up, ARP will be sent to hosts in this list. + * Conditions 1 and 2 above can be recovered if ARP is sent when the + * relevant L3 interface is added or the port facing host comes up. Whenever + * L3 interface is added or host facing port comes up, ARP will be sent to + * hosts in this list. * * We can't recover from condition 3 above */ - private ArrayList failedARPReqList = new ArrayList(); + ConcurrentMap failedARPReqList; public HostTracker() { } private void startUp() { + nonClusterObjectCreate(); allocateCache(); retrieveCache(); + stopping = false; timer = new Timer(); timer.schedule(new OutStandingARPHandler(), 4000, 4000); - + executor = Executors.newFixedThreadPool(2); /* ARP Refresh Timer to go off every 5 seconds to implement ARP aging */ - arp_refresh_timer = new Timer(); - arp_refresh_timer.schedule(new ARPRefreshHandler(), 5000, 5000); + arpRefreshTimer = new Timer(); + arpRefreshTimer.schedule(new ARPRefreshHandler(), 5000, 5000); + keyScheme = HostIdFactory.getScheme(); logger.debug("startUp: Caches created, timers started"); } - @SuppressWarnings("deprecation") - private void allocateCache() { + private void allocateCache() { if (this.clusterContainerService == null) { - logger - .error("un-initialized clusterContainerService, can't create cache"); + logger.error("un-initialized clusterContainerService, can't create cache"); return; } logger.debug("Creating Cache for HostTracker"); try { - this.clusterContainerService.createCache("hostTrackerAH", EnumSet - .of(IClusterServices.cacheMode.NON_TRANSACTIONAL)); - this.clusterContainerService.createCache("hostTrackerIH", EnumSet - .of(IClusterServices.cacheMode.NON_TRANSACTIONAL)); + this.clusterContainerService.createCache(ACTIVE_HOST_CACHE, + EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL)); + this.clusterContainerService.createCache(INACTIVE_HOST_CACHE, + EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL)); } catch (CacheConfigException cce) { - logger - .error("Cache couldn't be created for HostTracker - check cache mode"); + logger.error("Cache couldn't be created for HostTracker - check cache mode"); } catch (CacheExistException cce) { - logger - .error("Cache for HostTracker already exists, destroy and recreate"); + logger.error("Cache for HostTracker already exists, destroy and recreate"); } logger.debug("Cache successfully created for HostTracker"); } - @SuppressWarnings({ "unchecked", "deprecation" }) + @SuppressWarnings({ "unchecked" }) private void retrieveCache() { if (this.clusterContainerService == null) { - logger - .error("un-initialized clusterContainerService, can't retrieve cache"); + logger.error("un-initialized clusterContainerService, can't retrieve cache"); return; } logger.debug("Retrieving cache for HostTrackerAH"); - hostsDB = (ConcurrentMap) this.clusterContainerService - .getCache("hostTrackerAH"); + hostsDB = (ConcurrentMap) this.clusterContainerService.getCache(ACTIVE_HOST_CACHE); if (hostsDB == null) { logger.error("Cache couldn't be retrieved for HostTracker"); } logger.debug("Cache was successfully retrieved for HostTracker"); logger.debug("Retrieving cache for HostTrackerIH"); inactiveStaticHosts = (ConcurrentMap) this.clusterContainerService - .getCache("hostTrackerIH"); + .getCache(INACTIVE_HOST_CACHE); if (inactiveStaticHosts == null) { logger.error("Cache couldn't be retrieved for HostTrackerIH"); } @@ -212,20 +238,10 @@ public class HostTracker implements IfIptoHost, IfHostListener, } public void nonClusterObjectCreate() { - hostsDB = new ConcurrentHashMap(); + hostsDB = new ConcurrentHashMap(); inactiveStaticHosts = new ConcurrentHashMap(); - } - - @SuppressWarnings("deprecation") - private void destroyCache() { - if (this.clusterContainerService == null) { - logger.error("un-initialized clusterMger, can't destroy cache"); - return; - } - logger.debug("Destroying Cache for HostTracker"); - this.clusterContainerService.destroyCache("hostTrackerAH"); - this.clusterContainerService.destroyCache("hostTrackerIH"); - nonClusterObjectCreate(); + ARPPendingList = new ConcurrentHashMap(); + failedARPReqList = new ConcurrentHashMap(); } public void shutDown() { @@ -240,13 +256,15 @@ public class HostTracker implements IfIptoHost, IfHostListener, } public void setArpHandler(IHostFinder hostFinder) { - this.hostFinder = hostFinder; + if (this.hostFinder != null) { + this.hostFinder.add(hostFinder); + } } public void unsetArpHandler(IHostFinder hostFinder) { - if (this.hostFinder == hostFinder) { + if (this.hostFinder != null) { logger.debug("Arp Handler Service removed!"); - this.hostFinder = null; + this.hostFinder.remove(hostFinder); } } @@ -262,79 +280,76 @@ public class HostTracker implements IfIptoHost, IfHostListener, } private boolean hostExists(HostNodeConnector host) { - HostNodeConnector lhost = hostsDB.get(host.getNetworkAddress()); + IHostId id = HostIdFactory.create(host.getNetworkAddress(), host.getDataLayerAddress()); + HostNodeConnector lhost = hostsDB.get(id); return host.equals(lhost); } - private HostNodeConnector getHostFromOnActiveDB(InetAddress networkAddress) { - return hostsDB.get(networkAddress); + private HostNodeConnector getHostFromOnActiveDB(IHostId id) { + return hostsDB.get(id); } - private Entry getHostFromInactiveDB( - InetAddress networkAddress) { - for (Entry entry : inactiveStaticHosts - .entrySet()) { - if (entry.getValue().equalsByIP(networkAddress)) { - logger - .debug( - "getHostFromInactiveDB(): Inactive Host found for IP:{} ", - networkAddress.getHostAddress()); + private Entry getHostFromInactiveDB(IHostId id) { + for (Entry entry : inactiveStaticHosts.entrySet()) { + HostNodeConnector hnc = entry.getValue(); + IHostId cmpId = HostIdFactory.create(hnc.getNetworkAddress(), hnc.getDataLayerAddress()); + if (cmpId.equals(id)) { + logger.debug("getHostFromInactiveDB(): Inactive Host found for ID:{} ", decodeIPFromId(id)); return entry; } } - logger.debug( - "getHostFromInactiveDB() Inactive Host Not found for IP: {}", - networkAddress.getHostAddress()); + logger.debug("getHostFromInactiveDB() Inactive Host Not found for ID: {}", decodeIPFromId(id)); return null; } - private void removeHostFromInactiveDB(InetAddress networkAddress) { + private void removeHostFromInactiveDB(IHostId id) { NodeConnector nodeConnector = null; - for (Entry entry : inactiveStaticHosts - .entrySet()) { - if (entry.getValue().equalsByIP(networkAddress)) { + for (Entry entry : inactiveStaticHosts.entrySet()) { + HostNodeConnector hnc = entry.getValue(); + IHostId cmpId = HostIdFactory.create(hnc.getNetworkAddress(), hnc.getDataLayerAddress()); + if (cmpId.equals(id)) { nodeConnector = entry.getKey(); break; } } if (nodeConnector != null) { inactiveStaticHosts.remove(nodeConnector); - logger.debug("removeHostFromInactiveDB(): Host Removed for IP: {}", - networkAddress.getHostAddress()); + logger.debug("removeHostFromInactiveDB(): Host Removed for IP: {}", decodeIPFromId(id)); return; } - logger.debug("removeHostFromInactiveDB(): Host Not found for IP: {}", - networkAddress.getHostAddress()); + logger.debug("removeHostFromInactiveDB(): Host Not found for IP: {}", decodeIPFromId(id)); } protected boolean hostMoved(HostNodeConnector host) { - if (hostQuery(host.getNetworkAddress()) != null) { + IHostId id = HostIdFactory.create(host.getNetworkAddress(), host.getDataLayerAddress()); + if (hostQuery(id) != null) { return true; } return false; } - public HostNodeConnector hostQuery(InetAddress networkAddress) { - return hostsDB.get(networkAddress); + @Override + public HostNodeConnector hostQuery(IHostId id) { + return hostsDB.get(id); } - public Future discoverHost(InetAddress networkAddress) { - ExecutorService executor = Executors.newFixedThreadPool(1); + @Override + public Future discoverHost(IHostId id) { if (executor == null) { - logger.error("discoverHost: Null executor"); + logger.debug("discoverHost: Null executor"); return null; } - Callable worker = new HostTrackerCallable(this, - networkAddress); + Callable worker = new HostTrackerCallable(this, id); Future submit = executor.submit(worker); return submit; } - public HostNodeConnector hostFind(InetAddress networkAddress) { + @Override + public HostNodeConnector hostFind(IHostId id) { /* - * Sometimes at boot with containers configured in the startup - * we hit this path (from TIF) when hostFinder has not been set yet - * Caller already handles the null return + * Sometimes at boot with containers configured in the startup we hit + * this path (from TIF) when hostFinder has not been set yet Caller + * already handles the null return */ if (hostFinder == null) { @@ -342,207 +357,214 @@ public class HostTracker implements IfIptoHost, IfHostListener, return null; } - HostNodeConnector host = hostQuery(networkAddress); + HostNodeConnector host = hostQuery(id); if (host != null) { - logger.debug("hostFind(): Host found for IP: {}", networkAddress - .getHostAddress()); + logger.debug("hostFind(): Host found for IP: {}", id); return host; } + + /* Add this host to ARPPending List for any potential retries */ + + addToARPPendingList(id); + logger.debug("hostFind(): Host Not Found for IP: {}, Inititated Host Discovery ...", id); + /* host is not found, initiate a discovery */ - hostFinder.find(networkAddress); - /* Also add this host to ARPPending List for any potential retries */ - AddtoARPPendingList(networkAddress); - logger - .debug( - "hostFind(): Host Not Found for IP: {}, Inititated Host Discovery ...", - networkAddress.getHostAddress()); + for (IHostFinder hf : hostFinder) { + InetAddress addr = decodeIPFromId(id); + hf.find(addr); + } return null; } + @Override public Set getAllHosts() { - Set allHosts = new HashSet(); - for (Entry entry : hostsDB.entrySet()) { - HostNodeConnector host = entry.getValue(); - allHosts.add(host); - } - logger.debug("Exiting getAllHosts, Found {} Hosts", allHosts.size()); + Set allHosts = new HashSet(hostsDB.values()); return allHosts; } @Override public Set getActiveStaticHosts() { Set list = new HashSet(); - for (Entry entry : hostsDB.entrySet()) { + for (Entry entry : hostsDB.entrySet()) { HostNodeConnector host = entry.getValue(); if (host.isStaticHost()) { list.add(host); } } - logger.debug("getActiveStaticHosts(): Found {} Hosts", list.size()); return list; } @Override public Set getInactiveStaticHosts() { - Set list = new HashSet(); - for (Entry entry : inactiveStaticHosts - .entrySet()) { - list.add(entry.getValue()); - } - logger.debug("getInactiveStaticHosts(): Found {} Hosts", list.size()); + Set list = new HashSet(inactiveStaticHosts.values()); return list; } - private void AddtoARPPendingList(InetAddress networkAddr) { + private void addToARPPendingList(IHostId id) { ARPPending arphost = new ARPPending(); - arphost.setHostIP(networkAddr); + arphost.setHostId(id); arphost.setSent_count((short) 1); - ARPPendingList.add(arphost); - logger.debug("Host Added to ARPPending List, IP: {}", networkAddr - .toString()); - } + ARPPendingList.put(id, arphost); + logger.debug("Host Added to ARPPending List, IP: {}", decodeIPFromId(id)); - private void removePendingARPFromList(int index) { - if (index >= ARPPendingList.size()) { - logger - .warn( - "removePendingARPFromList(): index greater than the List. Size:{}, Index:{}", - ARPPendingList.size(), index); - return; - } - ARPPending arphost = ARPPendingList.remove(index); - HostTrackerCallable htCallable = arphost.getHostTrackerCallable(); - if (htCallable != null) - htCallable.wakeup(); } - public void setCallableOnPendingARP(InetAddress networkAddr, - HostTrackerCallable callable) { + public void setCallableOnPendingARP(IHostId id, HostTrackerCallable callable) { ARPPending arphost; - for (int i = 0; i < ARPPendingList.size(); i++) { - arphost = ARPPendingList.get(i); - if (arphost.getHostIP().equals(networkAddr)) { + for (Entry entry : ARPPendingList.entrySet()) { + arphost = entry.getValue(); + if (arphost.getHostId().equals(id)) { arphost.setHostTrackerCallable(callable); } } } - private void ProcPendingARPReqs(InetAddress networkAddr) { + private void processPendingARPReqs(IHostId id) { ARPPending arphost; - for (int i = 0; i < ARPPendingList.size(); i++) { - arphost = ARPPendingList.get(i); - if (arphost.getHostIP().equals(networkAddr)) { - /* An ARP was sent for this host. The address is learned, - * remove the request - */ - removePendingARPFromList(i); - logger.debug("Host Removed from ARPPending List, IP: {}", - networkAddr.toString()); - return; + if ((arphost = ARPPendingList.remove(id)) != null) { + // Remove the arphost from ARPPendingList as it has been learned now + logger.debug("Host Removed from ARPPending List, IP: {}", id); + HostTrackerCallable htCallable = arphost.getHostTrackerCallable(); + if (htCallable != null) { + htCallable.wakeup(); } + return; } /* * It could have been a host from the FailedARPReqList */ - for (int i = 0; i < failedARPReqList.size(); i++) { - arphost = failedARPReqList.get(i); - if (arphost.getHostIP().equals(networkAddr)) { - /* An ARP was sent for this host. The address is learned, - * remove the request - */ - failedARPReqList.remove(i); - logger.debug("Host Removed from FailedARPReqList List, IP: {}", - networkAddr.toString()); - return; - } + if (failedARPReqList.containsKey(id)) { + failedARPReqList.remove(id); + logger.debug("Host Removed from FailedARPReqList List, IP: {}", decodeIPFromId(id)); } } // Learn a new Host private void learnNewHost(HostNodeConnector host) { + IHostId id = HostIdFactory.create(host.getNetworkAddress(), host.getDataLayerAddress()); host.initArpSendCountDown(); - hostsDB.put(host.getNetworkAddress(), host); - logger.debug("New Host Learned: MAC: {} IP: {}", HexEncode - .bytesToHexString(host.getDataLayerAddressBytes()), host - .getNetworkAddress().getHostAddress()); + HostNodeConnector rHost = hostsDB.putIfAbsent(id, host); + if (rHost != null) { + // Another host is already learned for this IP address, replace it + replaceHost(id, rHost, host); + } else { + logger.debug("New Host Learned: MAC: {} IP: {}", HexEncode.bytesToHexString(host + .getDataLayerAddressBytes()), host.getNetworkAddress().getHostAddress()); + } + } + + private void replaceHost(IHostId id, HostNodeConnector removedHost, HostNodeConnector newHost) { + // Ignore ARP messages from internal nodes + NodeConnector newHostNc = newHost.getnodeConnector(); + boolean newHostIsInternal = topologyManager.isInternal(newHostNc); + if (newHostIsInternal) { + return; + } + + newHost.initArpSendCountDown(); + + if (hostsDB.replace(id, removedHost, newHost)) { + logger.debug("Host move occurred: Old Host IP:{}, New Host IP: {}", removedHost.getNetworkAddress() + .getHostAddress(), newHost.getNetworkAddress().getHostAddress()); + logger.debug("Old Host MAC: {}, New Host MAC: {}", + HexEncode.bytesToHexString(removedHost.getDataLayerAddressBytes()), + HexEncode.bytesToHexString(newHost.getDataLayerAddressBytes())); + // Display the Old and New HostNodeConnectors also + logger.debug("Old {}, New {}", removedHost, newHost); + } else { + /* + * Host replacement has failed, do the recovery + */ + hostsDB.put(id, newHost); + logger.error("Host replacement failed. Overwrite the host. Replaced Host: {}, New Host: {}", removedHost, + newHost); + } + notifyHostLearnedOrRemoved(removedHost, false); + notifyHostLearnedOrRemoved(newHost, true); + if (!newHost.isStaticHost()) { + processPendingARPReqs(id); + } } // Remove known Host - private void removeKnownHost(InetAddress key) { + private void removeKnownHost(IHostId key) { HostNodeConnector host = hostsDB.get(key); if (host != null) { - logger.debug("Removing Host: IP:{}", host.getNetworkAddress() - .getHostAddress()); + logger.debug("Removing Host: IP:{}", host.getNetworkAddress().getHostAddress()); hostsDB.remove(key); } else { - logger - .error( - "removeKnownHost(): Host for IP address {} not found in hostsDB", - key.getHostAddress()); + logger.error("removeKnownHost(): Host for IP address {} not found in hostsDB", decodeIPFromId(key)); } } private class NotifyHostThread extends Thread { - private HostNodeConnector host; + private final HostNodeConnector host; public NotifyHostThread(HostNodeConnector h) { this.host = h; } + @Override public void run() { + HostNodeConnector removedHost = null; + InetAddress networkAddr = host.getNetworkAddress(); + IHostId id = HostIdFactory.create(networkAddr, host.getDataLayerAddress()); /* Check for Host Move case */ if (hostMoved(host)) { /* - * Host has been moved from one location (switch,port, MAC, or VLAN). - * Remove the existing host with its previous location parameters, - * inform the applications, and add it as a new Host + * Host has been moved from one location (switch,port, MAC, or + * VLAN) to another. Replace the existing host and its previous + * location parameters with new information, and notify the + * applications listening to host move. */ - HostNodeConnector removedHost = hostsDB.get(host - .getNetworkAddress()); - removeKnownHost(host.getNetworkAddress()); + + removedHost = hostsDB.get(id); if (removedHost != null) { - notifyHostLearnedOrRemoved(removedHost, false); - logger.debug( - "Host move occurred. Old Host:{}, New Host: {}", - removedHost, host); + replaceHost(id, removedHost, host); + return; } else { - logger.error( - "Host to be removed not found in hostsDB. Host {}", - removedHost); + logger.error("Host to be removed not found in hostsDB"); } } - /* check if there is an outstanding request for this host */ - InetAddress networkAddr = host.getNetworkAddress(); - - // add and notify + // It is a new host learnNewHost(host); - ProcPendingARPReqs(networkAddr); + + /* check if there is an outstanding request for this host */ + processPendingARPReqs(id); notifyHostLearnedOrRemoved(host, true); } } + @Override public void hostListener(HostNodeConnector host) { - + logger.debug("Received for Host: IP {}, MAC {}, {}", host.getNetworkAddress().getHostAddress(), + HexEncode.bytesToHexString(host.getDataLayerAddressBytes()), host); if (hostExists(host)) { - logger.debug("ARP received for Host: {}", host); - HostNodeConnector existinghost = hostsDB.get(host - .getNetworkAddress()); + IHostId id = HostIdFactory.create(host.getNetworkAddress(), host.getDataLayerAddress()); + HostNodeConnector existinghost = hostsDB.get(id); existinghost.initArpSendCountDown(); + // Update the host + + hostsDB.put(id, existinghost); + logger.debug("hostListener returned without adding the host"); return; } new NotifyHostThread(host).start(); } - // Notify whoever is interested that a new host was learned (dynamically or statically) + // Notify whoever is interested that a new host was learned (dynamically or + // statically) private void notifyHostLearnedOrRemoved(HostNodeConnector host, boolean add) { // Update listeners if any if (newHostNotify != null) { + logger.debug("Notifying Applications for Host {} Being {}", host.getNetworkAddress().getHostAddress(), + add ? "Added" : "Deleted"); synchronized (this.newHostNotify) { for (IfNewHostNotify ta : newHostNotify) { try { @@ -552,52 +574,39 @@ public class HostTracker implements IfIptoHost, IfHostListener, ta.notifyHTClientHostRemoved(host); } } catch (Exception e) { - logger.error("Exception on callback", e); + logger.error("Exception on new host notification", e); } } } } else { - logger - .error("notifyHostLearnedOrRemoved(): New host notify is null"); + logger.error("notifyHostLearnedOrRemoved(): New host notify is null"); } - // Topology update is for some reason outside of listeners registry logic + // Topology update is for some reason outside of listeners registry + // logic Node node = host.getnodeconnectorNode(); Host h = null; NodeConnector p = host.getnodeConnector(); try { - DataLinkAddress dla = new EthernetAddress(host - .getDataLayerAddressBytes()); - h = new org.opendaylight.controller.sal.core.Host(dla, host - .getNetworkAddress()); + DataLinkAddress dla = new EthernetAddress(host.getDataLayerAddressBytes()); + h = new Host(dla, host.getNetworkAddress()); } catch (ConstructionException ce) { p = null; h = null; } if (topologyManager != null && p != null && h != null) { + logger.debug("Notifying Topology Manager for Host {} Being {}", h.getNetworkAddress().getHostAddress(), + add ? "Added" : "Deleted"); if (add == true) { Tier tier = new Tier(1); switchManager.setNodeProp(node, tier); topologyManager.updateHostLink(p, h, UpdateType.ADDED, null); - /* - * This is a temporary fix for Cisco Live's Hadoop Demonstration. - * The concept of Tiering must be revisited based on other application requirements - * and the design might warrant a separate module (as it involves tracking the topology/ - * host changes & updating the Tiering numbers in an effective manner). - */ - updateSwitchTiers(node, 1); - - /* - * The following 2 lines are added for testing purposes. - * We can remove it once the North-Bound APIs are available for testing. - - ArrayList> hierarchies = getHostNetworkHierarchy(host.getNetworkAddress()); - logHierarchies(hierarchies); - */ } else { - // No need to reset the tiering if no other hosts are currently connected - // If this switch was discovered to be an access switch, it still is even if the host is down + // No need to reset the tiering if no other hosts are currently + // connected + // If this switch was discovered to be an access switch, it + // still is even if the host is down Tier tier = new Tier(0); switchManager.setNodeProp(node, tier); topologyManager.updateHostLink(p, h, UpdateType.REMOVED, null); @@ -606,34 +615,33 @@ public class HostTracker implements IfIptoHost, IfHostListener, } /** - * When a new Host is learnt by the hosttracker module, it places the directly connected Node - * in Tier-1 & using this function, updates the Tier value for all other Nodes in the network - * hierarchy. + * When a new Host is learnt by the hosttracker module, it places the + * directly connected Node in Tier-1 & using this function, updates the Tier + * value for all other Nodes in the network hierarchy. * - * This is a recursive function and it takes care of updating the Tier value for all the connected - * and eligible Nodes. + * This is a recursive function and it takes care of updating the Tier value + * for all the connected and eligible Nodes. * - * @param n Node that represents one of the Vertex in the Topology Graph. - * @param currentTier The Tier on which n belongs + * @param n + * Node that represents one of the Vertex in the Topology Graph. + * @param currentTier + * The Tier on which n belongs */ + @SuppressWarnings("unused") private void updateSwitchTiers(Node n, int currentTier) { Map> ndlinks = topologyManager.getNodeEdges(); if (ndlinks == null) { - logger.debug( - "updateSwitchTiers(): ndlinks null for Node: {}, Tier:{}", - n, currentTier); + logger.debug("updateSwitchTiers(): ndlinks null for Node: {}, Tier:{}", n, currentTier); return; } Set links = ndlinks.get(n); if (links == null) { - logger.debug("updateSwitchTiers(): links null for ndlinks:{}", - ndlinks); + logger.debug("updateSwitchTiers(): links null for ndlinks:{}", ndlinks); return; } ArrayList needsVisiting = new ArrayList(); for (Edge lt : links) { - if (!lt.getHeadNodeConnector().getType().equals( - NodeConnector.NodeConnectorIDType.OPENFLOW)) { + if (!lt.getHeadNodeConnector().getType().equals(NodeConnector.NodeConnectorIDType.OPENFLOW)) { // We don't want to work on Node that are not openflow // for now continue; @@ -656,20 +664,21 @@ public class HostTracker implements IfIptoHost, IfHostListener, } /** - * Internal convenience routine to check the eligibility of a Switch for a Tier update. - * Any Node with Tier=0 or a Tier value that is greater than the new Tier Value is eligible - * for the update. + * Internal convenience routine to check the eligibility of a Switch for a + * Tier update. Any Node with Tier=0 or a Tier value that is greater than + * the new Tier Value is eligible for the update. * - * @param n Node for which the Tier update eligibility is checked - * @param tier new Tier Value + * @param n + * Node for which the Tier update eligibility is checked + * @param tier + * new Tier Value * @return true if the Node is eligible for Tier Update * false otherwise */ private boolean switchNeedsTieringUpdate(Node n, int tier) { if (n == null) { - logger.error("switchNeedsTieringUpdate(): Null node for tier: {}", - tier); + logger.error("switchNeedsTieringUpdate(): Null node for tier: {}", tier); return false; } /* @@ -680,20 +689,23 @@ public class HostTracker implements IfIptoHost, IfHostListener, } // This is the case where Tier was never set for this node Tier t = (Tier) switchManager.getNodeProp(n, Tier.TierPropName); - if (t == null) + if (t == null) { return true; - if (t.getValue() == 0) + } + if (t.getValue() == 0) { return true; - else if (t.getValue() > tier) + } else if (t.getValue() > tier) { return true; + } return false; } /** - * Internal convenience routine to clear all the Tier values to 0. - * This cleanup is performed during cases such as Topology Change where the existing Tier values - * might become incorrect + * Internal convenience routine to clear all the Tier values to 0. This + * cleanup is performed during cases such as Topology Change where the + * existing Tier values might become incorrect */ + @SuppressWarnings("unused") private void clearTiers() { Set nodes = null; if (switchManager == null) { @@ -717,27 +729,32 @@ public class HostTracker implements IfIptoHost, IfHostListener, int num = 1; for (ArrayList hierarchy : hierarchies) { StringBuffer buf = new StringBuffer(); - buf.append("Hierarchy#" + num + " : "); + buf.append("Hierarchy#").append(num).append(" : "); for (String switchName : hierarchy) { - buf.append(switchName + "/"); + buf.append(switchName).append("/"); } - logger.debug("{} -> {}", getContainerName(), buf.toString()); + logger.debug("{} -> {}", getContainerName(), buf); num++; } } /** - * getHostNetworkHierarchy is the Back-end routine for the North-Bound API that returns - * the Network Hierarchy for a given Host. This API is typically used by applications like - * Hadoop for Rack Awareness functionality. + * getHostNetworkHierarchy is the Back-end routine for the North-Bound API + * that returns the Network Hierarchy for a given Host. This API is + * typically used by applications like Hadoop for Rack Awareness + * functionality. * - * @param hostAddress IP-Address of the host/node. - * @return Network Hierarchies represented by an Array of Array (of Switch-Ids as String). + * @param hostAddress + * IP-Address of the host/node. + * @return Network Hierarchies represented by an Array of Array (of + * Switch-Ids as String). */ - public List> getHostNetworkHierarchy(InetAddress hostAddress) { - HostNodeConnector host = hostQuery(hostAddress); - if (host == null) + @Override + public List> getHostNetworkHierarchy(IHostId id) { + HostNodeConnector host = hostQuery(id); + if (host == null) { return null; + } List> hierarchies = new ArrayList>(); ArrayList currHierarchy = new ArrayList(); @@ -749,12 +766,13 @@ public class HostTracker implements IfIptoHost, IfHostListener, } /** - * dpidToHostNameHack is a hack function for Cisco Live Hadoop Demo. - * Mininet is used as the network for Hadoop Demos & in order to give a meaningful - * rack-awareness switch names, the DPID is organized in ASCII Characters and - * retrieved as string. + * dpidToHostNameHack is a hack function for Cisco Live Hadoop Demo. Mininet + * is used as the network for Hadoop Demos & in order to give a meaningful + * rack-awareness switch names, the DPID is organized in ASCII Characters + * and retrieved as string. * - * @param dpid Switch DataPath Id + * @param dpid + * Switch DataPath Id * @return Ascii String represented by the DPID. */ private String dpidToHostNameHack(long dpid) { @@ -764,10 +782,12 @@ public class HostTracker implements IfIptoHost, IfHostListener, int result = 0; for (int i = 0; i < hex.length(); i++) { result = (int) ((dpid >> (i * 8)) & 0xff); - if (result == 0) + if (result == 0) { continue; - if (result < 0x30) + } + if (result < 0x30) { result += 0x40; + } sb.append(String.format("%c", result)); } return sb.reverse().toString(); @@ -776,24 +796,23 @@ public class HostTracker implements IfIptoHost, IfHostListener, /** * A convenient recursive routine to obtain the Hierarchy of Switches. * - * @param node Current Node in the Recursive routine. - * @param currHierarchy Array of Nodes that make this hierarchy on which the Current Switch belong - * @param fullHierarchy Array of multiple Hierarchies that represent a given host. + * @param node + * Current Node in the Recursive routine. + * @param currHierarchy + * Array of Nodes that make this hierarchy on which the Current + * Switch belong + * @param fullHierarchy + * Array of multiple Hierarchies that represent a given host. */ @SuppressWarnings("unchecked") - private void updateCurrentHierarchy(Node node, - ArrayList currHierarchy, List> fullHierarchy) { - //currHierarchy.add(String.format("%x", currSw.getId())); + private void updateCurrentHierarchy(Node node, ArrayList currHierarchy, List> fullHierarchy) { currHierarchy.add(dpidToHostNameHack((Long) node.getID())); - ArrayList currHierarchyClone = (ArrayList) currHierarchy - .clone(); //Shallow copy as required + // Shallow copy as required + ArrayList currHierarchyClone = (ArrayList) currHierarchy.clone(); Map> ndlinks = topologyManager.getNodeEdges(); if (ndlinks == null) { - logger - .debug( - "updateCurrentHierarchy(): topologyManager returned null ndlinks for node: {}", - node); + logger.debug("updateCurrentHierarchy(): topologyManager returned null ndlinks for node: {}", node); return; } Node n = NodeCreator.createOFNode((Long) node.getID()); @@ -803,23 +822,35 @@ public class HostTracker implements IfIptoHost, IfHostListener, return; } for (Edge lt : links) { - if (!lt.getHeadNodeConnector().getType().equals( - NodeConnector.NodeConnectorIDType.OPENFLOW)) { + if (!lt.getHeadNodeConnector().getType().equals(NodeConnector.NodeConnectorIDType.OPENFLOW)) { // We don't want to work on Node that are not openflow // for now continue; } Node dstNode = lt.getHeadNodeConnector().getNode(); - Tier nodeTier = (Tier) switchManager.getNodeProp(node, - Tier.TierPropName); - Tier dstNodeTier = (Tier) switchManager.getNodeProp(dstNode, - Tier.TierPropName); + Tier nodeTier = (Tier) switchManager.getNodeProp(node, Tier.TierPropName); + /* + * If the host is directly attached to the src node, then the node + * should have been assigned the "Access" tier in + * notifyHostLearnedOrRemoved. If not, it would be assigned + * "Unknown" tier. Thus the tier of host attached node cannot be + * null. If the src node here, is the next node in the hierarchy of + * the nodes, then its tier cannot be null + */ + + Tier dstNodeTier = (Tier) switchManager.getNodeProp(dstNode, Tier.TierPropName); + /* + * Skip if the tier of the destination node is null + */ + if (dstNodeTier == null) { + continue; + } if (dstNodeTier.getValue() > nodeTier.getValue()) { ArrayList buildHierarchy = currHierarchy; if (currHierarchy.size() > currHierarchyClone.size()) { - buildHierarchy = (ArrayList) currHierarchyClone - .clone(); //Shallow copy as required + // Shallow copy as required + buildHierarchy = (ArrayList) currHierarchyClone.clone(); fullHierarchy.add(buildHierarchy); } updateCurrentHierarchy(dstNode, buildHierarchy, fullHierarchy); @@ -827,8 +858,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, } } - @Override - public void edgeUpdate(Edge e, UpdateType type, Set props) { + private void debugEdgeUpdate(Edge e, UpdateType type, Set props) { Long srcNid = null; Short srcPort = null; Long dstNid = null; @@ -850,8 +880,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, } if (!srcType.equals(NodeConnector.NodeConnectorIDType.OPENFLOW)) { - logger.error("For now we cannot handle updates for " - + "non-openflow nodes"); + logger.debug("For now we cannot handle updates for non-openflow nodes"); return; } @@ -861,18 +890,15 @@ public class HostTracker implements IfIptoHost, IfHostListener, } if (!dstType.equals(NodeConnector.NodeConnectorIDType.OPENFLOW)) { - logger.error("For now we cannot handle updates for " - + "non-openflow nodes"); + logger.debug("For now we cannot handle updates for non-openflow nodes"); return; } // At this point we know we got an openflow update, so // lets fill everything accordingly. - srcNid = (Long) e.getTailNodeConnector().getNode() - .getID(); + srcNid = (Long) e.getTailNodeConnector().getNode().getID(); srcPort = (Short) e.getTailNodeConnector().getID(); - dstNid = (Long) e.getHeadNodeConnector().getNode() - .getID(); + dstNid = (Long) e.getHeadNodeConnector().getNode().getID(); dstPort = (Short) e.getHeadNodeConnector().getID(); // Now lets update the added flag @@ -887,97 +913,120 @@ public class HostTracker implements IfIptoHost, IfHostListener, } logger.debug("HostTracker Topology linkUpdate handling src:{}[port {}] dst:{}[port {}] added: {}", - new Object[] { srcNid, srcPort, dstNid, dstPort, added }); - clearTiers(); - for (Entry entry : hostsDB.entrySet()) { - HostNodeConnector host = entry.getValue(); - Node node = host.getnodeconnectorNode(); - if (node != null) { - Tier t = new Tier(1); - switchManager.setNodeProp(node, t); - updateSwitchTiers(node, 1); + new Object[] { srcNid, srcPort, dstNid, dstPort, added }); + } + + @Override + public void edgeUpdate(List topoedgeupdateList) { + if (logger.isDebugEnabled()) { + for (TopoEdgeUpdate topoEdgeUpdate : topoedgeupdateList) { + Edge e = topoEdgeUpdate.getEdge(); + Set p = topoEdgeUpdate.getProperty(); + UpdateType type = topoEdgeUpdate.getUpdateType(); + + debugEdgeUpdate(e, type, p); } } } + @Override public void subnetNotify(Subnet sub, boolean add) { logger.debug("Received subnet notification: {} add={}", sub, add); if (add) { - for (int i = 0; i < failedARPReqList.size(); i++) { + for (Entry entry : failedARPReqList.entrySet()) { ARPPending arphost; - arphost = failedARPReqList.get(i); - logger.debug( - "Sending the ARP from FailedARPReqList fors IP: {}", - arphost.getHostIP().getHostAddress()); - hostFinder.find(arphost.getHostIP()); + arphost = entry.getValue(); + if (hostFinder == null) { + logger.warn("ARPHandler Services are not available on subnet addition"); + continue; + } + logger.debug("Sending the ARP from FailedARPReqList fors IP: {}", decodeIPFromId(arphost.getHostId())); + for (IHostFinder hf : hostFinder) { + hf.find(decodeIPFromId(arphost.getHostId())); + } } } } class OutStandingARPHandler extends TimerTask { + @Override public void run() { + if (stopping) { + return; + } ARPPending arphost; /* This routine runs every 4 seconds */ - // logger.info ("ARP Handler called"); - for (int i = 0; i < ARPPendingList.size(); i++) { - arphost = ARPPendingList.get(i); - if (arphost.getSent_count() < switchManager.getHostRetryCount()) { - /* No reply has been received of first ARP Req, send the next one */ - hostFinder.find(arphost.getHostIP()); + logger.trace("Number of Entries in ARP Pending/Failed Lists: ARPPendingList = {}, failedARPReqList = {}", + ARPPendingList.size(), failedARPReqList.size()); + for (Entry entry : ARPPendingList.entrySet()) { + arphost = entry.getValue(); + + if (hostsDB.containsKey(arphost.getHostId())) { + // this host is already learned, shouldn't be in + // ARPPendingList + // Remove it and continue + logger.warn("Learned Host {} found in ARPPendingList", decodeIPFromId(arphost.getHostId())); + ARPPendingList.remove(entry.getKey()); + continue; + } + if (arphost.getSent_count() < hostRetryCount) { + /* + * No reply has been received of first ARP Req, send the + * next one. Before sending the ARP, check if ARPHandler is + * available or not + */ + if (hostFinder == null) { + logger.warn("ARPHandler Services are not available for Outstanding ARPs"); + continue; + } + for (IHostFinder hf : hostFinder) { + hf.find(decodeIPFromId(arphost.getHostId())); + } arphost.sent_count++; - logger.debug("ARP Sent from ARPPending List, IP: {}", - arphost.getHostIP().getHostAddress()); - } else if (arphost.getSent_count() >= switchManager - .getHostRetryCount()) { - /* Two ARP requests have been sent without - * receiving a reply, remove this from the - * pending list + logger.debug("ARP Sent from ARPPending List, IP: {}", decodeIPFromId(arphost.getHostId())); + } else if (arphost.getSent_count() >= hostRetryCount) { + /* + * ARP requests have been sent without receiving a reply, + * remove this from the pending list */ - removePendingARPFromList(i); - logger - .debug( - "ARP reply not received after two attempts, removing from Pending List IP: {}", - arphost.getHostIP().getHostAddress()); + ARPPendingList.remove(entry.getKey()); + logger.debug("ARP reply not received after multiple attempts, removing from Pending List IP: {}", + decodeIPFromId(arphost.getHostId())); /* - * Add this host to a different list which will be processed on link - * up events + * Add this host to a different list which will be processed + * on link up events */ - logger.debug("Adding the host to FailedARPReqList IP: {}", - arphost.getHostIP().getHostAddress()); - failedARPReqList.add(arphost); + logger.debug("Adding the host to FailedARPReqList IP: {}", decodeIPFromId(arphost.getHostId())); + failedARPReqList.put(entry.getKey(), arphost); } else { - logger - .error( - "Inavlid arp_sent count for entery at index: {}", - i); + logger.error("Inavlid arp_sent count for entry: {}", entry); } } } } private class ARPRefreshHandler extends TimerTask { - @SuppressWarnings("deprecation") + @Override public void run() { - if ((clusterContainerService != null) - && !clusterContainerService.amICoordinator()) { + if (stopping) { return; } - if ((switchManager != null) - && !switchManager.isHostRefreshEnabled()) { + if ((clusterContainerService != null) && !clusterContainerService.amICoordinator()) { + return; + } + if (!hostRefresh) { /* - * The host probe procedure was disabled by CLI + * The host probe procedure is turned off */ return; } if (hostsDB == null) { /* hostsDB is not allocated yet */ - logger - .error("ARPRefreshHandler(): hostsDB is not allocated yet:"); + logger.error("ARPRefreshHandler(): hostsDB is not allocated yet:"); return; } - for (Entry entry : hostsDB - .entrySet()) { + for (Entry entry : hostsDB.entrySet()) { HostNodeConnector host = entry.getValue(); if (host.isStaticHost()) { /* this host was learned via API3, don't age it out */ @@ -986,153 +1035,227 @@ public class HostTracker implements IfIptoHost, IfHostListener, short arp_cntdown = host.getArpSendCountDown(); arp_cntdown--; - if (arp_cntdown > switchManager.getHostRetryCount()) { + if (arp_cntdown > hostRetryCount) { host.setArpSendCountDown(arp_cntdown); } else if (arp_cntdown <= 0) { - /* No ARP Reply received in last 2 minutes, remove this host and inform applications*/ + /* + * No ARP Reply received in last 2 minutes, remove this host + * and inform applications + */ removeKnownHost(entry.getKey()); notifyHostLearnedOrRemoved(host, false); - } else if (arp_cntdown <= switchManager.getHostRetryCount()) { - /* Use the services of arphandler to check if host is still there */ - logger.trace("ARP Probing ({}) for {}({})", new Object[] { - arp_cntdown, - host.getNetworkAddress().getHostAddress(), - HexEncode.bytesToHexString(host - .getDataLayerAddressBytes()) }); + } else if (arp_cntdown <= hostRetryCount) { + /* + * Use the services of arphandler to check if host is still + * there + */ + if (logger.isTraceEnabled()) { + logger.trace( + "ARP Probing ({}) for {}({})", + new Object[] { arp_cntdown, host.getNetworkAddress().getHostAddress(), + HexEncode.bytesToHexString(host.getDataLayerAddressBytes()) }); + } host.setArpSendCountDown(arp_cntdown); - hostFinder.probe(host); + if (hostFinder == null) { + /* + * If hostfinder is not available, then can't send the + * probe. However, continue the age out the hosts since + * we don't know if the host is indeed out there or not. + */ + logger.trace("ARPHandler is not avaialable, can't send the probe"); + continue; + } + for (IHostFinder hf : hostFinder) { + hf.probe(host); + } } } } } /** - * Inform the controller IP to MAC binding of a host and its - * connectivity to an openflow switch in terms of Node, port, and - * VLAN. + * Inform the controller IP to MAC binding of a host and its connectivity to + * an openflow switch in terms of Node, port, and VLAN. * - * @param networkAddr IP address of the host - * @param dataLayer Address MAC address of the host - * @param nc NodeConnector to which host is connected - * @param port Port of the switch to which host is connected - * @param vlan Vlan of which this host is member of + * @param networkAddr + * IP address of the host + * @param dataLayer + * Address MAC address of the host + * @param nc + * NodeConnector to which host is connected + * @param port + * Port of the switch to which host is connected + * @param vlan + * Vlan of which this host is member of * - * @return Status The status object as described in {@code Status} - * indicating the result of this action. + * @return Status The status object as described in {@code Status} + * indicating the result of this action. */ - public Status addStaticHostReq(InetAddress networkAddr, - byte[] dataLayerAddress, NodeConnector nc, short vlan) { - if (dataLayerAddress.length != 6) { - return new Status(StatusCode.BADREQUEST, "Invalid MAC address"); + protected Status addStaticHostReq(InetAddress networkAddr, byte[] dataLayerAddress, NodeConnector nc, short vlan) { + if (dataLayerAddress.length != NetUtils.MACAddrLengthInBytes) { + return new Status(StatusCode.BADREQUEST, "Invalid MAC address"); } + if (nc == null) { + return new Status(StatusCode.BADREQUEST, "Invalid NodeConnector"); + } HostNodeConnector host = null; try { - host = new HostNodeConnector(dataLayerAddress, networkAddr, nc, - vlan); + host = new HostNodeConnector(dataLayerAddress, networkAddr, nc, vlan); + IHostId id = HostIdFactory.create(networkAddr, new EthernetAddress(dataLayerAddress)); if (hostExists(host)) { - // This host is already learned either via ARP or through a northbound request + // This host is already learned either via ARP or through a + // northbound request HostNodeConnector transHost = hostsDB.get(networkAddr); transHost.setStaticHost(true); - return new Status(StatusCode.SUCCESS, null); + return new Status(StatusCode.SUCCESS); + } + + if (hostsDB.get(id) != null) { + // There is already a host with this IP address (but behind + // a different (switch, port, vlan) tuple. Return an error + return new Status(StatusCode.CONFLICT, "Host with this IP already exists."); } host.setStaticHost(true); /* - * Before adding host, Check if the switch and the port have already come up + * Check if the nc is an ISL port + */ + if (topologyManager != null) { + if (topologyManager.isInternal(nc)) { + return new Status(StatusCode.BADREQUEST, "Cannot add host on ISL port"); + } + } + /* + * Before adding host, Check if the switch and the port have already + * come up */ if (switchManager.isNodeConnectorEnabled(nc)) { learnNewHost(host); + processPendingARPReqs(id); notifyHostLearnedOrRemoved(host, true); } else { inactiveStaticHosts.put(nc, host); - logger - .debug( - "Switch or switchport is not up, adding host {} to inactive list", - networkAddr.getHostName()); + logger.debug("Switch or switchport is not up, adding host {} to inactive list", + networkAddr.getHostName()); } - return new Status(StatusCode.SUCCESS, null); + return new Status(StatusCode.SUCCESS); } catch (ConstructionException e) { + logger.error("", e); return new Status(StatusCode.INTERNALERROR, "Host could not be created"); } } /** - * Update the controller IP to MAC binding of a host and its - * connectivity to an openflow switch in terms of - * switch id, switch port, and VLAN. + * Update the controller IP to MAC binding of a host and its connectivity to + * an openflow switch in terms of switch id, switch port, and VLAN. * - * @param networkAddr IP address of the host - * @param dataLayer Address MAC address of the host - * @param nc NodeConnector to which host is connected - * @param port Port of the switch to which host is connected - * @param vlan Vlan of which this host is member of + * @param networkAddr + * IP address of the host + * @param dataLayer + * Address MAC address of the host + * @param nc + * NodeConnector to which host is connected + * @param port + * Port of the switch to which host is connected + * @param vlan + * Vlan of which this host is member of * - * @return boolean true if the host was added successfully, - * false otherwise + * @return Status The status object as described in {@code Status} + * indicating the result of this action. */ - public boolean updateHostReq(InetAddress networkAddr, - byte[] dataLayerAddress, NodeConnector nc, - short vlan) { + public Status updateHostReq(InetAddress networkAddr, byte[] dataLayerAddress, NodeConnector nc, short vlan) { + HostNodeConnector tobeUpdatedHost; + HostNodeConnector host = null; + + if (dataLayerAddress.length != NetUtils.MACAddrLengthInBytes) { + return new Status(StatusCode.BADREQUEST, "Invalid MAC address"); + } + if (nc == null) { - return false; + return new Status(StatusCode.BADREQUEST, "Invalid NodeConnector"); } - HostNodeConnector host = null; + try { - host = new HostNodeConnector(dataLayerAddress, networkAddr, nc, - vlan); - if (!hostExists(host)) { - if ((inactiveStaticHosts.get(nc)) != null) { - inactiveStaticHosts.replace(nc, host); - return true; + host = new HostNodeConnector(dataLayerAddress, networkAddr, nc, vlan); + if (hostExists(host)) { + return new Status(StatusCode.BADREQUEST, "Host already exists"); + } + + IHostId id = HostIdFactory.create(networkAddr, new EthernetAddress(dataLayerAddress)); + + if ((tobeUpdatedHost = hostsDB.get(networkAddr)) != null) { + if (hostsDB.replace(id, tobeUpdatedHost, host)) { + logger.debug("Host replaced from hostsDB. Old host: {} New Host: {}", tobeUpdatedHost, host); + notifyHostLearnedOrRemoved(tobeUpdatedHost, false); + notifyHostLearnedOrRemoved(host, true); + return new Status(StatusCode.SUCCESS); + } else { + logger.error("Static host replacement failed from hostsDB, Replaced Host: {}, New Host: {}", + tobeUpdatedHost, host); + return new Status(StatusCode.INTERNALERROR, + "Host Replacement Failed due to presence of another host with same IP"); } - return false; } - hostsDB.replace(networkAddr, host); - return true; + + // Check if the host exists in inactive hosts database + if ((tobeUpdatedHost = inactiveStaticHosts.get(nc)) != null) { + if (inactiveStaticHosts.replace(nc, tobeUpdatedHost, host)) { + logger.debug("Host replaced from inactive hostsDB. Old host: {} New Host: {}", tobeUpdatedHost, + host); + return new Status(StatusCode.SUCCESS); + } else { + logger.error("Static host replacement failed, Replaced Host: {}, New Host: {}", tobeUpdatedHost, + host); + return new Status(StatusCode.INTERNALERROR, + "Host Replacement Failed due to presence of another host with same IP"); + } + } + + // Host doesn't exist + return new Status(StatusCode.BADREQUEST, "Host doesn't exists, can't update"); } catch (ConstructionException e) { + logger.error("", e); + return new Status(StatusCode.INTERNALERROR, "host object creation failure"); } - return false; } /** * Remove from the controller IP to MAC binding of a host and its * connectivity to an openflow switch * - * @param networkAddr IP address of the host + * @param networkAddr + * IP address of the host * - * @return boolean true if the host was removed successfully, - * false otherwise + * @return boolean true if the host was removed successfully, false + * otherwise */ - public Status removeStaticHostReq(InetAddress networkAddress) { + public Status removeStaticHostReq(InetAddress networkAddress, DataLinkAddress mac) { // Check if host is in active hosts database - HostNodeConnector host = getHostFromOnActiveDB(networkAddress); + IHostId id = HostIdFactory.create(networkAddress, mac); + HostNodeConnector host = getHostFromOnActiveDB(id); if (host != null) { // Validation check if (!host.isStaticHost()) { - return new Status(StatusCode.FORBIDDEN, - "Host " + networkAddress.getHostName() + - " is not static"); + return new Status(StatusCode.FORBIDDEN, "Host " + networkAddress.getHostName() + " is not static"); } // Remove and notify notifyHostLearnedOrRemoved(host, false); - removeKnownHost(networkAddress); + removeKnownHost(id); return new Status(StatusCode.SUCCESS, null); } // Check if host is in inactive hosts database - Entry entry = getHostFromInactiveDB(networkAddress); + Entry entry = getHostFromInactiveDB(id); if (entry != null) { host = entry.getValue(); // Validation check if (!host.isStaticHost()) { - return new Status(StatusCode.FORBIDDEN, - "Host " + networkAddress.getHostName() + - " is not static"); + return new Status(StatusCode.FORBIDDEN, "Host " + networkAddress.getHostName() + " is not static"); } - this.removeHostFromInactiveDB(networkAddress); + this.removeHostFromInactiveDB(id); return new Status(StatusCode.SUCCESS, null); } @@ -1146,22 +1269,18 @@ public class HostTracker implements IfIptoHost, IfHostListener, } @Override - public void notifyNode(Node node, UpdateType type, - Map propMap) { - if (node == null) + public void notifyNode(Node node, UpdateType type, Map propMap) { + if (node == null) { return; + } switch (type) { case REMOVED: - long sid = (Long) node.getID(); - logger.debug("Received removedSwitch for sw id {}", HexEncode - .longToHexString(sid)); - for (Entry entry : hostsDB - .entrySet()) { + logger.debug("Received removed node {}", node); + for (Entry entry : hostsDB.entrySet()) { HostNodeConnector host = entry.getValue(); - if (host.getnodeconnectornodeId() == sid) { - logger.debug("Switch: {} is down, remove from Hosts_DB", - sid); + if (host.getnodeconnectorNode().equals(node)) { + logger.debug("Node: {} is down, remove from Hosts_DB", node); removeKnownHost(entry.getKey()); notifyHostLearnedOrRemoved(host, false); } @@ -1173,10 +1292,10 @@ public class HostTracker implements IfIptoHost, IfHostListener, } @Override - public void notifyNodeConnector(NodeConnector nodeConnector, - UpdateType type, Map propMap) { - if (nodeConnector == null) + public void notifyNodeConnector(NodeConnector nodeConnector, UpdateType type, Map propMap) { + if (nodeConnector == null) { return; + } boolean up = false; switch (type) { @@ -1203,68 +1322,130 @@ public class HostTracker implements IfIptoHost, IfHostListener, } @Override - public Status addStaticHost(String networkAddress, String dataLayerAddress, - NodeConnector nc, String vlan) { + public Status addStaticHost(String networkAddress, String dataLayerAddress, NodeConnector nc, String vlan) { try { InetAddress ip = InetAddress.getByName(networkAddress); - if (nc == null) { - return new Status(StatusCode.BADREQUEST, "Invalid NodeId"); + short vl = 0; + if (vlan != null && !vlan.isEmpty()) { + vl = Short.decode(vlan); + if (vl < 1 || vl > 4095) { + return new Status(StatusCode.BADREQUEST, "Host vlan out of range [1 - 4095]"); + } } - return addStaticHostReq(ip, - HexEncode - .bytesFromHexString(dataLayerAddress), - nc, - Short.valueOf(vlan)); + + return addStaticHostReq(ip, HexEncode.bytesFromHexString(dataLayerAddress), nc, vl); + } catch (UnknownHostException e) { - logger.error("",e); - return new Status(StatusCode.BADREQUEST, "Invalid Address"); + logger.debug("Invalid host IP specified when adding static host", e); + return new Status(StatusCode.BADREQUEST, "Invalid Host IP Address"); + } catch (NumberFormatException nfe) { + logger.debug("Invalid host vlan or MAC specified when adding static host", nfe); + return new Status(StatusCode.BADREQUEST, "Invalid Host vLan/MAC"); } } @Override public Status removeStaticHost(String networkAddress) { - InetAddress address; try { - address = InetAddress.getByName(networkAddress); - return removeStaticHostReq(address); + if ((keyScheme != null) && (!keyScheme.equals(HostIdFactory.DEFAULT_IP_KEY_SCHEME))) { + return new Status(StatusCode.NOTALLOWED, "Host DB Key scheme used is not IP only scheme."); + } + InetAddress address = InetAddress.getByName(networkAddress); + return removeStaticHostReq(address, null); + } catch (UnknownHostException e) { + logger.debug("Invalid IP Address when trying to remove host", e); + return new Status(StatusCode.BADREQUEST, "Invalid IP Address when trying to remove host"); + } + } + + @Override + public Status removeStaticHostUsingIPAndMac(String networkAddress, String macAddress) { + try { + if ((keyScheme != null) && (keyScheme.equals(HostIdFactory.DEFAULT_IP_KEY_SCHEME))) { + return new Status(StatusCode.NOTALLOWED, "Host DB Key scheme used is not IP only scheme."); + } + InetAddress address = InetAddress.getByName(networkAddress); + DataLinkAddress mac = new EthernetAddress(HexEncode.bytesFromHexString(macAddress)); + return removeStaticHostReq(address, mac); } catch (UnknownHostException e) { - logger.error("",e); - return new Status(StatusCode.BADREQUEST, "Invalid Address"); + logger.debug("Invalid IP Address when trying to remove host", e); + return new Status(StatusCode.BADREQUEST, "Invalid IP Address when trying to remove host"); + } catch (ConstructionException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return new Status(StatusCode.BADREQUEST, "Invalid Input parameters have been passed."); + } + } + + private InetAddress decodeIPFromId(IHostId id) { + if ((keyScheme != null) && (keyScheme.equals(HostIdFactory.DEFAULT_IP_KEY_SCHEME))) { + IPHostId ipId = (IPHostId) id; + return (ipId.getIpAddress()); + } else if ((keyScheme != null) && (keyScheme.equals(HostIdFactory.IP_MAC_KEY_SCHEME))) { + IPMacHostId ipMacId = (IPMacHostId) id; + return (ipMacId.getIpAddress()); } + return null; + } + + private DataLinkAddress decodeMacFromId(IHostId id) { + if ((keyScheme != null) && (!keyScheme.equals(HostIdFactory.DEFAULT_IP_KEY_SCHEME))) { + IPMacHostId ipMacId = (IPMacHostId) id; + return (ipMacId.getMacAddr()); + } + + return null; } private void handleNodeConnectorStatusUp(NodeConnector nodeConnector) { ARPPending arphost; + HostNodeConnector host = null; - logger.debug("handleNodeConnectorStatusUp {}", nodeConnector); + logger.trace("handleNodeConnectorStatusUp {}", nodeConnector); - for (int i = 0; i < failedARPReqList.size(); i++) { - arphost = failedARPReqList.get(i); - logger.debug("Sending the ARP from FailedARPReqList fors IP: {}", - arphost.getHostIP().getHostAddress()); - hostFinder.find(arphost.getHostIP()); + for (Entry entry : failedARPReqList.entrySet()) { + arphost = entry.getValue(); + logger.trace("Sending the ARP from FailedARPReqList fors IP: {}", arphost.getHostId()); + if (hostFinder == null) { + logger.warn("ARPHandler is not available at interface up"); + logger.warn("Since this event is missed, host(s) connected to interface {} may not be discovered", + nodeConnector); + continue; + } + + // Send a broadcast ARP only on the interface which just came up. + // Use hostFinder's "probe" method + try { + byte[] dataLayerAddress = NetUtils.getBroadcastMACAddr(); + host = new HostNodeConnector(dataLayerAddress, decodeIPFromId(arphost.getHostId()), nodeConnector, + (short) 0); + for (IHostFinder hf : hostFinder) { + hf.probe(host); + } + } catch (ConstructionException e) { + logger.debug("HostNodeConnector couldn't be created for Host: {}, NodeConnector: {}", + arphost.getHostId(), nodeConnector); + logger.error("", e); + } } - HostNodeConnector host = inactiveStaticHosts.get(nodeConnector); + + host = inactiveStaticHosts.get(nodeConnector); if (host != null) { inactiveStaticHosts.remove(nodeConnector); learnNewHost(host); + IHostId id = HostIdFactory.create(host.getNetworkAddress(), host.getDataLayerAddress()); + processPendingARPReqs(id); notifyHostLearnedOrRemoved(host, true); } } private void handleNodeConnectorStatusDown(NodeConnector nodeConnector) { - long sid = (Long) nodeConnector.getNode().getID(); - short port = (Short) nodeConnector.getID(); - - logger.debug("handleNodeConnectorStatusDown {}", nodeConnector); + logger.trace("handleNodeConnectorStatusDown {}", nodeConnector); - for (Entry entry : hostsDB.entrySet()) { + for (Entry entry : hostsDB.entrySet()) { HostNodeConnector host = entry.getValue(); - if ((host.getnodeconnectornodeId() == sid) - && (host.getnodeconnectorportId() == port)) { - logger.debug( - "Switch: {}, Port: {} is down, remove from Hosts_DB", - sid, port); + if (host.getnodeConnector().equals(nodeConnector)) { + logger.debug(" NodeConnector: {} is down, remove from Hosts_DB", nodeConnector); removeKnownHost(entry.getKey()); notifyHostLearnedOrRemoved(host, false); } @@ -1296,8 +1477,9 @@ public class HostTracker implements IfIptoHost, IfHostListener, } public String getContainerName() { - if (containerName == null) + if (containerName == null) { return GlobalConstants.DEFAULT.toString(); + } return containerName; } @@ -1316,46 +1498,117 @@ public class HostTracker implements IfIptoHost, IfHostListener, this.containerName = ""; } startUp(); + + logger.debug("key Scheme in hosttracker is {}", keyScheme); } /** - * Function called by the dependency manager when at least one - * dependency become unsatisfied or when the component is shutting - * down because for example bundle is being stopped. + * Function called by the dependency manager when at least one dependency + * become unsatisfied or when the component is shutting down because for + * example bundle is being stopped. * */ void destroy() { - destroyCache(); } /** - * Function called by dependency manager after "init ()" is called - * and after the services provided by the class are registered in - * the service registry + * Function called by dependency manager after "init ()" is called and after + * the services provided by the class are registered in the service registry * */ void start() { + registerWithOSGIConsole(); } /** - * Function called by the dependency manager before the services - * exported by the component are unregistered, this will be - * followed by a "destroy ()" calls + * Function called by the dependency manager before the services exported by + * the component are unregistered, this will be followed by a "destroy ()" + * calls * */ void stop() { } + void stopping() { + stopping = true; + arpRefreshTimer.cancel(); + timer.cancel(); + executor.shutdownNow(); + } + @Override public void edgeOverUtilized(Edge edge) { - // TODO Auto-generated method stub } @Override public void edgeUtilBackToNormal(Edge edge) { - // TODO Auto-generated method stub } + @Override + public void entryCreated(IHostId key, String cacheName, boolean originLocal) { + if (originLocal) { + return; + } + processPendingARPReqs(key); + } + + @Override + public void entryUpdated(IHostId key, HostNodeConnector new_value, String cacheName, boolean originLocal) { + } + + @Override + public void entryDeleted(IHostId key, String cacheName, boolean originLocal) { + } + + private void registerWithOSGIConsole() { + BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext(); + bundleContext.registerService(CommandProvider.class.getName(), this, null); + } + + @Override + public String getHelp() { + return null; + } + + public void _dumpPendingARPReqList(CommandInterpreter ci) { + ARPPending arphost; + for (Entry entry : ARPPendingList.entrySet()) { + arphost = entry.getValue(); + ci.println(arphost.getHostId().toString()); + } + } + + public void _dumpFailedARPReqList(CommandInterpreter ci) { + ARPPending arphost; + for (Entry entry : failedARPReqList.entrySet()) { + arphost = entry.getValue(); + ci.println(arphost.getHostId().toString()); + } + } + + @Override + public HostNodeConnector hostFind(InetAddress addr) { + IHostId id = HostIdFactory.create(addr, null); + return (hostFind(id)); + } + + @Override + public HostNodeConnector hostQuery(InetAddress addr) { + IHostId id = HostIdFactory.create(addr, null); + return (hostQuery(id)); + } + + @Override + public Future discoverHost(InetAddress addr) { + IHostId id = HostIdFactory.create(addr, null); + return discoverHost(id); + } + + @Override + public List> getHostNetworkHierarchy(InetAddress addr) { + IHostId id = HostIdFactory.create(addr, null); + return getHostNetworkHierarchy(id); + } }