Merge "Northbound cleanup for Switch Manager"
authorMadhu Venugopal <vmadhu@cisco.com>
Mon, 26 Aug 2013 18:52:42 +0000 (18:52 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Mon, 26 Aug 2013 18:52:42 +0000 (18:52 +0000)
16 files changed:
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/Activator.java
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/scheme/AbstractScheme.java
opendaylight/distribution/opendaylight/src/main/resources/run.sh
opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowEntry.java
opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java
opendaylight/northbound/topology/src/main/java/org/opendaylight/controller/topology/northbound/TopologyNorthboundJAXRS.java
opendaylight/protocol_plugins/stub/src/main/java/org/opendaylight/controller/protocol_plugins/stub/internal/Activator.java
opendaylight/protocol_plugins/stub/src/main/java/org/opendaylight/controller/protocol_plugins/stub/internal/InventoryService.java
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/Match.java
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/MatchField.java
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/NetUtils.java
opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/match/MatchTest.java
opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java
opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/NodeJsonBean.java [new file with mode: 0644]
opendaylight/web/devices/src/main/resources/js/page.js

index 4bee2537656e3ac1ddfda162cd20735939a50a58..c0d1b50a4657a314fbcdd7010fdb89d28e72ffa8 100644 (file)
@@ -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<String, Object> props = new Hashtable<String, Object>();
@@ -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));
         }
     }
 }
index fdba533b5bfa827bf2ccd79bf2f061785d06d597..e2d8f6b03b270f7f602de6b4470bc0a971487f61 100644 (file)
@@ -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<ConnectionMgmtEvent> 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<Node, Map<String, Property>> nodeProp = this.inventoryService.getNodeProps();
+        for (Map.Entry<Node, Map<String, Property>> entry : nodeProp.entrySet()) {
+            Node node = entry.getKey();
+            logger.debug("getInventories for node:{}", new Object[] { node });
+            Map<String, Property> propMap = entry.getValue();
+            Set<Property> props = new HashSet<Property>();
+            for (Property property : propMap.values()) {
+                props.add(property);
+            }
+            updateNode(node, UpdateType.ADDED, props);
+        }
+
+        Map<NodeConnector, Map<String, Property>> nodeConnectorProp = this.inventoryService.getNodeConnectorProps();
+        for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProp.entrySet()) {
+            Map<String, Property> propMap = entry.getValue();
+            Set<Property> props = new HashSet<Property>();
+            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() {
index d93f74b4fe3a434cca4f6adc7f93ab52a56e748c..d7c4e5f933bdba54e0a0cd5411627546c0133b72 100644 (file)
@@ -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(),
index 313b3b2c79cdd18dbf585f6a76adbeecf8e688f9..2587a5d2ab54de060c4a258713352febf4c97725 100755 (executable)
@@ -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 {
index e8c5f648fa69c4c7d3c9f5f4846daf73a8e78341..83106a391cb7bdc00ff0911fa703e7dee1c1c2f4 100644 (file)
@@ -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;
     }
index ad694b0b65a4cccb6b7304a12299d5feea464fdd..76974d7541d5994631e979db74e87ced20709054 100644 (file)
@@ -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);
index fb4e8229ff48fd87dd8e6195ee272d497bfbd095..3ba1eb18435612565e8c6f68c125d75bb951420c 100644 (file)
@@ -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.
+     *
+     * <pre>
+     *
+     * Example:
+     *
+     * RequestURL:
+     * http://localhost:8080/controller/nb/v2/topology/default
+     *
+     * Response in XML:
+     * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;&lt;topology&gt;&lt;edgeProperties&gt;&lt;edge&gt;&lt;tailNodeConnector id="2" type="OF"&gt;&lt;node id="00:00:00:00:00:00:00:02" type="OF"/&gt;&lt;/tailNodeConnector&gt;&lt;headNodeConnector id="2" type="OF"&gt;&lt;node id="00:00:00:00:00:00:00:51" type="OF"/&gt;&lt;/headNodeConnector&gt;&lt;/edge&gt;&lt;properties&gt;&lt;state&gt;&lt;value&gt;1&lt;/value&gt;&lt;/state&gt;&lt;config&gt;&lt;value&gt;1&lt;/value&gt;&lt;/config&gt;&lt;name&gt;&lt;value&gt;C1_2-L2_2&lt;/value&gt;&lt;/name&gt;&lt;timeStamp&gt;&lt;value&gt;1377279422032&lt;/value&gt;&lt;name&gt;creation&lt;/name&gt;&lt;/timeStamp&gt;&lt;/properties&gt;&lt;/edgeProperties&gt;&lt;edgeProperties&gt;&lt;edge&gt;&lt;tailNodeConnector id="2" type="OF"&gt;&lt;node id="00:00:00:00:00:00:00:51" type="OF"/&gt;&lt;/tailNodeConnector&gt;&lt;headNodeConnector id="2" type="OF"&gt;&lt;node id="00:00:00:00:00:00:00:02" type="OF"/&gt;&lt;/headNodeConnector&gt;&lt;/edge&gt;&lt;properties&gt;&lt;state&gt;&lt;value&gt;1&lt;/value&gt;&lt;/state&gt;&lt;name&gt;&lt;value&gt;L2_2-C1_2&lt;/value&gt;&lt;/name&gt;&lt;config&gt;&lt;value&gt;1&lt;/value&gt;&lt;/config&gt;&lt;timeStamp&gt;&lt;value&gt;1377279423564&lt;/value&gt;&lt;name&gt;creation&lt;/name&gt;&lt;/timeStamp&gt;&lt;/properties&gt;&lt;/edgeProperties&gt;&lt;/topology&gt;
+     *
+     * 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"}}}]}
+     *
+     * </pre>
      */
     @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
+     *
+     * <pre>
+     *
+     * Example:
+     *
+     * RequestURL:
+     * http://localhost:8080/controller/nb/v2/topology/default/user-link
+     *
+     * Response in XML:
+     * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;&lt;topologyUserLinks&gt;&lt;userLinks&gt;&lt;status&gt;Success&lt;/status&gt;&lt;name&gt;link1&lt;/name&gt;&lt;srcNodeConnector&gt;OF|2@OF|00:00:00:00:00:00:00:02&lt;/srcNodeConnector&gt;&lt;dstNodeConnector&gt;OF|2@OF|00:00:00:00:00:00:00:51&lt;/dstNodeConnector&gt;&lt;/userLinks&gt;&lt;/topologyUserLinks&gt;
+     *
+     * 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"}}
+     *
+     * </pre>
      */
-    @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
+     *
+     * <pre>
+     *
+     * Example:
+     *
+     * RequestURL:
+     * http://localhost:8080/controller/nb/v2/topology/default/user-link/config1-content
+     *
+     * Request in XML:
+     * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;&lt;topologyUserLinks&gt;&lt;status&gt;Success&lt;/status&gt;&lt;name&gt;link1&lt;/name&gt;&lt;srcNodeConnector&gt;OF|2@OF|00:00:00:00:00:00:00:02&lt;/srcNodeConnector&gt;&lt;dstNodeConnector&gt;OF|2@OF|00:00:00:00:00:00:00:51&lt;/dstNodeConnector&gt;&lt;/topologyUserLinks&gt;
+     *
+     * 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"}
+     *
+     * </pre>
      */
-
-    @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
+     *
+     * <pre>
+     *
+     * Example:
+     *
+     * RequestURL:
+     * http://localhost:8080/controller/nb/v2/topology/default/user-link/config1
+     *
+     * </pre>
      */
-
-    @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());
     }
