<module>../../hosttracker/api</module>
<module>../../hosttracker/implementation</module>
<module>../../hosttracker/integrationtest</module>
+ <module>../../hosttracker_new/api</module>
+ <module>../../hosttracker_new/implementation</module>
<module>../../containermanager/api</module>
<module>../../containermanager/implementation</module>
<module>../../switchmanager/api</module>
<exclude>org.opendaylight.controller:logging.bridge</exclude>
<exclude>org.opendaylight.controller:protocol_plugins.stub</exclude>
<exclude>org.opendaylight.controller:*.integrationtest</exclude>
+ <exclude>org.opendaylight.controller:hosttracker_new</exclude>
+ <exclude>org.opendaylight.controller:hosttracker_new.implementation</exclude>
</excludes>
<binaries>
<outputDirectory>opendaylight/plugins</outputDirectory>
-
/*
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
*
import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
/**
- * This interface defines the method to notify detected Host on the
- * network. The information includes Host's IP address, MAC address,
- * switch ID, port, and VLAN.
+ * This interface defines the method to notify detected Host on the network. The
+ * information includes Host's IP address, MAC address, switch ID, port, and
+ * VLAN.
*
*/
public interface IfHostListener {
/**
- * Learns new Hosts. Called by ArpHandler and implemented in
- * HostTracker.java. If a Host is learned for the first time then
- * adds it to the local database and informs other applications
- * of coming up a new Host. For the hosts which it has already
- * learned, it refreshes them.
+ * Learns new Hosts. Called by ArpHandler and implemented in
+ * HostTracker.java. If a Host is learned for the first time then adds it to
+ * the local database and informs other applications of coming up a new
+ * Host. For the hosts which it has already learned, it refreshes them.
*
- * @param host Host info encapsulated in HostNodeConnector class
+ * @param host
+ * Host info encapsulated in HostNodeConnector class
*/
public void hostListener(HostNodeConnector host);
}
-
/*
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
*
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
-import org.opendaylight.controller.sal.core.NodeConnector;
+
import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
+import org.opendaylight.controller.sal.core.NodeConnector;
import org.opendaylight.controller.sal.utils.Status;
/**
- * This interface defines the methods to retrieve information about
- * learned Hosts. Also provides methods to statically add/remove
- * Hosts from the local database.
+ * This interface defines the methods to retrieve information about learned
+ * Hosts. Also provides methods to statically add/remove Hosts from the local
+ * database.
*
*/
public interface IfIptoHost {
/**
- * Applications call this interface methods to determine IP address to MAC binding and its
- * connectivity to an OpenFlow switch in term of Node, Port, and VLAN. These
- * bindings are learned dynamically as well as can be added statically through
- * Northbound APIs. If a binding is unknown, then an ARP request is initiated
- * immediately to discover the host.
+ * Applications call this interface methods to determine IP address to MAC
+ * binding and its connectivity to an OpenFlow switch in term of Node, Port,
+ * and VLAN. These bindings are learned dynamically as well as can be added
+ * statically through Northbound APIs. If a binding is unknown, then an ARP
+ * request is initiated immediately to discover the host.
*
- * @param networkAddress IP Address of the Host encapsulated in class InetAddress
- * @return {@link org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector}
- * Class that contains the Host info such as its MAC address,
- * Switch ID, port, VLAN. If Host is not found, returns NULL
+ * @param networkAddress
+ * IP Address of the Host encapsulated in class InetAddress
+ * @return {@link org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector}
+ * Class that contains the Host info such as its MAC address, Switch
+ * ID, port, VLAN. If Host is not found, returns NULL
*/
public HostNodeConnector hostFind(InetAddress networkAddress);
* Checks the local Host Database to see if a Host has been learned for a
* given IP address.
*
- * @param networkAddress IP Address of the Host encapsulated in class InetAddress
- * @return {@link org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector}
- * Class that contains the Host info such as its MAC address,
- * Switch ID, port, VLAN. If Host is not found, returns NULL
+ * @param networkAddress
+ * IP Address of the Host encapsulated in class InetAddress
+ * @return {@link org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector}
+ * Class that contains the Host info such as its MAC address, Switch
+ * ID, port, VLAN. If Host is not found, returns NULL
*
*/
public HostNodeConnector hostQuery(InetAddress networkAddress);
* Initiates an immediate discovery of the Host for a given IP address. This
* provides for the calling applications to block on the host discovery.
*
- * @param networkAddress IP address encapsulated in InetAddress class
- * @return Future {@link org.opendaylight.controller.hosttracker.HostTrackerCallable}
+ * @param networkAddress
+ * IP address encapsulated in InetAddress class
+ * @return Future
+ * {@link org.opendaylight.controller.hosttracker.HostTrackerCallable}
*/
public Future<HostNodeConnector> discoverHost(InetAddress networkAddress);
/**
- * Returns the Network Hierarchy for a given Host. This API is typically used by
- * applications like Hadoop for Rack Awareness functionality.
+ * Returns the Network Hierarchy for a given Host. This API is typically
+ * used by applications like Hadoop for Rack Awareness functionality.
*
- * @param IP address of the Host encapsulated in InetAddress class
- * @return List of String ArrayList containing the Hierarchies.
+ * @param IP
+ * address of the Host encapsulated in InetAddress class
+ * @return List of String ArrayList containing the Hierarchies.
*/
public List<List<String>> getHostNetworkHierarchy(InetAddress hostAddress);
/**
- * Returns all the the Hosts either learned dynamically or added statically via
- * Northbound APIs.
+ * Returns all the the Hosts either learned dynamically or added statically
+ * via Northbound APIs.
*
- * @return Set of {@link org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector}.
- * Class that contains the Host info such as its MAC address,
- * Switch ID, port, VLAN.
+ * @return Set of
+ * {@link org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector}
+ * . Class that contains the Host info such as its MAC address,
+ * Switch ID, port, VLAN.
*/
public Set<HostNodeConnector> getAllHosts();
/**
- * Returns all the "Active Hosts" learned "Statically" via Northbound APIs. These Hosts
- * are categorized as "Active" because the Switch and Port they are connected to, are in
- * up state.
+ * Returns all the "Active Hosts" learned "Statically" via Northbound APIs.
+ * These Hosts are categorized as "Active" because the Switch and Port they
+ * are connected to, are in up state.
*
- * @return Set of {@link org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector}.
- * Class that contains the Host info such as MAC address,
- * Switch ID, port, VLAN. If Host is not found, returns NULL
+ * @return Set of
+ * {@link org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector}
+ * . Class that contains the Host info such as MAC address, Switch
+ * ID, port, VLAN. If Host is not found, returns NULL
*/
public Set<HostNodeConnector> getActiveStaticHosts();
/**
- * Returns all the "Inactive Hosts" learned "Statically" via Northbound APIs. These Hosts
- * are categorized as "Inactive" because either the Switch or the Port they are connected
- * to, is in down state.
+ * Returns all the "Inactive Hosts" learned "Statically" via Northbound
+ * APIs. These Hosts are categorized as "Inactive" because either the Switch
+ * or the Port they are connected to, is in down state.
*
- * @return Set of HostNodeConnector {@link org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector}.
- * HostNodeConnector is Class that
- * contains the Host info such as its MAC address, OpenFlowNode
- * ID, port, VLAN.
+ * @return Set of HostNodeConnector
+ * {@link org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector}
+ * . HostNodeConnector is Class that contains the Host info such as
+ * its MAC address, OpenFlowNode ID, port, VLAN.
*/
public Set<HostNodeConnector> getInactiveStaticHosts();
/**
- * Hosts can be learned dynamically or added statically. This method allows the addition
- * of a Host to the local database statically.
+ * Hosts can be learned dynamically or added statically. This method allows
+ * the addition of a Host to the local database statically.
*
- * @param networkAddress IP Address of the Host
- * @param dataLayerAddress MAC Address of the Host
- * @param nc NodeConnector to which the host is attached
- * @param vlan VLAN the host belongs to
- * @return The status object as described in {@code Status}
- * indicating the result of this action.
+ * @param networkAddress
+ * IP Address of the Host
+ * @param dataLayerAddress
+ * MAC Address of the Host
+ * @param nc
+ * NodeConnector to which the host is attached
+ * @param vlan
+ * VLAN the host belongs to
+ * @return The status object as described in {@code Status} indicating the
+ * result of this action.
*/
public Status addStaticHost(String networkAddress, String dataLayerAddress,
- NodeConnector nc, String vlan);
+ NodeConnector nc, String vlan);
/**
* Allows the deletion of statically learned Host
*
* @param networkAddress
- * @return The status object as described in {@code Status}
- * indicating the result of this action.
+ * @return The status object as described in {@code Status} indicating the
+ * result of this action.
*/
public Status removeStaticHost(String networkAddress);
}
-
/*
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
*
import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
/**
- * This Interface defines the methods for client applications of
- * Host Tracker to get notifications when a new host is learned or
- * existing host is removed from the network.
+ * This Interface defines the methods for client applications of Host Tracker to
+ * get notifications when a new host is learned or existing host is removed from
+ * the network.
*
*/
public interface IfNewHostNotify {
/**
* Notifies the HostTracker Clients that a new Host has been learned
*
- * @param host Host Info encapsulated in HostNodeConnector class
+ * @param host
+ * Host Info encapsulated in HostNodeConnector class
*/
public void notifyHTClient(HostNodeConnector host);
/**
- * Notifies the HostTracker Clients that a Host which was learned in
- * the past has been removed either due to switch/port down event or
- * due to ARP Aging
+ * Notifies the HostTracker Clients that a Host which was learned in the
+ * past has been removed either due to switch/port down event or due to ARP
+ * Aging
*
- * @param host Host Info encapsulated in HostNodeConnector class
+ * @param host
+ * Host Info encapsulated in HostNodeConnector class
*/
public void notifyHTClientHostRemoved(HostNodeConnector host);
}
-
/*
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
*
import org.opendaylight.controller.sal.core.NodeConnector;
import org.opendaylight.controller.sal.packet.address.EthernetAddress;
-@XmlRootElement(name="host")
+@XmlRootElement(name = "host")
@XmlAccessorType(XmlAccessType.NONE)
public class HostNodeConnector extends Host {
private static final long serialVersionUID = 1L;
return !Arrays.equals(emptyArray, macaddr);
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see java.lang.Object#toString()
*/
@Override
-
/*
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
*
package org.opendaylight.controller.hosttracker.hostAware;
import java.net.InetAddress;
-import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
-
/**
- * This Interface defines the methods to trigger the discovery of
- * a Host and to probe if a learned Host is still in the network.
+ * This Interface defines the methods to trigger the discovery of a Host and to
+ * probe if a learned Host is still in the network.
*
*
*
*/
public interface IHostFinder {
/**
- * This method initiates the discovery of a host based on its IP address. This is triggered
- * by query of an application to the HostTracker. The requested IP address
- * doesn't exist in the local database at this point.
+ * This method initiates the discovery of a host based on its IP address.
+ * This is triggered by query of an application to the HostTracker. The
+ * requested IP address doesn't exist in the local database at this point.
*
- * @param networkAddress IP Address encapsulated in InetAddress class
+ * @param networkAddress
+ * IP Address encapsulated in InetAddress class
*
*/
public void find(InetAddress networkAddress);
/**
- * This method is called by HostTracker to see if a learned Host is still in the network.
- * Used mostly for ARP Aging.
+ * This method is called by HostTracker to see if a learned Host is still in
+ * the network. Used mostly for ARP Aging.
*
- * @param host The Host that needs to be probed
+ * @param host
+ * The Host that needs to be probed
*/
public void probe(HostNodeConnector host);
}
-\r
/*\r
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
*\r
import org.opendaylight.controller.sal.core.NodeConnector;\r
import org.opendaylight.controller.sal.utils.NodeCreator;\r
\r
-\r
public class HostNodeConnectorTest extends TestCase {\r
\r
- @Test\r
- public void testHostNodeConnector() throws UnknownHostException {\r
- HostNodeConnector hostnodeconnector_1, hostnodeconnector_2, hostnodeconnector_3;\r
- InetAddress hostIP_1 = InetAddress.getByName("192.168.0.8");\r
- InetAddress hostIP_2 = InetAddress.getByName("2001:420:281:1004:e123:e688:d655:a1b0");\r
- InetAddress hostIP_3 = InetAddress.getByName("192.168.0.28");\r
- byte[] hostMAC_2 = new byte[]{(byte)0x11,(byte)0x22,(byte)0x33,(byte)0x22,(byte)0x22,(byte)0x22};\r
- byte[] hostMAC_3 = new byte[]{(byte)0x11,(byte)0x22,(byte)0x33,(byte)0x33,(byte)0x33,(byte)0x33};\r
-\r
- Node node = NodeCreator.createOFNode(1L);\r
- NodeConnector nc1 = NodeConnectorCreator.createOFNodeConnector((short) 2, node);\r
- NodeConnector nc2 = NodeConnectorCreator.createOFNodeConnector((short) 1, node);\r
-\r
- try {\r
- hostnodeconnector_1 = new HostNodeConnector(hostIP_1);\r
- Assert.assertTrue(hostnodeconnector_1.equalsByIP(hostIP_1));\r
- Assert.assertTrue(hostnodeconnector_1.isV4Host());\r
- Assert.assertTrue(hostnodeconnector_1.equalsByIP(hostIP_1));\r
- } catch (ConstructionException e) {\r
- Assert.assertTrue(false);\r
- }\r
-\r
- try {\r
- hostnodeconnector_2 = new HostNodeConnector(\r
- hostMAC_2, hostIP_2, nc1, (short)2);\r
- Assert.assertTrue(hostnodeconnector_2.isV6Host());\r
- Assert.assertTrue(hostnodeconnector_2.getnodeConnector().equals(nc1));\r
- Assert.assertTrue(hostnodeconnector_2.getnodeconnectorNode().equals(node));\r
- Assert.assertTrue(node.equals(hostnodeconnector_2.getnodeconnectorNode()));\r
- } catch (ConstructionException e) {\r
- Assert.assertTrue(false);\r
- }\r
-\r
- try {\r
- hostnodeconnector_3 = new HostNodeConnector(\r
- new EthernetAddress(hostMAC_3), hostIP_3, nc2, (short)3);\r
- byte[] hostMAC_3_rb = hostnodeconnector_3.getDataLayerAddressBytes();\r
- HostNodeConnector hostnodeconnector_3rb = new HostNodeConnector(\r
- new EthernetAddress(hostMAC_3_rb), hostIP_3, nc2, (short)3);\r
- Assert.assertTrue(hostnodeconnector_3.equals(hostnodeconnector_3rb));\r
-\r
- Assert.assertTrue(hostnodeconnector_3.getVlan() == (short)3);\r
-\r
- hostnodeconnector_3.setStaticHost(true);\r
- Assert.assertTrue(hostnodeconnector_3.isStaticHost());\r
-\r
- Assert.assertTrue(hostnodeconnector_3.isRewriteEnabled());\r
-\r
- hostnodeconnector_3.initArpSendCountDown().setArpSendCountDown((short) 10);\r
- Assert.assertTrue(hostnodeconnector_3.getArpSendCountDown() == (short)10);\r
-\r
- } catch (ConstructionException e) {\r
- Assert.assertTrue(false);\r
- }\r
+ @Test\r
+ public void testHostNodeConnector() throws UnknownHostException {\r
+ HostNodeConnector hostnodeconnector_1, hostnodeconnector_2, hostnodeconnector_3;\r
+ InetAddress hostIP_1 = InetAddress.getByName("192.168.0.8");\r
+ InetAddress hostIP_2 = InetAddress\r
+ .getByName("2001:420:281:1004:e123:e688:d655:a1b0");\r
+ InetAddress hostIP_3 = InetAddress.getByName("192.168.0.28");\r
+ byte[] hostMAC_2 = new byte[] { (byte) 0x11, (byte) 0x22, (byte) 0x33,\r
+ (byte) 0x22, (byte) 0x22, (byte) 0x22 };\r
+ byte[] hostMAC_3 = new byte[] { (byte) 0x11, (byte) 0x22, (byte) 0x33,\r
+ (byte) 0x33, (byte) 0x33, (byte) 0x33 };\r
+\r
+ Node node = NodeCreator.createOFNode(1L);\r
+ NodeConnector nc1 = NodeConnectorCreator.createOFNodeConnector(\r
+ (short) 2, node);\r
+ NodeConnector nc2 = NodeConnectorCreator.createOFNodeConnector(\r
+ (short) 1, node);\r
+\r
+ try {\r
+ hostnodeconnector_1 = new HostNodeConnector(hostIP_1);\r
+ Assert.assertTrue(hostnodeconnector_1.equalsByIP(hostIP_1));\r
+ Assert.assertTrue(hostnodeconnector_1.isV4Host());\r
+ Assert.assertTrue(hostnodeconnector_1.equalsByIP(hostIP_1));\r
+ } catch (ConstructionException e) {\r
+ Assert.assertTrue(false);\r
+ }\r
\r
+ try {\r
+ hostnodeconnector_2 = new HostNodeConnector(hostMAC_2, hostIP_2,\r
+ nc1, (short) 2);\r
+ Assert.assertTrue(hostnodeconnector_2.isV6Host());\r
+ Assert.assertTrue(hostnodeconnector_2.getnodeConnector()\r
+ .equals(nc1));\r
+ Assert.assertTrue(hostnodeconnector_2.getnodeconnectorNode()\r
+ .equals(node));\r
+ Assert.assertTrue(node.equals(hostnodeconnector_2\r
+ .getnodeconnectorNode()));\r
+ } catch (ConstructionException e) {\r
+ Assert.assertTrue(false);\r
}\r
\r
+ try {\r
+ hostnodeconnector_3 = new HostNodeConnector(new EthernetAddress(\r
+ hostMAC_3), hostIP_3, nc2, (short) 3);\r
+ byte[] hostMAC_3_rb = hostnodeconnector_3\r
+ .getDataLayerAddressBytes();\r
+ HostNodeConnector hostnodeconnector_3rb = new HostNodeConnector(\r
+ new EthernetAddress(hostMAC_3_rb), hostIP_3, nc2, (short) 3);\r
+ Assert.assertTrue(hostnodeconnector_3.equals(hostnodeconnector_3rb));\r
+\r
+ Assert.assertTrue(hostnodeconnector_3.getVlan() == (short) 3);\r
+\r
+ hostnodeconnector_3.setStaticHost(true);\r
+ Assert.assertTrue(hostnodeconnector_3.isStaticHost());\r
+\r
+ Assert.assertTrue(hostnodeconnector_3.isRewriteEnabled());\r
+\r
+ hostnodeconnector_3.initArpSendCountDown().setArpSendCountDown(\r
+ (short) 10);\r
+ Assert.assertTrue(hostnodeconnector_3.getArpSendCountDown() == (short) 10);\r
+\r
+ } catch (ConstructionException e) {\r
+ Assert.assertTrue(false);\r
+ }\r
+\r
+ }\r
+\r
}\r
-
/*
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
*
package org.opendaylight.controller.hosttracker.internal;
import org.apache.felix.dm.Component;
-import org.opendaylight.controller.hosttracker.internal.HostTracker;
+import org.opendaylight.controller.clustering.services.IClusterContainerServices;
import org.opendaylight.controller.hosttracker.IfHostListener;
import org.opendaylight.controller.hosttracker.IfIptoHost;
import org.opendaylight.controller.hosttracker.IfNewHostNotify;
import org.opendaylight.controller.hosttracker.hostAware.IHostFinder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.opendaylight.controller.clustering.services.IClusterContainerServices;
import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
import org.opendaylight.controller.switchmanager.IInventoryListener;
import org.opendaylight.controller.switchmanager.ISwitchManager;
import org.opendaylight.controller.switchmanager.ISwitchManagerAware;
import org.opendaylight.controller.topologymanager.ITopologyManager;
import org.opendaylight.controller.topologymanager.ITopologyManagerAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class Activator extends ComponentActivatorAbstractBase {
protected static final Logger logger = LoggerFactory
.getLogger(Activator.class);
/**
- * Function called when the activator starts just after some
- * initializations are done by the
- * ComponentActivatorAbstractBase.
+ * Function called when the activator starts just after some initializations
+ * are done by the ComponentActivatorAbstractBase.
*
*/
public void init() {
}
/**
- * Function called when the activator stops just before the
- * cleanup done by ComponentActivatorAbstractBase
+ * Function called when the activator stops just before the cleanup done by
+ * ComponentActivatorAbstractBase
*
*/
public void destroy() {
}
/**
- * Function that is used to communicate to dependency manager the
- * list of known implementations for services inside a container
+ * Function that is used to communicate to dependency manager the list of
+ * known implementations for services inside a container
*
*
* @return An array containing all the CLASS objects that will be
- * instantiated in order to get an fully working implementation
- * Object
+ * instantiated in order to get an fully working implementation
+ * Object
*/
public Object[] getImplementations() {
Object[] res = { HostTracker.class };
}
/**
- * Function that is called when configuration of the dependencies
- * is required.
+ * Function that is called when configuration of the dependencies is
+ * required.
*
- * @param c dependency manager Component object, used for
- * configuring the dependencies exported and imported
- * @param imp Implementation class that is being configured,
- * needed as long as the same routine can configure multiple
- * implementations
- * @param containerName The containerName being configured, this allow
- * also optional per-container different behavior if needed, usually
- * should not be the case though.
+ * @param c
+ * dependency manager Component object, used for configuring the
+ * dependencies exported and imported
+ * @param imp
+ * Implementation class that is being configured, needed as long
+ * as the same routine can configure multiple implementations
+ * @param containerName
+ * The containerName being configured, this allow also optional
+ * per-container different behavior if needed, usually should not
+ * be the case though.
*/
public void configureInstance(Component c, Object imp, String containerName) {
if (imp.equals(HostTracker.class)) {
// export the service
- c.setInterface(new String[] { ISwitchManagerAware.class.getName(),
- IInventoryListener.class.getName(),
- IfIptoHost.class.getName(), IfHostListener.class.getName(),
- ITopologyManagerAware.class.getName() }, null);
+ c.setInterface(
+ new String[] { ISwitchManagerAware.class.getName(),
+ IInventoryListener.class.getName(),
+ IfIptoHost.class.getName(),
+ IfHostListener.class.getName(),
+ ITopologyManagerAware.class.getName() }, null);
- c.add(createContainerServiceDependency(containerName).setService(
- ISwitchManager.class).setCallbacks("setSwitchManager",
- "unsetSwitchManager").setRequired(false));
- c.add(createContainerServiceDependency(containerName).setService(
- IClusterContainerServices.class).setCallbacks(
- "setClusterContainerService",
- "unsetClusterContainerService").setRequired(true));
- c.add(createContainerServiceDependency(containerName).setService(
- IHostFinder.class).setCallbacks("setArpHandler",
- "unsetArpHandler").setRequired(false));
- c.add(createContainerServiceDependency(containerName).setService(
- ITopologyManager.class).setCallbacks("setTopologyManager",
- "unsetTopologyManager").setRequired(false));
- c.add(createContainerServiceDependency(containerName).setService(
- IfNewHostNotify.class).setCallbacks("setnewHostNotify",
- "unsetnewHostNotify").setRequired(false));
+ c.add(createContainerServiceDependency(containerName)
+ .setService(ISwitchManager.class)
+ .setCallbacks("setSwitchManager", "unsetSwitchManager")
+ .setRequired(false));
+ c.add(createContainerServiceDependency(containerName)
+ .setService(IClusterContainerServices.class)
+ .setCallbacks("setClusterContainerService",
+ "unsetClusterContainerService").setRequired(true));
+ c.add(createContainerServiceDependency(containerName)
+ .setService(IHostFinder.class)
+ .setCallbacks("setArpHandler", "unsetArpHandler")
+ .setRequired(false));
+ c.add(createContainerServiceDependency(containerName)
+ .setService(ITopologyManager.class)
+ .setCallbacks("setTopologyManager", "unsetTopologyManager")
+ .setRequired(false));
+ c.add(createContainerServiceDependency(containerName)
+ .setService(IfNewHostNotify.class)
+ .setCallbacks("setnewHostNotify", "unsetnewHostNotify")
+ .setRequired(false));
}
}
/**
- * Method which tells how many Global implementations are
- * supported by the bundle. This way we can tune the number of
- * components created. This components will be created ONLY at the
- * time of bundle startup and will be destroyed only at time of
- * bundle destruction, this is the major difference with the
- * implementation retrieved via getImplementations where all of
- * them are assumed to be in a container !
+ * Method which tells how many Global implementations are supported by the
+ * bundle. This way we can tune the number of components created. This
+ * components will be created ONLY at the time of bundle startup and will be
+ * destroyed only at time of bundle destruction, this is the major
+ * difference with the implementation retrieved via getImplementations where
+ * all of them are assumed to be in a container !
*
*
- * @return The list of implementations the bundle will support,
- * in Global version
+ * @return The list of implementations the bundle will support, in Global
+ * version
*/
protected Object[] getGlobalImplementations() {
return null;
/**
* Configure the dependency for a given instance Global
*
- * @param c Component assigned for this instance, this will be
- * what will be used for configuration
- * @param imp implementation to be configured
- * @param containerName container on which the configuration happens
+ * @param c
+ * Component assigned for this instance, this will be what will
+ * be used for configuration
+ * @param imp
+ * implementation to be configured
+ * @param containerName
+ * container on which the configuration happens
*/
protected void configureGlobalInstance(Component c, Object imp) {
if (imp.equals(HostTracker.class)) {
import org.opendaylight.controller.clustering.services.CacheExistException;
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.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;
*/
removePendingARPFromList(i);
logger.debug("Host Removed from ARPPending List, IP: {}",
- networkAddr);
+ networkAddr);
return;
}
}
* there
*/
if (logger.isTraceEnabled()) {
- logger.trace(
- "ARP Probing ({}) for {}({})",
- new Object[] {
- arp_cntdown,
- host.getNetworkAddress().getHostAddress(),
- HexEncode.bytesToHexString(host
- .getDataLayerAddressBytes()) });
+ logger.trace(
+ "ARP Probing ({}) for {}({})",
+ new Object[] {
+ arp_cntdown,
+ host.getNetworkAddress()
+ .getHostAddress(),
+ HexEncode.bytesToHexString(host
+ .getDataLayerAddressBytes()) });
}
host.setArpSendCountDown(arp_cntdown);
hostFinder.probe(host);
for (Entry<InetAddress, HostNodeConnector> entry : hostsDB.entrySet()) {
HostNodeConnector host = entry.getValue();
if (host.getnodeConnector().equals(nodeConnector)) {
- logger.debug(" NodeConnector: {} is down, remove from Hosts_DB", nodeConnector);
+ logger.debug(
+ " NodeConnector: {} is down, remove from Hosts_DB",
+ nodeConnector);
removeKnownHost(entry.getKey());
notifyHostLearnedOrRemoved(host, false);
}
-
/*
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
*
-\r
/*\r
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
*\r
\r
package org.opendaylight.controller.hosttracker.internal;\r
\r
-\r
import java.net.InetAddress;\r
import java.net.UnknownHostException;\r
import java.util.concurrent.Future;\r
\r
+import junit.framework.TestCase;\r
+\r
import org.junit.Assert;\r
import org.junit.Test;\r
-\r
-import junit.framework.TestCase;\r
import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;\r
\r
public class HostTrackerTest extends TestCase {\r
\r
- @Test\r
- public void testHostTrackerCallable() throws UnknownHostException {\r
-\r
- HostTracker hostTracker = null;\r
- hostTracker = new HostTracker();\r
- Assert.assertFalse(hostTracker== null);\r
-\r
- InetAddress hostIP = InetAddress.getByName("192.168.0.8");\r
-\r
- HostTrackerCallable htCallable = new HostTrackerCallable (hostTracker, hostIP);\r
- Assert.assertTrue(htCallable.trackedHost.equals(hostIP));\r
- Assert.assertTrue(htCallable.hostTracker.equals(hostTracker));\r
-\r
- long count = htCallable.latch.getCount();\r
- htCallable.wakeup();\r
- Assert.assertTrue(htCallable.latch.getCount() == --count );\r
- }\r
-\r
-\r
-\r
- @Test\r
- public void testHostTracker() throws UnknownHostException {\r
- HostTracker hostTracker = null;\r
- hostTracker = new HostTracker();\r
- Assert.assertFalse(hostTracker== null);\r
-\r
- InetAddress hostIP_1 = InetAddress.getByName("192.168.0.8");\r
- InetAddress hostIP_2 = InetAddress.getByName("192.168.0.18");\r
- Future<HostNodeConnector> dschost = hostTracker.discoverHost(hostIP_1);\r
- dschost = hostTracker.discoverHost(hostIP_2);\r
- hostTracker.nonClusterObjectCreate();\r
- }\r
-\r
+ @Test\r
+ public void testHostTrackerCallable() throws UnknownHostException {\r
+\r
+ HostTracker hostTracker = null;\r
+ hostTracker = new HostTracker();\r
+ Assert.assertFalse(hostTracker == null);\r
+\r
+ InetAddress hostIP = InetAddress.getByName("192.168.0.8");\r
+\r
+ HostTrackerCallable htCallable = new HostTrackerCallable(hostTracker,\r
+ hostIP);\r
+ Assert.assertTrue(htCallable.trackedHost.equals(hostIP));\r
+ Assert.assertTrue(htCallable.hostTracker.equals(hostTracker));\r
+\r
+ long count = htCallable.latch.getCount();\r
+ htCallable.wakeup();\r
+ Assert.assertTrue(htCallable.latch.getCount() == --count);\r
+ }\r
+\r
+ @Test\r
+ public void testHostTracker() throws UnknownHostException {\r
+ HostTracker hostTracker = null;\r
+ hostTracker = new HostTracker();\r
+ Assert.assertFalse(hostTracker == null);\r
+\r
+ InetAddress hostIP_1 = InetAddress.getByName("192.168.0.8");\r
+ InetAddress hostIP_2 = InetAddress.getByName("192.168.0.18");\r
+ Future<HostNodeConnector> dschost = hostTracker.discoverHost(hostIP_1);\r
+ dschost = hostTracker.discoverHost(hostIP_2);\r
+ hostTracker.nonClusterObjectCreate();\r
+ }\r
\r
}\r
\r
@RunWith(PaxExam.class)\r
public class HostTrackerIT {\r
- private Logger log = LoggerFactory\r
- .getLogger(HostTrackerIT.class);\r
+ private Logger log = LoggerFactory.getLogger(HostTrackerIT.class);\r
// get the OSGI bundle context\r
@Inject\r
private BundleContext bc;\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">\r
+ <modelVersion>4.0.0</modelVersion>\r
+ <parent>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>commons.opendaylight</artifactId>\r
+ <version>1.4.0-SNAPSHOT</version>\r
+ <relativePath>../../commons/opendaylight</relativePath>\r
+ </parent>\r
+ <artifactId>hosttracker_new</artifactId>\r
+ <version>0.4.0-SNAPSHOT</version>\r
+ <packaging>bundle</packaging>\r
+\r
+ <build>\r
+ <plugins>\r
+ <plugin>\r
+ <groupId>org.apache.felix</groupId>\r
+ <artifactId>maven-bundle-plugin</artifactId>\r
+ <version>2.3.6</version>\r
+ <extensions>true</extensions>\r
+ <configuration>\r
+ <instructions>\r
+ <Export-Package>\r
+ org.opendaylight.controller.hosttracker_new,\r
+ org.opendaylight.controller.hosttracker_new.hostAware\r
+ </Export-Package>\r
+ <Import-Package>\r
+ org.opendaylight.controller.sal.core,\r
+ org.opendaylight.controller.sal.utils,\r
+ org.opendaylight.controller.topologymanager,\r
+ org.opendaylight.controller.sal.packet.address,\r
+ org.opendaylight.controller.switchmanager,\r
+ org.opendaylight.controller.clustering.services,\r
+ javax.xml.bind.annotation,\r
+ javax.xml.bind,\r
+ org.apache.felix.dm,\r
+ org.apache.commons.lang3.builder,\r
+ org.osgi.service.component,\r
+ org.slf4j,\r
+ org.eclipse.osgi.framework.console,\r
+ org.osgi.framework\r
+ </Import-Package>\r
+ </instructions>\r
+ <manifestLocation>${project.basedir}/META-INF</manifestLocation>\r
+ </configuration>\r
+ </plugin>\r
+ </plugins>\r
+ </build>\r
+ <dependencies>\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>topologymanager</artifactId>\r
+ <version>0.4.0-SNAPSHOT</version>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>switchmanager</artifactId>\r
+ <version>0.4.0-SNAPSHOT</version>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>clustering.services</artifactId>\r
+ <version>0.4.0-SNAPSHOT</version>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>sal</artifactId>\r
+ <version>0.5.0-SNAPSHOT</version>\r
+ </dependency>\r
+ </dependencies>\r
+</project>\r
--- /dev/null
+/*
+ * Copyright (c) 2011,2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker;
+
+import java.util.Date;
+
+import org.opendaylight.controller.sal.core.NodeConnector;
+
+/**
+ * An entity on the network is a visible trace of a device that corresponds to a
+ * packet received from a particular interface on the edge of a network, with a
+ * particular VLAN tag, and a particular MAC address, along with any other
+ * packet characteristics we might want to consider as helpful for
+ * disambiguating devices.
+ *
+ * Entities are the most basic element of devices; devices consist of one or
+ * more entities. Entities are immutable once created, except for the last seen
+ * timestamp.
+ *
+ * @author readams
+ *
+ */
+public class Entity implements Comparable<Entity> {
+ /**
+ * Timeout for computing {@link Entity#activeSince}.
+ *
+ * @see {@link Entity#activeSince}
+ */
+ protected static int ACTIVITY_TIMEOUT = 30000;
+
+ /**
+ * The MAC address associated with this entity
+ */
+ protected long macAddress;
+
+ /**
+ * The IP address associated with this entity, or null if no IP learned from
+ * the network observation associated with this entity
+ */
+ protected Integer ipv4Address;
+
+ /**
+ * The VLAN tag on this entity, or null if untagged
+ */
+ protected Short vlan;
+
+ /**
+ * The attachment point for this entity
+ */
+ NodeConnector port;
+
+ /**
+ * The last time we observed this entity on the network
+ */
+ protected Date lastSeenTimestamp;
+
+ /**
+ * The time between {@link Entity#activeSince} and
+ * {@link Entity#lastSeenTimestamp} is a period of activity for this entity
+ * where it was observed repeatedly. If, when the entity is observed, the is
+ * longer ago than the activity timeout, {@link Entity#lastSeenTimestamp}
+ * and {@link Entity#activeSince} will be set to the current time.
+ */
+ protected Date activeSince;
+
+ private int hashCode = 0;
+
+ // ************
+ // Constructors
+ // ************
+
+ /**
+ * Create a new entity
+ *
+ * @param macAddress
+ * @param vlan
+ * @param ipv4Address
+ * @param switchDPID
+ * @param switchPort
+ * @param lastSeenTimestamp
+ */
+ public Entity(long macAddress, Short vlan, Integer ipv4Address,
+ NodeConnector port, Date lastSeenTimestamp) {
+ this.macAddress = macAddress;
+ this.ipv4Address = ipv4Address;
+ this.vlan = vlan;
+ this.port = port;
+ this.lastSeenTimestamp = lastSeenTimestamp;
+ this.activeSince = lastSeenTimestamp;
+ }
+
+ // ***************
+ // Getters/Setters
+ // ***************
+
+ // @JsonSerialize(using=MACSerializer.class)
+ public long getMacAddress() {
+ return macAddress;
+ }
+
+ // @JsonSerialize(using=IPv4Serializer.class)
+ public Integer getIpv4Address() {
+ return ipv4Address;
+ }
+
+ public Short getVlan() {
+ return vlan;
+ }
+
+ public NodeConnector getPort() {
+ return port;
+ }
+
+ // @JsonIgnore
+ public boolean hasSwitchPort() {
+ return port != null;
+ }
+
+ public Date getLastSeenTimestamp() {
+ return lastSeenTimestamp;
+ }
+
+ /**
+ * Set the last seen timestamp and also update {@link Entity#activeSince} if
+ * appropriate
+ *
+ * @param lastSeenTimestamp
+ * the new last seen timestamp
+ * @see {@link Entity#activeSince}
+ */
+ public void setLastSeenTimestamp(Date lastSeenTimestamp) {
+ if (activeSince == null
+ || (activeSince.getTime() + ACTIVITY_TIMEOUT) < lastSeenTimestamp
+ .getTime())
+ this.activeSince = lastSeenTimestamp;
+ this.lastSeenTimestamp = lastSeenTimestamp;
+ }
+
+ public Date getActiveSince() {
+ return activeSince;
+ }
+
+ public void setActiveSince(Date activeSince) {
+ this.activeSince = activeSince;
+ }
+
+ @Override
+ public int hashCode() {
+ if (hashCode != 0)
+ return hashCode;
+ final int prime = 31;
+ hashCode = 1;
+ hashCode = prime * hashCode
+ + ((ipv4Address == null) ? 0 : ipv4Address.hashCode());
+ hashCode = prime * hashCode + (int) (macAddress ^ (macAddress >>> 32));
+ hashCode = prime * hashCode + ((port == null) ? 0 : port.hashCode());
+ hashCode = prime * hashCode + ((vlan == null) ? 0 : vlan.hashCode());
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Entity other = (Entity) obj;
+ if (ipv4Address == null) {
+ if (other.ipv4Address != null)
+ return false;
+ } else if (!ipv4Address.equals(other.ipv4Address))
+ return false;
+ if (macAddress != other.macAddress)
+ return false;
+ if (port == null) {
+ if (other.port != null)
+ return false;
+ } else if (!port.equals(other.port))
+ return false;
+ if (vlan == null) {
+ if (other.vlan != null)
+ return false;
+ } else if (!vlan.equals(other.vlan))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Entity [macAddress=" + macAddress + ", ipv4Address="
+ + ipv4Address + ", vlan=" + vlan + ", port=" + port + "]";
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Override
+ public int compareTo(Entity o) {
+ int r;
+ if (port == null)
+ r = o.port == null ? 0 : -1;
+ else if (o.port == null)
+ r = 1;
+ else {
+ // XXX - the node id is only defined as an object rather
+ // than something useful. We're just going to have to
+ // blindly cast to Comparable and hope it works.
+ Comparable switchId = (Comparable) port.getNode().getID();
+ Comparable oswitchId = (Comparable) o.port.getNode().getID();
+ r = switchId.compareTo(oswitchId);
+ if (r != 0)
+ return r;
+
+ Comparable portId = (Comparable) port.getID();
+ Comparable oportId = (Comparable) o.port.getID();
+ r = portId.compareTo(oportId);
+ }
+ return r;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2011,2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker;
+
+import java.util.Date;
+
+/**
+ * Represents an independent device on the network. A device consists of a set
+ * of entities, and all the information known about a given device comes only
+ * from merging all associated entities for that device.
+ *
+ * @author readams
+ */
+public interface IDevice {
+ /**
+ * Get the primary key for this device.
+ *
+ * @return the primary key
+ */
+ public Long getDeviceKey();
+
+ /**
+ * Get the MAC address of the device as a Long value.
+ *
+ * @return the MAC address for the device
+ */
+ public long getMACAddress();
+
+ /**
+ * Get the MAC address of the device as a String value.
+ *
+ * @return the MAC address for the device
+ */
+ public String getMACAddressString();
+
+ /**
+ * Get all unique VLAN IDs for the device. If the device has untagged
+ * entities, then the value -1 will be returned.
+ *
+ * @return an array containing all unique VLAN IDs for the device.
+ */
+ public Short[] getVlanId();
+
+ /**
+ * Get all unique IPv4 addresses associated with the device.
+ *
+ * @return an array containing the unique IPv4 addresses for the device.
+ */
+ public Integer[] getIPv4Addresses();
+
+ /**
+ * Get all unique attachment points associated with the device. This will
+ * not include any blocked attachment points.
+ *
+ * @return an array containing all unique attachment points for the device
+ */
+ public SwitchPort[] getAttachmentPoints();
+
+ /**
+ * Get all old attachment points associated with the device. this is used in
+ * host movement scenario.
+ *
+ * @return an array containing all unique old attachment points for the
+ * device
+ */
+ public SwitchPort[] getOldAP();
+
+ /**
+ * Get all unique attachment points associated with the device.
+ *
+ * @param includeError
+ * whether to include blocked attachment points. Blocked
+ * attachment points should not be used for forwarding, but could
+ * be useful to show to a user
+ * @return an array containing all unique attachment points for the device
+ */
+ public SwitchPort[] getAttachmentPoints(boolean includeError);
+
+ /**
+ * Returns all unique VLAN IDs for the device that were observed on the
+ * given switch port
+ *
+ * @param swp
+ * the switch port to query
+ * @return an array containing the unique VLAN IDs
+ */
+ public Short[] getSwitchPortVlanIds(SwitchPort swp);
+
+ /**
+ * Get the most recent timestamp for this device
+ *
+ * @return the last seen timestamp
+ */
+ public Date getLastSeen();
+
+ /**
+ * Get the entity class for the device.
+ *
+ * @return the entity class
+ * @see IEntityClassifierService
+ */
+ public IEntityClass getEntityClass();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2011 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker;
+
+import org.opendaylight.controller.sal.utils.IListener;
+
+/**
+ * Implementors of this interface can receive updates from DeviceManager about
+ * the state of devices under its control.
+ *
+ * @author David Erickson (daviderickson@cs.stanford.edu)
+ */
+public interface IDeviceListener extends IListener<String> {
+ /**
+ * Called when a new Device is found
+ *
+ * @param device
+ * the device that changed
+ */
+ public void deviceAdded(IDevice device);
+
+ /**
+ * Called when a Device is removed, this typically occurs when the port the
+ * Device is attached to goes down, or the switch it is attached to is
+ * removed.
+ *
+ * @param device
+ * the device that changed
+ */
+ public void deviceRemoved(IDevice device);
+
+ /**
+ * Called when a Device has moved to a new location on the network. Note
+ * that either the switch or the port or both has changed.
+ *
+ * @param device
+ * the device that changed
+ */
+ public void deviceMoved(IDevice device);
+
+ /**
+ * Called when a network address has been added or remove from a device
+ *
+ * @param device
+ * the device that changed
+ */
+ public void deviceIPV4AddrChanged(IDevice device);
+
+ /**
+ * Called when a VLAN tag for the device has been added or removed
+ *
+ * @param device
+ * the device that changed
+ */
+ public void deviceVlanChanged(IDevice device);
+}
--- /dev/null
+/*
+ * Copyright (c) 2011,2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker;
+
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.osgi.service.device.Device;
+
+/**
+ * Device manager allows interacting with devices on the network. Note that
+ * under normal circumstances, {@link Device} objects should be retrieved from
+ * the {@link FloodlightContext} rather than from {@link IDeviceManager}.
+ */
+public interface IDeviceService {
+
+ /**
+ * Fields used in devices for indexes and querying
+ *
+ * @see IDeviceService#addIndex
+ */
+ enum DeviceField {
+ MAC, IPV4, VLAN, SWITCHPORT
+ }
+
+ /**
+ * The source device for the current packet-in, if applicable.
+ */
+ // public static final String CONTEXT_SRC_DEVICE =
+ // "net.floodlightcontroller.devicemanager.srcDevice";
+
+ /**
+ * The destination device for the current packet-in, if applicable.
+ */
+ // public static final String CONTEXT_DST_DEVICE =
+ // / "net.floodlightcontroller.devicemanager.dstDevice";
+
+ /**
+ * The original destination device for the current packet-in
+ */
+ // public static final String CONTEXT_ORIG_DST_DEVICE =
+ // "net.floodlightcontroller.devicemanager.origDstDevice";
+
+ /**
+ * A FloodlightContextStore object that can be used to interact with the
+ * FloodlightContext information created by BVS manager.
+ */
+ // public static final FloodlightContextStore<IDevice> fcStore =
+ // new FloodlightContextStore<IDevice>();
+
+ /**
+ * Get the device with the given device key.
+ *
+ * @param deviceKey
+ * the key to search for
+ * @return the device associated with the key, or null if no such device
+ * @see IDevice#getDeviceKey()
+ */
+ public IDevice getDevice(Long deviceKey);
+
+ /**
+ * Search for a device exactly matching the provided device fields. This is
+ * the same lookup process that is used for packet_in processing and device
+ * learning. Thus, findDevice() can be used to match flow entries from
+ * switches to devices. Only the key fields as defined by the
+ * {@link IEntityClassifierService} will be important in this search. All
+ * key fields MUST be supplied.
+ *
+ * {@link queryDevices()} might be more appropriate!
+ *
+ * @param macAddress
+ * The MAC address
+ * @param vlan
+ * the VLAN. Null means no VLAN and is valid even if VLAN is a
+ * key field.
+ * @param ipv4Address
+ * the ipv4 address
+ * @param port
+ * the node connector
+ * @return an {@link IDevice} or null if no device is found.
+ * @see IDeviceManager#setEntityClassifier(IEntityClassifierService)
+ * @throws IllegalArgumentException
+ * if not all key fields of the current
+ * {@link IEntityClassifierService} are specified.
+ */
+ public IDevice findDevice(long macAddress, Short vlan, Integer ipv4Address,
+ NodeConnector port) throws IllegalArgumentException;
+
+ /**
+ * Get a destination device using entity fields that corresponds with the
+ * given source device. The source device is important since there could be
+ * ambiguity in the destination device without the attachment point
+ * information. Search for a device in a given entity class. This is the
+ * same as the lookup process for destination devices.
+ *
+ * Only the key fields as defined by the reference entity class will be
+ * important in this search. All key fields MUST be supplied.
+ *
+ * @param entityClass
+ * The entity class in which to perform the lookup.
+ * @param macAddress
+ * The MAC address for the destination
+ * @param vlan
+ * the VLAN if available
+ * @param ipv4Address
+ * The IP address if available.
+ * @return an {@link IDevice} or null if no device is found.
+ * @see IDeviceService#findDevice(long, Short, Integer, Long, Integer)
+ * @throws IllegalArgumentException
+ * if not all key fields of the source's {@link IEntityClass}
+ * are specified.
+ */
+ public IDevice findClassDevice(IEntityClass entityClass, long macAddress,
+ Short vlan, Integer ipv4Address) throws IllegalArgumentException;
+
+ /**
+ * Get an unmodifiable collection view over all devices currently known.
+ *
+ * @return the collection of all devices
+ */
+ public Collection<? extends IDevice> getAllDevices();
+
+ /**
+ * Create an index over a set of fields. This allows efficient lookup of
+ * devices when querying using the indexed set of specified fields. The
+ * index must be registered before any device learning takes place, or it
+ * may be incomplete. It's OK if this is called multiple times with the same
+ * fields; only one index will be created for each unique set of fields.
+ *
+ * @param perClass
+ * set to true if the index should be maintained for each entity
+ * class separately.
+ * @param keyFields
+ * the set of fields on which to index
+ */
+ public void addIndex(boolean perClass, EnumSet<DeviceField> keyFields);
+
+ /**
+ * Find devices that match the provided query. Any fields that are null will
+ * not be included in the query. If there is an index for the query, then it
+ * will be performed efficiently using the index. Otherwise, there will be a
+ * full scan of the device list.
+ *
+ * @param macAddress
+ * The MAC address
+ * @param vlan
+ * the VLAN
+ * @param ipv4Address
+ * the ipv4 address
+ * @param port
+ * the switch port
+ * @return an iterator over a set of devices matching the query
+ * @see IDeviceService#queryClassDevices(IEntityClass, Long, Short, Integer,
+ * Long, Integer)
+ */
+ public Iterator<? extends IDevice> queryDevices(Long macAddress,
+ Short vlan, Integer ipv4Address, NodeConnector port);
+
+ /**
+ * Find devices that match the provided query. Only the index for the
+ * specified class will be searched. Any fields that are null will not be
+ * included in the query. If there is an index for the query, then it will
+ * be performed efficiently using the index. Otherwise, there will be a full
+ * scan of the device list.
+ *
+ * @param entityClass
+ * The entity class in which to perform the query
+ * @param macAddress
+ * The MAC address
+ * @param vlan
+ * the VLAN
+ * @param ipv4Address
+ * the ipv4 address
+ * @param port
+ * the switch port
+ * @return an iterator over a set of devices matching the query
+ * @see IDeviceService#queryClassDevices(Long, Short, Integer, Long,
+ * Integer)
+ */
+ public Iterator<? extends IDevice> queryClassDevices(
+ IEntityClass entityClass, Long macAddress, Short vlan,
+ Integer ipv4Address, NodeConnector port);
+
+ /**
+ * Adds a listener to listen for IDeviceManagerServices notifications
+ *
+ * @param listener
+ * The listener that wants the notifications
+ * @param type
+ * The type of the listener
+ */
+ public void addListener(IDeviceListener listener);
+
+ /**
+ * Specify points in the network where attachment points are not to be
+ * learned.
+ *
+ * @param sw
+ * @param port
+ */
+ public void addSuppressAPs(NodeConnector port);
+
+ public void removeSuppressAPs(NodeConnector port);
+
+ public Set<SwitchPort> getSuppressAPs();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2011,2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker;
+
+import java.util.EnumSet;
+
+import org.opendaylight.controller.hosttracker.IDeviceService.DeviceField;
+import org.osgi.service.device.Device;
+
+/**
+ * Entities within an entity class are grouped into {@link Device} objects based
+ * on the {@link IEntityClass}, and the key fields specified by the entity
+ * class. A set of entities are considered to be the same device if and only if
+ * they belong to the same entity class and they match on all key fields for
+ * that entity class. A field is effectively wildcarded by not including it in
+ * the list of key fields returned by {@link IEntityClassifierService} and/or
+ * {@link IEntityClass}.
+ *
+ * Note that if you're not using static objects, you'll need to override
+ * {@link Object#equals(Object)} and {@link Object#hashCode()}.
+ *
+ * @author readams
+ *
+ */
+public interface IEntityClass {
+ /**
+ * Return the set of key fields for this entity class. Entities belonging to
+ * this class that differ in fields not included in this collection will be
+ * considered the same device. The key fields for an entity class must not
+ * change unless associated with a flush of that entity class.
+ *
+ * @return a set containing the fields that should not be wildcarded. May be
+ * null to indicate that all fields are key fields.
+ */
+ EnumSet<DeviceField> getKeyFields();
+
+ /**
+ * Returns a user-friendly, unique name for this EntityClass
+ *
+ * @return the name of the entity class
+ */
+ String getName();
+}
--- /dev/null
+/*
+ * Copyright (c) 2011 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker;
+
+import java.util.Set;
+
+/**
+ * Implementors of this interface can receive updates from the Entity Classifier
+ * about the changes to entity Classes.
+ *
+ * @author Ananth Suryanarayana (Ananth.Suryanarayana@bigswitch.com)
+ */
+public interface IEntityClassListener {
+
+ /**
+ * Process entity classes change event.
+ *
+ * @param entityClassNames
+ * Set of entity classes changed
+ */
+ public void entityClassChanged(Set<String> entityClassNames);
+}
--- /dev/null
+/*
+ * Copyright (c) 2011,2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker;
+
+import java.util.Collection;
+import java.util.EnumSet;
+
+import org.opendaylight.controller.hosttracker.IDeviceService.DeviceField;
+
+/**
+ * A component that wishes to participate in entity classification needs to
+ * implement the IEntityClassifier interface, and register with the Device
+ * Manager as an entity classifier. An entity is classified by the classifier
+ * into an {@link IEntityClass}
+ *
+ * @author readams
+ */
+public interface IEntityClassifierService {
+ /**
+ * Classify the given entity into an IEntityClass. It is important that the
+ * key fields returned by {@link IEntityClassifierService#getKeyFields()} be
+ * sufficient for classifying entities. That is, if two entities are
+ * identical except for a field that is not a key field, they must be
+ * assigned the same class. Furthermore, entity classification must be
+ * transitive: For all entities x, y, z, if x and y belong to a class c, and
+ * y and z belong class c, then x and z must belong to class c.
+ *
+ * @param entity
+ * the entity to classify
+ * @return the IEntityClass resulting from the classification.
+ * @see IEntityClassifierService#getKeyFields()
+ */
+ IEntityClass classifyEntity(Entity entity);
+
+ /**
+ * Return the most general list of fields that should be used as key fields.
+ * If devices differ in any fields not listed here, they can never be
+ * considered a different device by any {@link IEntityClass} returned by
+ * {@link IEntityClassifierService#classifyEntity}. The key fields for an
+ * entity classifier must not change unless associated with a flush of all
+ * entity state. The list of key fields must be the union of all key fields
+ * that could be returned by {@link IEntityClass#getKeyFields()}.
+ *
+ * @return a set containing the fields that should not be wildcarded. May be
+ * null to indicate that all fields are key fields.
+ * @see {@link IEntityClass#getKeyFields()}
+ * @see {@link IEntityClassifierService#classifyEntity}
+ */
+ EnumSet<DeviceField> getKeyFields();
+
+ /**
+ * Reclassify the given entity into a class. When reclassifying entities, it
+ * can be helpful to take into account the current classification either as
+ * an optimization or to allow flushing any cached state tied to the key for
+ * that device. The entity will be assigned to a new device with a new
+ * object if the entity class returned is different from the entity class
+ * for curDevice.
+ *
+ * <p>
+ * Note that you must take steps to ensure you always return classes in some
+ * consistent ordering.
+ *
+ * @param curDevice
+ * the device currently associated with the entity
+ * @param entity
+ * the entity to reclassify
+ * @return the IEntityClass resulting from the classification
+ */
+ IEntityClass reclassifyEntity(IDevice curDevice, Entity entity);
+
+ /**
+ * Once reclassification is complete for a device, this method will be
+ * called. If any entities within the device changed their classification,
+ * it will split into one or more new devices for each of the entities. If
+ * two devices are merged because of a reclassification, then this will be
+ * called on each of the devices, with the same device in the newDevices
+ * collection.
+ *
+ * @param oldDevice
+ * the original device object
+ * @param newDevices
+ * all the new devices derived from the entities of the old
+ * device. If null, the old device was unchanged.
+ */
+ void deviceUpdate(IDevice oldDevice,
+ Collection<? extends IDevice> newDevices);
+
+ /**
+ * Adds a listener to listen for IEntityClassifierServices notifications
+ *
+ * @param listener
+ * The listener that wants the notifications
+ */
+ public void addListener(IEntityClassListener listener);
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.hosttracker;
+
+import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
+
+/**
+ * This interface defines the method to notify detected Host on the network. The
+ * information includes Host's IP address, MAC address, switch ID, port, and
+ * VLAN.
+ *
+ */
+
+public interface IfHostListener {
+ /**
+ * Learns new Hosts. Called by ArpHandler and implemented in
+ * HostTracker.java. If a Host is learned for the first time then adds it to
+ * the local database and informs other applications of coming up a new
+ * Host. For the hosts which it has already learned, it refreshes them.
+ *
+ * @param host
+ * Host info encapsulated in HostNodeConnector class
+ */
+ public void hostListener(HostNodeConnector host);
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.hosttracker;
+
+import java.net.InetAddress;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.utils.Status;
+
+/**
+ * This interface defines the methods to retrieve information about learned
+ * Hosts. Also provides methods to statically add/remove Hosts from the local
+ * database.
+ *
+ */
+
+public interface IfIptoHost {
+ /**
+ * Applications call this interface methods to determine IP address to MAC
+ * binding and its connectivity to an OpenFlow switch in term of Node, Port,
+ * and VLAN. These bindings are learned dynamically as well as can be added
+ * statically through Northbound APIs. If a binding is unknown, then an ARP
+ * request is initiated immediately to discover the host.
+ *
+ * @param networkAddress
+ * IP Address of the Host encapsulated in class InetAddress
+ * @return {@link org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector}
+ * Class that contains the Host info such as its MAC address, Switch
+ * ID, port, VLAN. If Host is not found, returns NULL
+ */
+ public HostNodeConnector hostFind(InetAddress networkAddress);
+
+ /**
+ * Checks the local Host Database to see if a Host has been learned for a
+ * given IP address.
+ *
+ * @param networkAddress
+ * IP Address of the Host encapsulated in class InetAddress
+ * @return {@link org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector}
+ * Class that contains the Host info such as its MAC address, Switch
+ * ID, port, VLAN. If Host is not found, returns NULL
+ *
+ */
+ public HostNodeConnector hostQuery(InetAddress networkAddress);
+
+ /**
+ * Initiates an immediate discovery of the Host for a given IP address. This
+ * provides for the calling applications to block on the host discovery.
+ *
+ * @param networkAddress
+ * IP address encapsulated in InetAddress class
+ * @return Future
+ * {@link org.opendaylight.controller.hosttracker.HostTrackerCallable}
+ */
+ public Future<HostNodeConnector> discoverHost(InetAddress networkAddress);
+
+ /**
+ * Returns the Network Hierarchy for a given Host. This API is typically
+ * used by applications like Hadoop for Rack Awareness functionality.
+ *
+ * @param IP
+ * address of the Host encapsulated in InetAddress class
+ * @return List of String ArrayList containing the Hierarchies.
+ */
+ public List<List<String>> getHostNetworkHierarchy(InetAddress hostAddress);
+
+ /**
+ * Returns all the the Hosts either learned dynamically or added statically
+ * via Northbound APIs.
+ *
+ * @return Set of
+ * {@link org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector}
+ * . Class that contains the Host info such as its MAC address,
+ * Switch ID, port, VLAN.
+ */
+ public Set<HostNodeConnector> getAllHosts();
+
+ /**
+ * Returns all the "Active Hosts" learned "Statically" via Northbound APIs.
+ * These Hosts are categorized as "Active" because the Switch and Port they
+ * are connected to, are in up state.
+ *
+ * @return Set of
+ * {@link org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector}
+ * . Class that contains the Host info such as MAC address, Switch
+ * ID, port, VLAN. If Host is not found, returns NULL
+ */
+ public Set<HostNodeConnector> getActiveStaticHosts();
+
+ /**
+ * Returns all the "Inactive Hosts" learned "Statically" via Northbound
+ * APIs. These Hosts are categorized as "Inactive" because either the Switch
+ * or the Port they are connected to, is in down state.
+ *
+ * @return Set of HostNodeConnector
+ * {@link org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector}
+ * . HostNodeConnector is Class that contains the Host info such as
+ * its MAC address, OpenFlowNode ID, port, VLAN.
+ */
+ public Set<HostNodeConnector> getInactiveStaticHosts();
+
+ /**
+ * Hosts can be learned dynamically or added statically. This method allows
+ * the addition of a Host to the local database statically.
+ *
+ * @param networkAddress
+ * IP Address of the Host
+ * @param dataLayerAddress
+ * MAC Address of the Host
+ * @param nc
+ * NodeConnector to which the host is attached
+ * @param vlan
+ * VLAN the host belongs to
+ * @return The status object as described in {@code Status} indicating the
+ * result of this action.
+ */
+ public Status addStaticHost(String networkAddress, String dataLayerAddress,
+ NodeConnector nc, String vlan);
+
+ /**
+ * Allows the deletion of statically learned Host
+ *
+ * @param networkAddress
+ * @return The status object as described in {@code Status} indicating the
+ * result of this action.
+ */
+ public Status removeStaticHost(String networkAddress);
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.hosttracker;
+
+import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
+
+/**
+ * This Interface defines the methods for client applications of Host Tracker to
+ * get notifications when a new host is learned or existing host is removed from
+ * the network.
+ *
+ */
+public interface IfNewHostNotify {
+ /**
+ * Notifies the HostTracker Clients that a new Host has been learned
+ *
+ * @param host
+ * Host Info encapsulated in HostNodeConnector class
+ */
+ public void notifyHTClient(HostNodeConnector host);
+
+ /**
+ * Notifies the HostTracker Clients that a Host which was learned in the
+ * past has been removed either due to switch/port down event or due to ARP
+ * Aging
+ *
+ * @param host
+ * Host Info encapsulated in HostNodeConnector class
+ */
+ public void notifyHTClientHostRemoved(HostNodeConnector host);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker;
+
+import org.opendaylight.controller.sal.core.NodeConnector;
+
+/**
+ * A simple switch DPID/port pair This class is immutable
+ *
+ * @author readams
+ *
+ */
+public class SwitchPort {
+ public enum ErrorStatus {
+ DUPLICATE_DEVICE("duplicate-device");
+
+ private String value;
+
+ ErrorStatus(String v) {
+ value = v;
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ public static ErrorStatus fromString(String str) {
+ for (ErrorStatus m : ErrorStatus.values()) {
+ if (m.value.equals(str)) {
+ return m;
+ }
+ }
+ return null;
+ }
+ }
+
+ private final NodeConnector port;
+ private final ErrorStatus errorStatus;
+
+ /**
+ * Simple constructor
+ *
+ * @param switchDPID
+ * the dpid
+ * @param port
+ * the port
+ * @param errorStatus
+ * any error status for the switch port
+ */
+ public SwitchPort(NodeConnector port, ErrorStatus errorStatus) {
+ super();
+ this.port = port;
+ this.errorStatus = errorStatus;
+ }
+
+ /**
+ * Simple constructor
+ *
+ * @param switchDPID
+ * the dpid
+ * @param port
+ * the port
+ */
+ public SwitchPort(NodeConnector port) {
+ super();
+ this.port = port;
+ this.errorStatus = null;
+ }
+
+ // ***************
+ // Getters/Setters
+ // ***************
+
+ public NodeConnector getPort() {
+ return port;
+ }
+
+ public ErrorStatus getErrorStatus() {
+ return errorStatus;
+ }
+
+ // ******
+ // Object
+ // ******
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((errorStatus == null) ? 0 : errorStatus.hashCode());
+ result = prime * result + ((port == null) ? 0 : port.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ SwitchPort other = (SwitchPort) obj;
+ if (errorStatus != other.errorStatus)
+ return false;
+ if (port == null) {
+ if (other.port != null)
+ return false;
+ } else if (!port.equals(other.port))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "SwitchPort [port=" + port + ", errorStatus=" + errorStatus
+ + "]";
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.hosttracker.hostAware;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.Arrays;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
+import org.opendaylight.controller.sal.core.ConstructionException;
+import org.opendaylight.controller.sal.core.Host;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.packet.address.EthernetAddress;
+
+@XmlRootElement(name = "host")
+@XmlAccessorType(XmlAccessType.NONE)
+public class HostNodeConnector extends Host {
+ private static final long serialVersionUID = 1L;
+ @XmlElement
+ private NodeConnector nodeConnector;
+ @XmlElement
+ private short vlan;
+ @XmlElement
+ private boolean staticHost;
+ private transient short arpSendCountDown;
+
+ /**
+ * Private constructor used for JAXB mapping
+ */
+ @SuppressWarnings("unused")
+ private HostNodeConnector() {
+ }
+
+ public HostNodeConnector(InetAddress ip) throws ConstructionException {
+ this(ip, null);
+ }
+
+ public HostNodeConnector(InetAddress ip, NodeConnector nc)
+ throws ConstructionException {
+ this(new EthernetAddress(new byte[] { (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }), ip, nc,
+ (short) 0);
+ }
+
+ public HostNodeConnector(byte[] mac, InetAddress ip, NodeConnector nc,
+ short vlan) throws ConstructionException {
+ this(new EthernetAddress(mac.clone()), ip, nc, vlan);
+ }
+
+ public HostNodeConnector(EthernetAddress eaddr, InetAddress naddr,
+ NodeConnector nc, short vlan) throws ConstructionException {
+ super(eaddr, naddr);
+ this.nodeConnector = nc;
+ this.vlan = vlan;
+ }
+
+ /**
+ * @return the NodeConnector
+ */
+ public NodeConnector getnodeConnector() {
+ return this.nodeConnector;
+ }
+
+ /**
+ * @return the Node
+ */
+ public Node getnodeconnectorNode() {
+ return this.nodeConnector.getNode();
+ }
+
+ /**
+ * @return the NodeId
+ */
+ public Long getnodeconnectornodeId() {
+ return (Long) this.nodeConnector.getNode().getID();
+ }
+
+ /**
+ * @return the port
+ */
+ public Short getnodeconnectorportId() {
+ return (Short) this.nodeConnector.getID();
+ }
+
+ /**
+ * @return the DataLayerAddress
+ */
+ public byte[] getDataLayerAddressBytes() {
+ byte[] macaddr = null;
+ if (getDataLayerAddress() instanceof EthernetAddress) {
+ EthernetAddress e = (EthernetAddress) getDataLayerAddress();
+ macaddr = e.getValue();
+ }
+ return macaddr;
+ }
+
+ /**
+ * @return the vlan
+ */
+ public short getVlan() {
+ return this.vlan;
+ }
+
+ public boolean isStaticHost() {
+ return this.staticHost;
+ }
+
+ public HostNodeConnector setStaticHost(boolean statically_learned) {
+ this.staticHost = statically_learned;
+ return this;
+ }
+
+ public HostNodeConnector initArpSendCountDown() {
+ this.arpSendCountDown = 24;
+ return this;
+ }
+
+ public short getArpSendCountDown() {
+ return (this.arpSendCountDown);
+ }
+
+ public HostNodeConnector setArpSendCountDown(short cntdown) {
+ this.arpSendCountDown = cntdown;
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result
+ + ((nodeConnector == null) ? 0 : nodeConnector.hashCode());
+ result = prime * result + (staticHost ? 1231 : 1237);
+ result = prime * result + vlan;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ HostNodeConnector other = (HostNodeConnector) obj;
+ if (nodeConnector == null) {
+ if (other.nodeConnector != null)
+ return false;
+ } else if (!nodeConnector.equals(other.nodeConnector))
+ return false;
+ if (staticHost != other.staticHost)
+ return false;
+ if (vlan != other.vlan)
+ return false;
+ return true;
+ }
+
+ public boolean equalsByIP(InetAddress networkAddress) {
+ return (this.getNetworkAddress().equals(networkAddress));
+ }
+
+ public boolean isRewriteEnabled() {
+ byte[] emptyArray = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00 };
+ byte[] macaddr = null;
+ if (getDataLayerAddress() instanceof EthernetAddress) {
+ EthernetAddress e = (EthernetAddress) getDataLayerAddress();
+ macaddr = e.getValue();
+ }
+ if (macaddr == null)
+ return false;
+ return !Arrays.equals(emptyArray, macaddr);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "HostNodeConnector[" + ReflectionToStringBuilder.toString(this)
+ + "]";
+ }
+
+ public boolean isV4Host() {
+ return (getNetworkAddress() instanceof Inet4Address);
+ }
+
+ public boolean isV6Host() {
+ return (getNetworkAddress() instanceof Inet6Address);
+ }
+
+ public String toJson() {
+ return "{\"host\":\"" + super.toString() + "\", " + "\"vlan\":\""
+ + String.valueOf(vlan) + "\",\"NodeConnector\":\""
+ + nodeConnector.toString() + "\"," + "\"static\":\""
+ + String.valueOf(isStaticHost()) + "\"}";
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.hosttracker.hostAware;
+
+import java.net.InetAddress;
+
+/**
+ * This Interface defines the methods to trigger the discovery of a Host and to
+ * probe if a learned Host is still in the network.
+ *
+ *
+ *
+ */
+public interface IHostFinder {
+ /**
+ * This method initiates the discovery of a host based on its IP address.
+ * This is triggered by query of an application to the HostTracker. The
+ * requested IP address doesn't exist in the local database at this point.
+ *
+ * @param networkAddress
+ * IP Address encapsulated in InetAddress class
+ *
+ */
+ public void find(InetAddress networkAddress);
+
+ /**
+ * This method is called by HostTracker to see if a learned Host is still in
+ * the network. Used mostly for ARP Aging.
+ *
+ * @param host
+ * The Host that needs to be probed
+ */
+ public void probe(HostNodeConnector host);
+}
--- /dev/null
+/*\r
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+\r
+package org.opendaylight.controller.hosttracker.hostAware;\r
+\r
+import java.net.InetAddress;\r
+import java.net.UnknownHostException;\r
+\r
+import org.junit.Assert;\r
+import org.junit.Test;\r
+\r
+import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;\r
+import org.opendaylight.controller.sal.core.ConstructionException;\r
+import org.opendaylight.controller.sal.core.Node;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.opendaylight.controller.sal.packet.address.EthernetAddress;\r
+\r
+import org.opendaylight.controller.sal.utils.NodeConnectorCreator;\r
+import org.opendaylight.controller.sal.core.NodeConnector;\r
+import org.opendaylight.controller.sal.utils.NodeCreator;\r
+\r
+public class HostNodeConnectorTest extends TestCase {\r
+\r
+ @Test\r
+ public void testHostNodeConnector() throws UnknownHostException {\r
+ HostNodeConnector hostnodeconnector_1, hostnodeconnector_2, hostnodeconnector_3;\r
+ InetAddress hostIP_1 = InetAddress.getByName("192.168.0.8");\r
+ InetAddress hostIP_2 = InetAddress\r
+ .getByName("2001:420:281:1004:e123:e688:d655:a1b0");\r
+ InetAddress hostIP_3 = InetAddress.getByName("192.168.0.28");\r
+ byte[] hostMAC_2 = new byte[] { (byte) 0x11, (byte) 0x22, (byte) 0x33,\r
+ (byte) 0x22, (byte) 0x22, (byte) 0x22 };\r
+ byte[] hostMAC_3 = new byte[] { (byte) 0x11, (byte) 0x22, (byte) 0x33,\r
+ (byte) 0x33, (byte) 0x33, (byte) 0x33 };\r
+\r
+ Node node = NodeCreator.createOFNode(1L);\r
+ NodeConnector nc1 = NodeConnectorCreator.createOFNodeConnector(\r
+ (short) 2, node);\r
+ NodeConnector nc2 = NodeConnectorCreator.createOFNodeConnector(\r
+ (short) 1, node);\r
+\r
+ try {\r
+ hostnodeconnector_1 = new HostNodeConnector(hostIP_1);\r
+ Assert.assertTrue(hostnodeconnector_1.equalsByIP(hostIP_1));\r
+ Assert.assertTrue(hostnodeconnector_1.isV4Host());\r
+ Assert.assertTrue(hostnodeconnector_1.equalsByIP(hostIP_1));\r
+ } catch (ConstructionException e) {\r
+ Assert.assertTrue(false);\r
+ }\r
+\r
+ try {\r
+ hostnodeconnector_2 = new HostNodeConnector(hostMAC_2, hostIP_2,\r
+ nc1, (short) 2);\r
+ Assert.assertTrue(hostnodeconnector_2.isV6Host());\r
+ Assert.assertTrue(hostnodeconnector_2.getnodeConnector()\r
+ .equals(nc1));\r
+ Assert.assertTrue(hostnodeconnector_2.getnodeconnectorNode()\r
+ .equals(node));\r
+ Assert.assertTrue(node.getID().equals(\r
+ hostnodeconnector_2.getnodeconnectornodeId()));\r
+ Assert.assertTrue(hostnodeconnector_2.getnodeconnectorportId()\r
+ .equals((short) 2));\r
+ } catch (ConstructionException e) {\r
+ Assert.assertTrue(false);\r
+ }\r
+\r
+ try {\r
+ hostnodeconnector_3 = new HostNodeConnector(new EthernetAddress(\r
+ hostMAC_3), hostIP_3, nc2, (short) 3);\r
+ byte[] hostMAC_3_rb = hostnodeconnector_3\r
+ .getDataLayerAddressBytes();\r
+ HostNodeConnector hostnodeconnector_3rb = new HostNodeConnector(\r
+ new EthernetAddress(hostMAC_3_rb), hostIP_3, nc2, (short) 3);\r
+ Assert.assertTrue(hostnodeconnector_3.equals(hostnodeconnector_3rb));\r
+\r
+ Assert.assertTrue(hostnodeconnector_3.getVlan() == (short) 3);\r
+\r
+ hostnodeconnector_3.setStaticHost(true);\r
+ Assert.assertTrue(hostnodeconnector_3.isStaticHost());\r
+\r
+ Assert.assertTrue(hostnodeconnector_3.isRewriteEnabled());\r
+\r
+ hostnodeconnector_3.initArpSendCountDown().setArpSendCountDown(\r
+ (short) 10);\r
+ Assert.assertTrue(hostnodeconnector_3.getArpSendCountDown() == (short) 10);\r
+\r
+ } catch (ConstructionException e) {\r
+ Assert.assertTrue(false);\r
+ }\r
+\r
+ }\r
+\r
+}\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+ <artifactId>hosttracker_new.implementation</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <properties>
+ <!-- Sonar properties using jacoco to retrieve integration test results -->
+ <sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
+ <sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
+ <sonar.jacoco.Reportpath>target/jacoco.exec</sonar.jacoco.Reportpath>
+ <sonar.jacoco.itReportPath>target/jacoco-it.exec</sonar.jacoco.itReportPath>
+ <sonar.language>java</sonar.language>
+ </properties>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <version>0.5.3.201107060350</version>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Export-Package>
+ </Export-Package>
+ <Import-Package>
+ org.opendaylight.controller.sal.core,
+ org.opendaylight.controller.sal.utils,
+ org.opendaylight.controller.sal.topology,
+ org.opendaylight.controller.sal.packet,
+ org.opendaylight.controller.hosttracker,
+ org.opendaylight.controller.topologymanager,
+ org.opendaylight.controller.sal.packet.address,
+ org.opendaylight.controller.switchmanager,
+ org.opendaylight.controller.clustering.services,
+ org.opendaylight.controller.hosttracker_new.hostAware,
+ javax.xml.bind.annotation,
+ javax.xml.bind,
+ org.apache.felix.dm,
+ org.apache.commons.lang3.builder,
+ org.osgi.service.component,
+ org.slf4j,
+ org.eclipse.osgi.framework.console,
+ org.osgi.framework
+ </Import-Package>
+ <Bundle-Activator>
+ org.opendaylight.controller.hosttracker.internal.Activator
+ </Bundle-Activator>
+ <Service-Component>
+ </Service-Component>
+ </instructions>
+ <manifestLocation>${project.basedir}/META-INF</manifestLocation>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <configuration>
+ <includes>org.opendaylight.controller.*</includes>
+ </configuration>
+ <executions>
+ <execution>
+ <id>pre-test</id>
+ <goals>
+ <goal>prepare-agent</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>post-test</id>
+ <phase>test</phase>
+ <goals>
+ <goal>report</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>topologymanager</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>switchmanager</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.services</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>hosttracker_new</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.hosttracker.internal;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.hosttracker.IDeviceService;
+import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
+import org.opendaylight.controller.sal.packet.IDataPacketService;
+import org.opendaylight.controller.sal.packet.IListenDataPacket;
+import org.opendaylight.controller.switchmanager.ISwitchManager;
+import org.opendaylight.controller.topologymanager.ITopologyManager;
+import org.opendaylight.controller.topologymanager.ITopologyManagerAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Activator extends ComponentActivatorAbstractBase {
+ protected static final Logger logger = LoggerFactory
+ .getLogger(Activator.class);
+
+ @Override
+ protected void init() {
+
+ }
+
+ @Override
+ protected void destroy() {
+
+ }
+
+ /**
+ * Function that is used to communicate to dependency manager the list of
+ * known implementations for services inside a container
+ *
+ *
+ * @return An array containing all the CLASS objects that will be
+ * instantiated in order to get an fully working implementation
+ * Object
+ */
+ @Override
+ public Object[] getImplementations() {
+ Object[] res = { DeviceManagerImpl.class };
+ return res;
+ }
+
+ /**
+ * Function that is called when configuration of the dependencies is
+ * required.
+ *
+ * @param c
+ * dependency manager Component object, used for configuring the
+ * dependencies exported and imported
+ * @param imp
+ * Implementation class that is being configured, needed as long
+ * as the same routine can configure multiple implementations
+ * @param containerName
+ * The containerName being configured, this allow also optional
+ * per-container different behavior if needed, usually should not
+ * be the case though.
+ */
+ @Override
+ public void configureInstance(Component c, Object imp, String containerName) {
+ if (imp.equals(DeviceManagerImpl.class)) {
+ // export the service
+ // XXX - TODO merge with existing APIs
+ Dictionary<String, String> props = new Hashtable<String, String>();
+ props.put("salListenerName", "devicemanager");
+
+ c.setInterface(new String[] { IDeviceService.class.getName(),
+ IListenDataPacket.class.getName(),
+ ITopologyManagerAware.class.getName() }, props);
+
+ c.add(createContainerServiceDependency(containerName)
+ .setService(ISwitchManager.class)
+ .setCallbacks("setSwitchManager", "unsetSwitchManager")
+ .setRequired(false));
+
+ c.add(createContainerServiceDependency(containerName)
+ .setService(IDataPacketService.class)
+ .setCallbacks("setDataPacketService",
+ "unsetDataPacketService").setRequired(true));
+
+ // c.add(createContainerServiceDependency(containerName).setService(
+ // IClusterContainerServices.class).setCallbacks(
+ // "setClusterContainerService",
+ // "unsetClusterContainerService").setRequired(true));
+ c.add(createContainerServiceDependency(containerName)
+ .setService(ITopologyManager.class)
+ .setCallbacks("setTopologyManager", "unsetTopologyManager")
+ .setRequired(false));
+
+ c.add(createContainerServiceDependency(containerName)
+ .setService(IDataPacketService.class)
+ .setCallbacks("setDataPacketService",
+ "unsetDataPacketService").setRequired(true));
+ }
+ }
+
+ /**
+ * Method which tells how many Global implementations are supported by the
+ * bundle. This way we can tune the number of components created. This
+ * components will be created ONLY at the time of bundle startup and will be
+ * destroyed only at time of bundle destruction, this is the major
+ * difference with the implementation retrieved via getImplementations where
+ * all of them are assumed to be in a container !
+ *
+ *
+ * @return The list of implementations the bundle will support, in Global
+ * version
+ */
+ @Override
+ protected Object[] getGlobalImplementations() {
+ return null;
+ }
+
+ /**
+ * Configure the dependency for a given instance Global
+ *
+ * @param c
+ * Component assigned for this instance, this will be what will
+ * be used for configuration
+ * @param imp
+ * implementation to be configured
+ * @param containerName
+ * container on which the configuration happens
+ */
+ @Override
+ protected void configureGlobalInstance(Component c, Object imp) {
+
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2011,2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+/**
+ * @author Srini
+ */
+
+package org.opendaylight.controller.hosttracker.internal;
+
+import org.opendaylight.controller.sal.core.NodeConnector;
+
+public class AttachmentPoint {
+ NodeConnector port;
+ long activeSince;
+ long lastSeen;
+
+ // Timeout for moving attachment points from OF/broadcast
+ // domain to another.
+ public static final long INACTIVITY_INTERVAL = 30000; // 30 seconds
+ public static final long EXTERNAL_TO_EXTERNAL_TIMEOUT = 5000; // 5 seconds
+ public static final long OPENFLOW_TO_EXTERNAL_TIMEOUT = 30000; // 30 seconds
+ public static final long CONSISTENT_TIMEOUT = 30000; // 30 seconds
+
+ public AttachmentPoint(NodeConnector port, long activeSince, long lastSeen) {
+ this.port = port;
+ this.activeSince = activeSince;
+ this.lastSeen = lastSeen;
+ }
+
+ public AttachmentPoint(NodeConnector port, long lastSeen) {
+ this.port = port;
+ this.lastSeen = lastSeen;
+ this.activeSince = lastSeen;
+ }
+
+ public AttachmentPoint(AttachmentPoint ap) {
+ this.port = ap.port;
+ this.activeSince = ap.activeSince;
+ this.lastSeen = ap.lastSeen;
+ }
+
+ public NodeConnector getPort() {
+ return port;
+ }
+
+ public void setPort(NodeConnector port) {
+ this.port = port;
+ }
+
+ public long getActiveSince() {
+ return activeSince;
+ }
+
+ public void setActiveSince(long activeSince) {
+ this.activeSince = activeSince;
+ }
+
+ public long getLastSeen() {
+ return lastSeen;
+ }
+
+ public void setLastSeen(long lastSeen) {
+ if (this.lastSeen + INACTIVITY_INTERVAL < lastSeen)
+ this.activeSince = lastSeen;
+ if (this.lastSeen < lastSeen)
+ this.lastSeen = lastSeen;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((port == null) ? 0 : port.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ AttachmentPoint other = (AttachmentPoint) obj;
+ if (port == null) {
+ if (other.port != null)
+ return false;
+ } else if (!port.equals(other.port))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "AttachmentPoint [port=" + port + ", activeSince=" + activeSince
+ + ", lastSeen=" + lastSeen + "]";
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2011,2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker.internal;
+
+import java.util.Collection;
+import java.util.EnumSet;
+
+import org.opendaylight.controller.hosttracker.Entity;
+import org.opendaylight.controller.hosttracker.IDevice;
+import org.opendaylight.controller.hosttracker.IDeviceService;
+import org.opendaylight.controller.hosttracker.IDeviceService.DeviceField;
+import org.opendaylight.controller.hosttracker.IEntityClass;
+import org.opendaylight.controller.hosttracker.IEntityClassListener;
+import org.opendaylight.controller.hosttracker.IEntityClassifierService;
+
+/**
+ * This is a default entity classifier that simply classifies all entities into
+ * a fixed entity class, with key fields of MAC and VLAN.
+ *
+ * @author readams
+ */
+public class DefaultEntityClassifier implements IEntityClassifierService {
+ /**
+ * A default fixed entity class
+ */
+ protected static class DefaultEntityClass implements IEntityClass {
+ String name;
+
+ public DefaultEntityClass(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public EnumSet<IDeviceService.DeviceField> getKeyFields() {
+ return keyFields;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+ }
+
+ protected static EnumSet<DeviceField> keyFields;
+ static {
+ keyFields = EnumSet.of(DeviceField.MAC, DeviceField.VLAN);
+ }
+ protected static DefaultEntityClass entityClass = new DefaultEntityClass(
+ "DefaultEntityClass");
+
+ @Override
+ public IEntityClass classifyEntity(Entity entity) {
+ return entityClass;
+ }
+
+ @Override
+ public IEntityClass reclassifyEntity(IDevice curDevice, Entity entity) {
+ return entityClass;
+ }
+
+ @Override
+ public void deviceUpdate(IDevice oldDevice,
+ Collection<? extends IDevice> newDevices) {
+ // no-op
+ }
+
+ @Override
+ public EnumSet<DeviceField> getKeyFields() {
+ return keyFields;
+ }
+
+ @Override
+ public void addListener(IEntityClassListener listener) {
+ // no-op
+
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2011,2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker.internal;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeSet;
+
+import org.opendaylight.controller.hosttracker.Entity;
+import org.opendaylight.controller.hosttracker.IDevice;
+import org.opendaylight.controller.hosttracker.IDeviceService.DeviceField;
+import org.opendaylight.controller.hosttracker.IEntityClass;
+import org.opendaylight.controller.hosttracker.SwitchPort;
+import org.opendaylight.controller.hosttracker.SwitchPort.ErrorStatus;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Concrete implementation of {@link IDevice}
+ *
+ * @author readams
+ */
+public class Device implements IDevice {
+ protected static Logger log = LoggerFactory.getLogger(Device.class);
+ public static final short VLAN_UNTAGGED = (short) 0xffff;
+
+ private final Long deviceKey;
+ protected final DeviceManagerImpl deviceManager;
+
+ protected final Entity[] entities;
+ private final IEntityClass entityClass;
+
+ protected final String macAddressString;
+ // the vlan Ids from the entities of this device
+ protected final Short[] vlanIds;
+ protected volatile String dhcpClientName;
+
+ /**
+ * These are the old attachment points for the device that were valid no
+ * more than INACTIVITY_TIME ago.
+ */
+ protected volatile List<AttachmentPoint> oldAPs;
+ /**
+ * The current attachment points for the device.
+ */
+ protected volatile List<AttachmentPoint> attachmentPoints;
+
+ // ************
+ // Constructors
+ // ************
+
+ /**
+ * Create a device from an entities
+ *
+ * @param deviceManager
+ * the device manager for this device
+ * @param deviceKey
+ * the unique identifier for this device object
+ * @param entity
+ * the initial entity for the device
+ * @param entityClass
+ * the entity classes associated with the entity
+ */
+ public Device(DeviceManagerImpl deviceManager, Long deviceKey,
+ Entity entity, IEntityClass entityClass) {
+ this.deviceManager = deviceManager;
+ this.deviceKey = deviceKey;
+ this.entities = new Entity[] { entity };
+ this.macAddressString = HexEncode.longToHexString(entity
+ .getMacAddress());
+ this.entityClass = entityClass;
+ Arrays.sort(this.entities);
+
+ this.dhcpClientName = null;
+ this.oldAPs = null;
+ this.attachmentPoints = null;
+
+ if (entity.getPort() != null) {
+ NodeConnector port = entity.getPort();
+
+ if (deviceManager.isValidAttachmentPoint(port)) {
+ AttachmentPoint ap;
+ ap = new AttachmentPoint(port, entity.getLastSeenTimestamp()
+ .getTime());
+
+ this.attachmentPoints = new ArrayList<AttachmentPoint>();
+ this.attachmentPoints.add(ap);
+ }
+ }
+ vlanIds = computeVlandIds();
+ }
+
+ /**
+ * Create a device from a set of entities
+ *
+ * @param deviceManager
+ * the device manager for this device
+ * @param deviceKey
+ * the unique identifier for this device object
+ * @param entities
+ * the initial entities for the device
+ * @param entityClass
+ * the entity class associated with the entities
+ */
+ public Device(DeviceManagerImpl deviceManager, Long deviceKey,
+ String dhcpClientName, Collection<AttachmentPoint> oldAPs,
+ Collection<AttachmentPoint> attachmentPoints,
+ Collection<Entity> entities, IEntityClass entityClass) {
+ this.deviceManager = deviceManager;
+ this.deviceKey = deviceKey;
+ this.dhcpClientName = dhcpClientName;
+ this.entities = entities.toArray(new Entity[entities.size()]);
+ this.oldAPs = null;
+ this.attachmentPoints = null;
+ if (oldAPs != null) {
+ this.oldAPs = new ArrayList<AttachmentPoint>(oldAPs);
+ }
+ if (attachmentPoints != null) {
+ this.attachmentPoints = new ArrayList<AttachmentPoint>(
+ attachmentPoints);
+ }
+ this.macAddressString = HexEncode.longToHexString(this.entities[0]
+ .getMacAddress());
+ this.entityClass = entityClass;
+ Arrays.sort(this.entities);
+ vlanIds = computeVlandIds();
+ }
+
+ /**
+ * Construct a new device consisting of the entities from the old device
+ * plus an additional entity. The caller needs to ensure that the additional
+ * entity is not already present in the array
+ *
+ * @param device
+ * the old device object
+ * @param newEntity
+ * the entity to add. newEntity must be have the same entity
+ * class as device
+ * @param if positive indicates the index in the entities array were the new
+ * entity should be inserted. If negative we will compute the correct
+ * insertion point
+ */
+ public Device(Device device, Entity newEntity, int insertionpoint) {
+ this.deviceManager = device.deviceManager;
+ this.deviceKey = device.deviceKey;
+ this.dhcpClientName = device.dhcpClientName;
+
+ this.entities = new Entity[device.entities.length + 1];
+ if (insertionpoint < 0) {
+ insertionpoint = -(Arrays.binarySearch(device.entities, newEntity) + 1);
+ }
+ if (insertionpoint > 0) {
+ // insertion point is not the beginning:
+ // copy up to insertion point
+ System.arraycopy(device.entities, 0, this.entities, 0,
+ insertionpoint);
+ }
+ if (insertionpoint < device.entities.length) {
+ // insertion point is not the end
+ // copy from insertion point
+ System.arraycopy(device.entities, insertionpoint, this.entities,
+ insertionpoint + 1, device.entities.length - insertionpoint);
+ }
+ this.entities[insertionpoint] = newEntity;
+ /*
+ * this.entities = Arrays.<Entity>copyOf(device.entities,
+ * device.entities.length + 1); this.entities[this.entities.length - 1]
+ * = newEntity; Arrays.sort(this.entities);
+ */
+ this.oldAPs = null;
+ if (device.oldAPs != null) {
+ this.oldAPs = new ArrayList<AttachmentPoint>(device.oldAPs);
+ }
+ this.attachmentPoints = null;
+ if (device.attachmentPoints != null) {
+ this.attachmentPoints = new ArrayList<AttachmentPoint>(
+ device.attachmentPoints);
+ }
+
+ this.macAddressString = HexEncode.longToHexString(this.entities[0]
+ .getMacAddress());
+
+ this.entityClass = device.entityClass;
+ vlanIds = computeVlandIds();
+ }
+
+ private Short[] computeVlandIds() {
+ if (entities.length == 1) {
+ if (entities[0].getVlan() != null) {
+ return new Short[] { entities[0].getVlan() };
+ } else {
+ return new Short[] { Short.valueOf((short) -1) };
+ }
+ }
+
+ TreeSet<Short> vals = new TreeSet<Short>();
+ for (Entity e : entities) {
+ if (e.getVlan() == null)
+ vals.add((short) -1);
+ else
+ vals.add(e.getVlan());
+ }
+ return vals.toArray(new Short[vals.size()]);
+ }
+
+ /**
+ * Given a list of attachment points (apList), the procedure would return a
+ * map of attachment points for each L2 domain. L2 domain id is the key.
+ *
+ * @param apList
+ * @return
+ */
+ private Map<Long, AttachmentPoint> getAPMap(List<AttachmentPoint> apList) {
+
+ if (apList == null)
+ return null;
+ // ITopologyService topology = deviceManager.topology;
+
+ // Get the old attachment points and sort them.
+ List<AttachmentPoint> oldAP = new ArrayList<AttachmentPoint>();
+ if (apList != null)
+ oldAP.addAll(apList);
+
+ // Remove invalid attachment points before sorting.
+ List<AttachmentPoint> tempAP = new ArrayList<AttachmentPoint>();
+ for (AttachmentPoint ap : oldAP) {
+ if (deviceManager.isValidAttachmentPoint(ap.getPort())) {
+ tempAP.add(ap);
+ }
+ }
+ oldAP = tempAP;
+
+ Collections.sort(oldAP, deviceManager.apComparator);
+
+ // Map of attachment point by L2 domain Id.
+ Map<Long, AttachmentPoint> apMap = new HashMap<Long, AttachmentPoint>();
+
+ for (int i = 0; i < oldAP.size(); ++i) {
+ AttachmentPoint ap = oldAP.get(i);
+ // if this is not a valid attachment point, continue
+ if (!deviceManager.isValidAttachmentPoint(ap.getPort()))
+ continue;
+
+ // long id = topology.getL2DomainId(ap.getSw());
+ // XXX - Missing functionality
+ long id = 0;
+
+ apMap.put(id, ap);
+ }
+
+ if (apMap.isEmpty())
+ return null;
+ return apMap;
+ }
+
+ /**
+ * Remove all attachment points that are older than INACTIVITY_INTERVAL from
+ * the list.
+ *
+ * @param apList
+ * @return
+ */
+ private boolean removeExpiredAttachmentPoints(List<AttachmentPoint> apList) {
+
+ List<AttachmentPoint> expiredAPs = new ArrayList<AttachmentPoint>();
+
+ if (apList == null)
+ return false;
+
+ for (AttachmentPoint ap : apList) {
+ if (ap.getLastSeen() + AttachmentPoint.INACTIVITY_INTERVAL < System
+ .currentTimeMillis())
+ expiredAPs.add(ap);
+ }
+ if (expiredAPs.size() > 0) {
+ apList.removeAll(expiredAPs);
+ return true;
+ } else
+ return false;
+ }
+
+ /**
+ * Get a list of duplicate attachment points, given a list of old attachment
+ * points and one attachment point per L2 domain. Given a true attachment
+ * point in the L2 domain, say trueAP, another attachment point in the same
+ * L2 domain, say ap, is duplicate if: 1. ap is inconsistent with trueAP,
+ * and 2. active time of ap is after that of trueAP; and 3. last seen time
+ * of ap is within the last INACTIVITY_INTERVAL
+ *
+ * @param oldAPList
+ * @param apMap
+ * @return
+ */
+ List<AttachmentPoint> getDuplicateAttachmentPoints(
+ List<AttachmentPoint> oldAPList, Map<Long, AttachmentPoint> apMap) {
+ // ITopologyService topology = deviceManager.topology;
+ List<AttachmentPoint> dupAPs = new ArrayList<AttachmentPoint>();
+ long timeThreshold = System.currentTimeMillis()
+ - AttachmentPoint.INACTIVITY_INTERVAL;
+
+ if (oldAPList == null || apMap == null)
+ return dupAPs;
+
+ for (AttachmentPoint ap : oldAPList) {
+ // XXX - Missing functionality
+ // long id = topology.getL2DomainId(ap.getSw());
+ long id = 0;
+ AttachmentPoint trueAP = apMap.get(id);
+
+ if (trueAP == null)
+ continue;
+ // XXX - Missing functionality
+ // boolean c = (topology.isConsistent(trueAP.getSw(),
+ // trueAP.getPort(),
+ // ap.getSw(), ap.getPort()));
+ boolean c = true;
+ boolean active = (ap.getActiveSince() > trueAP.getActiveSince());
+ boolean last = ap.getLastSeen() > timeThreshold;
+ if (!c && active && last) {
+ dupAPs.add(ap);
+ }
+ }
+
+ return dupAPs;
+ }
+
+ /**
+ * Update the known attachment points. This method is called whenever
+ * topology changes. The method returns true if there's any change to the
+ * list of attachment points -- which indicates a possible device move.
+ *
+ * @return
+ */
+ protected boolean updateAttachmentPoint() {
+ boolean moved = false;
+ this.oldAPs = attachmentPoints;
+ if (attachmentPoints == null || attachmentPoints.isEmpty())
+ return false;
+
+ List<AttachmentPoint> apList = new ArrayList<AttachmentPoint>();
+ if (attachmentPoints != null)
+ apList.addAll(attachmentPoints);
+ Map<Long, AttachmentPoint> newMap = getAPMap(apList);
+ if (newMap == null || newMap.size() != apList.size()) {
+ moved = true;
+ }
+
+ // Prepare the new attachment point list.
+ if (moved) {
+ log.info("updateAttachmentPoint: ap {} newmap {} ",
+ attachmentPoints, newMap);
+ List<AttachmentPoint> newAPList = new ArrayList<AttachmentPoint>();
+ if (newMap != null)
+ newAPList.addAll(newMap.values());
+ this.attachmentPoints = newAPList;
+ }
+
+ // Set the oldAPs to null.
+ return moved;
+ }
+
+ /**
+ * Update the list of attachment points given that a new packet-in was seen
+ * from (sw, port) at time (lastSeen). The return value is true if there was
+ * any change to the list of attachment points for the device -- which
+ * indicates a device move.
+ *
+ * @param sw
+ * @param port
+ * @param lastSeen
+ * @return
+ */
+ protected boolean updateAttachmentPoint(NodeConnector port, long lastSeen) {
+ // ITopologyService topology = deviceManager.topology;
+ List<AttachmentPoint> oldAPList;
+ List<AttachmentPoint> apList;
+ boolean oldAPFlag = false;
+
+ if (!deviceManager.isValidAttachmentPoint(port))
+ return false;
+ AttachmentPoint newAP = new AttachmentPoint(port, lastSeen);
+ // Copy the oldAP and ap list.
+ apList = new ArrayList<AttachmentPoint>();
+ if (attachmentPoints != null)
+ apList.addAll(attachmentPoints);
+ oldAPList = new ArrayList<AttachmentPoint>();
+ if (oldAPs != null)
+ oldAPList.addAll(oldAPs);
+
+ // if the sw, port is in old AP, remove it from there
+ // and update the lastSeen in that object.
+ if (oldAPList.contains(newAP)) {
+ int index = oldAPList.indexOf(newAP);
+ newAP = oldAPList.remove(index);
+ newAP.setLastSeen(lastSeen);
+ this.oldAPs = oldAPList;
+ oldAPFlag = true;
+ }
+
+ // newAP now contains the new attachment point.
+
+ // Get the APMap is null or empty.
+ Map<Long, AttachmentPoint> apMap = getAPMap(apList);
+ if (apMap == null || apMap.isEmpty()) {
+ apList.add(newAP);
+ attachmentPoints = apList;
+ // there are no old attachment points - since the device exists,
+ // this
+ // may be because the host really moved (so the old AP port went
+ // down);
+ // or it may be because the switch restarted (so old APs were
+ // nullified).
+ // For now we will treat both cases as host moved.
+ return true;
+ }
+
+ // XXX - Missing functionality
+ // long id = topology.getL2DomainId(sw);
+ long id = 0;
+ AttachmentPoint oldAP = apMap.get(id);
+
+ if (oldAP == null) // No attachment on this L2 domain.
+ {
+ apList = new ArrayList<AttachmentPoint>();
+ apList.addAll(apMap.values());
+ apList.add(newAP);
+ this.attachmentPoints = apList;
+ return true; // new AP found on an L2 island.
+ }
+
+ // There is already a known attachment point on the same L2 island.
+ // we need to compare oldAP and newAP.
+ if (oldAP.equals(newAP)) {
+ // nothing to do here. just the last seen has to be changed.
+ if (newAP.lastSeen > oldAP.lastSeen) {
+ oldAP.setLastSeen(newAP.lastSeen);
+ }
+ this.attachmentPoints = new ArrayList<AttachmentPoint>(
+ apMap.values());
+ return false; // nothing to do here.
+ }
+
+ int x = deviceManager.apComparator.compare(oldAP, newAP);
+ if (x < 0) {
+ // newAP replaces oldAP.
+ apMap.put(id, newAP);
+ this.attachmentPoints = new ArrayList<AttachmentPoint>(
+ apMap.values());
+
+ oldAPList = new ArrayList<AttachmentPoint>();
+ if (oldAPs != null)
+ oldAPList.addAll(oldAPs);
+ oldAPList.add(oldAP);
+ this.oldAPs = oldAPList;
+ // XXX - Missing functionality
+ // if (!topology.isInSameBroadcastDomain(oldAP.getSw(),
+ // oldAP.getPort(),
+ // newAP.getSw(), newAP.getPort()))
+ // return true; // attachment point changed.
+ return true;
+ } else if (oldAPFlag) {
+ // retain oldAP as is. Put the newAP in oldAPs for flagging
+ // possible duplicates.
+ oldAPList = new ArrayList<AttachmentPoint>();
+ if (oldAPs != null)
+ oldAPList.addAll(oldAPs);
+ // Add to oldAPList only if it was picked up from the oldAPList
+ oldAPList.add(newAP);
+ this.oldAPs = oldAPList;
+ // XXX - Missing functionality
+ // if (!topology.isInSameBroadcastDomain(oldAP.getSw(),
+ // oldAP.getPort(),
+ // newAP.getSw(), newAP.getPort()))
+ // return true; // attachment point changed.
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Delete (sw,port) from the list of list of attachment points and oldAPs.
+ *
+ * @param sw
+ * @param port
+ * @return
+ */
+ public boolean deleteAttachmentPoint(NodeConnector port) {
+ AttachmentPoint ap = new AttachmentPoint(port, 0);
+
+ if (this.oldAPs != null) {
+ ArrayList<AttachmentPoint> apList = new ArrayList<AttachmentPoint>();
+ apList.addAll(this.oldAPs);
+ int index = apList.indexOf(ap);
+ if (index > 0) {
+ apList.remove(index);
+ this.oldAPs = apList;
+ }
+ }
+
+ if (this.attachmentPoints != null) {
+ ArrayList<AttachmentPoint> apList = new ArrayList<AttachmentPoint>();
+ apList.addAll(this.attachmentPoints);
+ int index = apList.indexOf(ap);
+ if (index > 0) {
+ apList.remove(index);
+ this.attachmentPoints = apList;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // *******
+ // IDevice
+ // *******
+
+ @Override
+ public SwitchPort[] getOldAP() {
+ List<SwitchPort> sp = new ArrayList<SwitchPort>();
+ SwitchPort[] returnSwitchPorts = new SwitchPort[] {};
+ if (oldAPs == null)
+ return returnSwitchPorts;
+ if (oldAPs.isEmpty())
+ return returnSwitchPorts;
+
+ // copy ap list.
+ List<AttachmentPoint> oldAPList;
+ oldAPList = new ArrayList<AttachmentPoint>();
+
+ if (oldAPs != null)
+ oldAPList.addAll(oldAPs);
+ removeExpiredAttachmentPoints(oldAPList);
+
+ if (oldAPList != null) {
+ for (AttachmentPoint ap : oldAPList) {
+ SwitchPort swport = new SwitchPort(ap.getPort());
+ sp.add(swport);
+ }
+ }
+ return sp.toArray(new SwitchPort[sp.size()]);
+ }
+
+ @Override
+ public SwitchPort[] getAttachmentPoints() {
+ return getAttachmentPoints(false);
+ }
+
+ @Override
+ public SwitchPort[] getAttachmentPoints(boolean includeError) {
+ List<SwitchPort> sp = new ArrayList<SwitchPort>();
+ SwitchPort[] returnSwitchPorts = new SwitchPort[] {};
+ if (attachmentPoints == null)
+ return returnSwitchPorts;
+ if (attachmentPoints.isEmpty())
+ return returnSwitchPorts;
+
+ // copy ap list.
+ List<AttachmentPoint> apList = attachmentPoints;
+
+ if (apList != null) {
+ for (AttachmentPoint ap : apList) {
+ SwitchPort swport = new SwitchPort(ap.getPort());
+ sp.add(swport);
+ }
+ }
+
+ if (!includeError)
+ return sp.toArray(new SwitchPort[sp.size()]);
+
+ List<AttachmentPoint> oldAPList;
+ oldAPList = new ArrayList<AttachmentPoint>();
+
+ if (oldAPs != null)
+ oldAPList.addAll(oldAPs);
+
+ if (removeExpiredAttachmentPoints(oldAPList))
+ this.oldAPs = oldAPList;
+
+ List<AttachmentPoint> dupList;
+ // get AP map.
+ Map<Long, AttachmentPoint> apMap = getAPMap(apList);
+ dupList = this.getDuplicateAttachmentPoints(oldAPList, apMap);
+ if (dupList != null) {
+ for (AttachmentPoint ap : dupList) {
+ SwitchPort swport = new SwitchPort(ap.getPort(),
+ ErrorStatus.DUPLICATE_DEVICE);
+ sp.add(swport);
+ }
+ }
+ return sp.toArray(new SwitchPort[sp.size()]);
+ }
+
+ @Override
+ public Long getDeviceKey() {
+ return deviceKey;
+ }
+
+ @Override
+ public long getMACAddress() {
+ // we assume only one MAC per device for now.
+ return entities[0].getMacAddress();
+ }
+
+ @Override
+ public String getMACAddressString() {
+ return macAddressString;
+ }
+
+ @Override
+ public Short[] getVlanId() {
+ return Arrays.copyOf(vlanIds, vlanIds.length);
+ }
+
+ static final EnumSet<DeviceField> ipv4Fields = EnumSet.of(DeviceField.IPV4);
+
+ @Override
+ public Integer[] getIPv4Addresses() {
+ // XXX - TODO we can cache this result. Let's find out if this
+ // is really a performance bottleneck first though.
+
+ TreeSet<Integer> vals = new TreeSet<Integer>();
+ for (Entity e : entities) {
+ if (e.getIpv4Address() == null)
+ continue;
+
+ // We have an IP address only if among the devices within the class
+ // we have the most recent entity with that IP.
+ boolean validIP = true;
+ Iterator<Device> devices = deviceManager.queryClassByEntity(
+ entityClass, ipv4Fields, e);
+ while (devices.hasNext()) {
+ Device d = devices.next();
+ if (deviceKey.equals(d.getDeviceKey()))
+ continue;
+ for (Entity se : d.entities) {
+ if (se.getIpv4Address() != null
+ && se.getIpv4Address().equals(e.getIpv4Address())
+ && se.getLastSeenTimestamp() != null
+ && 0 < se.getLastSeenTimestamp().compareTo(
+ e.getLastSeenTimestamp())) {
+ validIP = false;
+ break;
+ }
+ }
+ if (!validIP)
+ break;
+ }
+
+ if (validIP)
+ vals.add(e.getIpv4Address());
+ }
+
+ return vals.toArray(new Integer[vals.size()]);
+ }
+
+ @Override
+ public Short[] getSwitchPortVlanIds(SwitchPort swp) {
+ TreeSet<Short> vals = new TreeSet<Short>();
+ for (Entity e : entities) {
+ if (e.getPort().equals(swp.getPort())) {
+ if (e.getVlan() == null)
+ vals.add(VLAN_UNTAGGED);
+ else
+ vals.add(e.getVlan());
+ }
+ }
+ return vals.toArray(new Short[vals.size()]);
+ }
+
+ @Override
+ public Date getLastSeen() {
+ Date d = null;
+ for (int i = 0; i < entities.length; i++) {
+ if (d == null
+ || entities[i].getLastSeenTimestamp().compareTo(d) > 0)
+ d = entities[i].getLastSeenTimestamp();
+ }
+ return d;
+ }
+
+ // ***************
+ // Getters/Setters
+ // ***************
+
+ @Override
+ public IEntityClass getEntityClass() {
+ return entityClass;
+ }
+
+ public Entity[] getEntities() {
+ return entities;
+ }
+
+ public String getDHCPClientName() {
+ return dhcpClientName;
+ }
+
+ // ***************
+ // Utility Methods
+ // ***************
+
+ /**
+ * Check whether the device contains the specified entity
+ *
+ * @param entity
+ * the entity to search for
+ * @return the index of the entity, or <0 if not found
+ */
+ protected int entityIndex(Entity entity) {
+ return Arrays.binarySearch(entities, entity);
+ }
+
+ // ******
+ // Object
+ // ******
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(entities);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Device other = (Device) obj;
+ if (!deviceKey.equals(other.deviceKey))
+ return false;
+ if (!Arrays.equals(entities, other.entities))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Device [deviceKey=");
+ builder.append(deviceKey);
+ builder.append(", entityClass=");
+ builder.append(entityClass.getName());
+ builder.append(", MAC=");
+ builder.append(macAddressString);
+ builder.append(", IPs=[");
+ boolean isFirst = true;
+ for (Integer ip : getIPv4Addresses()) {
+ if (!isFirst)
+ builder.append(", ");
+ isFirst = false;
+ // builder.append(IPv4.fromIPv4Address(ip));
+ builder.append(ip);
+ }
+ builder.append("], APs=");
+ builder.append(Arrays.toString(getAttachmentPoints(true)));
+ builder.append("]");
+ return builder.toString();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker.internal;
+
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.Iterator;
+
+import org.opendaylight.controller.hosttracker.Entity;
+import org.opendaylight.controller.hosttracker.IDeviceService.DeviceField;
+
+/**
+ * An index that maps key fields of an entity to device keys
+ */
+public abstract class DeviceIndex {
+ /**
+ * The key fields for this index
+ */
+ protected EnumSet<DeviceField> keyFields;
+
+ /**
+ * Construct a new device index using the provided key fields
+ *
+ * @param keyFields
+ * the key fields to use
+ */
+ public DeviceIndex(EnumSet<DeviceField> keyFields) {
+ super();
+ this.keyFields = keyFields;
+ }
+
+ /**
+ * Find all device keys in the index that match the given entity on all the
+ * key fields for this index
+ *
+ * @param e
+ * the entity to search for
+ * @return an iterator over device keys
+ */
+ public abstract Iterator<Long> queryByEntity(Entity entity);
+
+ /**
+ * Get all device keys in the index. If certain devices exist multiple
+ * times, then these devices may be returned multiple times
+ *
+ * @return an iterator over device keys
+ */
+ public abstract Iterator<Long> getAll();
+
+ /**
+ * Attempt to update an index with the entities in the provided
+ * {@link Device}. If the update fails because of a concurrent update, will
+ * return false.
+ *
+ * @param device
+ * the device to update
+ * @param deviceKey
+ * the device key for the device
+ * @return true if the update succeeded, false otherwise.
+ */
+ public abstract boolean updateIndex(Device device, Long deviceKey);
+
+ /**
+ * Add a mapping from the given entity to the given device key. This update
+ * will not fail because of a concurrent update
+ *
+ * @param device
+ * the device to update
+ * @param deviceKey
+ * the device key for the device
+ */
+ public abstract void updateIndex(Entity entity, Long deviceKey);
+
+ /**
+ * Remove the entry for the given entity
+ *
+ * @param entity
+ * the entity to remove
+ */
+ public abstract void removeEntity(Entity entity);
+
+ /**
+ * Remove the given device key from the index for the given entity
+ *
+ * @param entity
+ * the entity to search for
+ * @param deviceKey
+ * the key to remove
+ */
+ public abstract void removeEntity(Entity entity, Long deviceKey);
+
+ /**
+ * Remove the give device from the index only if this the collection of
+ * others does not contain an entity that is identical on all the key fields
+ * for this index.
+ *
+ * @param entity
+ * the entity to search for
+ * @param deviceKey
+ * the key to remove
+ * @param others
+ * the others against which to check
+ */
+ public void removeEntityIfNeeded(Entity entity, Long deviceKey,
+ Collection<Entity> others) {
+ IndexedEntity ie = new IndexedEntity(keyFields, entity);
+ for (Entity o : others) {
+ IndexedEntity oio = new IndexedEntity(keyFields, o);
+ if (oio.equals(ie))
+ return;
+ }
+
+ Iterator<Long> keyiter = this.queryByEntity(entity);
+ while (keyiter.hasNext()) {
+ Long key = keyiter.next();
+ if (key.equals(deviceKey)) {
+ removeEntity(entity, deviceKey);
+ break;
+ }
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker.internal;
+
+import java.util.Iterator;
+
+/**
+ * An iterator for handling device index queries
+ */
+public class DeviceIndexInterator implements Iterator<Device> {
+ private DeviceManagerImpl deviceManager;
+ private Iterator<Long> subIterator;
+
+ /**
+ * Construct a new device index iterator referring to a device manager
+ * instance and an iterator over device keys
+ *
+ * @param deviceManager
+ * the device manager
+ * @param subIterator
+ * an iterator over device keys
+ */
+ public DeviceIndexInterator(DeviceManagerImpl deviceManager,
+ Iterator<Long> subIterator) {
+ super();
+ this.deviceManager = deviceManager;
+ this.subIterator = subIterator;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return subIterator.hasNext();
+ }
+
+ @Override
+ public Device next() {
+ Long next = subIterator.next();
+ return deviceManager.deviceMap.get(next);
+ }
+
+ @Override
+ public void remove() {
+ subIterator.remove();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker.internal;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+import org.opendaylight.controller.hosttracker.IEntityClass;
+import org.opendaylight.controller.hosttracker.SwitchPort;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.utils.FilterIterator;
+
+/**
+ * An iterator for handling device queries
+ */
+public class DeviceIterator extends FilterIterator<Device> {
+ private IEntityClass[] entityClasses;
+
+ private Long macAddress;
+ private Short vlan;
+ private Integer ipv4Address;
+ private NodeConnector port;
+
+ /**
+ * Construct a new device iterator over the key fields
+ *
+ * @param subIterator
+ * an iterator over the full data structure to scan
+ * @param entityClasses
+ * the entity classes to search for
+ * @param macAddress
+ * The MAC address
+ * @param vlan
+ * the VLAN
+ * @param ipv4Address
+ * the ipv4 address
+ * @param switchDPID
+ * the switch DPID
+ * @param switchPort
+ * the switch port
+ */
+ public DeviceIterator(Iterator<Device> subIterator,
+ IEntityClass[] entityClasses, Long macAddress, Short vlan,
+ Integer ipv4Address, NodeConnector port) {
+ super(subIterator);
+ this.entityClasses = entityClasses;
+ this.subIterator = subIterator;
+ this.macAddress = macAddress;
+ this.vlan = vlan;
+ this.ipv4Address = ipv4Address;
+ this.port = port;
+ }
+
+ @Override
+ protected boolean matches(Device value) {
+ boolean match;
+ if (entityClasses != null) {
+ IEntityClass clazz = value.getEntityClass();
+ if (clazz == null)
+ return false;
+
+ match = false;
+ for (IEntityClass entityClass : entityClasses) {
+ if (clazz.equals(entityClass)) {
+ match = true;
+ break;
+ }
+ }
+ if (!match)
+ return false;
+ }
+ if (macAddress != null) {
+ if (macAddress.longValue() != value.getMACAddress())
+ return false;
+ }
+ if (vlan != null) {
+ Short[] vlans = value.getVlanId();
+ if (Arrays.binarySearch(vlans, vlan) < 0)
+ return false;
+ }
+ if (ipv4Address != null) {
+ Integer[] ipv4Addresses = value.getIPv4Addresses();
+ if (Arrays.binarySearch(ipv4Addresses, ipv4Address) < 0)
+ return false;
+ }
+ if (port != null) {
+ SwitchPort[] sps = value.getAttachmentPoints();
+ if (sps == null)
+ return false;
+
+ match = false;
+ for (SwitchPort sp : sps) {
+ if (sp.getPort().equals(sp.getPort())) {
+ match = true;
+ break;
+ }
+ }
+ if (!match)
+ return false;
+ }
+ return true;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2011,2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker.internal;
+
+import static org.opendaylight.controller.hosttracker.internal.DeviceManagerImpl.DeviceUpdate.Change.ADD;
+import static org.opendaylight.controller.hosttracker.internal.DeviceManagerImpl.DeviceUpdate.Change.CHANGE;
+import static org.opendaylight.controller.hosttracker.internal.DeviceManagerImpl.DeviceUpdate.Change.DELETE;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.opendaylight.controller.hosttracker.Entity;
+import org.opendaylight.controller.hosttracker.IDevice;
+import org.opendaylight.controller.hosttracker.IDeviceListener;
+import org.opendaylight.controller.hosttracker.IDeviceService;
+import org.opendaylight.controller.hosttracker.IEntityClass;
+import org.opendaylight.controller.hosttracker.IEntityClassListener;
+import org.opendaylight.controller.hosttracker.IEntityClassifierService;
+import org.opendaylight.controller.hosttracker.SwitchPort;
+import org.opendaylight.controller.sal.core.Edge;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.NodeConnector.NodeConnectorIDType;
+import org.opendaylight.controller.sal.packet.ARP;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.IDataPacketService;
+import org.opendaylight.controller.sal.packet.IListenDataPacket;
+import org.opendaylight.controller.sal.packet.Packet;
+import org.opendaylight.controller.sal.packet.PacketResult;
+import org.opendaylight.controller.sal.packet.RawPacket;
+import org.opendaylight.controller.sal.topology.TopoEdgeUpdate;
+import org.opendaylight.controller.sal.utils.ListenerDispatcher;
+import org.opendaylight.controller.sal.utils.MultiIterator;
+import org.opendaylight.controller.sal.utils.SingletonTask;
+import org.opendaylight.controller.switchmanager.ISwitchManager;
+import org.opendaylight.controller.topologymanager.ITopologyManager;
+import org.opendaylight.controller.topologymanager.ITopologyManagerAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * DeviceManager creates Devices based upon MAC addresses seen in the network.
+ * It tracks any network addresses mapped to the Device, and its location within
+ * the network.
+ *
+ * @author readams
+ */
+public class DeviceManagerImpl implements IDeviceService, IEntityClassListener,
+ IListenDataPacket, ITopologyManagerAware {
+ protected static Logger logger = LoggerFactory
+ .getLogger(DeviceManagerImpl.class);
+
+ public static final String MODULE_NAME = "devicemanager";
+
+ // protected ITopologyService topology;
+ // protected IStorageSourceService storageSource;
+ // protected IRestApiService restApi;
+ // protected IThreadPoolService threadPool;
+ // protected IFlowReconcileService flowReconcileMgr;
+ // protected IFlowReconcileEngineService flowReconcileEngine;
+ // protected IDebugCounterService debugCounters;
+ // private ISyncService syncService;
+ // private IStoreClient<String,DeviceSyncRepresentation> storeClient;
+ // private DeviceSyncManager deviceSyncManager;
+
+ private ITopologyManager topology;
+ private ISwitchManager switchManager = null;
+ private IDataPacketService dataPacketService = null;
+
+ public static final String CNT_INCOMING = MODULE_NAME + "-incoming";
+ public static final String CNT_RECONCILE_REQUEST = MODULE_NAME
+ + "-reconcileRequest";
+ public static final String CNT_RECONCILE_NO_SOURCE = MODULE_NAME
+ + "-reconcileNoSourceDevice";
+ public static final String CNT_RECONCILE_NO_DEST = MODULE_NAME
+ + "-reconcileNoDestDevice";
+ public static final String CNT_BROADCAST_SOURCE = MODULE_NAME
+ + "-broadcastSource";
+ public static final String CNT_NO_SOURCE = MODULE_NAME + "-noSourceDevice";
+ public static final String CNT_NO_DEST = MODULE_NAME + "-noDestDevice";
+ public static final String CNT_DHCP_CLIENT_NAME_SNOOPED = MODULE_NAME
+ + "-dhcpClientNameSnooped";
+ public static final String CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED = MODULE_NAME
+ + "-deviceOnInternalPortNotLearned";
+ public static final String CNT_PACKET_NOT_ALLOWED = MODULE_NAME
+ + "-packetNotAllowed";
+ public static final String CNT_NEW_DEVICE = MODULE_NAME + "-newDevice";
+ public static final String CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE = MODULE_NAME
+ + "-packetOnInternalPortForKnownDevice";
+ public static final String CNT_NEW_ENTITY = MODULE_NAME + "-newEntity";
+ public static final String CNT_DEVICE_CHANGED = MODULE_NAME
+ + "-deviceChanged";
+ public static final String CNT_DEVICE_MOVED = MODULE_NAME + "-deviceMoved";
+ public static final String CNT_CLEANUP_ENTITIES_RUNS = MODULE_NAME
+ + "-cleanupEntitiesRuns";
+ public static final String CNT_ENTITY_REMOVED_TIMEOUT = MODULE_NAME
+ + "-entityRemovedTimeout";
+ public static final String CNT_DEVICE_DELETED = MODULE_NAME
+ + "-deviceDeleted";
+ public static final String CNT_DEVICE_RECLASSIFY_DELETE = MODULE_NAME
+ + "-deviceReclassifyDelete";
+ public static final String CNT_DEVICE_STORED = MODULE_NAME
+ + "-deviceStored";
+ public static final String CNT_DEVICE_STORE_THROTTLED = MODULE_NAME
+ + "-deviceStoreThrottled";
+ public static final String CNT_DEVICE_REMOVED_FROM_STORE = MODULE_NAME
+ + "-deviceRemovedFromStore";
+ public static final String CNT_SYNC_EXCEPTION = MODULE_NAME
+ + "-syncException";
+ public static final String CNT_DEVICES_FROM_STORE = MODULE_NAME
+ + "-devicesFromStore";
+ public static final String CNT_CONSOLIDATE_STORE_RUNS = MODULE_NAME
+ + "-consolidateStoreRuns";
+ public static final String CNT_CONSOLIDATE_STORE_DEVICES_REMOVED = MODULE_NAME
+ + "-consolidateStoreDevicesRemoved";
+
+ static final String DEVICE_SYNC_STORE_NAME = DeviceManagerImpl.class
+ .getCanonicalName() + ".stateStore";
+
+ /**
+ * Time interval between writes of entries for the same device to the sync
+ * store.
+ */
+ // static final int DEFAULT_SYNC_STORE_WRITE_INTERVAL_MS =
+ // 5*60*1000; // 5 min
+ // private int syncStoreWriteIntervalMs =
+ // DEFAULT_SYNC_STORE_WRITE_INTERVAL_MS;
+
+ /**
+ * Time after SLAVE->MASTER until we run the consolidate store code.
+ */
+ // static final int DEFAULT_INITIAL_SYNC_STORE_CONSOLIDATE_MS =
+ // 15*1000; // 15 sec
+ // private int initialSyncStoreConsolidateMs =
+ // DEFAULT_INITIAL_SYNC_STORE_CONSOLIDATE_MS;
+
+ /**
+ * Time interval between consolidate store runs.
+ */
+ // static final int DEFAULT_SYNC_STORE_CONSOLIDATE_INTERVAL_MS =
+ // 75*60*1000; // 75 min
+ // private final int syncStoreConsolidateIntervalMs =
+ // DEFAULT_SYNC_STORE_CONSOLIDATE_INTERVAL_MS;
+
+ /**
+ * Time in milliseconds before entities will expire
+ */
+ protected static final int ENTITY_TIMEOUT = 60 * 60 * 1000;
+
+ /**
+ * Time in seconds between cleaning up old entities/devices
+ */
+ protected static final int ENTITY_CLEANUP_INTERVAL = 60 * 60;
+
+ /**
+ * This is the master device map that maps device IDs to {@link Device}
+ * objects.
+ */
+ protected ConcurrentHashMap<Long, Device> deviceMap;
+
+ /**
+ * Counter used to generate device keys
+ */
+ protected long deviceKeyCounter = 0;
+
+ /**
+ * Lock for incrementing the device key counter
+ */
+ protected Object deviceKeyLock = new Object();
+
+ /**
+ * This is the primary entity index that contains all entities
+ */
+ protected DeviceUniqueIndex primaryIndex;
+
+ /**
+ * This stores secondary indices over the fields in the devices
+ */
+ protected Map<EnumSet<DeviceField>, DeviceIndex> secondaryIndexMap;
+
+ /**
+ * This map contains state for each of the {@ref IEntityClass} that exist
+ */
+ protected ConcurrentHashMap<String, ClassState> classStateMap;
+
+ /**
+ * This is the list of indices we want on a per-class basis
+ */
+ protected Set<EnumSet<DeviceField>> perClassIndices;
+
+ /**
+ * The entity classifier currently in use
+ */
+ protected IEntityClassifierService entityClassifier;
+
+ /**
+ * Used to cache state about specific entity classes
+ */
+ protected class ClassState {
+
+ /**
+ * The class index
+ */
+ protected DeviceUniqueIndex classIndex;
+
+ /**
+ * This stores secondary indices over the fields in the device for the
+ * class
+ */
+ protected Map<EnumSet<DeviceField>, DeviceIndex> secondaryIndexMap;
+
+ /**
+ * Allocate a new {@link ClassState} object for the class
+ *
+ * @param clazz
+ * the class to use for the state
+ */
+ public ClassState(IEntityClass clazz) {
+ EnumSet<DeviceField> keyFields = clazz.getKeyFields();
+ EnumSet<DeviceField> primaryKeyFields = entityClassifier
+ .getKeyFields();
+ boolean keyFieldsMatchPrimary = primaryKeyFields.equals(keyFields);
+
+ if (!keyFieldsMatchPrimary)
+ classIndex = new DeviceUniqueIndex(keyFields);
+
+ secondaryIndexMap = new HashMap<EnumSet<DeviceField>, DeviceIndex>();
+ for (EnumSet<DeviceField> fields : perClassIndices) {
+ secondaryIndexMap.put(fields, new DeviceMultiIndex(fields));
+ }
+ }
+ }
+
+ /**
+ * Device manager event listeners reclassifyDeviceListeners are notified
+ * first before reconcileDeviceListeners. This is to make sure devices are
+ * correctly reclassified before reconciliation.
+ */
+ protected ListenerDispatcher<String, IDeviceListener> deviceListeners;
+
+ /**
+ * A device update event to be dispatched
+ */
+ protected static class DeviceUpdate {
+ public enum Change {
+ ADD, DELETE, CHANGE;
+ }
+
+ /**
+ * The affected device
+ */
+ protected Device device;
+
+ /**
+ * The change that was made
+ */
+ protected Change change;
+
+ /**
+ * If not added, then this is the list of fields changed
+ */
+ protected EnumSet<DeviceField> fieldsChanged;
+
+ public DeviceUpdate(Device device, Change change,
+ EnumSet<DeviceField> fieldsChanged) {
+ super();
+ this.device = device;
+ this.change = change;
+ this.fieldsChanged = fieldsChanged;
+ }
+
+ @Override
+ public String toString() {
+ String devIdStr = device.getEntityClass().getName() + "::"
+ + device.getMACAddressString();
+ return "DeviceUpdate [device=" + devIdStr + ", change=" + change
+ + ", fieldsChanged=" + fieldsChanged + "]";
+ }
+
+ }
+
+ /**
+ * AttachmentPointComparator
+ *
+ * Compares two attachment points and returns the latest one. It is assumed
+ * that the two attachment points are in the same L2 domain.
+ *
+ * @author srini
+ */
+ protected class AttachmentPointComparator implements
+ Comparator<AttachmentPoint> {
+ public AttachmentPointComparator() {
+ super();
+ }
+
+ @Override
+ public int compare(AttachmentPoint oldAP, AttachmentPoint newAP) {
+ // First compare based on L2 domain ID;
+
+ // XXX - missing functionality -- need topology
+ // long oldDomain = topology.getL2DomainId(oldSw);
+ // boolean oldBD = topology.isBroadcastDomainPort(oldSw, oldPort);
+ long oldDomain = 0;
+ boolean oldBD = false;
+
+ // XXX - missing functionality -- need topology
+ // long newDomain = topology.getL2DomainId(newSw);
+ // boolean newBD = topology.isBroadcastDomainPort(newSw, newPort);
+ long newDomain = 0;
+ boolean newBD = false;
+
+ if (oldDomain < newDomain)
+ return -1;
+ else if (oldDomain > newDomain)
+ return 1;
+
+ // Give preference to OFPP_LOCAL always
+ if (!oldAP.getPort().getType().equals(NodeConnectorIDType.SWSTACK)
+ && newAP.getPort().getType()
+ .equals(NodeConnectorIDType.SWSTACK)) {
+ return -1;
+ } else if (oldAP.getPort().getType()
+ .equals(NodeConnectorIDType.SWSTACK)
+ && !newAP.getPort().getType()
+ .equals(NodeConnectorIDType.SWSTACK)) {
+ return 1;
+ }
+
+ // We expect that the last seen of the new AP is higher than
+ // old AP, if it is not, just reverse and send the negative
+ // of the result.
+ if (oldAP.getActiveSince() > newAP.getActiveSince())
+ return -compare(newAP, oldAP);
+
+ long activeOffset = 0;
+ // XXX - missing functionality -- need topology
+ // if (!topology.isConsistent(oldSw, oldPort, newSw, newPort)) {
+ if (!newBD && oldBD) {
+ return -1;
+ }
+ if (newBD && oldBD) {
+ activeOffset = AttachmentPoint.EXTERNAL_TO_EXTERNAL_TIMEOUT;
+ } else if (newBD && !oldBD) {
+ activeOffset = AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT;
+ }
+
+ // } else {
+ // // The attachment point is consistent.
+ // activeOffset = AttachmentPoint.CONSISTENT_TIMEOUT;
+ // }
+
+ if ((newAP.getActiveSince() > oldAP.getLastSeen() + activeOffset)
+ || (newAP.getLastSeen() > oldAP.getLastSeen()
+ + AttachmentPoint.INACTIVITY_INTERVAL)) {
+ return -1;
+ }
+ return 1;
+ }
+ }
+
+ /**
+ * Comparator for sorting by cluster ID
+ */
+ public AttachmentPointComparator apComparator;
+
+ /**
+ * Switch ports where attachment points shouldn't be learned
+ */
+ private Set<SwitchPort> suppressAPs;
+
+ /**
+ * Periodic task to clean up expired entities
+ */
+ public SingletonTask entityCleanupTask;
+
+ // ********************
+ // Dependency injection
+ // ********************
+
+ void setDataPacketService(IDataPacketService s) {
+ this.dataPacketService = s;
+ }
+
+ void unsetDataPacketService(IDataPacketService s) {
+ if (this.dataPacketService == s) {
+ this.dataPacketService = null;
+ }
+ }
+
+ public void setTopologyManager(ITopologyManager s) {
+ this.topology = s;
+ }
+
+ public void unsetTopologyManager(ITopologyManager s) {
+ if (this.topology == s) {
+ logger.debug("Topology Manager Service removed!");
+ this.topology = null;
+ }
+ }
+
+ private volatile boolean stopped = true;
+ private ScheduledExecutorService ses;
+
+ public void stop() {
+ stopped = true;
+ if (ses != null)
+ ses.shutdownNow();
+ }
+
+ public void start() {
+ this.perClassIndices = new HashSet<EnumSet<DeviceField>>();
+
+ // XXX - TODO need to make it possible to register a non-default
+ // classifier
+ entityClassifier = new DefaultEntityClassifier();
+ this.deviceListeners = new ListenerDispatcher<String, IDeviceListener>();
+ this.suppressAPs = Collections
+ .newSetFromMap(new ConcurrentHashMap<SwitchPort, Boolean>());
+ primaryIndex = new DeviceUniqueIndex(entityClassifier.getKeyFields());
+ secondaryIndexMap = new HashMap<EnumSet<DeviceField>, DeviceIndex>();
+
+ deviceMap = new ConcurrentHashMap<Long, Device>();
+ classStateMap = new ConcurrentHashMap<String, ClassState>();
+ apComparator = new AttachmentPointComparator();
+
+ addIndex(true, EnumSet.of(DeviceField.IPV4));
+
+ // floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
+ // floodlightProvider.addHAListener(this.haListenerDelegate);
+ // if (topology != null)
+ // topology.addListener(this);
+ // flowReconcileMgr.addFlowReconcileListener(this);
+ // entityClassifier.addListener(this);
+
+ stopped = false;
+ // XXX - Should use a common threadpool but this doesn't currently exist
+ ses = Executors.newScheduledThreadPool(1);
+ Runnable ecr = new Runnable() {
+ @Override
+ public void run() {
+ cleanupEntities();
+ if (!stopped)
+ entityCleanupTask.reschedule(ENTITY_CLEANUP_INTERVAL,
+ TimeUnit.SECONDS);
+ }
+ };
+ entityCleanupTask = new SingletonTask(ses, ecr);
+ entityCleanupTask.reschedule(ENTITY_CLEANUP_INTERVAL, TimeUnit.SECONDS);
+
+ /*
+ * XXX Missing functionality if (restApi != null) {
+ * restApi.addRestletRoutable(new DeviceRoutable()); } else {
+ * logger.debug("Could not instantiate REST API"); }
+ */
+
+ registerDeviceManagerDebugCounters();
+
+ /*
+ * XXX Missing functionality try {
+ * this.syncService.registerStore(DEVICE_SYNC_STORE_NAME, Scope.LOCAL);
+ * this.storeClient = this.syncService
+ * .getStoreClient(DEVICE_SYNC_STORE_NAME, String.class,
+ * DeviceSyncRepresentation.class); } catch (SyncException e) { throw
+ * new FloodlightModuleException("Error while setting up sync service",
+ * e); }
+ *
+ * Runnable consolidateStoreRunner = new Runnable() {
+ *
+ * @Override public void run() { deviceSyncManager.consolidateStore();
+ * storeConsolidateTask.reschedule(syncStoreConsolidateIntervalMs,
+ * TimeUnit.MILLISECONDS); debugCounters.flushCounters(); } };
+ * storeConsolidateTask = new SingletonTask(ses,
+ * consolidateStoreRunner); if (isMaster)
+ * storeConsolidateTask.reschedule(syncStoreConsolidateIntervalMs,
+ * TimeUnit.MILLISECONDS);
+ */
+ }
+
+ /**
+ * Periodic task to consolidate entries in the store. I.e., delete entries
+ * in the store that are not known to DeviceManager
+ */
+ // XXX - Missing functionality
+ // private SingletonTask storeConsolidateTask;
+
+ // *********************
+ // IDeviceManagerService
+ // *********************
+
+ @Override
+ public IDevice getDevice(Long deviceKey) {
+ return deviceMap.get(deviceKey);
+ }
+
+ @Override
+ public IDevice findDevice(long macAddress, Short vlan, Integer ipv4Address,
+ NodeConnector port) throws IllegalArgumentException {
+ if (vlan != null && vlan.shortValue() <= 0)
+ vlan = null;
+ if (ipv4Address != null && ipv4Address == 0)
+ ipv4Address = null;
+ Entity e = new Entity(macAddress, vlan, ipv4Address, port, null);
+ if (!allKeyFieldsPresent(e, entityClassifier.getKeyFields())) {
+ throw new IllegalArgumentException("Not all key fields specified."
+ + " Required fields: " + entityClassifier.getKeyFields());
+ }
+ return findDeviceByEntity(e);
+ }
+
+ @Override
+ public IDevice findClassDevice(IEntityClass entityClass, long macAddress,
+ Short vlan, Integer ipv4Address) throws IllegalArgumentException {
+ if (vlan != null && vlan.shortValue() <= 0)
+ vlan = null;
+ if (ipv4Address != null && ipv4Address == 0)
+ ipv4Address = null;
+ Entity e = new Entity(macAddress, vlan, ipv4Address, null, null);
+ if (entityClass == null
+ || !allKeyFieldsPresent(e, entityClass.getKeyFields())) {
+ throw new IllegalArgumentException("Not all key fields and/or "
+ + " no source device specified. Required fields: "
+ + entityClassifier.getKeyFields());
+ }
+ return findDestByEntity(entityClass, e);
+ }
+
+ @Override
+ public Collection<? extends IDevice> getAllDevices() {
+ return Collections.unmodifiableCollection(deviceMap.values());
+ }
+
+ @Override
+ public void addIndex(boolean perClass, EnumSet<DeviceField> keyFields) {
+ if (perClass) {
+ perClassIndices.add(keyFields);
+ } else {
+ secondaryIndexMap.put(keyFields, new DeviceMultiIndex(keyFields));
+ }
+ }
+
+ @Override
+ public Iterator<? extends IDevice> queryDevices(Long macAddress,
+ Short vlan, Integer ipv4Address, NodeConnector port) {
+ DeviceIndex index = null;
+ if (secondaryIndexMap.size() > 0) {
+ EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
+ ipv4Address, port);
+ index = secondaryIndexMap.get(keys);
+ }
+
+ Iterator<Device> deviceIterator = null;
+ if (index == null) {
+ // Do a full table scan
+ deviceIterator = deviceMap.values().iterator();
+ } else {
+ // index lookup
+ Entity entity = new Entity((macAddress == null ? 0 : macAddress),
+ vlan, ipv4Address, port, null);
+ deviceIterator = new DeviceIndexInterator(this,
+ index.queryByEntity(entity));
+ }
+
+ DeviceIterator di = new DeviceIterator(deviceIterator, null,
+ macAddress, vlan, ipv4Address, port);
+ return di;
+ }
+
+ @Override
+ public Iterator<? extends IDevice> queryClassDevices(
+ IEntityClass entityClass, Long macAddress, Short vlan,
+ Integer ipv4Address, NodeConnector port) {
+ ArrayList<Iterator<Device>> iterators = new ArrayList<Iterator<Device>>();
+ ClassState classState = getClassState(entityClass);
+
+ DeviceIndex index = null;
+ if (classState.secondaryIndexMap.size() > 0) {
+ EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
+ ipv4Address, port);
+ index = classState.secondaryIndexMap.get(keys);
+ }
+
+ Iterator<Device> iter;
+ if (index == null) {
+ index = classState.classIndex;
+ if (index == null) {
+ // scan all devices
+ return new DeviceIterator(deviceMap.values().iterator(),
+ new IEntityClass[] { entityClass }, macAddress, vlan,
+ ipv4Address, port);
+ } else {
+ // scan the entire class
+ iter = new DeviceIndexInterator(this, index.getAll());
+ }
+ } else {
+ // index lookup
+ Entity entity = new Entity((macAddress == null ? 0 : macAddress),
+ vlan, ipv4Address, port, null);
+ iter = new DeviceIndexInterator(this, index.queryByEntity(entity));
+ }
+ iterators.add(iter);
+
+ return new MultiIterator<Device>(iterators.iterator());
+ }
+
+ protected Iterator<Device> getDeviceIteratorForQuery(Long macAddress,
+ Short vlan, Integer ipv4Address, NodeConnector port) {
+ DeviceIndex index = null;
+ if (secondaryIndexMap.size() > 0) {
+ EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan,
+ ipv4Address, port);
+ index = secondaryIndexMap.get(keys);
+ }
+
+ Iterator<Device> deviceIterator = null;
+ if (index == null) {
+ // Do a full table scan
+ deviceIterator = deviceMap.values().iterator();
+ } else {
+ // index lookup
+ Entity entity = new Entity((macAddress == null ? 0 : macAddress),
+ vlan, ipv4Address, port, null);
+ deviceIterator = new DeviceIndexInterator(this,
+ index.queryByEntity(entity));
+ }
+
+ DeviceIterator di = new DeviceIterator(deviceIterator, null,
+ macAddress, vlan, ipv4Address, port);
+ return di;
+ }
+
+ @Override
+ public void addListener(IDeviceListener listener) {
+ deviceListeners.addListener("device", listener);
+ logListeners();
+ }
+
+ @Override
+ public void addSuppressAPs(NodeConnector port) {
+ suppressAPs.add(new SwitchPort(port));
+ }
+
+ @Override
+ public void removeSuppressAPs(NodeConnector port) {
+ suppressAPs.remove(new SwitchPort(port));
+ }
+
+ @Override
+ public Set<SwitchPort> getSuppressAPs() {
+ return Collections.unmodifiableSet(suppressAPs);
+ }
+
+ private void logListeners() {
+ List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
+ if (listeners != null) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("DeviceListeners: ");
+ for (IDeviceListener l : listeners) {
+ sb.append(l.getName());
+ sb.append(",");
+ }
+ logger.debug(sb.toString());
+ }
+ }
+
+ // ***************
+ // IFlowReconcileListener
+ // ***************
+ /*
+ * XXX - Missing functionality
+ *
+ * @Override public Command reconcileFlows(ArrayList<OFMatchReconcile>
+ * ofmRcList) { ListIterator<OFMatchReconcile> iter =
+ * ofmRcList.listIterator(); while (iter.hasNext()) { OFMatchReconcile ofm =
+ * iter.next();
+ *
+ * // Remove the STOPPed flow. if (Command.STOP == reconcileFlow(ofm)) {
+ * iter.remove(); } }
+ *
+ * if (ofmRcList.size() > 0) { return Command.CONTINUE; } else { return
+ * Command.STOP; } }
+ *
+ * protected Command reconcileFlow(OFMatchReconcile ofm) {
+ * debugCounters.updateCounter(CNT_RECONCILE_REQUEST); // Extract source
+ * entity information Entity srcEntity =
+ * getEntityFromFlowMod(ofm.ofmWithSwDpid, true); if (srcEntity == null) {
+ * debugCounters.updateCounter(CNT_RECONCILE_NO_SOURCE); return
+ * Command.STOP; }
+ *
+ * // Find the device by source entity Device srcDevice =
+ * findDeviceByEntity(srcEntity); if (srcDevice == null) {
+ * debugCounters.updateCounter(CNT_RECONCILE_NO_SOURCE); return
+ * Command.STOP; } // Store the source device in the context
+ * fcStore.put(ofm.cntx, CONTEXT_SRC_DEVICE, srcDevice);
+ *
+ * // Find the device matching the destination from the entity // classes of
+ * the source. Entity dstEntity = getEntityFromFlowMod(ofm.ofmWithSwDpid,
+ * false); Device dstDevice = null; if (dstEntity != null) { dstDevice =
+ * findDestByEntity(srcDevice.getEntityClass(), dstEntity); if (dstDevice !=
+ * null) fcStore.put(ofm.cntx, CONTEXT_DST_DEVICE, dstDevice); else
+ * debugCounters.updateCounter(CNT_RECONCILE_NO_DEST); } else {
+ * debugCounters.updateCounter(CNT_RECONCILE_NO_DEST); } if
+ * (logger.isTraceEnabled()) {
+ * logger.trace("Reconciling flow: match={}, srcEntity={}, srcDev={}, " +
+ * "dstEntity={}, dstDev={}", new Object[] {ofm.ofmWithSwDpid.getOfMatch(),
+ * srcEntity, srcDevice, dstEntity, dstDevice } ); } return
+ * Command.CONTINUE; }
+ */
+
+ // *****************
+ // IListenDataPacket
+ // *****************
+
+ @Override
+ public PacketResult receiveDataPacket(RawPacket inPkt) {
+ // XXX - Can this really pass in null? Why would you ever want that?
+ if (inPkt == null) {
+ return PacketResult.IGNORED;
+ }
+ try {
+ throw new Exception("Sample");
+ } catch (Exception e) {
+ logger.error("Sample stack trace", e);
+ }
+
+ Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
+ Ethernet eth;
+ if (formattedPak instanceof Ethernet) {
+ eth = (Ethernet) formattedPak;
+ } else {
+ return PacketResult.IGNORED;
+ }
+
+ // Extract source entity information
+ NodeConnector inPort = inPkt.getIncomingNodeConnector();
+ Entity srcEntity = getSourceEntityFromPacket(eth, inPort);
+ if (srcEntity == null) {
+ // debugCounters.updateCounter(CNT_BROADCAST_SOURCE);
+ return PacketResult.CONSUME;
+ }
+
+ // Learn from ARP packet for special VRRP settings.
+ // In VRRP settings, the source MAC address and sender MAC
+ // addresses can be different. In such cases, we need to learn
+ // the IP to MAC mapping of the VRRP IP address. The source
+ // entity will not have that information. Hence, a separate call
+ // to learn devices in such cases.
+ learnDeviceFromArpResponseData(eth, inPort);
+
+ // Learn/lookup device information
+ Device srcDevice = learnDeviceByEntity(srcEntity);
+ if (srcDevice == null) {
+ // debugCounters.updateCounter(CNT_NO_SOURCE);
+ return PacketResult.CONSUME;
+ }
+ logger.trace("Saw packet from device {}", srcDevice);
+
+ // // Store the source device in the context
+ // fcStore.put(cntx, CONTEXT_SRC_DEVICE, srcDevice);
+ //
+ // // Find the device matching the destination from the entity
+ // // classes of the source.
+ // Entity dstEntity = getDestEntityFromPacket(eth);
+ // Device dstDevice = null;
+ // if (dstEntity != null) {
+ // dstDevice =
+ // findDestByEntity(srcDevice.getEntityClass(), dstEntity);
+ // if (dstDevice != null)
+ // fcStore.put(cntx, CONTEXT_DST_DEVICE, dstDevice);
+ // //else
+ // //debugCounters.updateCounter(CNT_NO_DEST);
+ // } else {
+ // //debugCounters.updateCounter(CNT_NO_DEST);
+ // }
+ //
+ // if (logger.isTraceEnabled()) {
+ // logger.trace("Received PI: {} on switch {}, port {} *** eth={}" +
+ // " *** srcDev={} *** dstDev={} *** ",
+ // new Object[] { pi, sw.getStringId(), pi.getInPort(), eth,
+ // srcDevice, dstDevice });
+ // }
+ //
+ // snoopDHCPClientName(eth, srcDevice);
+
+ return PacketResult.KEEP_PROCESSING;
+ }
+
+ // ****************
+ // Internal methods
+ // ****************
+
+ /**
+ * Snoop and record client-provided host name from DHCP requests
+ *
+ * @param eth
+ * @param srcDevice
+ */
+ // private void snoopDHCPClientName(Ethernet eth, Device srcDevice) {
+ // if (! (eth.getPayload() instanceof IPv4) )
+ // return;
+ // IPv4 ipv4 = (IPv4) eth.getPayload();
+ // if (! (ipv4.getPayload() instanceof UDP) )
+ // return;
+ // UDP udp = (UDP) ipv4.getPayload();
+ // if (!(udp.getPayload() instanceof DHCP))
+ // return;
+ // DHCP dhcp = (DHCP) udp.getPayload();
+ // byte opcode = dhcp.getOpCode();
+ // if (opcode == DHCP.OPCODE_REQUEST) {
+ // DHCPOption dhcpOption = dhcp.getOption(
+ // DHCPOptionCode.OptionCode_Hostname);
+ // if (dhcpOption != null) {
+ // debugCounters.updateCounter(CNT_DHCP_CLIENT_NAME_SNOOPED);
+ // srcDevice.dhcpClientName = new String(dhcpOption.getData());
+ // }
+ // }
+ // }
+
+ /**
+ * Check whether the given attachment point is valid given the current
+ * topology
+ *
+ * @param switchDPID
+ * the DPID
+ * @param switchPort
+ * the port
+ * @return true if it's a valid attachment point
+ */
+ public boolean isValidAttachmentPoint(NodeConnector port) {
+ // XXX - missing functionality -- need topology module
+ // if (topology.isAttachmentPointPort(port) == false)
+ // return false;
+ if (topology.isInternal(port))
+ return false;
+ if (!switchManager.isNodeConnectorEnabled(port))
+ return false;
+ if (suppressAPs.contains(new SwitchPort(port)))
+ return false;
+
+ return true;
+ }
+
+ /**
+ * Get sender IP address from packet if the packet is either an ARP packet.
+ *
+ * @param eth
+ * @param dlAddr
+ * @return
+ */
+ private int getSrcNwAddr(Ethernet eth, long dlAddr) {
+ if (eth.getPayload() instanceof ARP) {
+ ARP arp = (ARP) eth.getPayload();
+ if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP)
+ && (toLong(arp.getSenderHardwareAddress()) == dlAddr)) {
+ return toIPv4Address(arp.getSenderProtocolAddress());
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Parse an entity from an {@link Ethernet} packet.
+ *
+ * @param eth
+ * the packet to parse
+ * @param sw
+ * the switch on which the packet arrived
+ * @param pi
+ * the original packetin
+ * @return the entity from the packet
+ */
+ protected Entity getSourceEntityFromPacket(Ethernet eth, NodeConnector port) {
+ byte[] dlAddrArr = eth.getSourceMACAddress();
+ long dlAddr = toLong(dlAddrArr);
+
+ // Ignore broadcast/multicast source
+ if ((dlAddrArr[0] & 0x1) != 0)
+ return null;
+
+ // XXX missing functionality
+ // short vlan = 0;
+ int nwSrc = getSrcNwAddr(eth, dlAddr);
+ return new Entity(dlAddr, null, ((nwSrc != 0) ? nwSrc : null), port,
+ new Date());
+ }
+
+ /**
+ * Learn device from ARP data in scenarios where the Ethernet source MAC is
+ * different from the sender hardware address in ARP data.
+ */
+ protected void learnDeviceFromArpResponseData(Ethernet eth,
+ NodeConnector port) {
+
+ if (!(eth.getPayload() instanceof ARP))
+ return;
+ ARP arp = (ARP) eth.getPayload();
+
+ byte[] dlAddrArr = eth.getSourceMACAddress();
+ long dlAddr = toLong(dlAddrArr);
+
+ byte[] senderHardwareAddr = arp.getSenderHardwareAddress();
+ long senderAddr = toLong(senderHardwareAddr);
+
+ if (dlAddr == senderAddr)
+ return;
+
+ // Ignore broadcast/multicast source
+ if ((senderHardwareAddr[0] & 0x1) != 0)
+ return;
+
+ // short vlan = eth.getVlanID();
+ int nwSrc = toIPv4Address(arp.getSenderProtocolAddress());
+
+ Entity e = new Entity(senderAddr, null, ((nwSrc != 0) ? nwSrc : null),
+ port, new Date());
+
+ learnDeviceByEntity(e);
+ }
+
+ /**
+ * Get a (partial) entity for the destination from the packet.
+ *
+ * @param eth
+ * @return
+ */
+ // protected Entity getDestEntityFromPacket(Ethernet eth) {
+ // byte[] dlAddrArr = eth.getDestinationMACAddress();
+ // long dlAddr = Ethernet.toLong(dlAddrArr);
+ // short vlan = eth.getVlanID();
+ // int nwDst = 0;
+ //
+ // // Ignore broadcast/multicast destination
+ // if ((dlAddrArr[0] & 0x1) != 0)
+ // return null;
+ //
+ // if (eth.getPayload() instanceof IPv4) {
+ // IPv4 ipv4 = (IPv4) eth.getPayload();
+ // nwDst = ipv4.getDestinationAddress();
+ // }
+ //
+ // return new Entity(dlAddr,
+ // ((vlan >= 0) ? vlan : null),
+ // ((nwDst != 0) ? nwDst : null),
+ // null,
+ // null,
+ // null);
+ // }
+
+ /**
+ * Parse an entity from an OFMatchWithSwDpid.
+ *
+ * @param ofmWithSwDpid
+ * @return the entity from the packet
+ */
+ // private Entity getEntityFromFlowMod(OFMatchWithSwDpid ofmWithSwDpid,
+ // boolean isSource) {
+ // byte[] dlAddrArr = ofmWithSwDpid.getOfMatch().getDataLayerSource();
+ // int nwSrc = ofmWithSwDpid.getOfMatch().getNetworkSource();
+ // if (!isSource) {
+ // dlAddrArr = ofmWithSwDpid.getOfMatch().getDataLayerDestination();
+ // nwSrc = ofmWithSwDpid.getOfMatch().getNetworkDestination();
+ // }
+ //
+ // long dlAddr = Ethernet.toLong(dlAddrArr);
+ //
+ // // Ignore broadcast/multicast source
+ // if ((dlAddrArr[0] & 0x1) != 0)
+ // return null;
+ //
+ // Long swDpid = null;
+ // Short inPort = null;
+ //
+ // if (isSource) {
+ // swDpid = ofmWithSwDpid.getSwitchDataPathId();
+ // inPort = ofmWithSwDpid.getOfMatch().getInputPort();
+ // }
+ //
+ // /**for the new flow cache design, the flow mods retrived are not always
+ // from the source, learn AP should be disabled --meiyang*/
+ // boolean learnap = false;
+ // /**
+ // * if (swDpid == null ||
+ // inPort == null ||
+ // !isValidAttachmentPoint(swDpid, inPort)) {
+ // // If this is an internal port or we otherwise don't want
+ // // to learn on these ports. In the future, we should
+ // // handle this case by labeling flows with something that
+ // // will give us the entity class. For now, we'll do our
+ // // best assuming attachment point information isn't used
+ // // as a key field.
+ // learnap = false;
+ // }
+ // */
+ //
+ // short vlan = ofmWithSwDpid.getOfMatch().getDataLayerVirtualLan();
+ // return new Entity(dlAddr,
+ // ((vlan >= 0) ? vlan : null),
+ // ((nwSrc != 0) ? nwSrc : null),
+ // (learnap ? swDpid : null),
+ // (learnap ? (int)inPort : null),
+ // new Date());
+ // }
+
+ /**
+ * Look up a {@link Device} based on the provided {@link Entity}. We first
+ * check the primary index. If we do not find an entry there we classify the
+ * device into its IEntityClass and query the classIndex. This implies that
+ * all key field of the current IEntityClassifier must be present in the
+ * entity for the lookup to succeed!
+ *
+ * @param entity
+ * the entity to search for
+ * @return The {@link Device} object if found
+ */
+ protected Device findDeviceByEntity(Entity entity) {
+ // Look up the fully-qualified entity to see if it already
+ // exists in the primary entity index.
+ Long deviceKey = primaryIndex.findByEntity(entity);
+ IEntityClass entityClass = null;
+
+ if (deviceKey == null) {
+ // If the entity does not exist in the primary entity index,
+ // use the entity classifier for find the classes for the
+ // entity. Look up the entity in the returned class'
+ // class entity index.
+ entityClass = entityClassifier.classifyEntity(entity);
+ if (entityClass == null) {
+ return null;
+ }
+ ClassState classState = getClassState(entityClass);
+
+ if (classState.classIndex != null) {
+ deviceKey = classState.classIndex.findByEntity(entity);
+ }
+ }
+ if (deviceKey == null)
+ return null;
+ return deviceMap.get(deviceKey);
+ }
+
+ /**
+ * Get a destination device using entity fields that corresponds with the
+ * given source device. The source device is important since there could be
+ * ambiguity in the destination device without the attachment point
+ * information.
+ *
+ * @param reference
+ * the source device's entity class. The returned destination
+ * will be in the same entity class as the source.
+ * @param dstEntity
+ * the entity to look up
+ * @return an {@link Device} or null if no device is found.
+ */
+ protected Device findDestByEntity(IEntityClass reference, Entity dstEntity) {
+
+ // Look up the fully-qualified entity to see if it
+ // exists in the primary entity index
+ Long deviceKey = primaryIndex.findByEntity(dstEntity);
+
+ if (deviceKey == null) {
+ // This could happen because:
+ // 1) no destination known, or a broadcast destination
+ // 2) if we have attachment point key fields since
+ // attachment point information isn't available for
+ // destination devices.
+ // For the second case, we'll need to match up the
+ // destination device with the class of the source
+ // device.
+ ClassState classState = getClassState(reference);
+ if (classState.classIndex == null) {
+ return null;
+ }
+ deviceKey = classState.classIndex.findByEntity(dstEntity);
+ }
+ if (deviceKey == null)
+ return null;
+ return deviceMap.get(deviceKey);
+ }
+
+ /**
+ * Look up a {@link Device} within a particular entity class based on the
+ * provided {@link Entity}.
+ *
+ * @param clazz
+ * the entity class to search for the entity
+ * @param entity
+ * the entity to search for
+ * @return The {@link Device} object if found private Device
+ * findDeviceInClassByEntity(IEntityClass clazz, Entity entity) { //
+ * XXX - TODO throw new UnsupportedOperationException(); }
+ */
+
+ /**
+ * Look up a {@link Device} based on the provided {@link Entity}. Also
+ * learns based on the new entity, and will update existing devices as
+ * required.
+ *
+ * @param entity
+ * the {@link Entity}
+ * @return The {@link Device} object if found
+ */
+ protected Device learnDeviceByEntity(Entity entity) {
+ logger.info("Primary index {}", primaryIndex);
+ ArrayList<Long> deleteQueue = null;
+ LinkedList<DeviceUpdate> deviceUpdates = null;
+ Device device = null;
+
+ // we may need to restart the learning process if we detect
+ // concurrent modification. Note that we ensure that at least
+ // one thread should always succeed so we don't get into infinite
+ // starvation loops
+ while (true) {
+ deviceUpdates = null;
+
+ // Look up the fully-qualified entity to see if it already
+ // exists in the primary entity index.
+ Long deviceKey = primaryIndex.findByEntity(entity);
+ IEntityClass entityClass = null;
+
+ if (deviceKey == null) {
+ // If the entity does not exist in the primary entity index,
+ // use the entity classifier for find the classes for the
+ // entity. Look up the entity in the returned class'
+ // class entity index.
+ entityClass = entityClassifier.classifyEntity(entity);
+ if (entityClass == null) {
+ // could not classify entity. No device
+ device = null;
+ break;
+ }
+ ClassState classState = getClassState(entityClass);
+
+ if (classState.classIndex != null) {
+ deviceKey = classState.classIndex.findByEntity(entity);
+ }
+ }
+ if (deviceKey != null) {
+ // If the primary or secondary index contains the entity
+ // use resulting device key to look up the device in the
+ // device map, and use the referenced Device below.
+ device = deviceMap.get(deviceKey);
+ if (device == null) {
+ // This can happen due to concurrent modification
+ if (logger.isDebugEnabled()) {
+ logger.debug("No device for deviceKey {} while "
+ + "while processing entity {}", deviceKey,
+ entity);
+ }
+ // if so, then try again till we don't even get the device
+ // key
+ // and so we recreate the device
+ continue;
+ }
+ } else {
+ // If the secondary index does not contain the entity,
+ // create a new Device object containing the entity, and
+ // generate a new device ID if the the entity is on an
+ // attachment point port. Otherwise ignore.
+ if (entity.hasSwitchPort()
+ && !isValidAttachmentPoint(entity.getPort())) {
+ // debugCounters.updateCounter(CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Not learning new device on internal"
+ + " link: {}", entity);
+ }
+ device = null;
+ break;
+ }
+ // Before we create the new device also check if
+ // the entity is allowed (e.g., for spoofing protection)
+ if (!isEntityAllowed(entity, entityClass)) {
+ // debugCounters.updateCounter(CNT_PACKET_NOT_ALLOWED);
+ if (logger.isDebugEnabled()) {
+ logger.debug("PacketIn is not allowed {} {}",
+ entityClass.getName(), entity);
+ }
+ device = null;
+ break;
+ }
+ synchronized (deviceKeyLock) {
+ deviceKey = Long.valueOf(deviceKeyCounter++);
+ }
+ device = allocateDevice(deviceKey, entity, entityClass);
+
+ // Add the new device to the primary map with a simple put
+ deviceMap.put(deviceKey, device);
+
+ // update indices
+ if (!updateIndices(device, deviceKey)) {
+ if (deleteQueue == null)
+ deleteQueue = new ArrayList<Long>();
+ deleteQueue.add(deviceKey);
+ continue;
+ }
+
+ updateSecondaryIndices(entity, entityClass, deviceKey);
+
+ // We need to count and log here. If we log earlier we could
+ // hit a concurrent modification and restart the dev creation
+ // and potentially count the device twice.
+ // debugCounters.updateCounter(CNT_NEW_DEVICE);
+ if (logger.isDebugEnabled()) {
+ logger.debug(
+ "New device created: {} deviceKey={}, entity={}",
+ new Object[] { device, deviceKey, entity });
+ }
+ // generate new device update
+ deviceUpdates = updateUpdates(deviceUpdates, new DeviceUpdate(
+ device, ADD, null));
+
+ break;
+ }
+ // if it gets here, we have a pre-existing Device for this Entity
+ if (!isEntityAllowed(entity, device.getEntityClass())) {
+ // debugCounters.updateCounter(CNT_PACKET_NOT_ALLOWED);
+ if (logger.isDebugEnabled()) {
+ logger.info("PacketIn is not allowed {} {}", device
+ .getEntityClass().getName(), entity);
+ }
+ return null;
+ }
+ // If this is not an attachment point port we don't learn the new
+ // entity
+ // and don't update indexes. But we do allow the device to continue
+ // up
+ // the chain.
+ if (entity.hasSwitchPort()
+ && !isValidAttachmentPoint(entity.getPort())) {
+ // debugCounters.updateCounter(CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE);
+ break;
+ }
+ int entityindex = -1;
+ if ((entityindex = device.entityIndex(entity)) >= 0) {
+ // Entity already exists
+ // update timestamp on the found entity
+ Date lastSeen = entity.getLastSeenTimestamp();
+ if (lastSeen == null) {
+ lastSeen = new Date();
+ entity.setLastSeenTimestamp(lastSeen);
+ }
+ device.entities[entityindex].setLastSeenTimestamp(lastSeen);
+ // we break the loop after checking for changes to the AP
+ } else {
+ // New entity for this device
+ // compute the insertion point for the entity.
+ // see Arrays.binarySearch()
+ entityindex = -(entityindex + 1);
+ Device newDevice = allocateDevice(device, entity, entityindex);
+
+ // generate updates
+ EnumSet<DeviceField> changedFields = findChangedFields(device,
+ entity);
+
+ // update the device map with a replace call
+ boolean res = deviceMap.replace(deviceKey, device, newDevice);
+ // If replace returns false, restart the process from the
+ // beginning (this implies another thread concurrently
+ // modified this Device).
+ if (!res)
+ continue;
+
+ device = newDevice;
+ // update indices
+ if (!updateIndices(device, deviceKey)) {
+ continue;
+ }
+ updateSecondaryIndices(entity, device.getEntityClass(),
+ deviceKey);
+
+ // We need to count here after all the possible "continue"
+ // statements in this branch
+ // debugCounters.updateCounter(CNT_NEW_ENTITY);
+ if (changedFields.size() > 0) {
+ // debugCounters.updateCounter(CNT_DEVICE_CHANGED);
+ deviceUpdates = updateUpdates(deviceUpdates,
+ new DeviceUpdate(newDevice, CHANGE, changedFields));
+ }
+ // we break the loop after checking for changed AP
+ }
+ // Update attachment point (will only be hit if the device
+ // already existed and no concurrent modification)
+ if (entity.hasSwitchPort()) {
+ boolean moved = device.updateAttachmentPoint(entity.getPort(),
+ entity.getLastSeenTimestamp().getTime());
+ // TODO: use update mechanism instead of sending the
+ // notification directly
+ if (moved) {
+ // we count device moved events in
+ // sendDeviceMovedNotification()
+ sendDeviceMovedNotification(device);
+ if (logger.isTraceEnabled()) {
+ logger.trace("Device moved: attachment points {},"
+ + "entities {}", device.attachmentPoints,
+ device.entities);
+ }
+ } else {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Device attachment point updated: "
+ + "attachment points {}," + "entities {}",
+ device.attachmentPoints, device.entities);
+ }
+ }
+ }
+ break;
+ }
+
+ if (deleteQueue != null) {
+ for (Long l : deleteQueue) {
+ Device dev = deviceMap.get(l);
+ this.deleteDevice(dev);
+ }
+ }
+
+ processUpdates(deviceUpdates);
+ // deviceSyncManager.storeDeviceThrottled(device);
+
+ return device;
+ }
+
+ protected boolean isEntityAllowed(Entity entity, IEntityClass entityClass) {
+ return true;
+ }
+
+ protected EnumSet<DeviceField> findChangedFields(Device device,
+ Entity newEntity) {
+ EnumSet<DeviceField> changedFields = EnumSet.of(DeviceField.IPV4,
+ DeviceField.VLAN, DeviceField.SWITCHPORT);
+
+ if (newEntity.getIpv4Address() == null)
+ changedFields.remove(DeviceField.IPV4);
+ if (newEntity.getVlan() == null)
+ changedFields.remove(DeviceField.VLAN);
+ if (newEntity.getPort() == null)
+ changedFields.remove(DeviceField.SWITCHPORT);
+
+ if (changedFields.size() == 0)
+ return changedFields;
+
+ for (Entity entity : device.getEntities()) {
+ if (newEntity.getIpv4Address() == null
+ || (entity.getIpv4Address() != null && entity
+ .getIpv4Address()
+ .equals(newEntity.getIpv4Address())))
+ changedFields.remove(DeviceField.IPV4);
+ if (newEntity.getVlan() == null
+ || (entity.getVlan() != null && entity.getVlan().equals(
+ newEntity.getVlan())))
+ changedFields.remove(DeviceField.VLAN);
+ if (newEntity.getPort() == null
+ || (entity.getPort() != null && entity.getPort().equals(
+ newEntity.getPort())))
+ changedFields.remove(DeviceField.SWITCHPORT);
+ }
+
+ return changedFields;
+ }
+
+ /**
+ * Send update notifications to listeners
+ *
+ * @param updates
+ * the updates to process.
+ */
+ protected void processUpdates(Queue<DeviceUpdate> updates) {
+ if (updates == null)
+ return;
+ DeviceUpdate update = null;
+ while (null != (update = updates.poll())) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Dispatching device update: {}", update);
+ }
+ // if (update.change == DeviceUpdate.Change.DELETE)
+ // deviceSyncManager.removeDevice(update.device);
+ // else
+ // deviceSyncManager.storeDevice(update.device);
+ List<IDeviceListener> listeners = deviceListeners
+ .getOrderedListeners();
+ notifyListeners(listeners, update);
+ }
+ }
+
+ protected void notifyListeners(List<IDeviceListener> listeners,
+ DeviceUpdate update) {
+ if (listeners == null) {
+ return;
+ }
+ for (IDeviceListener listener : listeners) {
+ switch (update.change) {
+ case ADD:
+ listener.deviceAdded(update.device);
+ break;
+ case DELETE:
+ listener.deviceRemoved(update.device);
+ break;
+ case CHANGE:
+ for (DeviceField field : update.fieldsChanged) {
+ switch (field) {
+ case IPV4:
+ listener.deviceIPV4AddrChanged(update.device);
+ break;
+ case SWITCHPORT:
+ // listener.deviceMoved(update.device);
+ break;
+ case VLAN:
+ listener.deviceVlanChanged(update.device);
+ break;
+ default:
+ logger.debug("Unknown device field changed {}",
+ update.fieldsChanged.toString());
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Check if the entity e has all the keyFields set. Returns false if not
+ *
+ * @param e
+ * entity to check
+ * @param keyFields
+ * the key fields to check e against
+ * @return
+ */
+ protected boolean allKeyFieldsPresent(Entity e,
+ EnumSet<DeviceField> keyFields) {
+ for (DeviceField f : keyFields) {
+ switch (f) {
+ case MAC:
+ // MAC address is always present
+ break;
+ case IPV4:
+ if (e.getIpv4Address() == null)
+ return false;
+ break;
+ case SWITCHPORT:
+ if (e.getPort() == null)
+ return false;
+ break;
+ case VLAN:
+ // FIXME: vlan==null is ambiguous: it can mean: not present
+ // or untagged
+ // if (e.vlan == null) return false;
+ break;
+ default:
+ // we should never get here. unless somebody extended
+ // DeviceFields
+ throw new IllegalStateException();
+ }
+ }
+ return true;
+ }
+
+ private LinkedList<DeviceUpdate> updateUpdates(
+ LinkedList<DeviceUpdate> list, DeviceUpdate update) {
+ if (update == null)
+ return list;
+ if (list == null)
+ list = new LinkedList<DeviceUpdate>();
+ list.add(update);
+
+ return list;
+ }
+
+ /**
+ * Get the secondary index for a class. Will return null if the secondary
+ * index was created concurrently in another thread.
+ *
+ * @param clazz
+ * the class for the index
+ * @return
+ */
+ private ClassState getClassState(IEntityClass clazz) {
+ ClassState classState = classStateMap.get(clazz.getName());
+ if (classState != null)
+ return classState;
+
+ classState = new ClassState(clazz);
+ ClassState r = classStateMap.putIfAbsent(clazz.getName(), classState);
+ if (r != null) {
+ // concurrent add
+ return r;
+ }
+ return classState;
+ }
+
+ /**
+ * Update both the primary and class indices for the provided device. If the
+ * update fails because of an concurrent update, will return false.
+ *
+ * @param device
+ * the device to update
+ * @param deviceKey
+ * the device key for the device
+ * @return true if the update succeeded, false otherwise.
+ */
+ private boolean updateIndices(Device device, Long deviceKey) {
+ if (!primaryIndex.updateIndex(device, deviceKey)) {
+ return false;
+ }
+ IEntityClass entityClass = device.getEntityClass();
+ ClassState classState = getClassState(entityClass);
+
+ if (classState.classIndex != null) {
+ if (!classState.classIndex.updateIndex(device, deviceKey))
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Update the secondary indices for the given entity and associated entity
+ * classes
+ *
+ * @param entity
+ * the entity to update
+ * @param entityClass
+ * the entity class for the entity
+ * @param deviceKey
+ * the device key to set up
+ */
+ private void updateSecondaryIndices(Entity entity,
+ IEntityClass entityClass, Long deviceKey) {
+ for (DeviceIndex index : secondaryIndexMap.values()) {
+ index.updateIndex(entity, deviceKey);
+ }
+ ClassState state = getClassState(entityClass);
+ for (DeviceIndex index : state.secondaryIndexMap.values()) {
+ index.updateIndex(entity, deviceKey);
+ }
+ }
+
+ /**
+ * Clean up expired entities/devices
+ */
+ protected void cleanupEntities() {
+ // debugCounters.updateCounter(CNT_CLEANUP_ENTITIES_RUNS);
+
+ Calendar c = Calendar.getInstance();
+ c.add(Calendar.MILLISECOND, -ENTITY_TIMEOUT);
+ Date cutoff = c.getTime();
+
+ ArrayList<Entity> toRemove = new ArrayList<Entity>();
+ ArrayList<Entity> toKeep = new ArrayList<Entity>();
+
+ Iterator<Device> diter = deviceMap.values().iterator();
+ LinkedList<DeviceUpdate> deviceUpdates = new LinkedList<DeviceUpdate>();
+
+ while (diter.hasNext()) {
+ Device d = diter.next();
+
+ while (true) {
+ deviceUpdates.clear();
+ toRemove.clear();
+ toKeep.clear();
+ for (Entity e : d.getEntities()) {
+ if (e.getLastSeenTimestamp() != null
+ && 0 > e.getLastSeenTimestamp().compareTo(cutoff)) {
+ // individual entity needs to be removed
+ toRemove.add(e);
+ } else {
+ toKeep.add(e);
+ }
+ }
+ if (toRemove.size() == 0) {
+ break;
+ }
+
+ // debugCounters.updateCounter(CNT_ENTITY_REMOVED_TIMEOUT);
+ for (Entity e : toRemove) {
+ removeEntity(e, d.getEntityClass(), d.getDeviceKey(),
+ toKeep);
+ }
+
+ if (toKeep.size() > 0) {
+ Device newDevice = allocateDevice(d.getDeviceKey(),
+ d.getDHCPClientName(), d.oldAPs,
+ d.attachmentPoints, toKeep, d.getEntityClass());
+
+ EnumSet<DeviceField> changedFields = EnumSet
+ .noneOf(DeviceField.class);
+ for (Entity e : toRemove) {
+ changedFields.addAll(findChangedFields(newDevice, e));
+ }
+ DeviceUpdate update = null;
+ if (changedFields.size() > 0) {
+ update = new DeviceUpdate(d, CHANGE, changedFields);
+ }
+
+ if (!deviceMap.replace(newDevice.getDeviceKey(), d,
+ newDevice)) {
+ // concurrent modification; try again
+ // need to use device that is the map now for the next
+ // iteration
+ d = deviceMap.get(d.getDeviceKey());
+ if (null != d)
+ continue;
+ }
+ if (update != null) {
+ // need to count after all possibly continue stmts in
+ // this branch
+ // debugCounters.updateCounter(CNT_DEVICE_CHANGED);
+ deviceUpdates.add(update);
+ }
+ } else {
+ DeviceUpdate update = new DeviceUpdate(d, DELETE, null);
+ if (!deviceMap.remove(d.getDeviceKey(), d)) {
+ // concurrent modification; try again
+ // need to use device that is the map now for the next
+ // iteration
+ d = deviceMap.get(d.getDeviceKey());
+ if (null != d)
+ continue;
+ // debugCounters.updateCounter(CNT_DEVICE_DELETED);
+ }
+ deviceUpdates.add(update);
+ }
+ processUpdates(deviceUpdates);
+ break;
+ }
+ }
+ }
+
+ protected void removeEntity(Entity removed, IEntityClass entityClass,
+ Long deviceKey, Collection<Entity> others) {
+ // Don't count in this method. This method CAN BE called to clean-up
+ // after concurrent device adds/updates and thus counting here
+ // is misleading
+ for (DeviceIndex index : secondaryIndexMap.values()) {
+ index.removeEntityIfNeeded(removed, deviceKey, others);
+ }
+ ClassState classState = getClassState(entityClass);
+ for (DeviceIndex index : classState.secondaryIndexMap.values()) {
+ index.removeEntityIfNeeded(removed, deviceKey, others);
+ }
+
+ primaryIndex.removeEntityIfNeeded(removed, deviceKey, others);
+
+ if (classState.classIndex != null) {
+ classState.classIndex.removeEntityIfNeeded(removed, deviceKey,
+ others);
+ }
+ }
+
+ /**
+ * method to delete a given device, remove all entities first and then
+ * finally delete the device itself.
+ *
+ * @param device
+ */
+ protected void deleteDevice(Device device) {
+ // Don't count in this method. This method CAN BE called to clean-up
+ // after concurrent device adds/updates and thus counting here
+ // is misleading
+ ArrayList<Entity> emptyToKeep = new ArrayList<Entity>();
+ for (Entity entity : device.getEntities()) {
+ this.removeEntity(entity, device.getEntityClass(),
+ device.getDeviceKey(), emptyToKeep);
+ }
+ if (!deviceMap.remove(device.getDeviceKey(), device)) {
+ if (logger.isDebugEnabled())
+ logger.debug("device map does not have this device -"
+ + device.toString());
+ }
+ }
+
+ private EnumSet<DeviceField> getEntityKeys(Long macAddress, Short vlan,
+ Integer ipv4Address, NodeConnector port) {
+ // FIXME: vlan==null is a valid search. Need to handle this
+ // case correctly. Note that the code will still work correctly.
+ // But we might do a full device search instead of using an index.
+ EnumSet<DeviceField> keys = EnumSet.noneOf(DeviceField.class);
+ if (macAddress != null)
+ keys.add(DeviceField.MAC);
+ if (vlan != null)
+ keys.add(DeviceField.VLAN);
+ if (ipv4Address != null)
+ keys.add(DeviceField.IPV4);
+ if (port != null)
+ keys.add(DeviceField.SWITCHPORT);
+ return keys;
+ }
+
+ protected Iterator<Device> queryClassByEntity(IEntityClass clazz,
+ EnumSet<DeviceField> keyFields, Entity entity) {
+ ClassState classState = getClassState(clazz);
+ DeviceIndex index = classState.secondaryIndexMap.get(keyFields);
+ if (index == null)
+ return Collections.<Device> emptySet().iterator();
+ return new DeviceIndexInterator(this, index.queryByEntity(entity));
+ }
+
+ protected Device allocateDevice(Long deviceKey, Entity entity,
+ IEntityClass entityClass) {
+ return new Device(this, deviceKey, entity, entityClass);
+ }
+
+ // TODO: FIX THIS.
+ protected Device allocateDevice(Long deviceKey, String dhcpClientName,
+ List<AttachmentPoint> aps, List<AttachmentPoint> trueAPs,
+ Collection<Entity> entities, IEntityClass entityClass) {
+ return new Device(this, deviceKey, dhcpClientName, aps, trueAPs,
+ entities, entityClass);
+ }
+
+ protected Device allocateDevice(Device device, Entity entity,
+ int insertionpoint) {
+ return new Device(device, entity, insertionpoint);
+ }
+
+ // not used
+ protected Device allocateDevice(Device device, Set<Entity> entities) {
+ List<AttachmentPoint> newPossibleAPs = new ArrayList<AttachmentPoint>();
+ List<AttachmentPoint> newAPs = new ArrayList<AttachmentPoint>();
+ for (Entity entity : entities) {
+ if (entity.getPort() != null) {
+ AttachmentPoint aP = new AttachmentPoint(entity.getPort(), 0);
+ newPossibleAPs.add(aP);
+ }
+ }
+ if (device.attachmentPoints != null) {
+ for (AttachmentPoint oldAP : device.attachmentPoints) {
+ if (newPossibleAPs.contains(oldAP)) {
+ newAPs.add(oldAP);
+ }
+ }
+ }
+ if (newAPs.isEmpty())
+ newAPs = null;
+ Device d = new Device(this, device.getDeviceKey(),
+ device.getDHCPClientName(), newAPs, null, entities,
+ device.getEntityClass());
+ d.updateAttachmentPoint();
+ return d;
+ }
+
+ // *********************
+ // ITopologyManagerAware
+ // *********************
+
+ @Override
+ public void edgeUpdate(List<TopoEdgeUpdate> topoedgeupdateList) {
+ Iterator<Device> diter = deviceMap.values().iterator();
+
+ while (diter.hasNext()) {
+ Device d = diter.next();
+ if (d.updateAttachmentPoint()) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Attachment point changed for device: {}", d);
+ }
+ sendDeviceMovedNotification(d);
+ }
+ }
+ }
+
+ @Override
+ public void edgeOverUtilized(Edge edge) {
+ // nothing to do
+ }
+
+ @Override
+ public void edgeUtilBackToNormal(Edge edge) {
+ // nothing to do
+ }
+
+ // *********************
+ // IEntityClassListener
+ // *********************
+
+ @Override
+ public void entityClassChanged(Set<String> entityClassNames) {
+ /*
+ * iterate through the devices, reclassify the devices that belong to
+ * these entity class names
+ */
+ Iterator<Device> diter = deviceMap.values().iterator();
+ while (diter.hasNext()) {
+ Device d = diter.next();
+ if (d.getEntityClass() == null
+ || entityClassNames.contains(d.getEntityClass().getName()))
+ reclassifyDevice(d);
+ }
+ }
+
+ // *************
+ // Local methods
+ // *************
+ /**
+ * Send update notifications to listeners
+ *
+ * @param updates
+ * the updates to process.
+ */
+ protected void sendDeviceMovedNotification(Device d) {
+ // debugCounters.updateCounter(CNT_DEVICE_MOVED);
+ // deviceSyncManager.storeDevice(d);
+ List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
+ if (listeners != null) {
+ for (IDeviceListener listener : listeners) {
+ listener.deviceMoved(d);
+ }
+ }
+ }
+
+ /**
+ * this method will reclassify and reconcile a device - possibilities are -
+ * create new device(s), remove entities from this device. If the device
+ * entity class did not change then it returns false else true.
+ *
+ * @param device
+ */
+ protected boolean reclassifyDevice(Device device) {
+ // first classify all entities of this device
+ if (device == null) {
+ logger.debug("In reclassify for null device");
+ return false;
+ }
+ boolean needToReclassify = false;
+ for (Entity entity : device.entities) {
+ IEntityClass entityClass = this.entityClassifier
+ .classifyEntity(entity);
+ if (entityClass == null || device.getEntityClass() == null) {
+ needToReclassify = true;
+ break;
+ }
+ if (!entityClass.getName()
+ .equals(device.getEntityClass().getName())) {
+ needToReclassify = true;
+ break;
+ }
+ }
+ if (needToReclassify == false) {
+ return false;
+ }
+
+ // debugCounters.updateCounter(CNT_DEVICE_RECLASSIFY_DELETE);
+ LinkedList<DeviceUpdate> deviceUpdates = new LinkedList<DeviceUpdate>();
+ // delete this device and then re-learn all the entities
+ this.deleteDevice(device);
+ deviceUpdates.add(new DeviceUpdate(device, DeviceUpdate.Change.DELETE,
+ null));
+ if (!deviceUpdates.isEmpty())
+ processUpdates(deviceUpdates);
+ for (Entity entity : device.entities) {
+ this.learnDeviceByEntity(entity);
+ }
+ return true;
+ }
+
+ /**
+ * For testing: sets the interval between writes of the same device to the
+ * device store.
+ *
+ * @param intervalMs
+ */
+ // void setSyncStoreWriteInterval(int intervalMs) {
+ // this.syncStoreWriteIntervalMs = intervalMs;
+ // }
+
+ /**
+ * For testing: sets the time between transition to MASTER and consolidate
+ * store
+ *
+ * @param intervalMs
+ */
+ // void setInitialSyncStoreConsolidateMs(int intervalMs) {
+ // this.initialSyncStoreConsolidateMs = intervalMs;
+ // }
+
+ private long toLong(byte[] address) {
+ long mac = 0;
+ for (int i = 0; i < 6; i++) {
+ long t = (address[i] & 0xffL) << ((5 - i) * 8);
+ mac |= t;
+ }
+ return mac;
+ }
+
+ /**
+ * Accepts an IPv4 address in a byte array and returns the corresponding
+ * 32-bit integer value.
+ *
+ * @param ipAddress
+ * @return
+ */
+ private static int toIPv4Address(byte[] ipAddress) {
+ int ip = 0;
+ for (int i = 0; i < 4; i++) {
+ int t = (ipAddress[i] & 0xff) << ((3 - i) * 8);
+ ip |= t;
+ }
+ return ip;
+ }
+
+ private void registerDeviceManagerDebugCounters() {
+ /*
+ * XXX Missing functionality if (debugCounters == null) {
+ * logger.error("Debug Counter Service not found."); debugCounters = new
+ * NullDebugCounter(); return; }
+ * debugCounters.registerCounter(CNT_INCOMING,
+ * "All incoming packets seen by this module",
+ * CounterType.ALWAYS_COUNT);
+ * debugCounters.registerCounter(CNT_RECONCILE_REQUEST,
+ * "Number of flows that have been received for reconciliation by " +
+ * "this module", CounterType.ALWAYS_COUNT);
+ * debugCounters.registerCounter(CNT_RECONCILE_NO_SOURCE,
+ * "Number of flow reconcile events that failed because no source " +
+ * "device could be identified", CounterType.WARN); // is this really a
+ * warning debugCounters.registerCounter(CNT_RECONCILE_NO_DEST,
+ * "Number of flow reconcile events that failed because no " +
+ * "destination device could be identified", CounterType.WARN); // is
+ * this really a warning
+ * debugCounters.registerCounter(CNT_BROADCAST_SOURCE,
+ * "Number of packetIns that were discarded because the source " +
+ * "MAC was broadcast or multicast", CounterType.WARN);
+ * debugCounters.registerCounter(CNT_NO_SOURCE,
+ * "Number of packetIns that were discarded because the " +
+ * "could not identify a source device. This can happen if a " +
+ * "packet is not allowed, appears on an illegal port, does not " +
+ * "have a valid address space, etc.", CounterType.WARN);
+ * debugCounters.registerCounter(CNT_NO_DEST,
+ * "Number of packetIns that did not have an associated " +
+ * "destination device. E.g., because the destination MAC is " +
+ * "broadcast/multicast or is not yet known to the controller.",
+ * CounterType.ALWAYS_COUNT);
+ * debugCounters.registerCounter(CNT_DHCP_CLIENT_NAME_SNOOPED,
+ * "Number of times a DHCP client name was snooped from a " +
+ * "packetIn.", CounterType.ALWAYS_COUNT);
+ * debugCounters.registerCounter(CNT_DEVICE_ON_INTERAL_PORT_NOT_LEARNED,
+ * "Number of times packetIn was received on an internal port and" +
+ * "no source device is known for the source MAC. The packetIn is " +
+ * "discarded.", CounterType.WARN);
+ * debugCounters.registerCounter(CNT_PACKET_NOT_ALLOWED,
+ * "Number of times a packetIn was not allowed due to spoofing " +
+ * "protection configuration.", CounterType.WARN); // is this really a
+ * warning? debugCounters.registerCounter(CNT_NEW_DEVICE,
+ * "Number of times a new device was learned",
+ * CounterType.ALWAYS_COUNT); debugCounters.registerCounter(
+ * CNT_PACKET_ON_INTERNAL_PORT_FOR_KNOWN_DEVICE,
+ * "Number of times a packetIn was received on an internal port " +
+ * "for a known device.", CounterType.ALWAYS_COUNT);
+ * debugCounters.registerCounter(CNT_NEW_ENTITY,
+ * "Number of times a new entity was learned for an existing device",
+ * CounterType.ALWAYS_COUNT);
+ * debugCounters.registerCounter(CNT_DEVICE_CHANGED,
+ * "Number of times device properties have changed",
+ * CounterType.ALWAYS_COUNT);
+ * debugCounters.registerCounter(CNT_DEVICE_MOVED,
+ * "Number of times devices have moved", CounterType.ALWAYS_COUNT);
+ * debugCounters.registerCounter(CNT_CLEANUP_ENTITIES_RUNS,
+ * "Number of times the entity cleanup task has been run",
+ * CounterType.ALWAYS_COUNT);
+ * debugCounters.registerCounter(CNT_ENTITY_REMOVED_TIMEOUT,
+ * "Number of times entities have been removed due to timeout " +
+ * "(entity has been inactive for " + ENTITY_TIMEOUT/1000 + "s)",
+ * CounterType.ALWAYS_COUNT);
+ * debugCounters.registerCounter(CNT_DEVICE_DELETED,
+ * "Number of devices that have been removed due to inactivity",
+ * CounterType.ALWAYS_COUNT);
+ * debugCounters.registerCounter(CNT_DEVICE_RECLASSIFY_DELETE,
+ * "Number of devices that required reclassification and have been " +
+ * "temporarily delete for reclassification", CounterType.ALWAYS_COUNT);
+ * debugCounters.registerCounter(CNT_DEVICE_STORED,
+ * "Number of device entries written or updated to the sync store",
+ * CounterType.ALWAYS_COUNT);
+ * debugCounters.registerCounter(CNT_DEVICE_STORE_THROTTLED,
+ * "Number of times a device update to the sync store was " +
+ * "requested but not performed because the same device entities " +
+ * "have recently been updated already", CounterType.ALWAYS_COUNT);
+ * debugCounters.registerCounter(CNT_DEVICE_REMOVED_FROM_STORE,
+ * "Number of devices that were removed from the sync store " +
+ * "because the local controller removed the device due to " +
+ * "inactivity", CounterType.ALWAYS_COUNT);
+ * debugCounters.registerCounter(CNT_SYNC_EXCEPTION,
+ * "Number of times an operation on the sync store resulted in " +
+ * "sync exception", CounterType.WARN); // it this an error?
+ * debugCounters.registerCounter(CNT_DEVICES_FROM_STORE,
+ * "Number of devices that were read from the sync store after " +
+ * "the local controller transitioned from SLAVE to MASTER",
+ * CounterType.ALWAYS_COUNT);
+ * debugCounters.registerCounter(CNT_CONSOLIDATE_STORE_RUNS,
+ * "Number of times the task to consolidate entries in the " +
+ * "store witch live known devices has been run",
+ * CounterType.ALWAYS_COUNT);
+ * debugCounters.registerCounter(CNT_CONSOLIDATE_STORE_DEVICES_REMOVED,
+ * "Number of times a device has been removed from the sync " +
+ * "store because no corresponding live device is known. " +
+ * "This indicates a remote controller still writing device " +
+ * "entries despite the local controller being MASTER or an " +
+ * "incosistent store update from the local controller.",
+ * CounterType.WARN);
+ * debugCounters.registerCounter(CNT_TRANSITION_TO_MASTER,
+ * "Number of times this controller has transitioned from SLAVE " +
+ * "to MASTER role. Will be 0 or 1.", CounterType.ALWAYS_COUNT);
+ */
+ }
+
+ /**
+ * For testing: consolidate the store NOW
+ */
+ // void scheduleConsolidateStoreNow() {
+ // this.storeConsolidateTask.reschedule(0, TimeUnit.MILLISECONDS);
+ // }
+
+ // private class DeviceSyncManager {
+ // // maps (opaque) deviceKey to the time in System.nanoTime() when we
+ // // last wrote the device to the sync store
+ // private ConcurrentMap<Long, Long> lastWriteTimes =
+ // new ConcurrentHashMap<Long, Long>();
+ //
+ // /**
+ // * Write the given device to storage if we are MASTER.
+ // * Use this method if the device has significantly changed (e.g.,
+ // * new AP, new IP, entities removed).
+ // * @param d the device to store
+ // */
+ // public void storeDevice(Device d) {
+ // if (!isMaster)
+ // return;
+ // if (d == null)
+ // return;
+ // long now = System.nanoTime();
+ // writeUpdatedDeviceToStorage(d);
+ // lastWriteTimes.put(d.getDeviceKey(), now);
+ // }
+ //
+ // /**
+ // * Write the given device to storage if we are MASTER and if the
+ // * last write for the device was more than this.syncStoreIntervalNs
+ // * time ago.
+ // * Use this method to updated last active times in the store.
+ // * @param d the device to store
+ // */
+ // public void storeDeviceThrottled(Device d) {
+ // long intervalNs = syncStoreWriteIntervalMs*1000L*1000L;
+ // if (!isMaster)
+ // return;
+ // if (d == null)
+ // return;
+ // long now = System.nanoTime();
+ // Long last = lastWriteTimes.get(d.getDeviceKey());
+ // if (last == null ||
+ // now - last > intervalNs) {
+ // writeUpdatedDeviceToStorage(d);
+ // lastWriteTimes.put(d.getDeviceKey(), now);
+ // } else {
+ // debugCounters.updateCounter(CNT_DEVICE_STORE_THROTTLED);
+ // }
+ // }
+ //
+ // /**
+ // * Remove the given device from the store. If only some entities have
+ // * been removed the updated device should be written using
+ // * {@link #storeDevice(Device)}
+ // * @param d
+ // */
+ // public void removeDevice(Device d) {
+ // if (!isMaster)
+ // return;
+ // // FIXME: could we have a problem with concurrent put to the
+ // // hashMap? I.e., we write a stale entry to the map after the
+ // // delete and now are left with an entry we'll never clean up
+ // lastWriteTimes.remove(d.getDeviceKey());
+ // try {
+ // // TODO: should probably do versioned delete. OTOH, even
+ // // if we accidentally delete, we'll write it again after
+ // // the next entity ....
+ // debugCounters.updateCounter(CNT_DEVICE_REMOVED_FROM_STORE);
+ // storeClient.delete(DeviceSyncRepresentation.computeKey(d));
+ // } catch(ObsoleteVersionException e) {
+ // // FIXME
+ // } catch (SyncException e) {
+ // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
+ // logger.error("Could not remove device " + d + " from store", e);
+ // }
+ // }
+ //
+ // /**
+ // * Remove the given Versioned device from the store. If the device
+ // * was locally modified ignore the delete request.
+ // * @param syncedDeviceKey
+ // */
+ // private void removeDevice(Versioned<DeviceSyncRepresentation> dev) {
+ // try {
+ // debugCounters.updateCounter(CNT_DEVICE_REMOVED_FROM_STORE);
+ // storeClient.delete(dev.getValue().getKey(),
+ // dev.getVersion());
+ // } catch(ObsoleteVersionException e) {
+ // // Key was locally modified by another thread.
+ // // Do not delete and ignore.
+ // } catch(SyncException e) {
+ // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
+ // logger.error("Failed to remove device entry for " +
+ // dev.toString() + " from store.", e);
+ // }
+ // }
+ //
+ // /**
+ // * Synchronously transition from SLAVE to MASTER. By iterating through
+ // * the store and learning all devices from the store
+ // */
+ // private void goToMaster() {
+ // if (logger.isDebugEnabled()) {
+ // logger.debug("Transitioning to MASTER role");
+ // }
+ // debugCounters.updateCounter(CNT_TRANSITION_TO_MASTER);
+ // IClosableIterator<Map.Entry<String,Versioned<DeviceSyncRepresentation>>>
+ // iter = null;
+ // try {
+ // iter = storeClient.entries();
+ // } catch (SyncException e) {
+ // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
+ // logger.error("Failed to read devices from sync store", e);
+ // return;
+ // }
+ // try {
+ // while(iter.hasNext()) {
+ // Versioned<DeviceSyncRepresentation> versionedDevice =
+ // iter.next().getValue();
+ // DeviceSyncRepresentation storedDevice =
+ // versionedDevice.getValue();
+ // if (storedDevice == null)
+ // continue;
+ // debugCounters.updateCounter(CNT_DEVICES_FROM_STORE);
+ // for(SyncEntity se: storedDevice.getEntities()) {
+ // learnDeviceByEntity(se.asEntity());
+ // }
+ // }
+ // } finally {
+ // if (iter != null)
+ // iter.close();
+ // }
+ // storeConsolidateTask.reschedule(initialSyncStoreConsolidateMs,
+ // TimeUnit.MILLISECONDS);
+ // }
+ //
+ // /**
+ // * Actually perform the write of the device to the store
+ // * FIXME: concurrent modification behavior
+ // * @param device The device to write
+ // */
+ // private void writeUpdatedDeviceToStorage(Device device) {
+ // try {
+ // debugCounters.updateCounter(CNT_DEVICE_STORED);
+ // // FIXME: use a versioned put
+ // DeviceSyncRepresentation storeDevice =
+ // new DeviceSyncRepresentation(device);
+ // storeClient.put(storeDevice.getKey(), storeDevice);
+ // } catch (ObsoleteVersionException e) {
+ // // FIXME: what's the right behavior here. Can the store client
+ // // even throw this error?
+ // } catch (SyncException e) {
+ // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
+ // logger.error("Could not write device " + device +
+ // " to sync store:", e);
+ // }
+ // }
+ //
+ // /**
+ // * Iterate through all entries in the sync store. For each device
+ // * in the store check if any stored entity matches a live device. If
+ // * no entities match a live device we remove the entry from the store.
+ // *
+ // * Note: we do not check if all devices known to device manager are
+ // * in the store. We rely on regular packetIns for that.
+ // * Note: it's possible that multiple entries in the store map to the
+ // * same device. We don't check or handle this case.
+ // *
+ // * We need to perform this check after a SLAVE->MASTER transition to
+ // * get rid of all entries the old master might have written to the
+ // * store after we took over. We also run it regularly in MASTER
+ // * state to ensure we don't have stale entries in the store
+ // */
+ // private void consolidateStore() {
+ // if (!isMaster)
+ // return;
+ // debugCounters.updateCounter(CNT_CONSOLIDATE_STORE_RUNS);
+ // if (logger.isDebugEnabled()) {
+ // logger.debug("Running consolidateStore.");
+ // }
+ // IClosableIterator<Map.Entry<String,Versioned<DeviceSyncRepresentation>>>
+ // iter = null;
+ // try {
+ // iter = storeClient.entries();
+ // } catch (SyncException e) {
+ // debugCounters.updateCounter(CNT_SYNC_EXCEPTION);
+ // logger.error("Failed to read devices from sync store", e);
+ // return;
+ // }
+ // try {
+ // while(iter.hasNext()) {
+ // boolean found = false;
+ // Versioned<DeviceSyncRepresentation> versionedDevice =
+ // iter.next().getValue();
+ // DeviceSyncRepresentation storedDevice =
+ // versionedDevice.getValue();
+ // if (storedDevice == null)
+ // continue;
+ // for(SyncEntity se: storedDevice.getEntities()) {
+ // try {
+ // // Do we have a device for this entity??
+ // IDevice d = findDevice(se.macAddress, se.vlan,
+ // se.ipv4Address,
+ // se.switchDPID,
+ // se.switchPort);
+ // if (d != null) {
+ // found = true;
+ // break;
+ // }
+ // } catch (IllegalArgumentException e) {
+ // // not all key fields provided. Skip entity
+ // }
+ // }
+ // if (!found) {
+ // // We currently DO NOT have a live device that
+ // // matches the current device from the store.
+ // // Delete device from store.
+ // if (logger.isDebugEnabled()) {
+ // logger.debug("Removing device {} from store. No "
+ // + "corresponding live device",
+ // storedDevice.getKey());
+ // }
+ // debugCounters.updateCounter(CNT_CONSOLIDATE_STORE_DEVICES_REMOVED);
+ // removeDevice(versionedDevice);
+ // }
+ // }
+ // } finally {
+ // if (iter != null)
+ // iter.close();
+ // }
+ // }
+ // }
+ //
+ //
+ // /**
+ // * For testing. Sets the syncService. Only call after init but before
+ // * startUp. Used by MockDeviceManager
+ // * @param syncService
+ // */
+ // protected void setSyncServiceIfNotSet(ISyncService syncService) {
+ // if (this.syncService == null)
+ // this.syncService = syncService;
+ // }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker.internal;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.opendaylight.controller.hosttracker.Entity;
+import org.opendaylight.controller.hosttracker.IDeviceService.DeviceField;
+import org.opendaylight.controller.sal.utils.IterableIterator;
+
+/**
+ * An index that maps key fields of an entity to device keys, with multiple
+ * device keys allowed per entity
+ */
+public class DeviceMultiIndex extends DeviceIndex {
+ /**
+ * The index
+ */
+ private ConcurrentHashMap<IndexedEntity, Collection<Long>> index;
+
+ /**
+ * @param keyFields
+ */
+ public DeviceMultiIndex(EnumSet<DeviceField> keyFields) {
+ super(keyFields);
+ index = new ConcurrentHashMap<IndexedEntity, Collection<Long>>();
+ }
+
+ // ***********
+ // DeviceIndex
+ // ***********
+
+ @Override
+ public Iterator<Long> queryByEntity(Entity entity) {
+ IndexedEntity ie = new IndexedEntity(keyFields, entity);
+ Collection<Long> devices = index.get(ie);
+ if (devices != null)
+ return devices.iterator();
+
+ return Collections.<Long> emptySet().iterator();
+ }
+
+ @Override
+ public Iterator<Long> getAll() {
+ Iterator<Collection<Long>> iter = index.values().iterator();
+ return new IterableIterator<Long>(iter);
+ }
+
+ @Override
+ public boolean updateIndex(Device device, Long deviceKey) {
+ for (Entity e : device.entities) {
+ updateIndex(e, deviceKey);
+ }
+ return true;
+ }
+
+ @Override
+ public void updateIndex(Entity entity, Long deviceKey) {
+ Collection<Long> devices = null;
+
+ IndexedEntity ie = new IndexedEntity(keyFields, entity);
+ if (!ie.hasNonNullKeys())
+ return;
+
+ devices = index.get(ie);
+ if (devices == null) {
+ Map<Long, Boolean> chm = new ConcurrentHashMap<Long, Boolean>();
+ devices = Collections.newSetFromMap(chm);
+ Collection<Long> r = index.putIfAbsent(ie, devices);
+ if (r != null)
+ devices = r;
+ }
+
+ devices.add(deviceKey);
+ }
+
+ @Override
+ public void removeEntity(Entity entity) {
+ IndexedEntity ie = new IndexedEntity(keyFields, entity);
+ index.remove(ie);
+ }
+
+ @Override
+ public void removeEntity(Entity entity, Long deviceKey) {
+ IndexedEntity ie = new IndexedEntity(keyFields, entity);
+ Collection<Long> devices = index.get(ie);
+ if (devices != null)
+ devices.remove(deviceKey);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker.internal;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.opendaylight.controller.hosttracker.Entity;
+import org.opendaylight.controller.hosttracker.IDeviceService;
+
+/**
+ * An index that maps key fields of an entity uniquely to a device key
+ */
+public class DeviceUniqueIndex extends DeviceIndex {
+ /**
+ * The index
+ */
+ private final ConcurrentHashMap<IndexedEntity, Long> index;
+
+ /**
+ * Construct a new device index using the provided key fields
+ *
+ * @param keyFields
+ * the key fields to use
+ */
+ public DeviceUniqueIndex(EnumSet<IDeviceService.DeviceField> keyFields) {
+ super(keyFields);
+ index = new ConcurrentHashMap<IndexedEntity, Long>();
+ }
+
+ // ***********
+ // DeviceIndex
+ // ***********
+
+ @Override
+ public Iterator<Long> queryByEntity(Entity entity) {
+ final Long deviceKey = findByEntity(entity);
+ if (deviceKey != null)
+ return Collections.<Long> singleton(deviceKey).iterator();
+
+ return Collections.<Long> emptySet().iterator();
+ }
+
+ @Override
+ public Iterator<Long> getAll() {
+ return index.values().iterator();
+ }
+
+ @Override
+ public boolean updateIndex(Device device, Long deviceKey) {
+ for (Entity e : device.entities) {
+ IndexedEntity ie = new IndexedEntity(keyFields, e);
+ if (!ie.hasNonNullKeys())
+ continue;
+
+ Long ret = index.putIfAbsent(ie, deviceKey);
+ if (ret != null && !ret.equals(deviceKey)) {
+ // If the return value is non-null, then fail the insert
+ // (this implies that a device using this entity has
+ // already been created in another thread).
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void updateIndex(Entity entity, Long deviceKey) {
+ IndexedEntity ie = new IndexedEntity(keyFields, entity);
+ if (!ie.hasNonNullKeys())
+ return;
+ index.put(ie, deviceKey);
+ }
+
+ @Override
+ public void removeEntity(Entity entity) {
+ IndexedEntity ie = new IndexedEntity(keyFields, entity);
+ index.remove(ie);
+ }
+
+ @Override
+ public void removeEntity(Entity entity, Long deviceKey) {
+ IndexedEntity ie = new IndexedEntity(keyFields, entity);
+ index.remove(ie, deviceKey);
+ }
+
+ // **************
+ // Public Methods
+ // **************
+
+ /**
+ * Look up a {@link Device} based on the provided {@link Entity}.
+ *
+ * @param entity
+ * the entity to search for
+ * @return The key for the {@link Device} object if found
+ */
+ public Long findByEntity(Entity entity) {
+ IndexedEntity ie = new IndexedEntity(keyFields, entity);
+ Long deviceKey = index.get(ie);
+ if (deviceKey == null)
+ return null;
+ return deviceKey;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker.internal;
+
+import java.util.EnumSet;
+
+import org.opendaylight.controller.hosttracker.Entity;
+import org.opendaylight.controller.hosttracker.IDeviceService;
+import org.opendaylight.controller.hosttracker.IDeviceService.DeviceField;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is a thin wrapper around {@link Entity} that allows overriding the
+ * behavior of {@link Object#hashCode()} and {@link Object#equals(Object)} so
+ * that the keying behavior in a hash map can be changed dynamically
+ *
+ * @author readams
+ */
+public class IndexedEntity {
+ protected EnumSet<DeviceField> keyFields;
+ protected Entity entity;
+ private int hashCode = 0;
+ protected static Logger logger = LoggerFactory
+ .getLogger(IndexedEntity.class);
+
+ /**
+ * Create a new {@link IndexedEntity} for the given {@link Entity} using the
+ * provided key fields.
+ *
+ * @param keyFields
+ * The key fields that will be used for computing
+ * {@link IndexedEntity#hashCode()} and
+ * {@link IndexedEntity#equals(Object)}
+ * @param entity
+ * the entity to wrap
+ */
+ public IndexedEntity(EnumSet<DeviceField> keyFields, Entity entity) {
+ super();
+ this.keyFields = keyFields;
+ this.entity = entity;
+ }
+
+ /**
+ * Check whether this entity has non-null values in any of its key fields
+ *
+ * @return true if any key fields have a non-null value
+ */
+ public boolean hasNonNullKeys() {
+ for (DeviceField f : keyFields) {
+ switch (f) {
+ case MAC:
+ return true;
+ case IPV4:
+ if (entity.getIpv4Address() != null)
+ return true;
+ break;
+ case SWITCHPORT:
+ if (entity.getPort() != null)
+ return true;
+ break;
+ case VLAN:
+ if (entity.getVlan() != null)
+ return true;
+ break;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+
+ if (hashCode != 0) {
+ return hashCode;
+ }
+
+ final int prime = 31;
+ hashCode = 1;
+ for (DeviceField f : keyFields) {
+ switch (f) {
+ case MAC:
+ hashCode = prime
+ * hashCode
+ + (int) (entity.getMacAddress() ^ (entity
+ .getMacAddress() >>> 32));
+ break;
+ case IPV4:
+ hashCode = prime
+ * hashCode
+ + ((entity.getIpv4Address() == null) ? 0 : entity
+ .getIpv4Address().hashCode());
+ break;
+ case SWITCHPORT:
+ hashCode = prime
+ * hashCode
+ + ((entity.getPort() == null) ? 0 : entity.getPort()
+ .hashCode());
+ break;
+ case VLAN:
+ hashCode = prime
+ * hashCode
+ + ((entity.getVlan() == null) ? 0 : entity.getVlan()
+ .hashCode());
+ break;
+ }
+ }
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ IndexedEntity other = (IndexedEntity) obj;
+
+ if (!keyFields.equals(other.keyFields))
+ return false;
+
+ for (IDeviceService.DeviceField f : keyFields) {
+ switch (f) {
+ case MAC:
+ if (entity.getMacAddress() != other.entity.getMacAddress())
+ return false;
+ break;
+ case IPV4:
+ if (entity.getIpv4Address() == null) {
+ if (other.entity.getIpv4Address() != null)
+ return false;
+ } else if (!entity.getIpv4Address().equals(
+ other.entity.getIpv4Address()))
+ return false;
+ break;
+ case SWITCHPORT:
+ if (entity.getPort() == null) {
+ if (other.entity.getPort() != null)
+ return false;
+ } else if (!entity.getPort().equals(other.entity.getPort()))
+ return false;
+ break;
+ case VLAN:
+ if (entity.getVlan() == null) {
+ if (other.entity.getVlan() != null)
+ return false;
+ } else if (!entity.getVlan().equals(other.entity.getVlan()))
+ return false;
+ break;
+ }
+ }
+
+ return true;
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.hosttracker.internal;
+
+///*
+// * Copyright (c) 2011,2013 Big Switch Networks, Inc.
+// *
+// * Licensed under the Eclipse Public License, Version 1.0 (the
+// * "License"); you may not use this file except in compliance with the
+// * License. You may obtain a copy of the License at
+// *
+// * http://www.eclipse.org/legal/epl-v10.html
+// *
+// * Unless required by applicable law or agreed to in writing, software
+// * distributed under the License is distributed on an "AS IS" BASIS,
+// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// * implied. See the License for the specific language governing
+// * permissions and limitations under the License.
+// *
+// * This file incorporates work covered by the following copyright and
+// * permission notice:
+// *
+// * Originally created by David Erickson, Stanford University
+// *
+// * Licensed under the Apache License, Version 2.0 (the "License");
+// * you may not use this file except in compliance with the
+// * License. You may obtain a copy of the License at
+// *
+// * http://www.apache.org/licenses/LICENSE-2.0
+// *
+// * Unless required by applicable law or agreed to in writing,
+// * software distributed under the License is distributed on an "AS
+// * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+// * express or implied. See the License for the specific language
+// * governing permissions and limitations under the License.
+// */
+//
+//package org.opendaylight.controller.hosttracker.internal;
+//
+//import static org.easymock.EasyMock.anyLong;
+//import static org.easymock.EasyMock.anyObject;
+//import static org.easymock.EasyMock.anyShort;
+//import static org.easymock.EasyMock.createMock;
+//import static org.easymock.EasyMock.createNiceMock;
+//import static org.easymock.EasyMock.eq;
+//import static org.easymock.EasyMock.expect;
+//import static org.easymock.EasyMock.expectLastCall;
+//import static org.easymock.EasyMock.isA;
+//import static org.easymock.EasyMock.or;
+//import static org.easymock.EasyMock.replay;
+//import static org.easymock.EasyMock.reset;
+//import static org.easymock.EasyMock.verify;
+//import static org.junit.Assert.*;
+//
+//import java.util.ArrayList;
+//import java.util.Arrays;
+//import java.util.Calendar;
+//import java.util.Collection;
+//import java.util.Collections;
+//import java.util.Date;
+//import java.util.EnumSet;
+//import java.util.HashMap;
+//import java.util.HashSet;
+//import java.util.Iterator;
+//import java.util.List;
+//import java.util.Map;
+//import java.util.Map.Entry;
+//import java.util.Set;
+//import java.util.concurrent.ConcurrentHashMap;
+//
+//import org.junit.Before;
+//import org.junit.Test;
+//
+//import org.slf4j.Logger;
+//import org.slf4j.LoggerFactory;
+//
+//public class DeviceManagerImplTest {
+//
+// protected static Logger logger =
+// LoggerFactory.getLogger(DeviceManagerImplTest.class);
+//
+// protected OFPacketIn packetIn_1, packetIn_2, packetIn_3;
+// protected IPacket testARPReplyPacket_1, testARPReplyPacket_2,
+// testARPReplyPacket_3;
+// protected IPacket testARPReqPacket_1, testARPReqPacket_2;
+// protected byte[] testARPReplyPacket_1_Srld, testARPReplyPacket_2_Srld;
+// private MockSyncService syncService;
+// private IStoreClient<String, DeviceSyncRepresentation> storeClient;
+//
+// DeviceManagerImpl deviceManager;
+// MemoryStorageSource storageSource;
+// FlowReconcileManager flowReconcileMgr;
+//
+// private IOFSwitch makeSwitchMock(long id) {
+// IOFSwitch mockSwitch = createMock(IOFSwitch.class);
+// ImmutablePort port = ImmutablePort.create("p1", (short)1);
+// expect(mockSwitch.getId()).andReturn(id).anyTimes();
+// expect(mockSwitch.getStringId())
+// .andReturn(HexString.toHexString(id, 6)).anyTimes();
+// expect(mockSwitch.getPort(anyShort()))
+// .andReturn(port).anyTimes();
+// return mockSwitch;
+// }
+//
+// /*
+// * return an EasyMock ITopologyService that's setup so that it will
+// * answer all questions a device or device manager will ask
+// * (isAttachmentPointPort, etc.) in a way so that every port is a
+// * non-BD, attachment point port.
+// * The returned mock is still in record mode
+// */
+// private ITopologyService makeMockTopologyAllPortsAp() {
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// mockTopology.isAttachmentPointPort(anyLong(), anyShort());
+// expectLastCall().andReturn(true).anyTimes();
+// mockTopology.getL2DomainId(anyLong());
+// expectLastCall().andReturn(1L).anyTimes();
+// mockTopology.isBroadcastDomainPort(anyLong(), anyShort());
+// expectLastCall().andReturn(false).anyTimes();
+// mockTopology.isConsistent(anyLong(), anyShort(), anyLong(), anyShort());
+// expectLastCall().andReturn(false).anyTimes();
+// mockTopology.isInSameBroadcastDomain(anyLong(), anyShort(),
+// anyLong(), anyShort());
+// expectLastCall().andReturn(false).anyTimes();
+// return mockTopology;
+// }
+//
+// @Override
+// @Before
+// public void setUp() throws Exception {
+// doSetUp(Role.MASTER);
+// }
+//
+// public void doSetUp(Role initialRole) throws Exception {
+// super.setUp();
+//
+// this.syncService = new MockSyncService();
+//
+// FloodlightModuleContext fmc = new FloodlightModuleContext();
+// RestApiServer restApi = new RestApiServer();
+// MockThreadPoolService tp = new MockThreadPoolService();
+// ITopologyService topology = createMock(ITopologyService.class);
+// fmc.addService(IThreadPoolService.class, tp);
+// mockFloodlightProvider = getMockFloodlightProvider();
+// mockFloodlightProvider.setRole(initialRole, "");
+//
+// deviceManager = new DeviceManagerImpl();
+// flowReconcileMgr = new FlowReconcileManager();
+// DefaultEntityClassifier entityClassifier = new DefaultEntityClassifier();
+// fmc.addService(IDeviceService.class, deviceManager);
+// storageSource = new MemoryStorageSource();
+// fmc.addService(IStorageSourceService.class, storageSource);
+// fmc.addService(IFloodlightProviderService.class, mockFloodlightProvider);
+// fmc.addService(IRestApiService.class, restApi);
+// fmc.addService(IFlowReconcileService.class, flowReconcileMgr);
+// fmc.addService(IEntityClassifierService.class, entityClassifier);
+// fmc.addService(ITopologyService.class, topology);
+// fmc.addService(ISyncService.class, syncService);
+// tp.init(fmc);
+// restApi.init(fmc);
+// storageSource.init(fmc);
+// deviceManager.init(fmc);
+// flowReconcileMgr.init(fmc);
+// entityClassifier.init(fmc);
+// syncService.init(fmc);
+// storageSource.startUp(fmc);
+// deviceManager.startUp(fmc);
+// flowReconcileMgr.startUp(fmc);
+// tp.startUp(fmc);
+// entityClassifier.startUp(fmc);
+// syncService.startUp(fmc);
+//
+// this.storeClient =
+// this.syncService.getStoreClient(DeviceManagerImpl.DEVICE_SYNC_STORE_NAME,
+// String.class, DeviceSyncRepresentation.class);
+//
+// reset(topology);
+// topology.addListener(deviceManager);
+// expectLastCall().anyTimes();
+// replay(topology);
+//
+// IOFSwitch mockSwitch1 = makeSwitchMock(1L);
+// IOFSwitch mockSwitch10 = makeSwitchMock(10L);
+// IOFSwitch mockSwitch5 = makeSwitchMock(5L);
+// IOFSwitch mockSwitch50 = makeSwitchMock(50L);
+// Map<Long, IOFSwitch> switches = new HashMap<Long,IOFSwitch>();
+// switches.put(1L, mockSwitch1);
+// switches.put(10L, mockSwitch10);
+// switches.put(5L, mockSwitch5);
+// switches.put(50L, mockSwitch50);
+// mockFloodlightProvider.setSwitches(switches);
+//
+// replay(mockSwitch1, mockSwitch5, mockSwitch10, mockSwitch50);
+//
+// // Build our test packet
+// this.testARPReplyPacket_1 = new Ethernet()
+// .setSourceMACAddress("00:44:33:22:11:01")
+// .setDestinationMACAddress("00:11:22:33:44:55")
+// .setEtherType(Ethernet.TYPE_ARP)
+// .setVlanID((short)5)
+// .setPayload(
+// new ARP()
+// .setHardwareType(ARP.HW_TYPE_ETHERNET)
+// .setProtocolType(ARP.PROTO_TYPE_IP)
+// .setHardwareAddressLength((byte) 6)
+// .setProtocolAddressLength((byte) 4)
+// .setOpCode(ARP.OP_REPLY)
+// .setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:01"))
+// .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.1"))
+// .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55"))
+// .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2")));
+// this.testARPReplyPacket_1_Srld = testARPReplyPacket_1.serialize();
+//
+// // Another test packet with a different source IP
+// this.testARPReplyPacket_2 = new Ethernet()
+// .setSourceMACAddress("00:99:88:77:66:55")
+// .setDestinationMACAddress("00:11:22:33:44:55")
+// .setEtherType(Ethernet.TYPE_ARP)
+// .setVlanID((short)5)
+// .setPayload(
+// new ARP()
+// .setHardwareType(ARP.HW_TYPE_ETHERNET)
+// .setProtocolType(ARP.PROTO_TYPE_IP)
+// .setHardwareAddressLength((byte) 6)
+// .setProtocolAddressLength((byte) 4)
+// .setOpCode(ARP.OP_REPLY)
+// .setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:01"))
+// .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.1"))
+// .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55"))
+// .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2")));
+// this.testARPReplyPacket_2_Srld = testARPReplyPacket_2.serialize();
+//
+// // Build the PacketIn
+// this.packetIn_1 = ((OFPacketIn) mockFloodlightProvider.
+// getOFMessageFactory().getMessage(OFType.PACKET_IN))
+// .setBufferId(-1)
+// .setInPort((short) 1)
+// .setPacketData(this.testARPReplyPacket_1_Srld)
+// .setReason(OFPacketInReason.NO_MATCH)
+// .setTotalLength((short) this.testARPReplyPacket_1_Srld.length);
+//
+// // Build the PacketIn
+// this.packetIn_2 = ((OFPacketIn) mockFloodlightProvider.
+// getOFMessageFactory().getMessage(OFType.PACKET_IN))
+// .setBufferId(-1)
+// .setInPort((short) 1)
+// .setPacketData(this.testARPReplyPacket_2_Srld)
+// .setReason(OFPacketInReason.NO_MATCH)
+// .setTotalLength((short) this.testARPReplyPacket_2_Srld.length);
+// }
+//
+//
+//
+//
+//
+// @Test
+// public void testLastSeen() throws Exception {
+// Calendar c = Calendar.getInstance();
+// Date d1 = c.getTime();
+// Entity entity1 = new Entity(1L, null, null, null, null, d1);
+// c.add(Calendar.SECOND, 1);
+// Entity entity2 = new Entity(1L, null, 1, null, null, c.getTime());
+//
+// IDevice d = deviceManager.learnDeviceByEntity(entity2);
+// assertEquals(c.getTime(), d.getLastSeen());
+// d = deviceManager.learnDeviceByEntity(entity1);
+// assertEquals(c.getTime(), d.getLastSeen());
+//
+// deviceManager.startUp(null);
+// d = deviceManager.learnDeviceByEntity(entity1);
+// assertEquals(d1, d.getLastSeen());
+// d = deviceManager.learnDeviceByEntity(entity2);
+// assertEquals(c.getTime(), d.getLastSeen());
+// }
+//
+// @Test
+// public void testEntityLearning() throws Exception {
+// IDeviceListener mockListener =
+// createMock(IDeviceListener.class);
+// expect(mockListener.getName()).andReturn("mockListener").atLeastOnce();
+// expect(mockListener.isCallbackOrderingPostreq((String)anyObject(), (String)anyObject()))
+// .andReturn(false).atLeastOnce();
+// expect(mockListener.isCallbackOrderingPrereq((String)anyObject(), (String)anyObject()))
+// .andReturn(false).atLeastOnce();
+//
+// replay(mockListener);
+// deviceManager.addListener(mockListener);
+// verify(mockListener);
+// reset(mockListener);
+// deviceManager.entityClassifier= new MockEntityClassifier();
+// deviceManager.startUp(null);
+//
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// expect(mockTopology.getL2DomainId(anyLong())).
+// andReturn(1L).anyTimes();
+// expect(mockTopology.isBroadcastDomainPort(anyLong(), anyShort())).
+// andReturn(false).anyTimes();
+//
+// expect(mockTopology.isAttachmentPointPort(anyLong(),
+// anyShort())).andReturn(true).anyTimes();
+// expect(mockTopology.isConsistent(10L, (short)1, 10L, (short)1)).
+// andReturn(true).anyTimes();
+// expect(mockTopology.isConsistent(1L, (short)1, 1L, (short)1)).
+// andReturn(true).anyTimes();
+// expect(mockTopology.isConsistent(50L, (short)3, 50L, (short)3)).
+// andReturn(true).anyTimes();
+//
+// Date topologyUpdateTime = new Date();
+// expect(mockTopology.getLastUpdateTime()).andReturn(topologyUpdateTime).
+// anyTimes();
+//
+// deviceManager.topology = mockTopology;
+//
+// Entity entity1 = new Entity(1L, null, null, 1L, 1, new Date());
+// Entity entity2 = new Entity(1L, null, null, 10L, 1, new Date());
+// Entity entity3 = new Entity(1L, null, 1, 10L, 1, new Date());
+// Entity entity4 = new Entity(1L, null, 1, 1L, 1, new Date());
+// Entity entity5 = new Entity(2L, (short)4, 1, 5L, 2, new Date());
+// Entity entity6 = new Entity(2L, (short)4, 1, 50L, 3, new Date());
+// Entity entity7 = new Entity(2L, (short)4, 2, 50L, 3, new Date());
+//
+// mockListener.deviceAdded(isA(IDevice.class));
+// replay(mockListener, mockTopology);
+//
+// Device d1 = deviceManager.learnDeviceByEntity(entity1);
+// assertSame(d1, deviceManager.learnDeviceByEntity(entity1));
+// assertSame(d1, deviceManager.findDeviceByEntity(entity1));
+// assertEquals(DefaultEntityClassifier.entityClass ,
+// d1.getEntityClass());
+// assertArrayEquals(new Short[] { -1 }, d1.getVlanId());
+// assertArrayEquals(new Integer[] { }, d1.getIPv4Addresses());
+//
+// assertEquals(1, deviceManager.getAllDevices().size());
+// verify(mockListener);
+//
+// reset(mockListener);
+// mockListener.deviceAdded(isA(IDevice.class));
+// replay(mockListener);
+//
+// Device d2 = deviceManager.learnDeviceByEntity(entity2);
+// assertFalse(d1.equals(d2));
+// assertNotSame(d1, d2);
+// assertNotSame(d1.getDeviceKey(), d2.getDeviceKey());
+// assertEquals(MockEntityClassifier.testEC, d2.getEntityClass());
+// assertArrayEquals(new Short[] { -1 }, d2.getVlanId());
+// assertArrayEquals(new Integer[] { }, d2.getIPv4Addresses());
+//
+// assertEquals(2, deviceManager.getAllDevices().size());
+// verify(mockListener);
+//
+// reset(mockListener);
+// mockListener.deviceIPV4AddrChanged(isA(IDevice.class));
+// replay(mockListener);
+//
+// Device d3 = deviceManager.learnDeviceByEntity(entity3);
+// assertNotSame(d2, d3);
+// assertEquals(d2.getDeviceKey(), d3.getDeviceKey());
+// assertEquals(MockEntityClassifier.testEC, d3.getEntityClass());
+// assertArrayEquals(new Integer[] { 1 },
+// d3.getIPv4Addresses());
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(10L, 1) },
+// d3.getAttachmentPoints());
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(10L, 1) },
+// d3.getAttachmentPoints(true));
+// assertArrayEquals(new Short[] { -1 },
+// d3.getVlanId());
+//
+// assertEquals(2, deviceManager.getAllDevices().size());
+// verify(mockListener);
+//
+// reset(mockListener);
+// mockListener.deviceIPV4AddrChanged(isA(IDevice.class));
+// replay(mockListener);
+//
+// Device d4 = deviceManager.learnDeviceByEntity(entity4);
+// assertNotSame(d1, d4);
+// assertEquals(d1.getDeviceKey(), d4.getDeviceKey());
+// assertEquals(DefaultEntityClassifier.entityClass, d4.getEntityClass());
+// assertArrayEquals(new Integer[] { 1 },
+// d4.getIPv4Addresses());
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) },
+// d4.getAttachmentPoints());
+// assertArrayEquals(new Short[] { -1 },
+// d4.getVlanId());
+//
+// assertEquals(2, deviceManager.getAllDevices().size());
+// verify(mockListener);
+//
+// reset(mockListener);
+// mockListener.deviceAdded((isA(IDevice.class)));
+// replay(mockListener);
+//
+// Device d5 = deviceManager.learnDeviceByEntity(entity5);
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(5L, 2) },
+// d5.getAttachmentPoints());
+// assertArrayEquals(new Short[] { (short) 4 },
+// d5.getVlanId());
+// assertEquals(2L, d5.getMACAddress());
+// assertEquals("00:00:00:00:00:02", d5.getMACAddressString());
+// verify(mockListener);
+//
+// reset(mockListener);
+// mockListener.deviceAdded(isA(IDevice.class));
+// replay(mockListener);
+//
+// Device d6 = deviceManager.learnDeviceByEntity(entity6);
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(50L, 3) },
+// d6.getAttachmentPoints());
+// assertArrayEquals(new Short[] { (short) 4 },
+// d6.getVlanId());
+//
+// assertEquals(4, deviceManager.getAllDevices().size());
+// verify(mockListener);
+//
+// reset(mockListener);
+// mockListener.deviceIPV4AddrChanged(isA(IDevice.class));
+// replay(mockListener);
+//
+// Device d7 = deviceManager.learnDeviceByEntity(entity7);
+// assertNotSame(d6, d7);
+// assertEquals(d6.getDeviceKey(), d7.getDeviceKey());
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(50L, 3) },
+// d7.getAttachmentPoints());
+// assertArrayEquals(new Short[] { (short) 4 },
+// d7.getVlanId());
+//
+// assertEquals(4, deviceManager.getAllDevices().size());
+// verify(mockListener);
+//
+//
+// reset(mockListener);
+// replay(mockListener);
+//
+// reset(deviceManager.topology);
+// deviceManager.topology.addListener(deviceManager);
+// expectLastCall().times(1);
+// replay(deviceManager.topology);
+//
+// deviceManager.entityClassifier = new MockEntityClassifierMac();
+// deviceManager.startUp(null);
+// Entity entityNoClass = new Entity(5L, (short)1, 5, -1L, 1, new Date());
+// assertEquals(null, deviceManager.learnDeviceByEntity(entityNoClass));
+//
+// verify(mockListener);
+// }
+//
+//
+// private void doTestEntityOrdering(boolean computeInsertionPoint) throws Exception {
+// Entity e = new Entity(10L, null, null, null, null, null);
+// IEntityClass ec = createNiceMock(IEntityClass.class);
+// Device d = new Device(deviceManager, 1L, e, ec);
+//
+// int expectedLength = 1;
+// Long[] macs = new Long[] { 5L, // new first element
+// 15L, // new last element
+// 7L, // insert in middle
+// 12L, // insert in middle
+// 6L, // insert at idx 1
+// 14L, // insert at idx length-2
+// 1L,
+// 20L
+// };
+//
+// for (Long mac: macs) {
+// e = new Entity(mac, null, null, null, null, null);
+// int insertionPoint;
+// if (computeInsertionPoint) {
+// insertionPoint = -(Arrays.binarySearch(d.entities, e)+1);
+// } else {
+// insertionPoint = -1;
+// }
+// d = deviceManager.allocateDevice(d, e, insertionPoint);
+// expectedLength++;
+// assertEquals(expectedLength, d.entities.length);
+// for (int i = 0; i < d.entities.length-1; i++)
+// assertEquals(-1, d.entities[i].compareTo(d.entities[i+1]));
+// }
+// }
+//
+// @Test
+// public void testEntityOrderingExternal() throws Exception {
+// doTestEntityOrdering(true);
+// }
+//
+// @Test
+// public void testEntityOrderingInternal() throws Exception {
+// doTestEntityOrdering(false);
+// }
+//
+// @Test
+// public void testAttachmentPointLearning() throws Exception {
+// IDeviceListener mockListener =
+// createMock(IDeviceListener.class);
+// expect(mockListener.getName()).andReturn("mockListener").atLeastOnce();
+// expect(mockListener.isCallbackOrderingPostreq((String)anyObject(), (String)anyObject()))
+// .andReturn(false).atLeastOnce();
+// expect(mockListener.isCallbackOrderingPrereq((String)anyObject(), (String)anyObject()))
+// .andReturn(false).atLeastOnce();
+//
+// replay(mockListener);
+// deviceManager.addListener(mockListener);
+// verify(mockListener);
+// reset(mockListener);
+//
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// expect(mockTopology.getL2DomainId(1L)).
+// andReturn(1L).anyTimes();
+// expect(mockTopology.getL2DomainId(5L)).
+// andReturn(1L).anyTimes();
+// expect(mockTopology.getL2DomainId(10L)).
+// andReturn(10L).anyTimes();
+// expect(mockTopology.getL2DomainId(50L)).
+// andReturn(10L).anyTimes();
+// expect(mockTopology.isBroadcastDomainPort(anyLong(), anyShort())).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isInSameBroadcastDomain(anyLong(), anyShort(),
+// anyLong(), anyShort())).andReturn(false).anyTimes();
+//
+// expect(mockTopology.isAttachmentPointPort(anyLong(),
+// anyShort())).andReturn(true).anyTimes();
+// expect(mockTopology.isConsistent(1L, (short)1, 5L, (short)1)).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isConsistent(5L, (short)1, 10L, (short)1)).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isConsistent(10L, (short)1, 50L, (short)1)).
+// andReturn(false).anyTimes();
+//
+// Date topologyUpdateTime = new Date();
+// expect(mockTopology.getLastUpdateTime()).andReturn(topologyUpdateTime).
+// anyTimes();
+//
+// replay(mockTopology);
+//
+// deviceManager.topology = mockTopology;
+//
+// Calendar c = Calendar.getInstance();
+// Entity entity1 = new Entity(1L, null, 1, 1L, 1, c.getTime());
+// Entity entity0 = new Entity(1L, null, null, null, null, c.getTime());
+// c.add(Calendar.SECOND, 1);
+// Entity entity2 = new Entity(1L, null, null, 5L, 1, c.getTime());
+// c.add(Calendar.SECOND, 1);
+// Entity entity3 = new Entity(1L, null, null, 10L, 1, c.getTime());
+// c.add(Calendar.SECOND, 1);
+// Entity entity4 = new Entity(1L, null, null, 50L, 1, c.getTime());
+//
+// IDevice d;
+// SwitchPort[] aps;
+// Integer[] ips;
+//
+// mockListener.deviceAdded(isA(IDevice.class));
+// replay(mockListener);
+//
+// deviceManager.learnDeviceByEntity(entity1);
+// d = deviceManager.learnDeviceByEntity(entity0);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) }, aps);
+// ips = d.getIPv4Addresses();
+// assertArrayEquals(new Integer[] { 1 }, ips);
+// verify(mockListener);
+//
+// reset(mockListener);
+// mockListener.deviceMoved((isA(IDevice.class)));
+// replay(mockListener);
+//
+// d = deviceManager.learnDeviceByEntity(entity2);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+//
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(5L, 1) }, aps);
+// ips = d.getIPv4Addresses();
+// assertArrayEquals(new Integer[] { 1 }, ips);
+// verify(mockListener);
+//
+// reset(mockListener);
+// mockListener.deviceMoved((isA(IDevice.class)));
+// replay(mockListener);
+//
+// d = deviceManager.learnDeviceByEntity(entity3);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+// assertArrayEquals(new SwitchPort[] {new SwitchPort(5L, 1), new SwitchPort(10L, 1)}, aps);
+// ips = d.getIPv4Addresses();
+// assertArrayEquals(new Integer[] { 1 }, ips);
+// verify(mockListener);
+//
+// reset(mockListener);
+// mockListener.deviceMoved((isA(IDevice.class)));
+// replay(mockListener);
+//
+// d = deviceManager.learnDeviceByEntity(entity4);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(5L, 1),
+// new SwitchPort(50L, 1) }, aps);
+// ips = d.getIPv4Addresses();
+// assertArrayEquals(new Integer[] { 1 }, ips);
+// verify(mockListener);
+// }
+//
+// private void verifyEntityArray(Entity[] expected, Device d) {
+// Arrays.sort(expected);
+// assertArrayEquals(expected, d.entities);
+// }
+//
+// @Test
+// public void testNoLearningOnInternalPorts() throws Exception {
+// IDeviceListener mockListener =
+// createMock(IDeviceListener.class);
+//
+// expect(mockListener.getName()).andReturn("mockListener").anyTimes();
+// expect(mockListener.isCallbackOrderingPostreq((String)anyObject(), (String)anyObject()))
+// .andReturn(false).atLeastOnce();
+// expect(mockListener.isCallbackOrderingPrereq((String)anyObject(), (String)anyObject()))
+// .andReturn(false).atLeastOnce();
+//
+// replay(mockListener);
+// deviceManager.addListener(mockListener);
+// verify(mockListener);
+// reset(mockListener);
+//
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// expect(mockTopology.getL2DomainId(1L)).
+// andReturn(1L).anyTimes();
+// expect(mockTopology.getL2DomainId(2L)).
+// andReturn(1L).anyTimes();
+// expect(mockTopology.getL2DomainId(3L)).
+// andReturn(1L).anyTimes();
+// expect(mockTopology.getL2DomainId(4L)).
+// andReturn(1L).anyTimes();
+// expect(mockTopology.isBroadcastDomainPort(anyLong(), anyShort()))
+// .andReturn(false).anyTimes();
+// expect(mockTopology.isInSameBroadcastDomain(anyLong(), anyShort(),
+// anyLong(), anyShort()))
+// .andReturn(false).anyTimes();
+//
+// expect(mockTopology.isAttachmentPointPort(or(eq(1L), eq(3L)), anyShort()))
+// .andReturn(true).anyTimes();
+// // Switches 2 and 4 have only internal ports
+// expect(mockTopology.isAttachmentPointPort(or(eq(2L), eq(4L)), anyShort()))
+// .andReturn(false).anyTimes();
+//
+// expect(mockTopology.isConsistent(1L, (short)1, 3L, (short)1))
+// .andReturn(false).once();
+//
+// Date topologyUpdateTime = new Date();
+// expect(mockTopology.getLastUpdateTime()).andReturn(topologyUpdateTime).
+// anyTimes();
+//
+// replay(mockTopology);
+//
+// deviceManager.topology = mockTopology;
+//
+// Calendar c = Calendar.getInstance();
+// Entity entity1 = new Entity(1L, null, 1, 1L, 1, c.getTime());
+// c.add(Calendar.SECOND, 1);
+// Entity entity2 = new Entity(1L, null, 2, 2L, 1, c.getTime());
+// c.add(Calendar.SECOND, 1);
+// Entity entity3 = new Entity(1L, null, 3, 3L, 1, c.getTime());
+// c.add(Calendar.SECOND, 1);
+// Entity entity4 = new Entity(1L, null, 4, 4L, 1, c.getTime());
+//
+// IDevice d;
+// SwitchPort[] aps;
+// Integer[] ips;
+//
+// mockListener.deviceAdded(isA(IDevice.class));
+// expectLastCall().once();
+// replay(mockListener);
+//
+// // cannot learn device internal ports
+// d = deviceManager.learnDeviceByEntity(entity2);
+// assertNull(d);
+// d = deviceManager.learnDeviceByEntity(entity4);
+// assertNull(d);
+//
+// d = deviceManager.learnDeviceByEntity(entity1);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) }, aps);
+// verifyEntityArray(new Entity[] { entity1 } , (Device)d);
+// ips = d.getIPv4Addresses();
+// assertArrayEquals(new Integer[] { 1 }, ips);
+// verify(mockListener);
+//
+// reset(mockListener);
+// replay(mockListener);
+//
+// // don't learn
+// d = deviceManager.learnDeviceByEntity(entity2);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) }, aps);
+// verifyEntityArray(new Entity[] { entity1 } , (Device)d);
+// ips = d.getIPv4Addresses();
+// assertArrayEquals(new Integer[] { 1 }, ips);
+// verify(mockListener);
+//
+// reset(mockListener);
+// mockListener.deviceMoved(isA(IDevice.class));
+// mockListener.deviceIPV4AddrChanged(isA(IDevice.class));
+// replay(mockListener);
+//
+// // learn
+// d = deviceManager.learnDeviceByEntity(entity3);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(3L, 1) }, aps);
+// verifyEntityArray(new Entity[] { entity1, entity3 } , (Device)d);
+// ips = d.getIPv4Addresses();
+// Arrays.sort(ips);
+// assertArrayEquals(new Integer[] { 1, 3 }, ips);
+// verify(mockListener);
+//
+// reset(mockListener);
+// replay(mockListener);
+//
+// // don't learn
+// d = deviceManager.learnDeviceByEntity(entity4);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(3L, 1) }, aps);
+// verifyEntityArray(new Entity[] { entity1, entity3 } , (Device)d);
+// ips = d.getIPv4Addresses();
+// Arrays.sort(ips);
+// assertArrayEquals(new Integer[] { 1, 3 }, ips);
+// verify(mockListener);
+// }
+//
+// @Test
+// public void testAttachmentPointSuppression() throws Exception {
+// IDeviceListener mockListener =
+// createMock(IDeviceListener.class);
+//
+// expect(mockListener.getName()).andReturn("mockListener").anyTimes();
+// expect(mockListener.isCallbackOrderingPostreq((String)anyObject(), (String)anyObject()))
+// .andReturn(false).atLeastOnce();
+// expect(mockListener.isCallbackOrderingPrereq((String)anyObject(), (String)anyObject()))
+// .andReturn(false).atLeastOnce();
+//
+// replay(mockListener);
+// deviceManager.addListener(mockListener);
+// verify(mockListener);
+// reset(mockListener);
+//
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// expect(mockTopology.getL2DomainId(1L)).
+// andReturn(1L).anyTimes();
+// expect(mockTopology.getL2DomainId(5L)).
+// andReturn(1L).anyTimes();
+// expect(mockTopology.getL2DomainId(10L)).
+// andReturn(10L).anyTimes();
+// expect(mockTopology.getL2DomainId(50L)).
+// andReturn(10L).anyTimes();
+// expect(mockTopology.isBroadcastDomainPort(anyLong(), anyShort()))
+// .andReturn(false).anyTimes();
+// expect(mockTopology.isInSameBroadcastDomain(anyLong(), anyShort(),
+// anyLong(), anyShort()))
+// .andReturn(false).anyTimes();
+//
+// expect(mockTopology.isAttachmentPointPort(anyLong(), anyShort()))
+// .andReturn(true).anyTimes();
+// expect(mockTopology.isConsistent(5L, (short)1, 50L, (short)1))
+// .andReturn(false).anyTimes();
+//
+// Date topologyUpdateTime = new Date();
+// expect(mockTopology.getLastUpdateTime()).andReturn(topologyUpdateTime).
+// anyTimes();
+//
+// replay(mockTopology);
+//
+// deviceManager.topology = mockTopology;
+// // suppress (1L, 1) and (10L, 1)
+// deviceManager.addSuppressAPs(1L, (short)1);
+// deviceManager.addSuppressAPs(10L, (short)1);
+//
+// Calendar c = Calendar.getInstance();
+// Entity entity0 = new Entity(1L, null, null, null, null, c.getTime());
+// // No attachment point should be learnt on 1L, 1
+// Entity entity1 = new Entity(1L, null, 1, 1L, 1, c.getTime());
+// c.add(Calendar.SECOND, 1);
+// Entity entity2 = new Entity(1L, null, 1, 5L, 1, c.getTime());
+// c.add(Calendar.SECOND, 1);
+// Entity entity3 = new Entity(1L, null, null, 10L, 1, c.getTime());
+// c.add(Calendar.SECOND, 1);
+// Entity entity4 = new Entity(1L, null, null, 50L, 1, c.getTime());
+//
+// IDevice d;
+// SwitchPort[] aps;
+// Integer[] ips;
+//
+// mockListener.deviceAdded(isA(IDevice.class));
+// mockListener.deviceIPV4AddrChanged((isA(IDevice.class)));
+// replay(mockListener);
+//
+// // TODO: we currently do learn entities on suppressed APs
+// // // cannot learn device on suppressed AP
+// // d = deviceManager.learnDeviceByEntity(entity1);
+// // assertNull(d);
+//
+// deviceManager.learnDeviceByEntity(entity0);
+// d = deviceManager.learnDeviceByEntity(entity1);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+// assertEquals(aps.length, 0);
+// verifyEntityArray(new Entity[] { entity0, entity1} , (Device)d);
+// ips = d.getIPv4Addresses();
+// assertArrayEquals(new Integer[] { 1 }, ips);
+// verify(mockListener);
+//
+// reset(mockListener);
+// mockListener.deviceMoved((isA(IDevice.class)));
+// //mockListener.deviceIPV4AddrChanged((isA(IDevice.class)));
+// replay(mockListener);
+// d = deviceManager.learnDeviceByEntity(entity2);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(5L, 1) }, aps);
+// verifyEntityArray(new Entity[] { entity0, entity1, entity2 } , (Device)d);
+// ips = d.getIPv4Addresses();
+// assertArrayEquals(new Integer[] { 1 }, ips);
+// verify(mockListener);
+//
+// reset(mockListener);
+// replay(mockListener);
+//
+// d = deviceManager.learnDeviceByEntity(entity3);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(5L, 1) }, aps);
+// verifyEntityArray(new Entity[] { entity0, entity1, entity2, entity3 } , (Device)d);
+// ips = d.getIPv4Addresses();
+// assertArrayEquals(new Integer[] { 1 }, ips);
+// verify(mockListener);
+//
+// reset(mockListener);
+// mockListener.deviceMoved((isA(IDevice.class)));
+// replay(mockListener);
+//
+// d = deviceManager.learnDeviceByEntity(entity4);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(5L, 1),
+// new SwitchPort(50L, 1) }, aps);
+// verifyEntityArray(new Entity[] { entity0, entity1, entity2, entity3, entity4} , (Device)d);
+// ips = d.getIPv4Addresses();
+// assertArrayEquals(new Integer[] { 1 }, ips);
+// verify(mockListener);
+// }
+//
+// @Test
+// public void testBDAttachmentPointLearning() throws Exception {
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// expect(mockTopology.getL2DomainId(anyLong())).
+// andReturn(1L).anyTimes();
+// expect(mockTopology.isAttachmentPointPort(anyLong(), anyShort())).
+// andReturn(true).anyTimes();
+// expect(mockTopology.isBroadcastDomainPort(1L, (short)1)).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isBroadcastDomainPort(1L, (short)2)).
+// andReturn(true).anyTimes();
+// expect(mockTopology.isInSameBroadcastDomain(1L, (short)1,
+// 1L, (short)2)).andReturn(true).anyTimes();
+// expect(mockTopology.isInSameBroadcastDomain(1L, (short)2,
+// 1L, (short)1)).andReturn(true).anyTimes();
+// expect(mockTopology.isConsistent(anyLong(), anyShort(), anyLong(), anyShort())).andReturn(false).anyTimes();
+//
+// Date topologyUpdateTime = new Date();
+// expect(mockTopology.getLastUpdateTime()).andReturn(topologyUpdateTime).
+// anyTimes();
+//
+// replay(mockTopology);
+//
+// deviceManager.topology = mockTopology;
+//
+// Calendar c = Calendar.getInstance();
+// Entity entity1 = new Entity(1L, null, 1, 1L, 1, c.getTime());
+// c.add(Calendar.MILLISECOND,
+// (int)AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT/ 2);
+// Entity entity2 = new Entity(1L, null, null, 1L, 2, c.getTime());
+// c.add(Calendar.MILLISECOND,
+// (int)AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT / 2 + 1);
+// Entity entity3 = new Entity(1L, null, null, 1L, 2, c.getTime());
+//
+// IDevice d;
+// SwitchPort[] aps;
+//
+// d = deviceManager.learnDeviceByEntity(entity1);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) }, aps);
+//
+// // this timestamp is too soon; don't switch
+// d = deviceManager.learnDeviceByEntity(entity2);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) }, aps);
+//
+// // it should switch when we learn with a timestamp after the
+// // timeout
+// d = deviceManager.learnDeviceByEntity(entity3);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 2) }, aps);
+// }
+//
+// /**
+// * This test verifies that the learning behavior on OFPP_LOCAL ports.
+// * Once a host is learned on OFPP_LOCAL, it is allowed to move only from
+// * one OFPP_LOCAL to another OFPP_LOCAL port.
+// * @throws Exception
+// */
+// @Test
+// public void testLOCALAttachmentPointLearning() throws Exception {
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// expect(mockTopology.getL2DomainId(anyLong())).
+// andReturn(1L).anyTimes();
+// expect(mockTopology.isAttachmentPointPort(anyLong(), anyShort())).
+// andReturn(true).anyTimes();
+// expect(mockTopology.isBroadcastDomainPort(1L, (short)1)).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isBroadcastDomainPort(1L, OFPort.OFPP_LOCAL.getValue())).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isBroadcastDomainPort(1L, (short)2)).
+// andReturn(true).anyTimes();
+// expect(mockTopology.isInSameBroadcastDomain(1L, (short)1,
+// 1L, OFPort.OFPP_LOCAL.getValue())).andReturn(true).anyTimes();
+// expect(mockTopology.isInSameBroadcastDomain(1L, OFPort.OFPP_LOCAL.getValue(),
+// 1L, (short)2)).andReturn(true).anyTimes();
+// expect(mockTopology.isInSameBroadcastDomain(1L, (short)2,
+// 1L, OFPort.OFPP_LOCAL.getValue())).andReturn(true).anyTimes();
+// expect(mockTopology.isConsistent(anyLong(), anyShort(), anyLong(), anyShort())).andReturn(false).anyTimes();
+//
+// Date topologyUpdateTime = new Date();
+// expect(mockTopology.getLastUpdateTime()).andReturn(topologyUpdateTime).
+// anyTimes();
+//
+// replay(mockTopology);
+//
+// deviceManager.topology = mockTopology;
+//
+// Calendar c = Calendar.getInstance();
+// Entity entity1 = new Entity(1L, null, 1, 1L, 1, c.getTime());
+// c.add(Calendar.MILLISECOND,
+// (int)AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT/ 2);
+// Entity entity2 = new Entity(1L, null, null, 1L, (int)OFPort.OFPP_LOCAL.getValue(), c.getTime());
+// c.add(Calendar.MILLISECOND,
+// (int)AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT + 1);
+// Entity entity3 = new Entity(1L, null, null, 1L, 2, c.getTime());
+//
+// IDevice d;
+// SwitchPort[] aps;
+//
+// d = deviceManager.learnDeviceByEntity(entity1);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) }, aps);
+//
+// // Ensure that the attachment point changes to OFPP_LOCAL
+// d = deviceManager.learnDeviceByEntity(entity2);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, OFPort.OFPP_LOCAL.getValue()) }, aps);
+//
+// // Even though the new attachment point is consistent with old
+// // and the time has elapsed, OFPP_LOCAL attachment point should
+// // be maintained.
+// d = deviceManager.learnDeviceByEntity(entity3);
+// assertEquals(1, deviceManager.getAllDevices().size());
+// aps = d.getAttachmentPoints();
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, OFPort.OFPP_LOCAL.getValue()) }, aps);
+// }
+//
+// @Test
+// public void testPacketInBasic(byte[] deviceMac, OFPacketIn packetIn) {
+// // Mock up our expected behavior
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// deviceManager.topology = mockTopology;
+// expect(mockTopology.isAttachmentPointPort(EasyMock.anyLong(),
+// EasyMock.anyShort())).
+// andReturn(true).anyTimes();
+// expect(mockTopology.isConsistent(EasyMock.anyLong(),
+// EasyMock.anyShort(),
+// EasyMock.anyLong(),
+// EasyMock.anyShort())).andReturn(false).
+// anyTimes();
+// expect(mockTopology.getL2DomainId(EasyMock.anyLong())).andReturn(1L).anyTimes();
+// replay(mockTopology);
+//
+// Date currentDate = new Date();
+//
+// // build our expected Device
+// Integer ipaddr = IPv4.toIPv4Address("192.168.1.1");
+// Device device =
+// new Device(deviceManager,
+// new Long(deviceManager.deviceKeyCounter),
+// new Entity(Ethernet.toLong(deviceMac),
+// (short)5,
+// ipaddr,
+// 1L,
+// 1,
+// currentDate),
+// DefaultEntityClassifier.entityClass);
+//
+// // Get the listener and trigger the packet in
+// IOFSwitch switch1 = mockFloodlightProvider.getSwitch(1L);
+// mockFloodlightProvider.dispatchMessage(switch1, packetIn);
+//
+// // Verify the replay matched our expectations
+// // verify(mockTopology);
+//
+// // Verify the device
+// Device rdevice = (Device)
+// deviceManager.findDevice(Ethernet.toLong(deviceMac),
+// (short)5, null, null, null);
+//
+// assertEquals(device, rdevice);
+// assertEquals(new Short((short)5), rdevice.getVlanId()[0]);
+//
+// Device result = null;
+// Iterator<? extends IDevice> dstiter =
+// deviceManager.queryClassDevices(device.getEntityClass(),
+// null, null, ipaddr,
+// null, null);
+// if (dstiter.hasNext()) {
+// result = (Device)dstiter.next();
+// }
+//
+// assertEquals(device, result);
+//
+// device =
+// new Device(device,
+// new Entity(Ethernet.toLong(deviceMac),
+// (short)5,
+// ipaddr,
+// 5L,
+// 2,
+// currentDate),
+// -1);
+//
+// reset(mockTopology);
+// expect(mockTopology.isAttachmentPointPort(anyLong(),
+// anyShort())).
+// andReturn(true).
+// anyTimes();
+// expect(mockTopology.isConsistent(EasyMock.anyLong(),
+// EasyMock.anyShort(),
+// EasyMock.anyLong(),
+// EasyMock.anyShort())).andReturn(false).
+// anyTimes();
+// expect(mockTopology.isBroadcastDomainPort(EasyMock.anyLong(),
+// EasyMock.anyShort()))
+// .andReturn(false)
+// .anyTimes();
+// expect(mockTopology.getL2DomainId(1L)).andReturn(1L).anyTimes();
+// expect(mockTopology.getL2DomainId(5L)).andReturn(1L).anyTimes();
+// expect(mockTopology.isInSameBroadcastDomain(1L, (short)1, 5L, (short)2)).
+// andReturn(false).anyTimes();
+//
+// // Start recording the replay on the mocks
+// replay(mockTopology);
+// // Get the listener and trigger the packet in
+// IOFSwitch switch5 = mockFloodlightProvider.getSwitch(5L);
+// mockFloodlightProvider.
+// dispatchMessage(switch5, this.packetIn_1.setInPort((short)2));
+//
+// // Verify the replay matched our expectations
+// verify(mockTopology);
+//
+// // Verify the device
+// rdevice = (Device)
+// deviceManager.findDevice(Ethernet.toLong(deviceMac),
+// (short)5, null, null, null);
+// assertEquals(device, rdevice);
+// }
+//
+// @Test
+// public void testPacketIn() throws Exception {
+// byte[] deviceMac1 =
+// ((Ethernet)this.testARPReplyPacket_1).getSourceMACAddress();
+// testPacketInBasic(deviceMac1, packetIn_1);
+// }
+//
+// /**
+// * This test ensures the device manager learns the source device
+// * corresponding to the senderHardwareAddress and senderProtocolAddress
+// * in an ARP response whenever the senderHardwareAddress is different
+// * from the source MAC address of the Ethernet frame.
+// *
+// * This test is the same as testPacketIn method, except for the
+// * packet-in that's used.
+// * @throws Exception
+// */
+// @Test
+// public void testDeviceLearningFromArpResponseData() throws Exception {
+// ARP arp = (ARP)((Ethernet)this.testARPReplyPacket_2).getPayload();
+// byte[] deviceMac2 = arp.getSenderHardwareAddress();
+//
+// testPacketInBasic(deviceMac2, packetIn_2);
+// }
+//
+// /**
+// * Note: Entity expiration does not result in device moved notification.
+// * @throws Exception
+// */
+// public void doTestEntityExpiration() throws Exception {
+// IDeviceListener mockListener =
+// createMock(IDeviceListener.class);
+// expect(mockListener.getName()).andReturn("mockListener").anyTimes();
+// expect(mockListener.isCallbackOrderingPostreq((String)anyObject(), (String)anyObject()))
+// .andReturn(false).atLeastOnce();
+// expect(mockListener.isCallbackOrderingPrereq((String)anyObject(), (String)anyObject()))
+// .andReturn(false).atLeastOnce();
+//
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// expect(mockTopology.isAttachmentPointPort(anyLong(),
+// anyShort())).
+// andReturn(true).anyTimes();
+//
+// expect(mockTopology.isBroadcastDomainPort(1L, (short)1)).andReturn(false).anyTimes();
+// expect(mockTopology.isBroadcastDomainPort(5L, (short)1)).andReturn(false).anyTimes();
+// expect(mockTopology.getL2DomainId(1L)).andReturn(1L).anyTimes();
+// expect(mockTopology.getL2DomainId(5L)).andReturn(5L).anyTimes();
+// expect(mockTopology.isConsistent(1L, (short)1, 5L, (short)1)).
+// andReturn(false).anyTimes();
+//
+// Date topologyUpdateTime = new Date();
+// expect(mockTopology.getLastUpdateTime()).andReturn(topologyUpdateTime).
+// anyTimes();
+//
+// replay(mockTopology);
+// deviceManager.topology = mockTopology;
+//
+// Calendar c = Calendar.getInstance();
+// Entity entity1 = new Entity(1L, null, 2, 1L, 1, c.getTime());
+// c.add(Calendar.MILLISECOND, -DeviceManagerImpl.ENTITY_TIMEOUT-1);
+// Entity entity2 = new Entity(1L, null, 1, 5L, 1, c.getTime());
+//
+// deviceManager.learnDeviceByEntity(entity1);
+// IDevice d = deviceManager.learnDeviceByEntity(entity2);
+// assertArrayEquals(new Integer[] { 1, 2 }, d.getIPv4Addresses());
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1),
+// new SwitchPort(5L, 1)},
+// d.getAttachmentPoints());
+// Iterator<? extends IDevice> diter =
+// deviceManager.queryClassDevices(d.getEntityClass(),
+// null, null, 1, null, null);
+// assertTrue(diter.hasNext());
+// assertEquals(d.getDeviceKey(), diter.next().getDeviceKey());
+// diter = deviceManager.queryClassDevices(d.getEntityClass(),
+// null, null, 2, null, null);
+// assertTrue(diter.hasNext());
+// assertEquals(d.getDeviceKey(), diter.next().getDeviceKey());
+//
+// replay(mockListener);
+// deviceManager.addListener(mockListener);
+// verify(mockListener);
+// reset(mockListener);
+//
+// mockListener.deviceIPV4AddrChanged(isA(IDevice.class));
+// replay(mockListener);
+// deviceManager.entityCleanupTask.reschedule(0, null);
+//
+// d = deviceManager.getDevice(d.getDeviceKey());
+// assertArrayEquals(new Integer[] { 2 }, d.getIPv4Addresses());
+//
+// // Attachment points are not removed, previous ones are still valid.
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1),
+// new SwitchPort(5L, 1) },
+// d.getAttachmentPoints());
+// diter = deviceManager.queryClassDevices(d.getEntityClass(),
+// null, null, 2, null, null);
+// assertTrue(diter.hasNext());
+// assertEquals(d.getDeviceKey(), diter.next().getDeviceKey());
+// diter = deviceManager.queryClassDevices(d.getEntityClass(),
+// null, null, 1, null, null);
+// assertFalse(diter.hasNext());
+//
+// d = deviceManager.findDevice(1L, null, null, null, null);
+// assertArrayEquals(new Integer[] { 2 }, d.getIPv4Addresses());
+//
+// // Attachment points are not removed, previous ones are still valid.
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1),
+// new SwitchPort(5L, 1) },
+// d.getAttachmentPoints());
+//
+// verify(mockListener);
+// }
+//
+// public void doTestDeviceExpiration() throws Exception {
+// IDeviceListener mockListener =
+// createMock(IDeviceListener.class);
+// expect(mockListener.getName()).andReturn("mockListener").anyTimes();
+// expect(mockListener.isCallbackOrderingPostreq((String)anyObject(), (String)anyObject()))
+// .andReturn(false).atLeastOnce();
+// expect(mockListener.isCallbackOrderingPrereq((String)anyObject(), (String)anyObject()))
+// .andReturn(false).atLeastOnce();
+//
+// Calendar c = Calendar.getInstance();
+// c.add(Calendar.MILLISECOND, -DeviceManagerImpl.ENTITY_TIMEOUT-1);
+// Entity entity1 = new Entity(1L, null, 1, 1L, 1, c.getTime());
+// Entity entity2 = new Entity(1L, null, 2, 5L, 1, c.getTime());
+//
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// deviceManager.topology = mockTopology;
+//
+// expect(mockTopology.isAttachmentPointPort(EasyMock.anyLong(),
+// EasyMock.anyShort())).
+// andReturn(true).
+// anyTimes();
+// expect(mockTopology.getL2DomainId(1L)).andReturn(1L).anyTimes();
+// expect(mockTopology.getL2DomainId(5L)).andReturn(1L).anyTimes();
+// expect(mockTopology.isConsistent(EasyMock.anyLong(),
+// EasyMock.anyShort(),
+// EasyMock.anyLong(),
+// EasyMock.anyShort())).andReturn(false).
+// anyTimes();
+// expect(mockTopology.isBroadcastDomainPort(EasyMock.anyLong(),
+// EasyMock.anyShort())).
+// andReturn(false).anyTimes();
+// replay(mockTopology);
+//
+// IDevice d = deviceManager.learnDeviceByEntity(entity2);
+// d = deviceManager.learnDeviceByEntity(entity1);
+// assertArrayEquals(new Integer[] { 1, 2 }, d.getIPv4Addresses());
+//
+// replay(mockListener);
+// deviceManager.addListener(mockListener);
+// verify(mockListener);
+// reset(mockListener);
+//
+// mockListener.deviceRemoved(isA(IDevice.class));
+// replay(mockListener);
+// deviceManager.entityCleanupTask.reschedule(0, null);
+//
+// IDevice r = deviceManager.getDevice(d.getDeviceKey());
+// assertNull(r);
+// Iterator<? extends IDevice> diter =
+// deviceManager.queryClassDevices(d.getEntityClass(),
+// null, null, 1, null, null);
+// assertFalse(diter.hasNext());
+//
+// r = deviceManager.findDevice(1L, null, null, null, null);
+// assertNull(r);
+//
+// verify(mockListener);
+// }
+//
+// /*
+// * A ConcurrentHashMap for devices (deviceMap) that can be used to test
+// * code that specially handles concurrent modification situations. In
+// * particular, we overwrite values() and will replace / remove all the
+// * elements returned by values.
+// *
+// * The remove flag in the constructor specifies if devices returned by
+// * values() should be removed or replaced.
+// */
+// protected static class ConcurrentlyModifiedDeviceMap
+// extends ConcurrentHashMap<Long, Device> {
+// private static final long serialVersionUID = 7784938535441180562L;
+// protected boolean remove;
+// public ConcurrentlyModifiedDeviceMap(boolean remove) {
+// super();
+// this.remove = remove;
+// }
+//
+// @Override
+// public Collection<Device> values() {
+// // Get the values from the real map and copy them since
+// // the collection returned by values can reflect changed
+// Collection<Device> devs = new ArrayList<Device>(super.values());
+// for (Device d: devs) {
+// if (remove) {
+// // We remove the device from the underlying map
+// super.remove(d.getDeviceKey());
+// } else {
+// super.remove(d.getDeviceKey());
+// // We add a different Device instance with the same
+// // key to the map. We'll do some hackery so the device
+// // is different enough to compare differently in equals
+// // but otherwise looks the same.
+// // It's ugly but it works.
+// // clone entities
+// Device newDevice = d;
+// for (Entity e: d.getEntities()) {
+// Entity newEntity = new Entity (e.macAddress,
+// e.vlan,
+// e.ipv4Address,
+// e.switchDPID,
+// e.switchPort,
+// e.lastSeenTimestamp);
+// if (e.vlan == null)
+// newEntity.vlan = (short)1;
+// else
+// newEntity.vlan = (short)((e.vlan + 1 % 4095)+1);
+// newDevice = new Device(newDevice, newEntity, -1);
+// }
+// assertEquals(false, newDevice.equals(d));
+// super.put(newDevice.getDeviceKey(), newDevice);
+// }
+// }
+// return devs;
+// }
+// }
+//
+// @Test
+// public void testEntityExpiration() throws Exception {
+// doTestEntityExpiration();
+// }
+//
+// @Test
+// public void testDeviceExpiration() throws Exception {
+// doTestDeviceExpiration();
+// }
+//
+// /* Test correct entity cleanup behavior when a concurrent modification
+// * occurs.
+// */
+// @Test
+// public void testEntityExpirationConcurrentModification() throws Exception {
+// deviceManager.deviceMap = new ConcurrentlyModifiedDeviceMap(false);
+// doTestEntityExpiration();
+// }
+//
+// /* Test correct entity cleanup behavior when a concurrent remove
+// * occurs.
+// */
+// @Test
+// public void testDeviceExpirationConcurrentRemove() throws Exception {
+// deviceManager.deviceMap = new ConcurrentlyModifiedDeviceMap(true);
+// doTestDeviceExpiration();
+// }
+//
+// /* Test correct entity cleanup behavior when a concurrent modification
+// * occurs.
+// */
+// @Test
+// public void testDeviceExpirationConcurrentModification() throws Exception {
+// deviceManager.deviceMap = new ConcurrentlyModifiedDeviceMap(false);
+// doTestDeviceExpiration();
+// }
+//
+//
+// @Test
+// public void testAttachmentPointFlapping() throws Exception {
+// Calendar c = Calendar.getInstance();
+//
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// expect(mockTopology.isAttachmentPointPort(anyLong(),
+// anyShort())).andReturn(true).anyTimes();
+// expect(mockTopology.isBroadcastDomainPort(anyLong(),
+// anyShort())).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isInSameBroadcastDomain(anyLong(), anyShort(),
+// anyLong(), anyShort())).andReturn(false).anyTimes();
+// expect(mockTopology.getL2DomainId(anyLong())).
+// andReturn(1L).anyTimes();
+// expect(mockTopology.isConsistent(1L, (short)1, 1L, (short)1)).
+// andReturn(true).anyTimes();
+// expect(mockTopology.isConsistent(1L, (short)1, 5L, (short)1)).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isConsistent(1L, (short)1, 10L, (short)1)).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isConsistent(5L, (short)1, 10L, (short)1)).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isConsistent(10L, (short)1, 1L, (short)1)).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isConsistent(5L, (short)1, 1L, (short)1)).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isConsistent(10L, (short)1, 5L, (short)1)).
+// andReturn(false).anyTimes();
+//
+// Date topologyUpdateTime = new Date();
+// expect(mockTopology.getLastUpdateTime()).andReturn(topologyUpdateTime).
+// anyTimes();
+//
+//
+// replay(mockTopology);
+// deviceManager.topology = mockTopology;
+//
+// Entity entity1 = new Entity(1L, null, null, 1L, 1, c.getTime());
+// Entity entity1a = new Entity(1L, null, 1, 1L, 1, c.getTime());
+// Entity entity2 = new Entity(1L, null, null, 5L, 1, c.getTime());
+// Entity entity3 = new Entity(1L, null, null, 10L, 1, c.getTime());
+// entity1.setLastSeenTimestamp(c.getTime());
+// c.add(Calendar.MILLISECOND, Entity.ACTIVITY_TIMEOUT/2);
+// entity1a.setLastSeenTimestamp(c.getTime());
+// c.add(Calendar.MILLISECOND, 1);
+// entity2.setLastSeenTimestamp(c.getTime());
+// c.add(Calendar.MILLISECOND, 1);
+// entity3.setLastSeenTimestamp(c.getTime());
+//
+//
+//
+// IDevice d;
+// d = deviceManager.learnDeviceByEntity(entity1);
+// d = deviceManager.learnDeviceByEntity(entity1a);
+// d = deviceManager.learnDeviceByEntity(entity2);
+// d = deviceManager.learnDeviceByEntity(entity3);
+//
+// // all entities are active, so entity3 should win
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(10L, 1) },
+// d.getAttachmentPoints());
+//
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(10L, 1),},
+// d.getAttachmentPoints(true));
+//
+// c.add(Calendar.MILLISECOND, Entity.ACTIVITY_TIMEOUT/4);
+// entity1.setLastSeenTimestamp(c.getTime());
+// d = deviceManager.learnDeviceByEntity(entity1);
+//
+// // all are still active; entity3 should still win
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) },
+// d.getAttachmentPoints());
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1),
+// new SwitchPort(5L, 1,
+// ErrorStatus.DUPLICATE_DEVICE),
+// new SwitchPort(10L, 1,
+// ErrorStatus.DUPLICATE_DEVICE) },
+// d.getAttachmentPoints(true));
+//
+// c.add(Calendar.MILLISECOND, Entity.ACTIVITY_TIMEOUT+2000);
+// entity1.setLastSeenTimestamp(c.getTime());
+// d = deviceManager.learnDeviceByEntity(entity1);
+//
+// assertEquals(entity1.getActiveSince(), entity1.getLastSeenTimestamp());
+// // entity1 should now be the only active entity
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) },
+// d.getAttachmentPoints());
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) },
+// d.getAttachmentPoints(true));
+// }
+//
+//
+// @Test
+// public void testAttachmentPointFlappingTwoCluster() throws Exception {
+// Calendar c = Calendar.getInstance();
+//
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// expect(mockTopology.isAttachmentPointPort(anyLong(),
+// anyShort())).andReturn(true).anyTimes();
+// expect(mockTopology.isBroadcastDomainPort(anyLong(),
+// anyShort())).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isInSameBroadcastDomain(anyLong(), anyShort(),
+// anyLong(), anyShort())).andReturn(false).anyTimes();
+// expect(mockTopology.getL2DomainId(1L)).
+// andReturn(1L).anyTimes();
+// expect(mockTopology.getL2DomainId(5L)).
+// andReturn(5L).anyTimes();
+// expect(mockTopology.isConsistent(1L, (short)1, 1L, (short)2)).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isConsistent(1L, (short)2, 5L, (short)1)).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isConsistent(5L, (short)1, 5L, (short)2)).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isConsistent(1L, (short)2, 1L, (short)1)).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isConsistent(1L, (short)1, 5L, (short)1)).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isConsistent(1L, (short)1, 5L, (short)2)).
+// andReturn(false).anyTimes();
+// expect(mockTopology.isConsistent(5L, (short)2, 5L, (short)1)).
+// andReturn(false).anyTimes();
+//
+// Date topologyUpdateTime = new Date();
+// expect(mockTopology.getLastUpdateTime()).andReturn(topologyUpdateTime).
+// anyTimes();
+//
+// replay(mockTopology);
+// deviceManager.topology = mockTopology;
+//
+// Entity entity1 = new Entity(1L, null, null, 1L, 1, c.getTime());
+// Entity entity2 = new Entity(1L, null, null, 1L, 2, c.getTime());
+// Entity entity3 = new Entity(1L, null, null, 5L, 1, c.getTime());
+// Entity entity4 = new Entity(1L, null, null, 5L, 2, c.getTime());
+// entity1.setLastSeenTimestamp(c.getTime());
+// c.add(Calendar.MILLISECOND, Entity.ACTIVITY_TIMEOUT/2);
+// c.add(Calendar.MILLISECOND, 1);
+// entity2.setLastSeenTimestamp(c.getTime());
+// c.add(Calendar.MILLISECOND, 1);
+// entity3.setLastSeenTimestamp(c.getTime());
+// c.add(Calendar.MILLISECOND, 1);
+// entity4.setLastSeenTimestamp(c.getTime());
+//
+// deviceManager.learnDeviceByEntity(entity1);
+// deviceManager.learnDeviceByEntity(entity2);
+// deviceManager.learnDeviceByEntity(entity3);
+// IDevice d = deviceManager.learnDeviceByEntity(entity4);
+//
+// // all entities are active, so entities 2,4 should win
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 2),
+// new SwitchPort(5L, 2) },
+// d.getAttachmentPoints());
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 2),
+// new SwitchPort(5L, 2)},
+// d.getAttachmentPoints(true));
+//
+// c.add(Calendar.MILLISECOND, 1);
+// entity1.setLastSeenTimestamp(c.getTime());
+// d = deviceManager.learnDeviceByEntity(entity1);
+//
+// // all entities are active, so entities 2,4 should win
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1),
+// new SwitchPort(5L, 2) },
+// d.getAttachmentPoints());
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1),
+// new SwitchPort(5L, 2),
+// new SwitchPort(1L, 2, ErrorStatus.DUPLICATE_DEVICE)},
+// d.getAttachmentPoints(true));
+//
+// c.add(Calendar.MILLISECOND, Entity.ACTIVITY_TIMEOUT+1);
+// entity1.setLastSeenTimestamp(c.getTime());
+// d = deviceManager.learnDeviceByEntity(entity1);
+//
+// // entities 3,4 are still in conflict, but 1 should be resolved
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1),
+// new SwitchPort(5L, 2) },
+// d.getAttachmentPoints());
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1),
+// new SwitchPort(5L, 2)},
+// d.getAttachmentPoints(true));
+//
+// entity3.setLastSeenTimestamp(c.getTime());
+// d = deviceManager.learnDeviceByEntity(entity3);
+//
+// // no conflicts, 1 and 3 will win
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1),
+// new SwitchPort(5L, 1) },
+// d.getAttachmentPoints());
+// assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1),
+// new SwitchPort(5L, 1) },
+// d.getAttachmentPoints(true));
+//
+// }
+//
+// protected void doTestDeviceQuery() throws Exception {
+// Entity entity1 = new Entity(1L, (short)1, 1, 1L, 1, new Date());
+// Entity entity2 = new Entity(2L, (short)2, 2, 1L, 2, new Date());
+// Entity entity3 = new Entity(3L, (short)3, 3, 5L, 1, new Date());
+// Entity entity4 = new Entity(4L, (short)4, 3, 5L, 2, new Date());
+// Entity entity5 = new Entity(1L, (short)4, 3, 5L, 2, new Date());
+//
+// Device d1 = deviceManager.learnDeviceByEntity(entity1);
+// deviceManager.learnDeviceByEntity(entity2);
+// Device d3 = deviceManager.learnDeviceByEntity(entity3);
+// Device d4 = deviceManager.learnDeviceByEntity(entity4);
+//
+// IDevice d;
+//
+// Iterator<? extends IDevice> iter =
+// deviceManager.queryDevices(null, (short)1, 1, null, null);
+// int count = 0;
+// while (iter.hasNext()) {
+// count += 1;
+// d = iter.next();
+// assertEquals(d1.getDeviceKey(), d.getDeviceKey());
+// }
+// assertEquals(1, count);
+//
+// iter = deviceManager.queryDevices(null, (short)3, 3, null, null);
+// count = 0;
+// while (iter.hasNext()) {
+// count += 1;
+// d = iter.next();
+// assertEquals(d3.getDeviceKey(), d.getDeviceKey());
+// }
+// assertEquals(1, count);
+//
+// iter = deviceManager.queryDevices(null, (short)1, 3, null, null);
+// count = 0;
+// while (iter.hasNext()) {
+// count += 1;
+// iter.next();
+// }
+// assertEquals(0, count);
+//
+// Device d5 = deviceManager.learnDeviceByEntity(entity5);
+// iter = deviceManager.queryDevices(null, (short)4, 3, null, null);
+// count = 0;
+// Set<Long> deviceKeysFromIterator = new HashSet<Long>();
+// while (iter.hasNext()) {
+// count += 1;
+// d = iter.next();
+// deviceKeysFromIterator.add(d.getDeviceKey());
+// }
+// Set<Long> expectedDeviceKeys = new HashSet<Long>();
+// expectedDeviceKeys.add(d4.getDeviceKey());
+// expectedDeviceKeys.add(d5.getDeviceKey());
+// assertEquals(expectedDeviceKeys, deviceKeysFromIterator);
+// assertEquals(2, count);
+//
+//
+// iter = deviceManager.queryDevices(1L, null, null, null, null);
+// count = 0;
+// deviceKeysFromIterator = new HashSet<Long>();
+// while (iter.hasNext()) {
+// count += 1;
+// d = iter.next();
+// deviceKeysFromIterator.add(d.getDeviceKey());
+// }
+// expectedDeviceKeys = new HashSet<Long>();
+// expectedDeviceKeys.add(d1.getDeviceKey());
+// expectedDeviceKeys.add(d5.getDeviceKey());
+// assertEquals(expectedDeviceKeys, deviceKeysFromIterator);
+// assertEquals(2, count);
+// }
+//
+// @Test
+// public void testDeviceIndex() throws Exception {
+// EnumSet<IDeviceService.DeviceField> indexFields =
+// EnumSet.noneOf(IDeviceService.DeviceField.class);
+// indexFields.add(IDeviceService.DeviceField.IPV4);
+// indexFields.add(IDeviceService.DeviceField.VLAN);
+// deviceManager.addIndex(false, indexFields);
+//
+// indexFields = EnumSet.noneOf(IDeviceService.DeviceField.class);
+// deviceManager.addIndex(false, indexFields);
+//
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// deviceManager.topology = mockTopology;
+// expect(mockTopology.isAttachmentPointPort(anyLong(),
+// anyShort())).
+// andReturn(true).anyTimes();
+// expect(mockTopology.getL2DomainId(EasyMock.anyLong())).andReturn(1L).anyTimes();
+// replay(mockTopology);
+// doTestDeviceQuery();
+// }
+//
+// @Test
+// public void testDeviceQuery() throws Exception {
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// deviceManager.topology = mockTopology;
+// expect(mockTopology.isAttachmentPointPort(anyLong(),
+// anyShort())).
+// andReturn(true).anyTimes();
+// expect(mockTopology.getL2DomainId(EasyMock.anyLong())).andReturn(1L).anyTimes();
+// replay(mockTopology);
+//
+// doTestDeviceQuery();
+// }
+//
+// protected void doTestDeviceClassQuery() throws Exception {
+// Entity entity1 = new Entity(1L, (short)1, 1, 1L, 1, new Date());
+// Entity entity2 = new Entity(2L, (short)2, 2, 1L, 2, new Date());
+// Entity entity3 = new Entity(3L, (short)3, 3, 5L, 1, new Date());
+// Entity entity4 = new Entity(4L, (short)4, 3, 5L, 2, new Date());
+// Entity entity5 = new Entity(1L, (short)4, 3, 5L, 2, new Date());
+//
+// IDevice d1 = deviceManager.learnDeviceByEntity(entity1);
+// IDevice d2 = deviceManager.learnDeviceByEntity(entity2);
+// IDevice d3 = deviceManager.learnDeviceByEntity(entity3);
+// IDevice d4 = deviceManager.learnDeviceByEntity(entity4);
+// assertEquals(d1.getEntityClass(), d2.getEntityClass());
+// assertEquals(d1.getEntityClass(), d3.getEntityClass());
+// assertEquals(d1.getEntityClass(), d4.getEntityClass());
+//
+// IDevice d;
+//
+// Iterator<? extends IDevice> iter =
+// deviceManager.queryClassDevices(d1.getEntityClass(), null,
+// (short)1, 1, null, null);
+// int count = 0;
+// while (iter.hasNext()) {
+// count += 1;
+// d = iter.next();
+// assertEquals(d1.getDeviceKey(), d.getDeviceKey());
+// }
+// assertEquals(1, count);
+//
+// iter = deviceManager.queryClassDevices(d1.getEntityClass(), null,
+// (short)3, 3, null, null);
+// count = 0;
+// while (iter.hasNext()) {
+// count += 1;
+// d = iter.next();
+// assertEquals(d3.getDeviceKey(), d.getDeviceKey());
+//
+// }
+// assertEquals(1, count);
+//
+// iter = deviceManager.queryClassDevices(d1.getEntityClass(), null,
+// (short)1, 3, null, null);
+// count = 0;
+// while (iter.hasNext()) {
+// count += 1;
+// iter.next();
+// }
+// assertEquals(0, count);
+//
+// IDevice d5 = deviceManager.learnDeviceByEntity(entity5);
+// assertEquals(d1.getEntityClass(), d5.getEntityClass());
+// iter = deviceManager.queryClassDevices(d1.getEntityClass(), null,
+// (short)4, 3, null, null);
+// count = 0;
+// Set<Long> deviceKeysFromIterator = new HashSet<Long>();
+// while (iter.hasNext()) {
+// count += 1;
+// d = iter.next();
+// deviceKeysFromIterator.add(d.getDeviceKey());
+// }
+// Set<Long> expectedDeviceKeys = new HashSet<Long>();
+// expectedDeviceKeys.add(d4.getDeviceKey());
+// expectedDeviceKeys.add(d5.getDeviceKey());
+// assertEquals(expectedDeviceKeys, deviceKeysFromIterator);
+// assertEquals(2, count);
+// }
+//
+// @Test
+// public void testDeviceClassIndex() throws Exception {
+// EnumSet<IDeviceService.DeviceField> indexFields =
+// EnumSet.noneOf(IDeviceService.DeviceField.class);
+// indexFields.add(IDeviceService.DeviceField.IPV4);
+// indexFields.add(IDeviceService.DeviceField.VLAN);
+// deviceManager.addIndex(true, indexFields);
+//
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// deviceManager.topology = mockTopology;
+// expect(mockTopology.isAttachmentPointPort(anyLong(),
+// anyShort())).
+// andReturn(true).anyTimes();
+// expect(mockTopology.getL2DomainId(EasyMock.anyLong())).andReturn(1L).anyTimes();
+// replay(mockTopology);
+//
+// doTestDeviceClassQuery();
+// }
+//
+// @Test
+// public void testDeviceClassQuery() throws Exception {
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// deviceManager.topology = mockTopology;
+// expect(mockTopology.isAttachmentPointPort(anyLong(),
+// anyShort())).
+// andReturn(true).anyTimes();
+// expect(mockTopology.getL2DomainId(EasyMock.anyLong())).andReturn(1L).anyTimes();
+// replay(mockTopology);
+//
+// doTestDeviceClassQuery();
+// }
+//
+// @Test
+// public void testFindDevice() throws FloodlightModuleException {
+// boolean exceptionCaught;
+// deviceManager.entityClassifier= new MockEntityClassifierMac();
+// deviceManager.startUp(null);
+//
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// deviceManager.topology = mockTopology;
+// expect(mockTopology.isAttachmentPointPort(anyLong(),
+// anyShort())).
+// andReturn(true).anyTimes();
+// expect(mockTopology.getL2DomainId(EasyMock.anyLong())).andReturn(1L).anyTimes();
+// replay(mockTopology);
+//
+// Entity entity1 = new Entity(1L, (short)1, 1, 1L, 1, new Date());
+// Entity entity2 = new Entity(2L, (short)2, 2, 1L, 2, new Date());
+// Entity entity2b = new Entity(22L, (short)2, 2, 1L, 2, new Date());
+//
+// Entity entity3 = new Entity(3L, (short)1, 3, 2L, 1, new Date());
+// Entity entity4 = new Entity(4L, (short)2, 4, 2L, 2, new Date());
+//
+// Entity entity5 = new Entity(5L, (short)1, 5, 3L, 1, new Date());
+//
+//
+// IDevice d1 = deviceManager.learnDeviceByEntity(entity1);
+// IDevice d2 = deviceManager.learnDeviceByEntity(entity2);
+// IDevice d3 = deviceManager.learnDeviceByEntity(entity3);
+// IDevice d4 = deviceManager.learnDeviceByEntity(entity4);
+// IDevice d5 = deviceManager.learnDeviceByEntity(entity5);
+//
+// // Make sure the entity classifier worked as expected
+// assertEquals(MockEntityClassifierMac.testECMac1, d1.getEntityClass());
+// assertEquals(MockEntityClassifierMac.testECMac1, d2.getEntityClass());
+// assertEquals(MockEntityClassifierMac.testECMac2, d3.getEntityClass());
+// assertEquals(MockEntityClassifierMac.testECMac2, d4.getEntityClass());
+// assertEquals(DefaultEntityClassifier.entityClass,
+// d5.getEntityClass());
+//
+// // Look up the device using findDevice() which uses only the primary
+// // index
+// assertEquals(d1, deviceManager.findDevice(entity1.getMacAddress(),
+// entity1.getVlan(),
+// entity1.getIpv4Address(),
+// entity1.getSwitchDPID(),
+// entity1.getSwitchPort()));
+// // port changed. Device will be found through class index
+// assertEquals(d1, deviceManager.findDevice(entity1.getMacAddress(),
+// entity1.getVlan(),
+// entity1.getIpv4Address(),
+// entity1.getSwitchDPID(),
+// entity1.getSwitchPort()+1));
+// // VLAN changed. No device matches
+// assertEquals(null, deviceManager.findDevice(entity1.getMacAddress(),
+// (short)42,
+// entity1.getIpv4Address(),
+// entity1.getSwitchDPID(),
+// entity1.getSwitchPort()));
+// assertEquals(null, deviceManager.findDevice(entity1.getMacAddress(),
+// null,
+// entity1.getIpv4Address(),
+// entity1.getSwitchDPID(),
+// entity1.getSwitchPort()));
+// assertEquals(d2, deviceManager.findDeviceByEntity(entity2));
+// assertEquals(null, deviceManager.findDeviceByEntity(entity2b));
+// assertEquals(d3, deviceManager.findDevice(entity3.getMacAddress(),
+// entity3.getVlan(),
+// entity3.getIpv4Address(),
+// entity3.getSwitchDPID(),
+// entity3.getSwitchPort()));
+// // switch and port not set. throws exception
+// exceptionCaught = false;
+// try {
+// assertEquals(null, deviceManager.findDevice(entity3.getMacAddress(),
+// entity3.getVlan(),
+// entity3.getIpv4Address(),
+// null,
+// null));
+// }
+// catch (IllegalArgumentException e) {
+// exceptionCaught = true;
+// }
+// if (!exceptionCaught)
+// fail("findDevice() did not throw IllegalArgumentException");
+// assertEquals(d4, deviceManager.findDeviceByEntity(entity4));
+// assertEquals(d5, deviceManager.findDevice(entity5.getMacAddress(),
+// entity5.getVlan(),
+// entity5.getIpv4Address(),
+// entity5.getSwitchDPID(),
+// entity5.getSwitchPort()));
+// // switch and port not set. throws exception (swith/port are key
+// // fields of IEntityClassifier but not d5.entityClass
+// exceptionCaught = false;
+// try {
+// assertEquals(d5, deviceManager.findDevice(entity5.getMacAddress(),
+// entity5.getVlan(),
+// entity5.getIpv4Address(),
+// null,
+// null));
+// }
+// catch (IllegalArgumentException e) {
+// exceptionCaught = true;
+// }
+// if (!exceptionCaught)
+// fail("findDevice() did not throw IllegalArgumentException");
+//
+//
+// Entity entityNoClass = new Entity(5L, (short)1, 5, -1L, 1, new Date());
+// assertEquals(null, deviceManager.findDeviceByEntity(entityNoClass));
+//
+//
+// // Now look up destination devices
+// assertEquals(d1, deviceManager.findClassDevice(d2.getEntityClass(),
+// entity1.getMacAddress(),
+// entity1.getVlan(),
+// entity1.getIpv4Address()));
+// assertEquals(d1, deviceManager.findClassDevice(d2.getEntityClass(),
+// entity1.getMacAddress(),
+// entity1.getVlan(),
+// null));
+// assertEquals(null, deviceManager.findClassDevice(d2.getEntityClass(),
+// entity1.getMacAddress(),
+// (short) -1,
+// 0));
+// }
+//
+//
+//
+// @Test
+// public void testGetIPv4Addresses() {
+// // Looks like Date is only 1s granularity
+//
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// deviceManager.topology = mockTopology;
+// expect(mockTopology.isAttachmentPointPort(anyLong(),
+// anyShort())).
+// andReturn(true).anyTimes();
+// expect(mockTopology.getL2DomainId(anyLong())).andReturn(1L).anyTimes();
+// expect(mockTopology.isConsistent(EasyMock.anyLong(),
+// EasyMock.anyShort(),
+// EasyMock.anyLong(),
+// EasyMock.anyShort()))
+// .andReturn(false)
+// .anyTimes();
+// expect(mockTopology.isBroadcastDomainPort(EasyMock.anyLong(),
+// EasyMock.anyShort()))
+// .andReturn(false)
+// .anyTimes();
+// expect(mockTopology.isInSameBroadcastDomain(EasyMock.anyLong(),
+// EasyMock.anyShort(),
+// EasyMock.anyLong(),
+// EasyMock.anyShort())).
+// andReturn(false).anyTimes();
+// replay(mockTopology);
+//
+// Entity e1 = new Entity(1L, (short)1, null, null, null, new Date(2000));
+// Device d1 = deviceManager.learnDeviceByEntity(e1);
+// assertArrayEquals(new Integer[0], d1.getIPv4Addresses());
+//
+//
+// Entity e2 = new Entity(2L, (short)2, 2, null, null, new Date(2000));
+// Device d2 = deviceManager.learnDeviceByEntity(e2);
+// d2 = deviceManager.learnDeviceByEntity(e2);
+// assertArrayEquals(new Integer[] { 2 }, d2.getIPv4Addresses());
+// // More than one entity
+// Entity e2b = new Entity(2L, (short)2, null, 2L, 2, new Date(3000));
+// d2 = deviceManager.learnDeviceByEntity(e2b);
+// assertEquals(2, d2.entities.length);
+// assertArrayEquals(new Integer[] { 2 }, d2.getIPv4Addresses());
+// // and now add an entity with an IP
+// Entity e2c = new Entity(2L, (short)2, 2, 2L, 3, new Date(3000));
+// d2 = deviceManager.learnDeviceByEntity(e2c);
+// assertArrayEquals(new Integer[] { 2 }, d2.getIPv4Addresses());
+// assertEquals(3, d2.entities.length);
+//
+// // Other devices with different IPs shouldn't interfere
+// Entity e3 = new Entity(3L, (short)3, 3, null, null, new Date(4000));
+// Entity e3b = new Entity(3L, (short)3, 3, 3L, 3, new Date(4400));
+// Device d3 = deviceManager.learnDeviceByEntity(e3);
+// d3 = deviceManager.learnDeviceByEntity(e3b);
+// assertArrayEquals(new Integer[] { 2 }, d2.getIPv4Addresses());
+// assertArrayEquals(new Integer[] { 3 }, d3.getIPv4Addresses());
+//
+// // Add another IP to d3
+// Entity e3c = new Entity(3L, (short)3, 33, 3L, 3, new Date(4400));
+// d3 = deviceManager.learnDeviceByEntity(e3c);
+// Integer[] ips = d3.getIPv4Addresses();
+// Arrays.sort(ips);
+// assertArrayEquals(new Integer[] { 3, 33 }, ips);
+//
+// // Add another device that also claims IP2 but is older than e2
+// Entity e4 = new Entity(4L, (short)4, 2, null, null, new Date(1000));
+// Entity e4b = new Entity(4L, (short)4, null, 4L, 4, new Date(1000));
+// Device d4 = deviceManager.learnDeviceByEntity(e4);
+// assertArrayEquals(new Integer[] { 2 }, d2.getIPv4Addresses());
+// assertArrayEquals(new Integer[0], d4.getIPv4Addresses());
+// // add another entity to d4
+// d4 = deviceManager.learnDeviceByEntity(e4b);
+// assertArrayEquals(new Integer[0], d4.getIPv4Addresses());
+//
+// // Make e4 and e4a newer
+// Entity e4c = new Entity(4L, (short)4, 2, null, null, new Date(5000));
+// Entity e4d = new Entity(4L, (short)4, null, 4L, 5, new Date(5000));
+// d4 = deviceManager.learnDeviceByEntity(e4c);
+// d4 = deviceManager.learnDeviceByEntity(e4d);
+// assertArrayEquals(new Integer[0], d2.getIPv4Addresses());
+// // FIXME: d4 should not return IP4
+// assertArrayEquals(new Integer[] { 2 }, d4.getIPv4Addresses());
+//
+// // Add another newer entity to d2 but with different IP
+// Entity e2d = new Entity(2L, (short)2, 22, 4L, 6, new Date(6000));
+// d2 = deviceManager.learnDeviceByEntity(e2d);
+// assertArrayEquals(new Integer[] { 22 }, d2.getIPv4Addresses());
+// assertArrayEquals(new Integer[] { 2 }, d4.getIPv4Addresses());
+//
+// // new IP for d2,d4 but with same timestamp. Both devices get the IP
+// Entity e2e = new Entity(2L, (short)2, 42, 2L, 4, new Date(7000));
+// d2 = deviceManager.learnDeviceByEntity(e2e);
+// ips= d2.getIPv4Addresses();
+// Arrays.sort(ips);
+// assertArrayEquals(new Integer[] { 22, 42 }, ips);
+// Entity e4e = new Entity(4L, (short)4, 42, 4L, 7, new Date(7000));
+// d4 = deviceManager.learnDeviceByEntity(e4e);
+// ips= d4.getIPv4Addresses();
+// Arrays.sort(ips);
+// assertArrayEquals(new Integer[] { 2, 42 }, ips);
+//
+// // add a couple more IPs
+// Entity e2f = new Entity(2L, (short)2, 4242, 2L, 5, new Date(8000));
+// d2 = deviceManager.learnDeviceByEntity(e2f);
+// ips= d2.getIPv4Addresses();
+// Arrays.sort(ips);
+// assertArrayEquals(new Integer[] { 22, 42, 4242 }, ips);
+// Entity e4f = new Entity(4L, (short)4, 4242, 4L, 8, new Date(9000));
+// d4 = deviceManager.learnDeviceByEntity(e4f);
+// ips= d4.getIPv4Addresses();
+// Arrays.sort(ips);
+// assertArrayEquals(new Integer[] { 2, 42, 4242 }, ips);
+// }
+//
+// // TODO: this test should really go into a separate class that collects
+// // unit tests for Device
+// @Test
+// public void testGetSwitchPortVlanId() {
+// Entity entity1 = new Entity(1L, (short)1, null, 10L, 1, new Date());
+// Entity entity2 = new Entity(1L, null, null, 10L, 1, new Date());
+// Entity entity3 = new Entity(1L, (short)3, null, 1L, 1, new Date());
+// Entity entity4 = new Entity(1L, (short)42, null, 1L, 1, new Date());
+// Entity[] entities = new Entity[] { entity1, entity2,
+// entity3, entity4
+// };
+// Device d = new Device(null,1L, null, null, null,
+// Arrays.asList(entities), null);
+// SwitchPort swp1x1 = new SwitchPort(1L, 1);
+// SwitchPort swp1x2 = new SwitchPort(1L, 2);
+// SwitchPort swp2x1 = new SwitchPort(2L, 1);
+// SwitchPort swp10x1 = new SwitchPort(10L, 1);
+// assertArrayEquals(new Short[] { -1, 1},
+// d.getSwitchPortVlanIds(swp10x1));
+// assertArrayEquals(new Short[] { 3, 42},
+// d.getSwitchPortVlanIds(swp1x1));
+// assertArrayEquals(new Short[0],
+// d.getSwitchPortVlanIds(swp1x2));
+// assertArrayEquals(new Short[0],
+// d.getSwitchPortVlanIds(swp2x1));
+// }
+//
+// @Test
+// public void testReclassifyDevice() throws FloodlightModuleException {
+// MockFlexEntityClassifier flexClassifier =
+// new MockFlexEntityClassifier();
+// deviceManager.entityClassifier= flexClassifier;
+// deviceManager.startUp(null);
+//
+// ITopologyService mockTopology = createMock(ITopologyService.class);
+// deviceManager.topology = mockTopology;
+// expect(mockTopology.isAttachmentPointPort(anyLong(),
+// anyShort())).
+// andReturn(true).anyTimes();
+// expect(mockTopology.getL2DomainId(anyLong())).andReturn(1L).anyTimes();
+// expect(mockTopology.isConsistent(EasyMock.anyLong(),
+// EasyMock.anyShort(),
+// EasyMock.anyLong(),
+// EasyMock.anyShort()))
+// .andReturn(false)
+// .anyTimes();
+// expect(mockTopology.isBroadcastDomainPort(EasyMock.anyLong(),
+// EasyMock.anyShort()))
+// .andReturn(false)
+// .anyTimes();
+// replay(mockTopology);
+//
+// //flexClassifier.createTestEntityClass("Class1");
+//
+// Entity entity1 = new Entity(1L, (short)1, 1, 1L, 1, new Date());
+// Entity entity1b = new Entity(1L, (short)2, 1, 1L, 1, new Date());
+// Entity entity2 = new Entity(2L, (short)1, 2, 2L, 2, new Date());
+// Entity entity2b = new Entity(2L, (short)2, 2, 2L, 2, new Date());
+//
+//
+// Device d1 = deviceManager.learnDeviceByEntity(entity1);
+// Device d2 = deviceManager.learnDeviceByEntity(entity2);
+// Device d1b = deviceManager.learnDeviceByEntity(entity1b);
+// Device d2b = deviceManager.learnDeviceByEntity(entity2b);
+//
+// d1 = deviceManager.getDeviceIteratorForQuery(entity1.getMacAddress(),
+// entity1.getVlan(), entity1.getIpv4Address(),
+// entity1.getSwitchDPID(), entity1.getSwitchPort())
+// .next();
+// d1b = deviceManager.getDeviceIteratorForQuery(entity1b.getMacAddress(),
+// entity1b.getVlan(), entity1b.getIpv4Address(),
+// entity1b.getSwitchDPID(), entity1b.getSwitchPort()).next();
+//
+// assertEquals(d1, d1b);
+//
+// d2 = deviceManager.getDeviceIteratorForQuery(entity2.getMacAddress(),
+// entity2.getVlan(), entity2.getIpv4Address(),
+// entity2.getSwitchDPID(), entity2.getSwitchPort()).next();
+// d2b = deviceManager.getDeviceIteratorForQuery(entity2b.getMacAddress(),
+// entity2b.getVlan(), entity2b.getIpv4Address(),
+// entity2b.getSwitchDPID(), entity2b.getSwitchPort()).next();
+// assertEquals(d2, d2b);
+//
+// IEntityClass eC1 = flexClassifier.createTestEntityClass("C1");
+// IEntityClass eC2 = flexClassifier.createTestEntityClass("C2");
+//
+// flexClassifier.addVlanEntities((short)1, eC1);
+// flexClassifier.addVlanEntities((short)2, eC1);
+//
+// deviceManager.reclassifyDevice(d1);
+// deviceManager.reclassifyDevice(d2);
+//
+// d1 = deviceManager.deviceMap.get(
+// deviceManager.primaryIndex.findByEntity(entity1));
+// d1b = deviceManager.deviceMap.get(
+// deviceManager.primaryIndex.findByEntity(entity1b));
+//
+// assertEquals(d1, d1b);
+//
+// d2 = deviceManager.deviceMap.get(
+// deviceManager.primaryIndex.findByEntity(entity2));
+// d2b = deviceManager.deviceMap.get(
+// deviceManager.primaryIndex.findByEntity(entity2b));
+//
+// assertEquals(d2, d2b);
+//
+// flexClassifier.addVlanEntities((short)1, eC2);
+//
+// deviceManager.reclassifyDevice(d1);
+// deviceManager.reclassifyDevice(d2);
+// d1 = deviceManager.deviceMap.get(
+// deviceManager.primaryIndex.findByEntity(entity1));
+// d1b = deviceManager.deviceMap.get(
+// deviceManager.primaryIndex.findByEntity(entity1b));
+// d2 = deviceManager.deviceMap.get(
+// deviceManager.primaryIndex.findByEntity(entity2));
+// d2b = deviceManager.deviceMap.get(
+// deviceManager.primaryIndex.findByEntity(entity2b));
+//
+// assertNotSame(d1, d1b);
+//
+// assertNotSame(d2, d2b);
+//
+// flexClassifier.addVlanEntities((short)1, eC1);
+// deviceManager.reclassifyDevice(d1);
+// deviceManager.reclassifyDevice(d2);
+// ClassState classState = deviceManager.classStateMap.get(eC1.getName());
+//
+// Long deviceKey1 = null;
+// Long deviceKey1b = null;
+// Long deviceKey2 = null;
+// Long deviceKey2b = null;
+//
+// deviceKey1 =
+// classState.classIndex.findByEntity(entity1);
+// deviceKey1b =
+// classState.classIndex.findByEntity(entity1b);
+// deviceKey2 =
+// classState.classIndex.findByEntity(entity2);
+// deviceKey2b =
+// classState.classIndex.findByEntity(entity2b);
+//
+// assertEquals(deviceKey1, deviceKey1b);
+//
+// assertEquals(deviceKey2, deviceKey2b);
+// }
+//
+// @Test
+// public void testSyncEntity() {
+// Date d1 = new Date();
+// Date d2 = new Date(0);
+// Entity e1 = new Entity(1L, (short)2, 3, 4L, 5, d1);
+// e1.setActiveSince(d2);
+// SyncEntity se1 = new SyncEntity(e1);
+// assertEntityEquals(e1, se1);
+// assertEquals(1L, se1.macAddress);
+// assertEquals(Short.valueOf((short)2), se1.vlan);
+// assertEquals(Integer.valueOf(3), se1.ipv4Address);
+// assertEquals(Long.valueOf(4L), se1.switchDPID);
+// assertEquals(Integer.valueOf(5), se1.switchPort);
+// assertEquals(d1, se1.lastSeenTimestamp);
+// assertEquals(d2, se1.activeSince);
+// assertNotSame(d1, se1.lastSeenTimestamp);
+// assertNotSame(d2, se1.activeSince);
+//
+// Entity e2 = new Entity(42L, null, null, null, null, null);
+// SyncEntity se2 = new SyncEntity(e2);
+// assertEntityEquals(e2, se2);
+//
+// SyncEntity se3 = new SyncEntity();
+// SyncEntity se4 = new SyncEntity();
+// se3.lastSeenTimestamp = new Date(1000);
+// se4.lastSeenTimestamp = new Date(2000);
+// assertTrue("", se3.compareTo(se4) < 0);
+// assertTrue("", se4.compareTo(se3) > 0);
+// se4.lastSeenTimestamp = new Date(1000);
+// assertTrue("", se3.compareTo(se4) == 0);
+// assertTrue("", se4.compareTo(se3) == 0);
+// se4.lastSeenTimestamp = new Date(500);
+// assertTrue("", se3.compareTo(se4) > 0);
+// assertTrue("", se4.compareTo(se3) < 0);
+// }
+//
+// /* Test basic DeviceSyncRepresentation behavior */
+// @Test
+// public void testDeviceSyncRepresentationBasics() {
+// DeviceSyncRepresentation dsr = new DeviceSyncRepresentation();
+// assertNull(dsr.getKey());
+// assertNull(dsr.getEntities());
+// dsr.setKey("MyKey");
+// assertEquals("MyKey", dsr.getKey());
+// assertEquals("MyKey", dsr.toString());
+//
+// List<SyncEntity> entities = new ArrayList<SyncEntity>();
+// Entity e1a = new Entity(1L, (short)2, 3, 4L, 5, new Date(1000));
+// Entity e1b = new Entity(1L, (short)2, null, 4L, 5, new Date(0));
+// entities.add(new SyncEntity(e1a));
+// entities.add(new SyncEntity(e1b));
+// // e1b comes before e1 (lastSeen) but we add it after it to test
+// // sorting
+// dsr.setEntities(entities);
+//
+// assertEquals(2, dsr.getEntities().size());
+// // e1b has earlier time
+// assertEquals(e1b, dsr.getEntities().get(0).asEntity());
+// assertEquals(e1a, dsr.getEntities().get(1).asEntity());
+//
+// dsr.setKey(null);
+// dsr.setEntities(null);
+// assertNull(dsr.getKey());
+// assertNull(dsr.getEntities());
+// }
+//
+// @Test
+// public void testDeviceSyncRepresentationFromDevice() {
+// ITopologyService mockTopology = makeMockTopologyAllPortsAp();
+// replay(mockTopology);
+// deviceManager.topology = mockTopology;
+//
+// deviceManager.entityClassifier = new MockEntityClassifier();
+//
+// //**************************************
+// // Test 1: a single entity
+// Entity e1 = new Entity(1L, (short)2, 3, 4L, 5, new Date(1000));
+// Device d1 = deviceManager.learnDeviceByEntity(e1);
+// assertEquals("Sanity check failed. Device doesn't have the expected " +
+// "entity class. Something with the test setup is strange",
+// "DefaultEntityClass", d1.getEntityClass().getName());
+// assertEquals("Sanity check failed. Device doesn't have the expected " +
+// "entity class. Something with the test setup is strange",
+// EnumSet.of(DeviceField.MAC, DeviceField.VLAN),
+// d1.getEntityClass().getKeyFields());
+//
+// Long deviceKey = d1.getDeviceKey();
+// DeviceSyncRepresentation dsr1 = new DeviceSyncRepresentation(d1);
+// assertEquals("DefaultEntityClass::00:00:00:00:00:01::[2]::",
+// dsr1.getKey());
+// assertEquals(1, dsr1.getEntities().size());
+// assertEquals(e1, dsr1.getEntities().get(0).asEntity());
+//
+// //**************************************
+// // Test 1b: same device, now with a second entity (no IP).
+// // this second entity has a lastSeen time that is earlier than the
+// // first entity
+// Entity e1b = new Entity(1L, (short)2, null, 4L, 5, new Date(0));
+// d1 = deviceManager.learnDeviceByEntity(e1b);
+// assertEquals("Sanity check failed. Should still be same device but " +
+// "deviceKeys differs", deviceKey, d1.getDeviceKey());
+// dsr1 = new DeviceSyncRepresentation(d1);
+// assertEquals("DefaultEntityClass::00:00:00:00:00:01::[2]::",
+// dsr1.getKey());
+// assertEquals(2, dsr1.getEntities().size());
+// // Entities are ordered by their lastSeen time. e1b should come
+// // before e1.
+// assertEquals(e1, dsr1.getEntities().get(1).asEntity());
+// assertEquals(e1b, dsr1.getEntities().get(0).asEntity());
+//
+// //**************************************
+// // Test 1c: same device with a third entity that does not have a
+// // switch port. It should be added to the DeviceSyncRepresentation
+// Entity e1c = new Entity(1L, (short)2, 33, null, null, new Date(2000));
+// d1 = deviceManager.learnDeviceByEntity(e1c);
+// assertEquals("Sanity check failed. Should still be same device but " +
+// "deviceKeys differs", deviceKey, d1.getDeviceKey());
+// dsr1 = new DeviceSyncRepresentation(d1);
+// assertEquals("DefaultEntityClass::00:00:00:00:00:01::[2]::",
+// dsr1.getKey());
+// assertEquals(3, dsr1.getEntities().size());
+// // Entities are ordered by their lastSeen time
+// assertEquals(e1c, dsr1.getEntities().get(2).asEntity());
+// assertEquals(e1, dsr1.getEntities().get(1).asEntity());
+// assertEquals(e1b, dsr1.getEntities().get(0).asEntity());
+//
+// //**************************************
+// // Test 1d: same device with a fourth entity that has a different
+// // attachment point and that is newer. Device should move and
+// // non-attachment point entities should be removed (e1b). Although
+// // e1 is non-attachment point it will remain because it has an IP
+// Entity e1d = new Entity(1L, (short)2, 33, 4L, 6, new Date(3000));
+// d1 = deviceManager.learnDeviceByEntity(e1d);
+// assertEquals("Sanity check failed. Should still be same device but " +
+// "deviceKeys differs", deviceKey, d1.getDeviceKey());
+// dsr1 = new DeviceSyncRepresentation(d1);
+// assertEquals("DefaultEntityClass::00:00:00:00:00:01::[2]::",
+// dsr1.getKey());
+// assertEquals(3, dsr1.getEntities().size());
+// assertEquals(e1, dsr1.getEntities().get(0).asEntity());
+// assertEquals(e1c, dsr1.getEntities().get(1).asEntity());
+// assertEquals(e1d, dsr1.getEntities().get(2).asEntity());
+//
+// d1 = null;
+//
+//
+// //**************************************
+// // Test 2: a second device with a different entity class. The
+// // mock entity classifier will return an entity class where all
+// // fields are keys if the DPID is > 10L
+// Entity e2 = new Entity(2L, (short)23, 24, 11L, 1, new Date(0));
+// Device d2 = deviceManager.learnDeviceByEntity(e2);
+// DeviceSyncRepresentation dsr2 = new DeviceSyncRepresentation(d2);
+// assertEquals("Sanity check failed. Device doesn't have the expected " +
+// "entity class. Something with the test setup is strange",
+// "TestEntityClass", d2.getEntityClass().getName());
+// assertEquals("Sanity check failed. Device doesn't have the expected " +
+// "entity class. Something with the test setup is strange",
+// EnumSet.of(DeviceField.MAC, DeviceField.VLAN,
+// DeviceField.SWITCH, DeviceField.PORT),
+// d2.getEntityClass().getKeyFields());
+// SwitchPort swp = new SwitchPort(11L, 1, null);
+// assertEquals("TestEntityClass::00:00:00:00:00:02::[23]::[" +
+// swp.toString() + "]::",
+// dsr2.getKey());
+// }
+//
+// /* interate through all entries in the sync store and return them as
+// * list. We don't return the key from the store however, we assert
+// * that the key from the store matches the key in the representation.
+// * If we have a null value (tombstone) we simply add the null value to
+// * the list to return.
+// */
+// private List<DeviceSyncRepresentation> getEntriesFromStore()
+// throws Exception {
+// List<DeviceSyncRepresentation> entries =
+// new ArrayList<DeviceSyncRepresentation>();
+// IClosableIterator<Entry<String, Versioned<DeviceSyncRepresentation>>> iter =
+// storeClient.entries();
+// try {
+// while(iter.hasNext()) {
+// Entry<String, Versioned<DeviceSyncRepresentation>> entry =
+// iter.next();
+// DeviceSyncRepresentation dsr = entry.getValue().getValue();
+// if (dsr != null)
+// assertEquals(entry.getKey(), dsr.getKey());
+// entries.add(dsr);
+// }
+// } finally {
+// if (iter != null)
+// iter.close();
+// }
+// return entries;
+// }
+//
+// /*
+// * assert whether the given Entity expected is equals to the given
+// * SyncEntity actual. This method also compares the times (lastSeen,
+// * activeSince). Entity.equals will not do that!
+// */
+// private static void assertEntityEquals(Entity expected, SyncEntity actual) {
+// assertNotNull(actual);
+// assertNotNull(expected);
+// Entity actualEntity = actual.asEntity();
+// assertEquals("entityFields", expected, actualEntity);
+// assertEquals("lastSeenTimestamp",
+// expected.getLastSeenTimestamp(),
+// actualEntity.getLastSeenTimestamp());
+// assertEquals("activeSince",
+// expected.getActiveSince(), actualEntity.getActiveSince());
+// }
+//
+// /* This test tests the normal operation as master when we write to the sync
+// * store or delete from the store.
+// */
+// @Test
+// public void testWriteToSyncStore() throws Exception {
+// int syncStoreIntervalMs = 50;
+// ITopologyService mockTopology = makeMockTopologyAllPortsAp();
+// replay(mockTopology);
+// deviceManager.topology = mockTopology;
+// deviceManager.setSyncStoreWriteInterval(syncStoreIntervalMs);
+//
+// Entity e1a = new Entity(1L, (short)2, 3, 4L, 5, new Date(1000));
+// e1a.setActiveSince(new Date(0));
+// deviceManager.learnDeviceByEntity(e1a);
+//
+// //storeClient.put("FooBar", new DeviceSyncRepresentation());
+//
+// List<DeviceSyncRepresentation> entries = getEntriesFromStore();
+// assertEquals(1, entries.size());
+// DeviceSyncRepresentation dsr1 = entries.get(0);
+// assertEquals(1, dsr1.getEntities().size());
+// assertEntityEquals(e1a, dsr1.getEntities().get(0));
+//
+// // Same entity but newer timestamp. Since the device hasn't changed,
+// // only the timestamp is updated and the write should be throttled.
+// Entity e1b = new Entity(1L, (short)2, 3, 4L, 5, new Date(2000));
+// e1b.setActiveSince(new Date(0));
+// deviceManager.learnDeviceByEntity(e1a);
+// entries = getEntriesFromStore();
+// assertEquals(1, entries.size());
+// dsr1 = entries.get(0);
+// assertEquals(1, dsr1.getEntities().size());
+// assertEntityEquals(e1a, dsr1.getEntities().get(0)); //e1a not e1b !!!
+//
+// // Wait for the write interval to expire then write again.
+// Thread.sleep(syncStoreIntervalMs+5);
+// Entity e1c = new Entity(1L, (short)2, 3, 4L, 5, new Date(3000));
+// e1c.setActiveSince(new Date(0));
+// deviceManager.learnDeviceByEntity(e1c);
+// entries = getEntriesFromStore();
+// assertEquals(1, entries.size());
+// dsr1 = entries.get(0);
+// assertEquals(1, dsr1.getEntities().size());
+// assertEntityEquals(e1c, dsr1.getEntities().get(0)); // e1c !!
+//
+// // Entity for same device but with different IP. should be added
+// // immediately
+// Entity e1d = new Entity(1L, (short)2, 33, 4L, 5, new Date(4000));
+// e1d.setActiveSince(new Date(0));
+// deviceManager.learnDeviceByEntity(e1d);
+// entries = getEntriesFromStore();
+// assertEquals(1, entries.size());
+// dsr1 = entries.get(0);
+// assertEquals(2, dsr1.getEntities().size());
+// assertEntityEquals(e1c, dsr1.getEntities().get(0)); // e1c !!
+// assertEntityEquals(e1d, dsr1.getEntities().get(1)); // e1d !!
+//
+// // Entity for same device with new switch port ==> moved ==> write
+// // update immediately without throttle.
+// // Note: the previous entities will still be there because they have
+// // IPs (even though they aren't for the current attachment point)
+// Entity e1e = new Entity(1L, (short)2, 33, 4L, 6, new Date(5000));
+// e1e.setActiveSince(new Date(0));
+// deviceManager.learnDeviceByEntity(e1e);
+// entries = getEntriesFromStore();
+// assertEquals(1, entries.size());
+// dsr1 = entries.get(0);
+// assertEquals(3, dsr1.getEntities().size());
+// assertEntityEquals(e1c, dsr1.getEntities().get(0));
+// assertEntityEquals(e1d, dsr1.getEntities().get(1));
+// assertEntityEquals(e1e, dsr1.getEntities().get(2));
+//
+// // Add a second device
+// Entity e2 = new Entity(2L, null, null, 5L, 5, new Date());
+// deviceManager.learnDeviceByEntity(e2);
+// entries = getEntriesFromStore();
+// assertEquals(2, entries.size());
+// for (DeviceSyncRepresentation dsr: entries) {
+// // This is a kinda ugly way to ensure we have the two
+// // devices we need..... but it will work for now
+// if (dsr.getKey().contains("::00:00:00:00:00:01::")) {
+// assertEquals(3, dsr.getEntities().size());
+// assertEntityEquals(e1c, dsr.getEntities().get(0));
+// assertEntityEquals(e1d, dsr.getEntities().get(1));
+// assertEntityEquals(e1e, dsr.getEntities().get(2));
+// } else if (dsr.getKey().contains("::00:00:00:00:00:02::")) {
+// assertEquals(1, dsr.getEntities().size());
+// assertEntityEquals(e2, dsr.getEntities().get(0));
+// } else {
+// fail("Unknown entry in store: " + dsr);
+// }
+// }
+//
+//
+// // Run entity cleanup. Since we've used phony time stamps for
+// // device 1 its entities should be cleared and the device should be
+// // removed from the store. Device 2 should remain in the store.
+// deviceManager.cleanupEntities();
+// entries = getEntriesFromStore();
+// assertEquals(2, entries.size());
+// for (DeviceSyncRepresentation dsr: entries) {
+// if (dsr == null) {
+// // pass
+// } else if (dsr.getKey().contains("::00:00:00:00:00:02::")) {
+// assertEquals(1, dsr.getEntities().size());
+// assertEntityEquals(e2, dsr.getEntities().get(0));
+// } else {
+// fail("Unknown entry in store: " + dsr);
+// }
+// }
+// }
+//
+//
+// private void assertDeviceIps(Integer[] expected, IDevice d) {
+// List<Integer> expectedList = Arrays.asList(expected);
+// Collections.sort(expectedList);
+// List<Integer> actualList = Arrays.asList(d.getIPv4Addresses());
+// Collections.sort(actualList);
+// assertEquals(expectedList, actualList);
+// }
+//
+// private IDevice getSingleDeviceFromDeviceManager(long mac) {
+// Iterator<? extends IDevice> diter =
+// deviceManager.queryDevices(mac, null, null, null, null);
+// assertTrue("Query didn't return a device", diter.hasNext());
+// IDevice d = diter.next();
+// assertFalse("Query returned more than one device", diter.hasNext());
+// return d;
+// }
+//
+// @Test
+// public void testToMaster() throws Exception {
+// int syncStoreWriteIntervalMs = 0;
+// int initialSyncStoreConsolidateIntervalMs = 50;
+// ITopologyService mockTopology = makeMockTopologyAllPortsAp();
+// replay(mockTopology);
+// deviceManager.topology = mockTopology;
+// // We want an EntityClassifier that has switch/port as key fields
+// deviceManager.entityClassifier = new MockEntityClassifier();
+// deviceManager.setSyncStoreWriteInterval(syncStoreWriteIntervalMs);
+// deviceManager.setInitialSyncStoreConsolidateMs(initialSyncStoreConsolidateIntervalMs);
+//
+// // Add Device1 with two entities with two different IPs
+// Entity e1a = new Entity(1L, null, 3, 4L, 5, new Date(1000));
+// Entity e1b = new Entity(1L, null, 33, 4L, 5, new Date(2000));
+// Device d1 = deviceManager.allocateDevice(1L, e1a,
+// DefaultEntityClassifier.entityClass);
+// d1 = deviceManager.allocateDevice(d1, e1b, -1);
+// DeviceSyncRepresentation dsr = new DeviceSyncRepresentation(d1);
+// storeClient.put(dsr.getKey(), dsr);
+//
+// // Add Device2 with different switch-ports. Only the most recent
+// // one should be the attachment point
+// Entity e2a = new Entity(2L, null, null, 4L, 4, new Date(1000));
+// Entity e2b = new Entity(2L, null, null, 4L, 5, new Date(2000));
+// Device d2 = deviceManager.allocateDevice(2L, e2a,
+// DefaultEntityClassifier.entityClass);
+// d2 = deviceManager.allocateDevice(d2, e2b, -1);
+// d2.updateAttachmentPoint(4L, (short)5,
+// e2b.getLastSeenTimestamp().getTime());
+// SwitchPort swp = new SwitchPort(4L, 5);
+// SwitchPort[] aps = d2.getAttachmentPoints();
+// // sanity check
+// assertArrayEquals("Sanity check: should only have AP(4L,5)",
+// new SwitchPort[] {swp}, aps);
+// dsr = new DeviceSyncRepresentation(d2);
+// storeClient.put(dsr.getKey(), dsr);
+//
+// // Add a tombstone entry to the store to make sure we don't trip a
+// // NPE
+// dsr = null;
+// Versioned<DeviceSyncRepresentation> versionedDsr =
+// storeClient.get("FooBar");
+// storeClient.put("FooBar", versionedDsr);
+//
+// deviceManager.getHAListener().transitionToMaster();
+//
+// // Query for the Device1. Make sure we have the two IPs we stored.
+// IDevice d = getSingleDeviceFromDeviceManager(1L);
+// assertDeviceIps(new Integer[] {3, 33}, d);
+// assertArrayEquals(new Short[] { Ethernet.VLAN_UNTAGGED }, d.getVlanId());
+// swp = new SwitchPort(4L, 5);
+// assertArrayEquals(new SwitchPort[] { swp }, d.getAttachmentPoints());
+//
+// // Query for Device2. Make sure we only have the more recent AP
+// // Query for the Device1. Make sure we have the two IPs we stored.
+// d = getSingleDeviceFromDeviceManager(2L);
+// assertArrayEquals(new Integer[0], d.getIPv4Addresses());
+// assertArrayEquals(new Short[] { Ethernet.VLAN_UNTAGGED }, d.getVlanId());
+// swp = new SwitchPort(4L, 5);
+// assertArrayEquals(new SwitchPort[] { swp }, d.getAttachmentPoints());
+//
+// //----------------------------
+// // add another entry device to the store. since device manager is
+// // already master we won't read this device and it should be
+// // removed from the store by the consolidate task
+// Entity e3 = new Entity(3L, null, null, 1L, 1, null);
+// dsr = new DeviceSyncRepresentation();
+// dsr.setKey("Device3");
+// dsr.setEntities(Collections.singletonList(new SyncEntity(e3)));
+// storeClient.put(dsr.getKey(), dsr);
+//
+// // make sure it's in the store
+// List<DeviceSyncRepresentation> entries = getEntriesFromStore();
+// boolean found = false;
+// for (DeviceSyncRepresentation entry: entries) {
+// if (entry!=null && entry.getKey().equals("Device3"))
+// found = true;
+// }
+// assertTrue("Device3 not in store. Entries in store: " + entries, found);
+// // make sure it's not in DevManager
+// Iterator<? extends IDevice> diter =
+// deviceManager.queryDevices(3L, null, null, null, null);
+// assertFalse("Device3 found in DeviceManager. Should be there",
+// diter.hasNext());
+//
+// // Wait for consolidate
+// Thread.sleep(initialSyncStoreConsolidateIntervalMs + 5);
+// // make sure it's in NOT the store
+// entries = getEntriesFromStore();
+// found = false;
+// for (DeviceSyncRepresentation entry: entries) {
+// if (entry!=null && entry.getKey().equals("Device3"))
+// found = true;
+// }
+// assertFalse("Device3 not is still in the store. Entries in store: "
+// + entries, found);
+// // make sure it's not in DevManager
+// diter = deviceManager.queryDevices(3L, null, null, null, null);
+// assertFalse("Device3 found in DeviceManager. Should be there",
+// diter.hasNext());
+// }
+//
+//
+// @Test
+// public void testConsolitateStore() throws Exception {
+// int syncStoreInternalMs = 0;
+// ITopologyService mockTopology = makeMockTopologyAllPortsAp();
+// replay(mockTopology);
+// deviceManager.topology = mockTopology;
+// // We want an EntityClassifier that has switch/port as key fields
+// deviceManager.entityClassifier = new MockEntityClassifier();
+// deviceManager.setSyncStoreWriteInterval(syncStoreInternalMs);
+//
+// // Add Device1 with two entities to store and let device manager
+// // learn
+// Entity e1a = new Entity(1L, null, null, 4L, 5, new Date(1000));
+// Entity e1b = new Entity(1L, null, 3, 4L, 5, new Date(2000));
+// Device d1 = deviceManager.learnDeviceByEntity(e1a);
+// deviceManager.learnDeviceByEntity(e1b);
+// String dev1Key = DeviceSyncRepresentation.computeKey(d1);
+//
+//
+// // Add a second device to the store but do NOT add to device manager
+// Entity e2 = new Entity(2L, null, null, 5L, 5, new Date());
+// Device d2 = deviceManager.allocateDevice(42L, e2,
+// DefaultEntityClassifier.entityClass);
+// DeviceSyncRepresentation dsr = new DeviceSyncRepresentation(d2);
+// storeClient.put(dsr.getKey(), dsr);
+// String dev2Key = DeviceSyncRepresentation.computeKey(d2);
+//
+// // Make sure we have two devices in the store
+// List<DeviceSyncRepresentation> entries = getEntriesFromStore();
+// assertEquals(2, entries.size());
+//
+// deviceManager.scheduleConsolidateStoreNow();
+// Thread.sleep(25); // give the scheduler time to run the task
+//
+// // We should still have two entries, however one of them will be a
+// // tombstone
+// entries = getEntriesFromStore();
+// assertEquals(2, entries.size());
+//
+// // Device 1 should still be in store
+// Versioned<DeviceSyncRepresentation> versioned =
+// storeClient.get(dev1Key);
+// dsr = versioned.getValue();
+// assertNotNull(dsr);
+// assertEquals(2, dsr.getEntities().size());
+// assertEntityEquals(e1a, dsr.getEntities().get(0));
+// assertEntityEquals(e1b, dsr.getEntities().get(1));
+//
+// // Device2 should be gone
+// versioned = storeClient.get(dev2Key);
+// assertNull(versioned.getValue());
+//
+// // Run consolitate again. This time we check that tombstones in
+// // the store are handled correctly
+// deviceManager.scheduleConsolidateStoreNow();
+// Thread.sleep(25); // give the scheduler time to run the task
+//
+// // Now write a device to the store that doesn't have any switch-port
+// // it should be removed
+// Entity e3 = new Entity(3L, null, null, null, null, null);
+// dsr.setKey("Device3");
+// dsr.setEntities(Collections.singletonList(new SyncEntity(e3)));
+// storeClient.put(dsr.getKey(), dsr);
+//
+// // Run consolitate again. This time we check that tombstones in
+// // the store are handled correctly
+// deviceManager.scheduleConsolidateStoreNow();
+// Thread.sleep(25); // give the scheduler time to run the task
+// versioned = storeClient.get("Device3");
+// assertNull(versioned.getValue());
+//
+// }
+//
+// }
--- /dev/null
+/*
+ * Copyright (c) 2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker.internal;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+import org.opendaylight.controller.hosttracker.Entity;
+import org.opendaylight.controller.hosttracker.IDeviceService.DeviceField;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.NodeConnector.NodeConnectorIDType;
+
+/**
+ *
+ * @author gregor
+ *
+ */
+public class DeviceUniqueIndexTest extends TestCase {
+ protected Entity e1a;
+ protected Entity e1b;
+ protected Device d1;
+ protected Entity e2;
+ protected Entity e2alt;
+ protected Entity e3;
+ protected Entity e3_ip;
+ protected Entity e4;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ Node n1 = new Node(Node.NodeIDType.OPENFLOW, Long.valueOf(1L));
+ NodeConnector n1_1 = new NodeConnector(NodeConnectorIDType.OPENFLOW,
+ Short.valueOf((short) 1), n1);
+ e1a = new Entity(1L, (short) 1, 1, n1_1, new Date());
+ e1b = new Entity(1L, (short) 2, 1, n1_1, new Date());
+ List<Entity> d1Entities = new ArrayList<Entity>(2);
+ d1Entities.add(e1a);
+ d1Entities.add(e1b);
+ d1 = new Device(null, Long.valueOf(1), null, null, null, d1Entities,
+ null);
+
+ Node n2 = new Node(Node.NodeIDType.OPENFLOW, Long.valueOf(2L));
+ NodeConnector n2_2 = new NodeConnector(NodeConnectorIDType.OPENFLOW,
+ Short.valueOf((short) 2), n2);
+ Node n3 = new Node(Node.NodeIDType.OPENFLOW, Long.valueOf(3L));
+ NodeConnector n3_3 = new NodeConnector(NodeConnectorIDType.OPENFLOW,
+ Short.valueOf((short) 3), n3);
+
+ // e2 and e2 alt match in MAC and VLAN
+ e2 = new Entity(2L, (short) 2, 2, n2_2, new Date());
+ e2alt = new Entity(2, (short) 2, null, null, null);
+
+ // IP is null
+ e3 = new Entity(3L, (short) 3, null, n3_3, new Date());
+ e3_ip = new Entity(3L, (short) 3, 3, n3_3, new Date());
+
+ // IP and switch and port are null
+ e4 = new Entity(4L, (short) 4, null, null, new Date());
+ }
+
+ /*
+ * Checks that the iterator it returns the elements in the Set expected
+ * Doesn't check how often an element is returned as long it's at least once
+ */
+ protected void verifyIterator(Set<Long> expected, Iterator<Long> it) {
+ HashSet<Long> actual = new HashSet<Long>();
+ while (it.hasNext()) {
+ actual.add(it.next());
+ }
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testDeviceUniqueIndex() {
+ DeviceUniqueIndex idx1 = new DeviceUniqueIndex(EnumSet.of(
+ DeviceField.MAC, DeviceField.VLAN));
+
+ idx1.updateIndex(d1, d1.getDeviceKey());
+ idx1.updateIndex(e2, 2L);
+
+ // -------------
+ // Test findByEntity lookups
+ assertEquals(Long.valueOf(1L), idx1.findByEntity(e1a));
+ assertEquals(Long.valueOf(1L), idx1.findByEntity(e1b));
+ assertEquals(Long.valueOf(2L), idx1.findByEntity(e2));
+ // we didn't add e2alt but since they key fields are the same we
+ // should find it
+ assertEquals(Long.valueOf(2L), idx1.findByEntity(e2alt));
+ assertEquals(null, idx1.findByEntity(e3));
+ assertEquals(null, idx1.findByEntity(e4));
+
+ // -------------
+ // Test getAll()
+ HashSet<Long> expectedKeys = new HashSet<Long>();
+ expectedKeys.add(1L);
+ expectedKeys.add(2L);
+ verifyIterator(expectedKeys, idx1.getAll());
+
+ // -------------
+ // Test queryByEntity()
+ verifyIterator(Collections.<Long> singleton(1L),
+ idx1.queryByEntity(e1a));
+ verifyIterator(Collections.<Long> singleton(1L),
+ idx1.queryByEntity(e1b));
+ verifyIterator(Collections.<Long> singleton(2L), idx1.queryByEntity(e2));
+ verifyIterator(Collections.<Long> singleton(2L),
+ idx1.queryByEntity(e2alt));
+ assertEquals(false, idx1.queryByEntity(e3).hasNext());
+ assertEquals(false, idx1.queryByEntity(e3).hasNext());
+
+ // -------------
+ // Test removal
+ idx1.removeEntity(e1a, 42L); // No-op. e1a isn't mapped to this key
+ assertEquals(Long.valueOf(1L), idx1.findByEntity(e1a));
+ idx1.removeEntity(e1a, 1L);
+ assertEquals(null, idx1.findByEntity(e1a));
+ assertEquals(Long.valueOf(1L), idx1.findByEntity(e1b));
+ assertEquals(Long.valueOf(2L), idx1.findByEntity(e2));
+ idx1.removeEntity(e2);
+ assertEquals(null, idx1.findByEntity(e2));
+ assertEquals(Long.valueOf(1L), idx1.findByEntity(e1b));
+
+ // -------------
+ // Test null keys
+ DeviceUniqueIndex idx2 = new DeviceUniqueIndex(EnumSet.of(
+ DeviceField.IPV4, DeviceField.SWITCHPORT));
+ // only one key field is null
+ idx2.updateIndex(e3, 3L);
+ assertEquals(Long.valueOf(3L), idx2.findByEntity(e3));
+ assertEquals(null, idx2.findByEntity(e3_ip));
+ // all key fields are null
+ idx2.updateIndex(e4, 4L);
+ assertEquals(null, idx2.findByEntity(e4));
+ Device d4 = new Device(null, 4L, null, null, null,
+ Collections.<Entity> singleton(e4), null);
+ idx2.updateIndex(d4, 4L);
+ assertEquals(null, idx2.findByEntity(e4));
+
+ // -------------
+ // entity already exists with different deviceKey
+ DeviceUniqueIndex idx3 = new DeviceUniqueIndex(EnumSet.of(
+ DeviceField.MAC, DeviceField.VLAN));
+ idx3.updateIndex(e1a, 42L);
+ assertEquals(false, idx3.updateIndex(d1, 1L));
+ // TODO: shouldn't this fail as well so that the behavior
+ // is consistent?
+ idx3.updateIndex(e1a, 1L);
+ // anyways. We can now add d1 ;-)
+ assertEquals(true, idx3.updateIndex(d1, 1L));
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2011,2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker.test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.TreeSet;
+
+import org.opendaylight.controller.hosttracker.Entity;
+import org.opendaylight.controller.hosttracker.IEntityClass;
+import org.opendaylight.controller.hosttracker.SwitchPort;
+import org.opendaylight.controller.hosttracker.internal.AttachmentPoint;
+import org.opendaylight.controller.hosttracker.internal.Device;
+import org.opendaylight.controller.hosttracker.internal.DeviceManagerImpl;
+
+/**
+ * This mock device removes the dependency on topology and a parent device
+ * manager and simply assumes all its entities are current and correct
+ */
+public class MockDevice extends Device {
+
+ public MockDevice(DeviceManagerImpl deviceManager, Long deviceKey,
+ Entity entity, IEntityClass entityClass) {
+ super(deviceManager, deviceKey, entity, entityClass);
+ }
+
+ public MockDevice(Device device, Entity newEntity, int insertionpoint) {
+ super(device, newEntity, insertionpoint);
+ }
+
+ public MockDevice(DeviceManagerImpl deviceManager, Long deviceKey,
+ List<AttachmentPoint> aps, List<AttachmentPoint> trueAPs,
+ Collection<Entity> entities, IEntityClass entityClass) {
+ super(deviceManager, deviceKey, null, aps, trueAPs, entities,
+ entityClass);
+ }
+
+ @Override
+ public Integer[] getIPv4Addresses() {
+ TreeSet<Integer> vals = new TreeSet<Integer>();
+ for (Entity e : entities) {
+ if (e.getIpv4Address() == null)
+ continue;
+ vals.add(e.getIpv4Address());
+ }
+
+ return vals.toArray(new Integer[vals.size()]);
+ }
+
+ @Override
+ public SwitchPort[] getAttachmentPoints() {
+ ArrayList<SwitchPort> vals = new ArrayList<SwitchPort>(entities.length);
+ for (Entity e : entities) {
+ if (e.getPort() != null
+ && deviceManager.isValidAttachmentPoint(e.getPort())) {
+ SwitchPort sp = new SwitchPort(e.getPort());
+ vals.add(sp);
+ }
+ }
+ return vals.toArray(new SwitchPort[vals.size()]);
+ }
+
+ @Override
+ public String toString() {
+ return "MockDevice [getEntityClass()=" + getEntityClass()
+ + ", getEntities()=" + Arrays.toString(getEntities()) + "]";
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker.test;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+import org.opendaylight.controller.hosttracker.Entity;
+import org.opendaylight.controller.hosttracker.IDevice;
+import org.opendaylight.controller.hosttracker.IDeviceListener;
+import org.opendaylight.controller.hosttracker.IEntityClass;
+import org.opendaylight.controller.hosttracker.IEntityClassifierService;
+import org.opendaylight.controller.hosttracker.internal.AttachmentPoint;
+import org.opendaylight.controller.hosttracker.internal.Device;
+import org.opendaylight.controller.hosttracker.internal.DeviceManagerImpl;
+import org.opendaylight.controller.sal.core.NodeConnector;
+
+/**
+ * Mock device manager useful for unit tests
+ *
+ * @author readams
+ */
+public class MockDeviceManager extends DeviceManagerImpl {
+ /**
+ * Set a new IEntityClassifier Use this as a quick way to use a particular
+ * entity classifier in a single test without having to setup the full
+ * FloodlightModuleContext again.
+ *
+ * @param ecs
+ */
+ public void setEntityClassifier(IEntityClassifierService ecs) {
+ this.entityClassifier = ecs;
+ // setSyncServiceIfNotSet(new MockSyncService());
+ this.start();
+ }
+
+ /**
+ * Learn a device using the given characteristics.
+ *
+ * @param macAddress
+ * the MAC
+ * @param vlan
+ * the VLAN (can be null)
+ * @param ipv4Address
+ * the IP (can be null)
+ * @param switchDPID
+ * the attachment point switch DPID (can be null)
+ * @param switchPort
+ * the attachment point switch port (can be null)
+ * @param processUpdates
+ * if false, will not send updates. Note that this method is not
+ * thread safe if this is false
+ * @return the device, either new or not
+ */
+ public IDevice learnEntity(long macAddress, Short vlan,
+ Integer ipv4Address, NodeConnector port, boolean processUpdates) {
+ List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
+ if (!processUpdates) {
+ deviceListeners.clearListeners();
+ }
+
+ if (vlan != null && vlan.shortValue() <= 0)
+ vlan = null;
+ if (ipv4Address != null && ipv4Address == 0)
+ ipv4Address = null;
+ IDevice res = learnDeviceByEntity(new Entity(macAddress, vlan,
+ ipv4Address, port, new Date()));
+ // Restore listeners
+ if (listeners != null) {
+ for (IDeviceListener listener : listeners) {
+ deviceListeners.addListener("device", listener);
+ }
+ }
+ return res;
+ }
+
+ @Override
+ public void deleteDevice(Device device) {
+ super.deleteDevice(device);
+ }
+
+ /**
+ * Learn a device using the given characteristics.
+ *
+ * @param macAddress
+ * the MAC
+ * @param vlan
+ * the VLAN (can be null)
+ * @param ipv4Address
+ * the IP (can be null)
+ * @param switchDPID
+ * the attachment point switch DPID (can be null)
+ * @param switchPort
+ * the attachment point switch port (can be null)
+ * @return the device, either new or not
+ */
+ public IDevice learnEntity(long macAddress, Short vlan,
+ Integer ipv4Address, NodeConnector port) {
+ return learnEntity(macAddress, vlan, ipv4Address, port, true);
+ }
+
+ @Override
+ protected Device allocateDevice(Long deviceKey, Entity entity,
+ IEntityClass entityClass) {
+ return new MockDevice(this, deviceKey, entity, entityClass);
+ }
+
+ @Override
+ protected Device allocateDevice(Long deviceKey, String dhcpClientName,
+ List<AttachmentPoint> aps, List<AttachmentPoint> trueAPs,
+ Collection<Entity> entities, IEntityClass entityClass) {
+ return new MockDevice(this, deviceKey, aps, trueAPs, entities,
+ entityClass);
+ }
+
+ @Override
+ protected Device allocateDevice(Device device, Entity entity,
+ int insertionpoint) {
+ return new MockDevice(device, entity, insertionpoint);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker.test;
+
+import static org.opendaylight.controller.hosttracker.IDeviceService.DeviceField.MAC;
+import static org.opendaylight.controller.hosttracker.IDeviceService.DeviceField.SWITCHPORT;
+import static org.opendaylight.controller.hosttracker.IDeviceService.DeviceField.VLAN;
+
+import java.util.EnumSet;
+
+import org.opendaylight.controller.hosttracker.Entity;
+import org.opendaylight.controller.hosttracker.IDeviceService;
+import org.opendaylight.controller.hosttracker.IDeviceService.DeviceField;
+import org.opendaylight.controller.hosttracker.IEntityClass;
+import org.opendaylight.controller.hosttracker.internal.DefaultEntityClassifier;
+
+/**
+ * A simple IEntityClassifier. Useful for tests that need IEntityClassifiers and
+ * IEntityClass'es with switch and/or port key fields
+ */
+public class MockEntityClassifier extends DefaultEntityClassifier {
+ public static class TestEntityClass implements IEntityClass {
+ @Override
+ public EnumSet<DeviceField> getKeyFields() {
+ return EnumSet.of(MAC, VLAN, SWITCHPORT);
+ }
+
+ @Override
+ public String getName() {
+ return "TestEntityClass";
+ }
+ }
+
+ public static IEntityClass testEC = new MockEntityClassifier.TestEntityClass();
+
+ @Override
+ public IEntityClass classifyEntity(Entity entity) {
+ if (((Long) entity.getPort().getNode().getID()) >= 10L) {
+ return testEC;
+ }
+ return DefaultEntityClassifier.entityClass;
+ }
+
+ @Override
+ public EnumSet<IDeviceService.DeviceField> getKeyFields() {
+ return EnumSet.of(MAC, VLAN, SWITCHPORT);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker.test;
+
+import static org.opendaylight.controller.hosttracker.IDeviceService.DeviceField.MAC;
+import static org.opendaylight.controller.hosttracker.IDeviceService.DeviceField.SWITCHPORT;
+import static org.opendaylight.controller.hosttracker.IDeviceService.DeviceField.VLAN;
+
+import java.util.EnumSet;
+
+import org.opendaylight.controller.hosttracker.Entity;
+import org.opendaylight.controller.hosttracker.IDeviceService;
+import org.opendaylight.controller.hosttracker.IDeviceService.DeviceField;
+import org.opendaylight.controller.hosttracker.IEntityClass;
+import org.opendaylight.controller.hosttracker.internal.DefaultEntityClassifier;
+
+/**
+ * A simple IEntityClassifier. Useful for tests that need an IEntityClassifier
+ * with switch/port as key fields.
+ */
+public class MockEntityClassifierMac extends DefaultEntityClassifier {
+ public static class TestEntityClassMac implements IEntityClass {
+ protected String name;
+
+ public TestEntityClassMac(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public EnumSet<DeviceField> getKeyFields() {
+ return EnumSet.of(MAC, VLAN);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+ }
+
+ public static IEntityClass testECMac1 = new MockEntityClassifierMac.TestEntityClassMac(
+ "testECMac1");
+ public static IEntityClass testECMac2 = new MockEntityClassifierMac.TestEntityClassMac(
+ "testECMac2");
+
+ @Override
+ public IEntityClass classifyEntity(Entity entity) {
+ if (((Long) entity.getPort().getNode().getID()) == null) {
+ throw new IllegalArgumentException("Not all key fields specified."
+ + " Required fields: " + getKeyFields());
+ } else if (((Long) entity.getPort().getNode().getID()) == 1L) {
+ return testECMac1;
+ } else if (((Long) entity.getPort().getNode().getID()) == 2L) {
+ return testECMac2;
+ } else if (((Long) entity.getPort().getNode().getID()) == -1L) {
+ return null;
+ }
+ return DefaultEntityClassifier.entityClass;
+ }
+
+ @Override
+ public EnumSet<IDeviceService.DeviceField> getKeyFields() {
+ return EnumSet.of(MAC, VLAN, SWITCHPORT);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.hosttracker.test;
+
+import static org.opendaylight.controller.hosttracker.IDeviceService.DeviceField.MAC;
+import static org.opendaylight.controller.hosttracker.IDeviceService.DeviceField.SWITCHPORT;
+import static org.opendaylight.controller.hosttracker.IDeviceService.DeviceField.VLAN;
+
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.opendaylight.controller.hosttracker.Entity;
+import org.opendaylight.controller.hosttracker.IDeviceService;
+import org.opendaylight.controller.hosttracker.IDeviceService.DeviceField;
+import org.opendaylight.controller.hosttracker.IEntityClass;
+import org.opendaylight.controller.hosttracker.internal.DefaultEntityClassifier;
+
+/**
+ * Extension to simple entity classifier to help in unit tests to provide table
+ * based multiple entity classification mock for reclassification tests
+ *
+ */
+public class MockFlexEntityClassifier extends DefaultEntityClassifier {
+ Map<Long, IEntityClass> switchEntities;
+ Map<Short, IEntityClass> vlanEntities;
+
+ public static class TestEntityClass implements IEntityClass {
+ String name;
+
+ public TestEntityClass(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public EnumSet<DeviceField> getKeyFields() {
+ return EnumSet.of(MAC);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+ }
+
+ public static IEntityClass defaultClass = new TestEntityClass("default");
+
+ public MockFlexEntityClassifier() {
+ switchEntities = new HashMap<Long, IEntityClass>();
+ vlanEntities = new HashMap<Short, IEntityClass>();
+ }
+
+ public IEntityClass createTestEntityClass(String name) {
+ return new TestEntityClass(name);
+ }
+
+ public void addSwitchEntity(Long dpid, IEntityClass entityClass) {
+ switchEntities.put(dpid, entityClass);
+ }
+
+ public void removeSwitchEntity(Long dpid) {
+ switchEntities.remove(dpid);
+ }
+
+ public void addVlanEntities(Short vlan, IEntityClass entityClass) {
+ vlanEntities.put(vlan, entityClass);
+ }
+
+ public void removeVlanEntities(Short vlan) {
+ vlanEntities.remove(vlan);
+ }
+
+ @Override
+ public IEntityClass classifyEntity(Entity entity) {
+ if (switchEntities.containsKey((Long) entity.getPort().getNode()
+ .getID()))
+ return switchEntities
+ .get((Long) entity.getPort().getNode().getID());
+ if (vlanEntities.containsKey(entity.getVlan()))
+ return vlanEntities.get(entity.getVlan());
+ return defaultClass;
+ }
+
+ @Override
+ public EnumSet<IDeviceService.DeviceField> getKeyFields() {
+ return EnumSet.of(MAC, VLAN, SWITCHPORT);
+ }
+}
public static short REQUEST = (short) 0x1;
public static short REPLY = (short) 0x2;
+ public static short PROTO_TYPE_IP = 0x800;
+
private static Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
--- /dev/null
+/*
+ * Copyright (c) 2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.sal.utils;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * An iterator that will filter values from an iterator and return only those
+ * values that match the predicate.
+ */
+public abstract class FilterIterator<T> implements Iterator<T> {
+ protected Iterator<T> subIterator;
+ protected T next;
+
+ /**
+ * Construct a filter iterator from the given sub iterator
+ *
+ * @param subIterator
+ * the sub iterator over which we'll filter
+ */
+ public FilterIterator(Iterator<T> subIterator) {
+ super();
+ this.subIterator = subIterator;
+ }
+
+ /**
+ * Check whether the given value should be returned by the filter
+ *
+ * @param value
+ * the value to check
+ * @return true if the value should be included
+ */
+ protected abstract boolean matches(T value);
+
+ // ***********
+ // Iterator<T>
+ // ***********
+
+ @Override
+ public boolean hasNext() {
+ if (next != null)
+ return true;
+
+ while (subIterator.hasNext()) {
+ next = subIterator.next();
+ if (matches(next))
+ return true;
+ }
+ next = null;
+ return false;
+ }
+
+ @Override
+ public T next() {
+ if (hasNext()) {
+ T cur = next;
+ next = null;
+ return cur;
+ }
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2011 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.sal.utils;
+
+public interface IListener<T> {
+ public enum Command {
+ CONTINUE, STOP
+ }
+
+ /**
+ * The name assigned to this listener
+ *
+ * @return
+ */
+ public String getName();
+
+ /**
+ * Check if the module called name is a callback ordering prerequisite for
+ * this module. In other words, if this function returns true for the given
+ * name, then this listener will be called after that message listener.
+ *
+ * @param type
+ * the object type to which this applies
+ * @param name
+ * the name of the module
+ * @return whether name is a prerequisite.
+ */
+ public boolean isCallbackOrderingPrereq(T type, String name);
+
+ /**
+ * Check if the module called name is a callback ordering post-requisite for
+ * this module. In other words, if this function returns true for the given
+ * name, then this listener will be called before that message listener.
+ *
+ * @param type
+ * the object type to which this applies
+ * @param name
+ * the name of the module
+ * @return whether name is a post-requisite.
+ */
+ public boolean isCallbackOrderingPostreq(T type, String name);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.sal.utils;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Iterator over all values in an iterator of iterators
+ *
+ * @param <T>
+ * the type of elements returned by this iterator
+ */
+public class IterableIterator<T> implements Iterator<T> {
+ Iterator<? extends Iterable<T>> subIterator;
+ Iterator<T> current = null;
+
+ public IterableIterator(Iterator<? extends Iterable<T>> subIterator) {
+ super();
+ this.subIterator = subIterator;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (current == null) {
+ if (subIterator.hasNext()) {
+ current = subIterator.next().iterator();
+ } else {
+ return false;
+ }
+ }
+ while (!current.hasNext() && subIterator.hasNext()) {
+ current = subIterator.next().iterator();
+ }
+
+ return current.hasNext();
+ }
+
+ @Override
+ public T next() {
+ if (hasNext())
+ return current.next();
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void remove() {
+ if (hasNext())
+ current.remove();
+ throw new NoSuchElementException();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2011 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.sal.utils;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Maintain lists of listeners ordered by dependency.
+ *
+ * @author readams
+ *
+ */
+public class ListenerDispatcher<U, T extends IListener<U>> {
+ protected static Logger logger = LoggerFactory
+ .getLogger(ListenerDispatcher.class);
+ volatile List<T> listeners = new ArrayList<T>();
+
+ private void visit(List<T> newlisteners, U type, HashSet<T> visited,
+ List<T> ordering, T listener) {
+ if (!visited.contains(listener)) {
+ visited.add(listener);
+
+ for (T i : newlisteners) {
+ if (ispre(type, i, listener)) {
+ visit(newlisteners, type, visited, ordering, i);
+ }
+ }
+ ordering.add(listener);
+ }
+ }
+
+ private boolean ispre(U type, T l1, T l2) {
+ return (l2.isCallbackOrderingPrereq(type, l1.getName()) || l1
+ .isCallbackOrderingPostreq(type, l2.getName()));
+ }
+
+ /**
+ * Add a listener to the list of listeners
+ *
+ * @param listener
+ */
+ public void addListener(U type, T listener) {
+ List<T> newlisteners = new ArrayList<T>();
+ if (listeners != null)
+ newlisteners.addAll(listeners);
+
+ newlisteners.add(listener);
+ // Find nodes without outgoing edges
+ List<T> terminals = new ArrayList<T>();
+ for (T i : newlisteners) {
+ boolean isterm = true;
+ for (T j : newlisteners) {
+ if (ispre(type, i, j)) {
+ isterm = false;
+ break;
+ }
+ }
+ if (isterm) {
+ terminals.add(i);
+ }
+ }
+
+ if (terminals.size() == 0) {
+ logger.error("No listener dependency solution: "
+ + "No listeners without incoming dependencies");
+ listeners = newlisteners;
+ return;
+ }
+
+ // visit depth-first traversing in the opposite order from
+ // the dependencies. Note we will not generally detect cycles
+ HashSet<T> visited = new HashSet<T>();
+ List<T> ordering = new ArrayList<T>();
+ for (T term : terminals) {
+ visit(newlisteners, type, visited, ordering, term);
+ }
+ listeners = ordering;
+ }
+
+ /**
+ * Remove the given listener
+ *
+ * @param listener
+ * the listener to remove
+ */
+ public void removeListener(T listener) {
+ if (listeners != null) {
+ List<T> newlisteners = new ArrayList<T>();
+ newlisteners.addAll(listeners);
+ newlisteners.remove(listener);
+ listeners = newlisteners;
+ }
+ }
+
+ /**
+ * Clear all listeners
+ */
+ public void clearListeners() {
+ listeners = new ArrayList<T>();
+ }
+
+ /**
+ * Get the ordered list of listeners ordered by dependencies
+ *
+ * @return
+ */
+ public List<T> getOrderedListeners() {
+ return listeners;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.sal.utils;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Iterator over all values in an iterator of iterators
+ *
+ * @param <T>
+ * the type of elements returned by this iterator
+ */
+public class MultiIterator<T> implements Iterator<T> {
+ Iterator<Iterator<T>> subIterator;
+ Iterator<T> current = null;
+
+ public MultiIterator(Iterator<Iterator<T>> subIterator) {
+ super();
+ this.subIterator = subIterator;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (current == null) {
+ if (subIterator.hasNext()) {
+ current = subIterator.next();
+ } else {
+ return false;
+ }
+ }
+ while (!current.hasNext() && subIterator.hasNext()) {
+ current = subIterator.next();
+ }
+
+ return current.hasNext();
+ }
+
+ @Override
+ public T next() {
+ if (hasNext())
+ return current.next();
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void remove() {
+ if (hasNext())
+ current.remove();
+ throw new NoSuchElementException();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2011,2013 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an "AS
+ * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.opendaylight.controller.sal.utils;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This allows you to represent a task that should be queued for future
+ * execution but where you only want the task to complete once in response to
+ * some sequence of events. For example, if you get a change notification and
+ * want to reload state, you only want to reload the state once, at the end, and
+ * don't want to queue an update for every notification that might come in.
+ *
+ * The semantics are as follows: * If the task hasn't begun yet, do not queue a
+ * new task * If the task has begun, set a bit to restart it after the current
+ * task finishes
+ */
+public class SingletonTask {
+ protected static Logger logger = LoggerFactory
+ .getLogger(SingletonTask.class);
+
+ protected static class SingletonTaskContext {
+ protected boolean taskShouldRun = false;
+ protected boolean taskRunning = false;
+
+ protected SingletonTaskWorker waitingTask = null;
+ }
+
+ protected static class SingletonTaskWorker implements Runnable {
+ SingletonTask parent;
+ boolean canceled = false;
+ long nextschedule = 0;
+
+ public SingletonTaskWorker(SingletonTask parent) {
+ super();
+ this.parent = parent;
+ }
+
+ @Override
+ public void run() {
+ synchronized (parent.context) {
+ if (canceled || !parent.context.taskShouldRun)
+ return;
+
+ parent.context.taskRunning = true;
+ parent.context.taskShouldRun = false;
+ }
+
+ try {
+ parent.task.run();
+ } catch (Exception e) {
+ logger.error("Exception while executing task", e);
+ }
+
+ synchronized (parent.context) {
+ parent.context.taskRunning = false;
+
+ if (parent.context.taskShouldRun) {
+ long now = System.nanoTime();
+ if ((nextschedule <= 0 || (nextschedule - now) <= 0)) {
+ parent.ses.execute(this);
+ } else {
+ parent.ses.schedule(this, nextschedule - now,
+ TimeUnit.NANOSECONDS);
+ }
+ }
+ }
+ }
+ }
+
+ protected SingletonTaskContext context = new SingletonTaskContext();
+ protected Runnable task;
+ protected ScheduledExecutorService ses;
+
+ /**
+ * Construct a new SingletonTask for the given runnable. The context is used
+ * to manage the state of the task execution and can be shared by more than
+ * one instance of the runnable.
+ *
+ * @param context
+ * @param Task
+ */
+ public SingletonTask(ScheduledExecutorService ses, Runnable task) {
+ super();
+ this.task = task;
+ this.ses = ses;
+ }
+
+ /**
+ * Schedule the task to run if there's not already a task scheduled If there
+ * is such a task waiting that has not already started, it cancel that task
+ * and reschedule it to run at the given time. If the task is already
+ * started, it will cause the task to be rescheduled once it completes to
+ * run after delay from the time of reschedule.
+ *
+ * @param delay
+ * the delay in scheduling
+ * @param unit
+ * the timeunit of the delay
+ */
+ public void reschedule(long delay, TimeUnit unit) {
+ boolean needQueue = true;
+ SingletonTaskWorker stw = null;
+
+ synchronized (context) {
+ if (context.taskRunning || context.taskShouldRun) {
+ if (context.taskRunning) {
+ // schedule to restart at the right time
+ if (delay > 0) {
+ long now = System.nanoTime();
+ long then = now
+ + TimeUnit.NANOSECONDS.convert(delay, unit);
+ context.waitingTask.nextschedule = then;
+ } else {
+ context.waitingTask.nextschedule = 0;
+ }
+ needQueue = false;
+ } else {
+ // cancel and requeue
+ context.waitingTask.canceled = true;
+ context.waitingTask = null;
+ }
+ }
+
+ context.taskShouldRun = true;
+
+ if (needQueue) {
+ stw = context.waitingTask = new SingletonTaskWorker(this);
+ }
+ }
+
+ if (needQueue) {
+ if (delay <= 0)
+ ses.execute(stw);
+ else
+ ses.schedule(stw, delay, unit);
+ }
+ }
+}
\ No newline at end of file