From: Madhu Venugopal Date: Mon, 26 Aug 2013 18:52:42 +0000 (+0000) Subject: Merge "Northbound cleanup for Switch Manager" X-Git-Tag: releasepom-0.1.0~174 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=5e20076e352bae1f6b0bd0f8ac8f7c85235e4b27;hp=48238a15a5d1fb46a7248d3fe276df71898f7c2a Merge "Northbound cleanup for Switch Manager" --- diff --git a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/Activator.java b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/Activator.java index 4bee253765..c0d1b50a46 100644 --- a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/Activator.java +++ b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/Activator.java @@ -25,6 +25,7 @@ import org.apache.felix.dm.Component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase; +import org.opendaylight.controller.sal.inventory.IInventoryService; import org.opendaylight.controller.sal.inventory.IListenInventoryUpdates; public class Activator extends ComponentActivatorAbstractBase { @@ -37,6 +38,7 @@ public class Activator extends ComponentActivatorAbstractBase { * ComponentActivatorAbstractBase. * */ + @Override public void init() { } @@ -45,6 +47,7 @@ public class Activator extends ComponentActivatorAbstractBase { * cleanup done by ComponentActivatorAbstractBase * */ + @Override public void destroy() { } @@ -61,6 +64,7 @@ public class Activator extends ComponentActivatorAbstractBase { * @return The list of implementations the bundle will support, * in Global version */ + @Override protected Object[] getGlobalImplementations() { Object[] res = { ConnectionManager.class }; return res; @@ -74,6 +78,7 @@ public class Activator extends ComponentActivatorAbstractBase { * @param imp implementation to be configured * @param containerName container on which the configuration happens */ + @Override protected void configureGlobalInstance(Component c, Object imp) { if (imp.equals(ConnectionManager.class)) { Dictionary props = new Hashtable(); @@ -100,6 +105,9 @@ public class Activator extends ComponentActivatorAbstractBase { c.add(createServiceDependency().setService(IConnectionService.class) .setCallbacks("setConnectionService", "unsetConnectionService") .setRequired(true)); + c.add(createServiceDependency().setService(IInventoryService.class, "(scope=Global)") + .setCallbacks("setInventoryService", "unsetInventoryService") + .setRequired(true)); } } } diff --git a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java index fdba533b5b..e2d8f6b03b 100644 --- a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java +++ b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java @@ -32,7 +32,6 @@ import java.util.concurrent.LinkedBlockingQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import org.eclipse.osgi.framework.console.CommandInterpreter; import org.eclipse.osgi.framework.console.CommandProvider; import org.opendaylight.controller.clustering.services.ICacheUpdateAware; @@ -49,6 +48,7 @@ import org.opendaylight.controller.sal.core.Node; import org.opendaylight.controller.sal.core.NodeConnector; import org.opendaylight.controller.sal.core.Property; import org.opendaylight.controller.sal.core.UpdateType; +import org.opendaylight.controller.sal.inventory.IInventoryService; import org.opendaylight.controller.sal.inventory.IListenInventoryUpdates; import org.opendaylight.controller.sal.utils.Status; import org.opendaylight.controller.sal.utils.StatusCode; @@ -66,6 +66,7 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene private IConnectionService connectionService; private Thread connectionEventThread; private BlockingQueue connectionEvents; + private IInventoryService inventoryService; public void setClusterServices(IClusterGlobalServices i) { this.clusterServices = i; @@ -87,12 +88,48 @@ public class ConnectionManager implements IConnectionManager, IConnectionListene } } + public void setInventoryService(IInventoryService service) { + logger.trace("Got inventory service set request {}", service); + this.inventoryService = service; + } + + public void unsetInventoryService(IInventoryService service) { + logger.trace("Got a service UNset request"); + this.inventoryService = null; + } + + private void getInventories() { + Map> nodeProp = this.inventoryService.getNodeProps(); + for (Map.Entry> entry : nodeProp.entrySet()) { + Node node = entry.getKey(); + logger.debug("getInventories for node:{}", new Object[] { node }); + Map propMap = entry.getValue(); + Set props = new HashSet(); + for (Property property : propMap.values()) { + props.add(property); + } + updateNode(node, UpdateType.ADDED, props); + } + + Map> nodeConnectorProp = this.inventoryService.getNodeConnectorProps(); + for (Map.Entry> entry : nodeConnectorProp.entrySet()) { + Map propMap = entry.getValue(); + Set props = new HashSet(); + for (Property property : propMap.values()) { + props.add(property); + } + updateNodeConnector(entry.getKey(), UpdateType.ADDED, props); + } + } + public void started() { connectionEventThread = new Thread(new EventHandler(), "ConnectionEvent Thread"); connectionEventThread.start(); registerWithOSGIConsole(); notifyClusterViewChanged(); + // Should pull the Inventory updates in case we missed it + getInventories(); } public void init() { diff --git a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/AbstractScheme.java b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/AbstractScheme.java index d93f74b4fe..d7c4e5f933 100644 --- a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/AbstractScheme.java +++ b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/AbstractScheme.java @@ -237,13 +237,12 @@ public abstract class AbstractScheme { * with this controller to take hold of a Node. */ if (isConnectionAllowed(node)) { - if (!nodeConnections.replace(node, oldControllers, newControllers)) { + if (oldControllers == null || !nodeConnections.replace(node, oldControllers, newControllers)) { clusterServices.trollback(); try { Thread.sleep(100); } catch ( InterruptedException e) {} - log.debug("Replace failed... old={} with new={} for {} to {}", oldControllers.toString(), newControllers.toString(), - controller.getHostAddress(), node.toString()); + log.debug("Retrying ... {} with {}", controller.getHostAddress(), node.toString()); return putNodeToController(node, controller); } else { log.debug("Replace successful old={} with new={} for {} to {}", oldControllers.toString(), newControllers.toString(), diff --git a/opendaylight/distribution/opendaylight/src/main/resources/run.sh b/opendaylight/distribution/opendaylight/src/main/resources/run.sh index 313b3b2c79..2587a5d2ab 100755 --- a/opendaylight/distribution/opendaylight/src/main/resources/run.sh +++ b/opendaylight/distribution/opendaylight/src/main/resources/run.sh @@ -1,9 +1,5 @@ #!/bin/bash -[[ -z ${JAVA_HOME} ]] && echo "Need to set JAVA_HOME environment variable" && exit -1; -[[ ! -x ${JAVA_HOME}/bin/java ]] && echo "Cannot find an executable \ -JVM at path ${JAVA_HOME}/bin/java check your JAVA_HOME" && exit -1; - platform='unknown' unamestr=`uname` if [[ "$unamestr" == 'Linux' ]]; then @@ -32,8 +28,15 @@ elif [[ $platform == 'osx' ]]; then PHYS_DIR=`pwd -P` RESULT=$PHYS_DIR/$TARGET_FILE fullpath=$RESULT + + [[ -z ${JAVA_HOME} ]] && [[ -x "/usr/libexec/java_home" ]] && export JAVA_HOME=`/usr/libexec/java_home -v 1.7`; + fi +[[ -z ${JAVA_HOME} ]] && echo "Need to set JAVA_HOME environment variable" && exit -1; +[[ ! -x ${JAVA_HOME}/bin/java ]] && echo "Cannot find an executable \ +JVM at path ${JAVA_HOME}/bin/java check your JAVA_HOME" && exit -1; + basedir=`dirname ${fullpath}` function usage { diff --git a/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowEntry.java b/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowEntry.java index e8c5f648fa..83106a391c 100644 --- a/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowEntry.java +++ b/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowEntry.java @@ -168,14 +168,13 @@ public class FlowEntry implements Cloneable, Serializable { public FlowEntry mergeWith(ContainerFlow containerFlow) { Match myMatch = flow.getMatch(); - // Based on this flow direction, rearrange the match - Match match = containerFlow.getMatch(); + Match filter = containerFlow.getMatch(); // Merge - myMatch.mergeWithFilter(match); + Match merge = myMatch.mergeWithFilter(filter); // Replace this Flow's match with merged version - flow.setMatch(myMatch); + flow.setMatch(merge); return this; } diff --git a/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java b/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java index ad694b0b65..76974d7541 100644 --- a/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java +++ b/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java @@ -1127,11 +1127,11 @@ public class NorthboundIT { .append("dstNodeConnector", nodeConnectorType_2 + "|" + nodeConnectorId_2 + "@" + nodeType_2 + "|" + nodeId_2); // execute HTTP request and verify response code - result = getJsonResult(baseURL + "/userLink", "POST", jo.toString()); + result = getJsonResult(baseURL + "/user-link", "POST", jo.toString()); Assert.assertTrue(httpResponseCode == 201); // === test GET method for getUserLinks() - result = getJsonResult(baseURL + "/userLink", "GET"); + result = getJsonResult(baseURL + "/user-link", "GET"); Assert.assertTrue(httpResponseCode == 200); if (debugMsg) { System.out.println("result:" + result); @@ -1176,12 +1176,12 @@ public class NorthboundIT { // === test DELETE method for deleteUserLink() String userName = "userLink_1"; - result = getJsonResult(baseURL + "/userLink/" + userName, "DELETE"); + result = getJsonResult(baseURL + "/user-link/" + userName, "DELETE"); Assert.assertTrue(httpResponseCode == 200); // execute another getUserLinks() request to verify that userLink_1 is // removed - result = getJsonResult(baseURL + "/userLink", "GET"); + result = getJsonResult(baseURL + "/user-link", "GET"); Assert.assertTrue(httpResponseCode == 200); if (debugMsg) { System.out.println("result:" + result); diff --git a/opendaylight/northbound/topology/src/main/java/org/opendaylight/controller/topology/northbound/TopologyNorthboundJAXRS.java b/opendaylight/northbound/topology/src/main/java/org/opendaylight/controller/topology/northbound/TopologyNorthboundJAXRS.java index fb4e8229ff..3ba1eb1843 100644 --- a/opendaylight/northbound/topology/src/main/java/org/opendaylight/controller/topology/northbound/TopologyNorthboundJAXRS.java +++ b/opendaylight/northbound/topology/src/main/java/org/opendaylight/controller/topology/northbound/TopologyNorthboundJAXRS.java @@ -78,16 +78,31 @@ public class TopologyNorthboundJAXRS { * Retrieve the Topology * * @param containerName - * The container for which we want to retrieve the topology + * The container for which we want to retrieve the topology (Eg. 'default') * * @return A List of EdgeProps each EdgeProp represent an Edge of the grap * with the corresponding properties attached to it. + * + *
+     *
+     * Example:
+     *
+     * RequestURL:
+     * http://localhost:8080/controller/nb/v2/topology/default
+     *
+     * Response in XML:
+     * <?xml version="1.0" encoding="UTF-8" standalone="yes"?><topology><edgeProperties><edge><tailNodeConnector id="2" type="OF"><node id="00:00:00:00:00:00:00:02" type="OF"/></tailNodeConnector><headNodeConnector id="2" type="OF"><node id="00:00:00:00:00:00:00:51" type="OF"/></headNodeConnector></edge><properties><state><value>1</value></state><config><value>1</value></config><name><value>C1_2-L2_2</value></name><timeStamp><value>1377279422032</value><name>creation</name></timeStamp></properties></edgeProperties><edgeProperties><edge><tailNodeConnector id="2" type="OF"><node id="00:00:00:00:00:00:00:51" type="OF"/></tailNodeConnector><headNodeConnector id="2" type="OF"><node id="00:00:00:00:00:00:00:02" type="OF"/></headNodeConnector></edge><properties><state><value>1</value></state><name><value>L2_2-C1_2</value></name><config><value>1</value></config><timeStamp><value>1377279423564</value><name>creation</name></timeStamp></properties></edgeProperties></topology>
+     *
+     * Response in JSON:
+     * {"edgeProperties":[{"edge":{"tailNodeConnector":{"@id":"2","@type":"OF","node":{"@id":"00:00:00:00:00:00:00:02","@type":"OF"}},"headNodeConnector":{"@id":"2","@type":"OF","node":{"@id":"00:00:00:00:00:00:00:51","@type":"OF"}}},"properties":{"timeStamp":{"value":"1377278961017","name":"creation"}}},{"edge":{"tailNodeConnector":{"@id":"2","@type":"OF","node":{"@id":"00:00:00:00:00:00:00:51","@type":"OF"}},"headNodeConnector":{"@id":"2","@type":"OF","node":{"@id":"00:00:00:00:00:00:00:02","@type":"OF"}}},"properties":{"timeStamp":{"value":"1377278961018","name":"creation"}}}]}
+     *
+     * 
*/ @Path("/{containerName}") @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @TypeHint(Topology.class) - @StatusCodes({ @ResponseCode(code = 404, condition = "The Container Name passed was not found") }) + @StatusCodes({ @ResponseCode(code = 404, condition = "The Container Name was not found") }) public Topology getTopology(@PathParam("containerName") String containerName) { if (!NorthboundUtils.isAuthorized( @@ -121,15 +136,30 @@ public class TopologyNorthboundJAXRS { * Retrieve the user configured links * * @param containerName - * The container for which we want to retrieve the user links + * The container for which we want to retrieve the user links (Eg. 'default') * * @return A List of user configured links + * + *
+     *
+     * Example:
+     *
+     * RequestURL:
+     * http://localhost:8080/controller/nb/v2/topology/default/user-link
+     *
+     * Response in XML:
+     * <?xml version="1.0" encoding="UTF-8" standalone="yes"?><topologyUserLinks><userLinks><status>Success</status><name>link1</name><srcNodeConnector>OF|2@OF|00:00:00:00:00:00:00:02</srcNodeConnector><dstNodeConnector>OF|2@OF|00:00:00:00:00:00:00:51</dstNodeConnector></userLinks></topologyUserLinks>
+     *
+     * Response in JSON:
+     * {"userLinks":{"status":"Success","name":"link1","srcNodeConnector":"OF|2@OF|00:00:00:00:00:00:00:02","dstNodeConnector":"OF|2@OF|00:00:00:00:00:00:00:51"}}
+     *
+     * 
*/ - @Path("/{containerName}/userLink") + @Path("/{containerName}/user-link") @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @TypeHint(TopologyUserLinks.class) - @StatusCodes({ @ResponseCode(code = 404, condition = "The Container Name passed was not found") }) + @StatusCodes({ @ResponseCode(code = 404, condition = "The Container Name was not found") }) public TopologyUserLinks getUserLinks( @PathParam("containerName") String containerName) { @@ -161,18 +191,33 @@ public class TopologyNorthboundJAXRS { * Add an User Link * * @param containerName - * Name of the Container. The base Container is "default". + * Name of the Container (Eg. 'default') * @param TopologyUserLinkConfig * in JSON or XML format * @return Response as dictated by the HTTP Response Status code + * + *
+     *
+     * Example:
+     *
+     * RequestURL:
+     * http://localhost:8080/controller/nb/v2/topology/default/user-link/config1-content
+     *
+     * Request in XML:
+     * <?xml version="1.0" encoding="UTF-8" standalone="yes"?><topologyUserLinks><status>Success</status><name>link1</name><srcNodeConnector>OF|2@OF|00:00:00:00:00:00:00:02</srcNodeConnector><dstNodeConnector>OF|2@OF|00:00:00:00:00:00:00:51</dstNodeConnector></topologyUserLinks>
+     *
+     * Request in JSON:
+     * {"status":"Success","name":"link1","srcNodeConnector":"OF|2@OF|00:00:00:00:00:00:00:02","dstNodeConnector":"OF|2@OF|00:00:00:00:00:00:00:51"}
+     *
+     * 
*/ - - @Path("/{containerName}/userLink") + @Path("/{containerName}/user-link") @POST @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @StatusCodes({ @ResponseCode(code = 201, condition = "User Link added successfully"), - @ResponseCode(code = 404, condition = "The Container Name passed was not found"), + @ResponseCode(code = 404, condition = "The Container Name was not found"), @ResponseCode(code = 409, condition = "Failed to add User Link due to Conflicting Name"), @ResponseCode(code = 500, condition = "Failed to add User Link. Failure Reason included in HTTP Error response"), @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") }) @@ -205,15 +250,24 @@ public class TopologyNorthboundJAXRS { * Delete an User Link * * @param containerName - * Name of the Container. The base Container is "default". + * Name of the Container (Eg. 'default') * @param name - * Name of the Link Configuration + * Name of the Link Configuration (Eg. 'config1') * @return Response as dictated by the HTTP Response Status code + * + *
+     *
+     * Example:
+     *
+     * RequestURL:
+     * http://localhost:8080/controller/nb/v2/topology/default/user-link/config1
+     *
+     * 
*/ - - @Path("/{containerName}/userLink/{name}") + @Path("/{containerName}/user-link/{name}") @DELETE @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"), @ResponseCode(code = 404, condition = "The Container Name or Link Configuration Name was not found"), @@ -242,5 +296,4 @@ public class TopologyNorthboundJAXRS { } throw new ResourceNotFoundException(ret.getDescription()); } - } diff --git a/opendaylight/protocol_plugins/stub/src/main/java/org/opendaylight/controller/protocol_plugins/stub/internal/Activator.java b/opendaylight/protocol_plugins/stub/src/main/java/org/opendaylight/controller/protocol_plugins/stub/internal/Activator.java index 76c38664d4..79edd93c67 100644 --- a/opendaylight/protocol_plugins/stub/src/main/java/org/opendaylight/controller/protocol_plugins/stub/internal/Activator.java +++ b/opendaylight/protocol_plugins/stub/src/main/java/org/opendaylight/controller/protocol_plugins/stub/internal/Activator.java @@ -40,6 +40,7 @@ public class Activator extends ComponentActivatorAbstractBase { * are done by the ComponentActivatorAbstractBase. * */ + @Override public void init() { Node.NodeIDType.registerIDType("STUB", Integer.class); NodeConnector.NodeConnectorIDType.registerIDType("STUB", Integer.class, "STUB"); @@ -50,6 +51,7 @@ public class Activator extends ComponentActivatorAbstractBase { * ComponentActivatorAbstractBase * */ + @Override public void destroy() { Node.NodeIDType.unRegisterIDType("STUB"); NodeConnector.NodeConnectorIDType.unRegisterIDType("STUB"); @@ -64,6 +66,7 @@ public class Activator extends ComponentActivatorAbstractBase { * instantiated in order to get an fully working implementation * Object */ + @Override public Object[] getImplementations() { Object[] res = { ReadService.class, InventoryService.class }; return res; @@ -84,6 +87,7 @@ public class Activator extends ComponentActivatorAbstractBase { * 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(ReadService.class)) { // export the service to be used by SAL @@ -104,11 +108,18 @@ public class Activator extends ComponentActivatorAbstractBase { } } + @Override public Object[] getGlobalImplementations() { - Object[] res = { FlowProgrammerService.class, StubNodeFactory.class, StubNodeConnectorFactory.class }; + Object[] res = + { + FlowProgrammerService.class, + StubNodeFactory.class, + StubNodeConnectorFactory.class, + InventoryService.class }; return res; } + @Override public void configureGlobalInstance(Component c, Object imp){ if (imp.equals(FlowProgrammerService.class)) { // export the service to be used by SAL @@ -136,6 +147,17 @@ public class Activator extends ComponentActivatorAbstractBase { props.put("protocolName", "STUB"); c.setInterface(INodeConnectorFactory.class.getName(), props); } - + if (imp.equals(InventoryService.class)) { + // export the service to be used by SAL + Dictionary props = new Hashtable(); + // Set the protocolPluginType property which will be used + // by SAL + props.put(GlobalConstants.PROTOCOLPLUGINTYPE.toString(), "STUB"); + props.put("scope", "Global"); + c.setInterface(IPluginInInventoryService.class.getName(), props); + c.add(createServiceDependency().setService(IPluginOutInventoryService.class, "(scope=Global)") + .setCallbacks("setPluginOutInventoryServices", "unsetPluginOutInventoryServices") + .setRequired(true)); + } } } diff --git a/opendaylight/protocol_plugins/stub/src/main/java/org/opendaylight/controller/protocol_plugins/stub/internal/InventoryService.java b/opendaylight/protocol_plugins/stub/src/main/java/org/opendaylight/controller/protocol_plugins/stub/internal/InventoryService.java index 22a4343f33..b94ffec1dd 100644 --- a/opendaylight/protocol_plugins/stub/src/main/java/org/opendaylight/controller/protocol_plugins/stub/internal/InventoryService.java +++ b/opendaylight/protocol_plugins/stub/src/main/java/org/opendaylight/controller/protocol_plugins/stub/internal/InventoryService.java @@ -12,11 +12,11 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArraySet; import org.apache.felix.dm.Component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import org.opendaylight.controller.sal.core.Actions; import org.opendaylight.controller.sal.core.Bandwidth; import org.opendaylight.controller.sal.core.Buffers; @@ -29,7 +29,9 @@ import org.opendaylight.controller.sal.core.Property; import org.opendaylight.controller.sal.core.State; import org.opendaylight.controller.sal.core.Tables; import org.opendaylight.controller.sal.core.TimeStamp; +import org.opendaylight.controller.sal.core.UpdateType; import org.opendaylight.controller.sal.inventory.IPluginInInventoryService; +import org.opendaylight.controller.sal.inventory.IPluginOutInventoryService; import org.opendaylight.controller.sal.utils.NodeCreator; import org.opendaylight.controller.sal.utils.NodeConnectorCreator; @@ -55,6 +57,22 @@ public class InventoryService implements IPluginInInventoryService { // global // container // only + private final Set pluginOutInventoryServices = + new CopyOnWriteArraySet(); + + public void setPluginOutInventoryServices(IPluginOutInventoryService service) { + logger.trace("Got a service set request {}", service); + if (this.pluginOutInventoryServices != null) { + this.pluginOutInventoryServices.add(service); + } + } + + public void unsetPluginOutInventoryServices(IPluginOutInventoryService service) { + logger.trace("Got a service UNset request"); + if (this.pluginOutInventoryServices != null) { + this.pluginOutInventoryServices.remove(service); + } + } /** * Function called by the dependency manager when all the required @@ -173,6 +191,29 @@ public class InventoryService implements IPluginInInventoryService { void start() { } + /** + * Method called when the plugin has exposed it's services, this will be + * used to publish the updates so connection manager can think the + * connection is local + */ + void started() { + // update sal and discovery + for (IPluginOutInventoryService service : pluginOutInventoryServices) { + for (Node node : nodeProps.keySet()) { + Set props = new HashSet(nodeProps.get(node) + .values()); + service.updateNode(node, UpdateType.ADDED, props); + logger.trace("Adding Node {} with props {}", node, props); + } + for (NodeConnector nc : nodeConnectorProps.keySet()) { + Set props = new HashSet(nodeConnectorProps.get(nc) + .values()); + service.updateNodeConnector(nc, UpdateType.ADDED, props); + logger.trace("Adding NodeConnectors {} with props {}", nc, props); + } + } + } + /** * Function called by the dependency manager before the services exported by * the component are unregistered, this will be followed by a "destroy ()" @@ -180,6 +221,7 @@ public class InventoryService implements IPluginInInventoryService { * */ void stop() { + pluginOutInventoryServices.clear(); } /** diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/Match.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/Match.java index 9bab839198..30c25af57f 100644 --- a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/Match.java +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/Match.java @@ -246,93 +246,195 @@ public class Match implements Cloneable, Serializable { /** * Check whether the current match conflicts with the passed filter match * This match conflicts with the filter if for at least a MatchType defined - * in the filter match, the respective MatchFields differ or are not compatible + * in the filter match, the respective MatchFields differ or are not + * compatible + * + * In other words the function returns true if the set of packets described + * by one match and the set of packets described by the other match are + * disjoint. Equivalently, if the intersection of the two sets of packets + * described by the two matches is an empty. * * For example, Let's suppose the filter has the following MatchFields: * DL_TYPE = 0x800 - * NW_DST = 172.20.30.110/24 + * NW_DST = 172.20.30.110/24 * * while this match has the following MatchFields: + * DL_TYPE = 0x800 * NW_DST = 172.20.30.45/24 * TP_DST = 80 * - * Then the function would return false as the two Match are not conflicting + * Then the function would return false as the two Match are not + * conflicting. * - * Note: the mask value is taken into account only for MatchType.NW_SRC and MatchType.NW_DST + * Note: the mask value is taken into account only for MatchType.NW_SRC and + * MatchType.NW_DST * - * @param match the MAtch describing the filter - * @return true if the match is conflicting with the filter, false otherwise + * @param match + * the Match describing the filter + * @return true if the set of packets described by one match and the set of + * packets described by the other match are disjoint, false + * otherwise */ public boolean conflictWithFilter(Match filter) { - // Iterate through the MatchType defined in the filter - for (MatchType type : filter.getMatchesList()) { + return !this.intersetcs(filter); + } + + /** + * Merge the current Match fields with the fields of the filter Match. A + * check is first run to see if this Match is compatible with the filter + * Match. If it is not, the merge is not attempted. + * + * The result is the match object representing the intersection of the set + * of packets described by this match with the set of packets described by + * the filter match. If the intersection of the two sets is empty, the + * return match will be null. + * + * @param filter + * the match with which attempting the merge + * @return a new Match object describing the set of packets represented by + * the intersection of this and the filter matches. null if the + * intersection is empty. + */ + public Match mergeWithFilter(Match filter) { + return this.getIntersection(filter); + } + + /** + * Return the match representing the intersection of the set of packets + * described by this match with the set of packets described by the other + * match. Such as m.getIntersection(m) == m, m.getIntersection(u) == m and + * m.getIntersection(o) == o where u is an empty match (universal set, all + * packets) and o is the null match (empty set). + * + * @param other + * the match with which computing the intersection + * @return a new Match object representing the intersection of the set of + * packets described by this match with the set of packets described + * by the other match. null when the intersection is the empty set. + */ + public Match getIntersection(Match other) { + // If no intersection, return the empty set + if (!this.intersetcs(other)) { + return null; + } + // Check if any of the two is the universal match + if (this.getMatches() == 0) { + return other.clone(); + } + if (other.getMatches() == 0) { + return this.clone(); + } + // Derive the intersection + Match intersection = new Match(); + for (MatchType type : MatchType.values()) { + if (this.isAny(type) && other.isAny(type)) { + continue; + } if (this.isAny(type)) { + intersection.setField(other.getField(type).clone()); + continue; + } else if (other.isAny(type)) { + intersection.setField(this.getField(type).clone()); + continue; + } + // Either they are equal or it is about IP address + switch (type) { + // When it is about IP address, take the wider prefix address + // between the twos + case NW_SRC: + case NW_DST: + MatchField thisField = this.getField(type); + MatchField otherField = other.getField(type); + InetAddress thisAddress = (InetAddress) thisField.getValue(); + InetAddress otherAddress = (InetAddress) otherField.getValue(); + InetAddress thisMask = (InetAddress) thisField.getMask(); + InetAddress otherMask = (InetAddress) otherField.getMask(); + + int thisMaskLen = (thisMask == null) ? ((thisAddress instanceof Inet4Address) ? 32 : 128) : NetUtils + .getSubnetMaskLength(thisMask); + int otherMaskLen = (otherMask == null) ? ((otherAddress instanceof Inet4Address) ? 32 : 128) : NetUtils + .getSubnetMaskLength(otherMask); + if (otherMaskLen < thisMaskLen) { + intersection.setField(new MatchField(type, NetUtils.getSubnetPrefix(otherAddress, otherMaskLen), + otherMask)); + } else { + intersection.setField(new MatchField(type, NetUtils.getSubnetPrefix(thisAddress, thisMaskLen), + thisMask)); + } + break; + default: + // this and other match field are equal for this type, pick this + // match field + intersection.setField(this.getField(type).clone()); + } + } + return intersection; + } + + /** + * Checks whether the intersection of the set of packets described by this + * match with the set of packets described by the other match is non empty + * + * For example, if this match is: DL_SRC = 00:cc:bb:aa:11:22 + * + * and the other match is: DL_TYPE = 0x800 NW_SRC = 1.2.3.4 + * + * then their respective matching packets set intersection is non empty: + * DL_SRC = 00:cc:bb:aa:11:22 DL_TYPE = 0x800 NW_SRC = 1.2.3.4 + * + * @param other + * the other match with which testing the intersection + * @return true if the intersection of the respective matching packets sets + * is non empty + */ + public boolean intersetcs(Match other) { + // No intersection with the empty set + if (other == null) { + return false; + } + // Always intersection with the universal set + if (this.getMatches() == 0 || other.getMatches() == 0) { + return true; + } + // Iterate through the MatchType defined in the filter + for (MatchType type : MatchType.values()) { + if (this.isAny(type) || other.isAny(type)) { continue; } MatchField thisField = this.getField(type); - MatchField filterField = filter.getField(type); + MatchField otherField = other.getField(type); switch (type) { case DL_SRC: case DL_DST: - if (Arrays.equals((byte[]) thisField.getValue(), - (byte[]) filterField.getValue())) { + if (!Arrays.equals((byte[]) thisField.getValue(), (byte[]) otherField.getValue())) { return false; } break; case NW_SRC: case NW_DST: InetAddress thisAddress = (InetAddress) thisField.getValue(); - InetAddress filterAddress = (InetAddress) filterField - .getValue(); + InetAddress otherAddress = (InetAddress) otherField.getValue(); // Validity check - if (thisAddress instanceof Inet4Address - && filterAddress instanceof Inet6Address - || thisAddress instanceof Inet6Address - && filterAddress instanceof Inet4Address) { - return true; + if (thisAddress instanceof Inet4Address && otherAddress instanceof Inet6Address + || thisAddress instanceof Inet6Address && otherAddress instanceof Inet4Address) { + return false; } InetAddress thisMask = (InetAddress) thisField.getMask(); - InetAddress filterMask = (InetAddress) filterField.getMask(); - // thisAddress has to be in same subnet of filterAddress - if (NetUtils.inetAddressConflict(thisAddress, filterAddress, - thisMask, filterMask)) { - return true; + InetAddress otherMask = (InetAddress) otherField.getMask(); + if (NetUtils.inetAddressConflict(thisAddress, otherAddress, thisMask, otherMask) + && NetUtils.inetAddressConflict(otherAddress, thisAddress, otherMask, thisMask)) { + return false; } break; default: - if (!thisField.getValue().equals(filterField.getValue())) { - return true; - } - } - //TODO: check v4 v6 incompatibility - } - return false; - } - - /** - * Merge the current Match fields with the fields of the filter Match - * A check is first run to see if this Match is compatible with the - * filter Match. If it is not, the merge is not attempted. - * - * - * @param filter - * @return - */ - public Match mergeWithFilter(Match filter) { - if (!this.conflictWithFilter(filter)) { - /* - * No conflict with the filter - * We can copy over the fields which this match does not have - */ - for (MatchType type : filter.getMatchesList()) { - if (this.isAny(type)) { - this.setField(filter.getField(type).clone()); + if (!thisField.getValue().equals(otherField.getValue())) { + return false; } } } - return this; + return true; } @Override diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/MatchField.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/MatchField.java index 3b8a295216..54be4c6718 100644 --- a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/MatchField.java +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/MatchField.java @@ -184,7 +184,8 @@ public class MatchField implements Cloneable, Serializable { @Override public String toString() { - return type + "(" + getValueString() + "," + getMaskString() + ")"; + return (mask == null) ? String.format("%s(%s)", getTypeString(), getValueString()) : + String.format("%s(%s,%s)", getTypeString(), getValueString(), getMaskString()); } @Override diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/NetUtils.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/NetUtils.java index 0fce4f4b0a..e94021119d 100644 --- a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/NetUtils.java +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/NetUtils.java @@ -247,11 +247,18 @@ public abstract class NetUtils { * Checks if the test address and mask conflicts with the filter address and * mask * - * For example: testAddress: 172.28.2.23 testMask: 255.255.255.0 - * filtAddress: 172.28.1.10 testMask: 255.255.255.0 conflict + * For example: + * testAddress: 172.28.2.23 + * testMask: 255.255.255.0 + * filterAddress: 172.28.1.10 + * testMask: 255.255.255.0 + * do conflict * - * testAddress: 172.28.2.23 testMask: 255.255.255.0 filtAddress: 172.28.1.10 - * testMask: 255.255.0.0 do not conflict + * testAddress: 172.28.2.23 + * testMask: 255.255.255.0 + * filterAddress: 172.28.1.10 + * testMask: 255.255.0.0 + * do not conflict * * Null parameters are permitted * @@ -275,7 +282,8 @@ public abstract class NetUtils { int testMaskLen = (testMask == null) ? ((testAddress instanceof Inet4Address) ? 32 : 128) : NetUtils .getSubnetMaskLength(testMask); - int filterMaskLen = NetUtils.getSubnetMaskLength(filterMask); + int filterMaskLen = (filterMask == null) ? ((testAddress instanceof Inet4Address) ? 32 : 128) : NetUtils + .getSubnetMaskLength(filterMask); // Mask length check. Test mask has to be more specific than filter one if (testMaskLen < filterMaskLen) { @@ -464,7 +472,7 @@ public abstract class NetUtils { /* * Following utilities are useful when you need to compare or bit shift java - * primitive type variable which are inerently signed + * primitive type variable which are inherently signed */ /** * Returns the unsigned value of the passed byte variable diff --git a/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/match/MatchTest.java b/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/match/MatchTest.java index ae78e1acc8..f3c7a95b0e 100644 --- a/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/match/MatchTest.java +++ b/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/match/MatchTest.java @@ -523,4 +523,98 @@ public class MatchTest { Assert.assertTrue(field.getValue().equals(new Short(vlan))); Assert.assertTrue(field.isValid()); } + + @Test + public void testIntersection() throws UnknownHostException { + Short ethType = Short.valueOf((short)0x800); + InetAddress ip1 = InetAddress.getByName("1.1.1.1"); + InetAddress ip2 = InetAddress.getByName("1.1.1.0"); + InetAddress ipm2 = InetAddress.getByName("255.255.255.0"); + InetAddress ip3 = InetAddress.getByName("1.3.0.0"); + InetAddress ipm3 = InetAddress.getByName("255.255.0.0"); + + Match m1 = new Match(); + m1.setField(MatchType.DL_TYPE, ethType); + m1.setField(MatchType.NW_SRC, ip1); + + Match m2 = new Match(); + m2.setField(MatchType.DL_TYPE, ethType); + m2.setField(MatchType.NW_SRC, ip2, ipm2); + + Match m3 = new Match(); + m3.setField(MatchType.DL_TYPE, ethType); + m3.setField(MatchType.NW_SRC, ip3, ipm3); + m3.setField(MatchType.NW_PROTO, IPProtocols.TCP.byteValue()); + + Match m3r = m3.reverse(); + Assert.assertTrue(m3.intersetcs(m3r)); + + Assert.assertTrue(m1.intersetcs(m2)); + Assert.assertTrue(m2.intersetcs(m1)); + Assert.assertFalse(m1.intersetcs(m3)); + Assert.assertTrue(m1.intersetcs(m3r)); + Assert.assertFalse(m3.intersetcs(m1)); + Assert.assertTrue(m3.intersetcs(m1.reverse())); + Assert.assertFalse(m2.intersetcs(m3)); + Assert.assertFalse(m3.intersetcs(m2)); + Assert.assertTrue(m2.intersetcs(m3r)); + + + Match i = m1.getIntersection(m2); + Assert.assertTrue(((Short)i.getField(MatchType.DL_TYPE).getValue()).equals(ethType)); + Assert.assertTrue(((InetAddress)i.getField(MatchType.NW_SRC).getValue()).equals(ip2)); + Assert.assertTrue(((InetAddress)i.getField(MatchType.NW_SRC).getMask()).equals(ipm2)); + + // Empty set + i = m2.getIntersection(m3); + Assert.assertNull(i); + + Match m4 = new Match(); + m4.setField(MatchType.DL_TYPE, ethType); + m4.setField(MatchType.NW_PROTO, IPProtocols.TCP.byteValue()); + Assert.assertTrue(m4.intersetcs(m3)); + + Match m5 = new Match(); + m5.setField(MatchType.DL_TYPE, ethType); + m3.setField(MatchType.NW_SRC, ip3, ipm3); + m5.setField(MatchType.NW_PROTO, IPProtocols.UDP.byteValue()); + Assert.assertFalse(m5.intersetcs(m3)); + Assert.assertFalse(m5.intersetcs(m4)); + Assert.assertTrue(m5.intersetcs(m5)); + Assert.assertFalse(m3.intersetcs(m5)); + Assert.assertFalse(m4.intersetcs(m5)); + + + Match i2 = m4.getIntersection(m3); + Assert.assertFalse(i2.getMatches() == 0); + Assert.assertFalse(i2.getMatchesList().isEmpty()); + Assert.assertTrue(((InetAddress)i2.getField(MatchType.NW_SRC).getValue()).equals(ip3)); + Assert.assertTrue(((InetAddress)i2.getField(MatchType.NW_SRC).getMask()).equals(ipm3)); + Assert.assertTrue(((Byte)i2.getField(MatchType.NW_PROTO).getValue()).equals(IPProtocols.TCP.byteValue())); + + byte src[] = {(byte)0, (byte)0xab,(byte)0xbc,(byte)0xcd,(byte)0xde,(byte)0xef}; + byte dst[] = {(byte)0x10, (byte)0x11,(byte)0x12,(byte)0x13,(byte)0x14,(byte)0x15}; + Short srcPort = (short)1024; + Short dstPort = (short)80; + + // Check identity + Match m6 = new Match(); + m6.setField(MatchType.DL_SRC, src); + m6.setField(MatchType.DL_DST, dst); + m6.setField(MatchType.NW_SRC, ip2, ipm2); + m6.setField(MatchType.NW_DST, ip3, ipm3); + m6.setField(MatchType.NW_PROTO, IPProtocols.UDP.byteValue()); + m6.setField(MatchType.TP_SRC, srcPort); + m6.setField(MatchType.TP_DST, dstPort); + Assert.assertTrue(m6.intersetcs(m6)); + Assert.assertTrue(m6.getIntersection(m6).equals(m6)); + + // Empty match, represents the universal set (all packets) + Match u = new Match(); + Assert.assertTrue(m6.getIntersection(u).equals(m6)); + Assert.assertTrue(u.getIntersection(m6).equals(m6)); + + // No intersection with null match, empty set + Assert.assertNull(m6.getIntersection(null)); + } } diff --git a/opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java b/opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java index 15ad9e9887..781242a969 100644 --- a/opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java +++ b/opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java @@ -636,7 +636,7 @@ public class Devices implements IDaylightWeb { @RequestMapping(value = "/nodeports") @ResponseBody - public Map getNodePorts(HttpServletRequest request, + public List getNodePorts(HttpServletRequest request, @RequestParam(required = false) String container) { String containerName = (container == null) ? GlobalConstants.DEFAULT .toString() : container; @@ -647,18 +647,16 @@ public class Devices implements IDaylightWeb { return null; } - ISwitchManager switchManager = (ISwitchManager) ServiceHelper .getInstance(ISwitchManager.class, containerName, this); if (switchManager == null) { return null; } - - Map nodes = new HashMap(); - Map port; + List nodeJsonBeans = new ArrayList(); for (Switch node : switchManager.getNetworkDevices()) { - port = new HashMap(); // new port + NodeJsonBean nodeJsonBean = new NodeJsonBean(); + List port = new ArrayList(); Set nodeConnectorSet = node.getNodeConnectors(); if (nodeConnectorSet != null) { @@ -666,15 +664,17 @@ public class Devices implements IDaylightWeb { String nodeConnectorName = ((Name) switchManager .getNodeConnectorProp(nodeConnector, Name.NamePropName)).getValue(); - port.put((Short) nodeConnector.getID(), nodeConnectorName + port.add(nodeConnectorName + "(" + nodeConnector.getID() + ")"); } } - - nodes.put(node.getNode().toString(), port); + nodeJsonBean.setNodeId(node.getNode().toString()); + nodeJsonBean.setNodeName(getNodeDesc(node.getNode().toString(), containerName)); + nodeJsonBean.setNodePorts(port); + nodeJsonBeans.add(nodeJsonBean); } - return nodes; + return nodeJsonBeans; } @RequestMapping(value = "/spanPorts/add", method = RequestMethod.GET) diff --git a/opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/NodeJsonBean.java b/opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/NodeJsonBean.java new file mode 100644 index 0000000000..0fd933a5ec --- /dev/null +++ b/opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/NodeJsonBean.java @@ -0,0 +1,42 @@ +/* + * 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.devices.web; + +import java.util.List; + +public class NodeJsonBean { + String nodeId; + String nodeName; + List nodePorts; + + public String getNodeId() { + return nodeId; + } + + public String getNodeName() { + return nodeName; + } + + public List getNodePorts() { + return nodePorts; + } + + public void setNodeId(String nodeId) { + this.nodeId = nodeId; + } + + public void setNodeName(String nodeName) { + this.nodeName = nodeName; + } + + public void setNodePorts(List port) { + this.nodePorts = port; + } + +} diff --git a/opendaylight/web/devices/src/main/resources/js/page.js b/opendaylight/web/devices/src/main/resources/js/page.js index 6916be6798..048c9e993e 100644 --- a/opendaylight/web/devices/src/main/resources/js/page.js +++ b/opendaylight/web/devices/src/main/resources/js/page.js @@ -1161,11 +1161,11 @@ one.f.switchmanager.spanPortConfig = { // bind onchange $select.change(function() { // retrieve port value - var node = $(this).find('option:selected').attr('value'); - one.f.switchmanager.spanPortConfig.registry['currentNode'] = node; + var nodeId = $(this).find('option:selected').attr('value'); + one.f.switchmanager.spanPortConfig.registry['currentNode'] = nodeId; var $ports = $('#' + one.f.switchmanager.spanPortConfig.id.modal.form.port); - var ports = nodeports[node]; - one.lib.form.select.inject($ports, ports); + var ports = one.f.switchmanager.spanPortConfig.registry['nodePorts'][nodeId] + one.lib.form.select.inject($ports, ports); }); $fieldset.append($label).append($select); @@ -1182,10 +1182,14 @@ one.f.switchmanager.spanPortConfig = { ajax: { nodes: function(callback) { $.getJSON(one.f.switchmanager.rootUrl + "/nodeports", function(data) { - var nodes = one.f.switchmanager.spanPortConfig.modal.data.nodes(data); - var nodeports = data; - one.f.switchmanager.spanPortConfig.registry['nodeports'] = nodeports; - callback(nodes, nodeports); + var nodes = {}; + var nodePorts = {}; + $(data).each(function(index, node) { + nodes[node.nodeId] = node.nodeName; + nodePorts[node.nodeId] = node.nodePorts; + }); + one.f.switchmanager.spanPortConfig.registry['nodePorts'] = nodePorts; + callback(nodes, nodePorts); }); }, saveSpanPortConfig: function(requestData, callback) { @@ -1196,15 +1200,6 @@ one.f.switchmanager.spanPortConfig = { }); } }, - data : { - nodes : function(data) { - result = {}; - $.each(data, function(key, value) { - result[key] = key; - }); - return result; - } - }, footer : function() { var footer = []; var saveButton = one.lib.dashlet.button.single("Save", one.f.switchmanager.spanPortConfig.id.modal.save, "btn-success", "");