-
 }
index 76c38664d4d44adde2e799d1f5ff595702aa0c67..79edd93c67466a6da6820fe654dd0aeea95ac656 100644 (file)
@@ -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<String, Object> props = new Hashtable<String, Object>();
+            // 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));
+        }
     }
 }
index 22a4343f332ed2e79e7f7ecc979d8535acc4ea26..b94ffec1ddba5d1f6cc699813d22fea6b02c7c0e 100644 (file)
@@ -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<IPluginOutInventoryService> pluginOutInventoryServices =
+            new CopyOnWriteArraySet<IPluginOutInventoryService>();
+
+    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<Property> props = new HashSet<Property>(nodeProps.get(node)
+                        .values());
+                service.updateNode(node, UpdateType.ADDED, props);
+                logger.trace("Adding Node {} with props {}", node, props);
+            }
+            for (NodeConnector nc : nodeConnectorProps.keySet()) {
+                Set<Property> props = new HashSet<Property>(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();
     }
 
     /**
index 9bab8391986df5a82dfc6b3aeeab51155eb84f88..30c25af57f688539c7bf569aa4ecccf0f804af26 100644 (file)
@@ -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
index 3b8a2952164eafb0cf463208d06cc29a182e2d6f..54be4c67189412a208dc5cfebb6fef2ff49f0301 100644 (file)
@@ -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
index 0fce4f4b0aea447a26c2884a1d6b6b3814ba9df5..e94021119d14f11e7581d5e68b6e096921a07725 100644 (file)
@@ -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
index ae78e1acc8af45f8ef879b0ad16477bf9040330a..f3c7a95b0ebcdfb44a5de81e8a5509572827d2f6 100644 (file)
@@ -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));
+    }
 }
index 15ad9e9887d8eb2aba1250bc278a48f7441db00a..781242a969b0a0f5964a7223672e1b08a74f60fb 100644 (file)
@@ -636,7 +636,7 @@ public class Devices implements IDaylightWeb {
 
     @RequestMapping(value = "/nodeports")
     @ResponseBody
-    public Map<String, Object> getNodePorts(HttpServletRequest request,
+    public List<NodeJsonBean> 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<String, Object> nodes = new HashMap<String, Object>();
-        Map<Short, String> port;
+        List<NodeJsonBean> nodeJsonBeans = new ArrayList<NodeJsonBean>();
 
         for (Switch node : switchManager.getNetworkDevices()) {
-            port = new HashMap<Short, String>(); // new port
+            NodeJsonBean nodeJsonBean = new NodeJsonBean();
+            List<String> port = new ArrayList<String>();
             Set<NodeConnector> 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 (file)
index 0000000..0fd933a
--- /dev/null
@@ -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<String> nodePorts;
+
+    public String getNodeId() {
+        return nodeId;
+    }
+
+    public String getNodeName() {
+        return nodeName;
+    }
+
+    public List<String> getNodePorts() {
+        return nodePorts;
+    }
+
+    public void setNodeId(String nodeId) {
+        this.nodeId = nodeId;
+    }
+
+    public void setNodeName(String nodeName) {
+        this.nodeName = nodeName;
+    }
+
+    public void setNodePorts(List<String> port) {
+        this.nodePorts = port;
+    }
+
+}
index 6916be679877fadb2423912e67bdb243f4d0fb3e..048c9e993eaecc9d17a96541f39f1591bdccd25d 100644 (file)
@@ -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", "");