Merge "Integration of MDSAL into distribution"
authorEd Warnicke <eaw@cisco.com>
Mon, 26 Aug 2013 19:19:18 +0000 (19:19 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Mon, 26 Aug 2013 19:19:18 +0000 (19:19 +0000)
22 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/switchmanager/src/main/java/org/opendaylight/controller/switchmanager/northbound/NodeConnectors.java
opendaylight/northbound/switchmanager/src/main/java/org/opendaylight/controller/switchmanager/northbound/Nodes.java
opendaylight/northbound/switchmanager/src/main/java/org/opendaylight/controller/switchmanager/northbound/SwitchNorthbound.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
opendaylight/web/flows/src/main/java/org/opendaylight/controller/flows/web/Flows.java
opendaylight/web/flows/src/main/resources/js/page.js
opendaylight/web/troubleshoot/src/main/java/org/opendaylight/controller/troubleshoot/web/Troubleshoot.java

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 4a31317d95b3b61926ec96ca751bc5205c5f59c5..32e1002c94afa6e3c6a1b81dce396b6c7fbae1ce 100644 (file)
@@ -15,7 +15,7 @@ import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 
-@XmlRootElement
+@XmlRootElement(name="list")
 @XmlAccessorType(XmlAccessType.NONE)
 
 public class NodeConnectors {
index b7f1c53ae850dec4c7d8bf258853bbec849e5801..061e51732ed3c41316d03d08eec5f63288a3826c 100644 (file)
@@ -15,7 +15,7 @@ import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 
-@XmlRootElement
+@XmlRootElement(name="list")
 @XmlAccessorType(XmlAccessType.NONE)
 
 public class Nodes {
index e67d1b75686934b48fb15cb409bf0cdf5cd3d123..98aa5ad94e06c713dcd02fe7a71141ec02b46782 100644 (file)
@@ -107,11 +107,56 @@ public class SwitchNorthbound {
      * Retrieve a list of all the nodes and their properties in the network
      *
      * @param containerName
-     *            The container for which we want to retrieve the list
+     *            Name of the Container (Eg. 'default')
      * @return A list of Pair each pair represents a
      *         {@link org.opendaylight.controller.sal.core.Node} and Set of
      *         {@link org.opendaylight.controller.sal.core.Property} attached to
      *         it.
+     *
+     * <pre>
+     *
+     * Example:
+     *
+     * RequestURL:
+     * http://.../default/nodes
+     *
+     * Response in XML:
+     * &lt;list&gt;
+     *     &#x20;&#x20;&#x20;&lt;nodeProperties&gt;
+     *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;node type="OF" id="00:00:00:00:00:00:00:02"/&gt;
+     *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;properties&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;tables&gt;
+     *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;-1&lt;/value&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/tables&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;description&gt;
+     *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;Switch2&lt;/value&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/description&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;actions&gt;
+     *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;4095&lt;/value&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/actions&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;macAddress&gt;
+     *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;00:00:00:00:00:02&lt;/value&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/macAddress&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;capabilities&gt;
+     *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;199&lt;/value&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/capabilities&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;timeStamp&gt;
+     *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;1377291227877&lt;/value&gt;
+     *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;name&gt;connectedSince&lt;/name&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/timeStamp&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;buffers&gt;
+     *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;256&lt;/value&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/buffers&gt;
+     *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/properties&gt;
+     *     &#x20;&#x20;&#x20;&lt;/nodeProperties&gt;
+     * &lt;/list&gt;
+     *
+     * Response in JSON:
+     * {"nodeProperties":[{"node":{"@type":"OF","@id":"00:00:00:00:00:00:00:02"},"properties":{"tables":{"value":"-1"},
+     * "description":{"value":"None"},"actions":{"value":"4095"},"macAddress":{"value":"00:00:00:00:00:02"},"capabilities"
+     * :{"value":"199"},"timeStamp":{"value":"1377291227877","name":"connectedSince"},"buffers":{"value":"256"}}}]}
+     *
+     * </pre>
      */
     @Path("/{containerName}/nodes")
     @GET
@@ -119,6 +164,7 @@ public class SwitchNorthbound {
     @TypeHint(Nodes.class)
     @StatusCodes({
             @ResponseCode(code = 200, condition = "Operation successful"),
+            @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
             @ResponseCode(code = 404, condition = "The containerName is not found"),
             @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
     public Nodes getNodes(@PathParam("containerName") String containerName) {
@@ -161,49 +207,54 @@ public class SwitchNorthbound {
     }
 
     /**
-     * Add a Name, Tier and Forwarding mode property to a node.
-     *
-     * <pre>
-     * Example Request:
-     *  http://localhost:8080/controller/nb/v2/switch/red/node/OF/00:00:00:00:00:03/property/description/Switch3
-     *  (Valid properties that can be configured are: description, forwarding(only for default container) and tier)
-     * </pre>
+     * Add a Description, Tier and Forwarding mode property to a node.
      *
      * @param containerName
-     *            Name of the Container
+     *            Name of the Container (Eg. 'default')
      * @param nodeType
-     *            Type of the node being programmed
+     *            Type of the node being programmed (Eg. 'OF')
      * @param nodeId
      *            Node Identifier as specified by
      *            {@link org.opendaylight.controller.sal.core.Node}
-     * @param propName
-     *            Name of the Property specified by
-     *            {@link org.opendaylight.controller.sal.core.Property} and its
-     *            extended classes
-     * @param propValue
-     *            Value of the Property specified by
-     *            {@link org.opendaylight.controller.sal.core.Property} and its
-     *            extended classes
+     *            (Eg. '00:00:00:00:00:00:00:03')
+     * @param propertyName
+     *            Name of the Property. Properties that can be
+     *            configured are: description, forwarding(only for default
+     *            container) and tier
+     * @param propertyValue
+     *            Value of the Property. Description can be any string (Eg. 'Node1'),
+     *            valid values for tier are 0, 1 and 2, and valid values for forwarding are 0 for
+     *            reactive and 1 for proactive forwarding.
      * @return Response as dictated by the HTTP Response Status code
+     *
+     * <pre>
+     *
+     * Example:
+     *
+     * RequestURL:
+     * http://.../default/node/OF/00:00:00:00:00:00:00:03/property/description/Switch3
+     *
+     * </pre>
      */
 
-    @Path("/{containerName}/node/{nodeType}/{nodeId}/property/{propName}/{propValue}")
+    @Path("/{containerName}/node/{nodeType}/{nodeId}/property/{propertyName}/{propertyValue}")
     @PUT
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @TypeHint(Response.class)
     @StatusCodes({
-            @ResponseCode(code = 200, condition = "Operation successful"),
+            @ResponseCode(code = 201, condition = "Operation successful"),
             @ResponseCode(code = 400, condition = "The nodeId or configuration is invalid"),
+            @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
             @ResponseCode(code = 404, condition = "The Container Name or node or configuration name is not found"),
             @ResponseCode(code = 406, condition = "The property cannot be configured in non-default container"),
-            @ResponseCode(code = 409, condition = "Unable to update configuration due to cluster conflict"),
+            @ResponseCode(code = 409, condition = "Unable to update configuration due to cluster conflict or conflicting description property"),
             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
     public Response addNodeProperty(
             @PathParam("containerName") String containerName,
             @PathParam("nodeType") String nodeType,
             @PathParam("nodeId") String nodeId,
-            @PathParam("propName") String propName,
-            @PathParam("propValue") String propValue) {
+            @PathParam("propertyName") String propertyName,
+            @PathParam("propertyValue") String propertyValue) {
 
         if (!isValidContainer(containerName)) {
             throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
@@ -222,9 +273,9 @@ public class SwitchNorthbound {
 
         handleNodeAvailability(containerName, nodeType, nodeId);
         Node node = Node.fromString(nodeType, nodeId);
-        Property prop = switchManager.createProperty(propName, propValue);
+        Property prop = switchManager.createProperty(propertyName, propertyValue);
         if (prop == null) {
-            throw new ResourceNotFoundException("Property with name " + propName + " does not exist.");
+            throw new ResourceNotFoundException("Property with name " + propertyName + " does not exist.");
         }
         SwitchConfig switchConfig = switchManager.getSwitchConfig(node.toString());
         Map<String, Property> nodeProperties = (switchConfig == null) ? new HashMap<String, Property>()
@@ -232,29 +283,36 @@ public class SwitchNorthbound {
         nodeProperties.put(prop.getName(), prop);
         SwitchConfig newSwitchConfig = new SwitchConfig(node.toString(), nodeProperties);
         Status status = switchManager.updateNodeConfig(newSwitchConfig);
+        if (status.isSuccess()) {
+            return Response.status(Response.Status.CREATED).build();
+        }
         return NorthboundUtils.getResponse(status);
     }
 
     /**
      * Delete a property of a node
      *
-     * <pre>
-     * Example Request:
-     *  http://localhost:8080/controller/nb/v2/switch/default/node/OF/00:00:00:00:00:03/property/forwarding
-     * </pre>
-     *
      * @param containerName
-     *            Name of the Container
+     *            Name of the Container (Eg. 'SliceRed')
      * @param nodeType
-     *            Type of the node being programmed
+     *            Type of the node being programmed (Eg. 'OF')
      * @param nodeId
      *            Node Identifier as specified by
      *            {@link org.opendaylight.controller.sal.core.Node}
+     *            (Eg. '00:00:00:00:00:03:01:02')
      * @param propertyName
-     *            Name of the Property specified by
-     *            {@link org.opendaylight.controller.sal.core.Property} and its
-     *            extended classes
+     *            Name of the Property. Properties that can be deleted are
+     *            description, forwarding(only in default container) and tier.
      * @return Response as dictated by the HTTP Response Status code
+     *
+     * <pre>
+     *
+     * Example:
+     *
+     * RequestURL:
+     * http://.../default/node/OF/00:00:00:00:00:00:00:03/property/forwarding
+     *
+     * </pre>
      */
 
     @Path("/{containerName}/node/{nodeType}/{nodeId}/property/{propertyName}")
@@ -263,6 +321,7 @@ public class SwitchNorthbound {
     @StatusCodes({
             @ResponseCode(code = 200, condition = "Operation successful"),
             @ResponseCode(code = 400, condition = "The nodeId or configuration is invalid"),
+            @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
             @ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
             @ResponseCode(code = 409, condition = "Unable to delete property due to cluster conflict"),
             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
@@ -313,21 +372,56 @@ public class SwitchNorthbound {
 
     /**
      *
-     * Retrieve a list of all the node connectors and their properties in a
+     * Retrieve a list of all the nodeconnectors and their properties in a
      * given node
      *
      * @param containerName
-     *            The container for which we want to retrieve the list
+     *            The container for which we want to retrieve the list (Eg.
+     *            'default')
      * @param nodeType
-     *            Type of the node being programmed
+     *            Type of the node being programmed (Eg. 'OF')
      * @param nodeId
      *            Node Identifier as specified by
-     *            {@link org.opendaylight.controller.sal.core.Node}
+     *            {@link org.opendaylight.controller.sal.core.Node} (Eg.
+     *            '00:00:00:00:00:00:00:03')
      * @return A List of Pair each pair represents a
      *         {@link org.opendaylight.controller.sal.core.NodeConnector} and
      *         its corresponding
      *         {@link org.opendaylight.controller.sal.core.Property} attached to
      *         it.
+     *
+     * <pre>
+     *
+     * Example:
+     *
+     * RequestURL:
+     * http://.../default/node/OF/00:00:00:00:00:00:00:01
+     *
+     * Response in XML:
+     * &lt;list&gt;
+     *     &#x20;&#x20;&#x20;&lt;nodeConnectorProperties&gt;
+     *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nodeconnector type="OF" id="2"&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;node type="OF" id="00:00:00:00:00:00:00:01"/&gt;
+     *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/nodeconnector&gt;
+     *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;properties&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;state&gt;
+     *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;1&lt;/value&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/state&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;config&gt;
+     *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;1&lt;/value&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/config&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;name&gt;
+     *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;L1_2-C2_1&lt;/value&gt;
+     *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/name&gt;
+     *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/properties&gt;
+     *     &#x20;&#x20;&#x20;&lt;/nodeConnectorProperties&gt;
+     * &lt;/list&gt;
+     *
+     * Response in JSON:
+     * {"nodeConnectorProperties":[{"nodeconnector":{"@type":"OF","@id":"2","node":{"@type":"OF","@id":"00:00:00:00:00:00:00:01"}},
+     * "properties":{"state":{"value":"1"},"config":{"value":"1"},"name":{"value":"L1_2-C2_1"}}}]}
+     *
+     * </pre>
      */
     @Path("/{containerName}/node/{nodeType}/{nodeId}")
     @GET
@@ -335,6 +429,7 @@ public class SwitchNorthbound {
     @TypeHint(NodeConnectors.class)
     @StatusCodes({
             @ResponseCode(code = 200, condition = "Operation successful"),
+            @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
             @ResponseCode(code = 404, condition = "The containerName is not found"),
             @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
     public NodeConnectors getNodeConnectors(
@@ -382,37 +477,51 @@ public class SwitchNorthbound {
     }
 
     /**
-     * Add a Name/Bandwidth property to a node connector
+     * Add Bandwidth property to a node connector
      *
      * @param containerName
-     *            Name of the Container
+     *            Name of the Container (Eg. 'default')
      * @param nodeType
-     *            Type of the node being programmed
+     *            Type of the node being programmed (Eg. 'OF')
      * @param nodeId
      *            Node Identifier as specified by
      *            {@link org.opendaylight.controller.sal.core.Node}
+     *            (Eg. '00:00:00:00:00:00:00:03')
      * @param nodeConnectorType
-     *            Type of the node connector being programmed
+     *            Type of the node connector being programmed (Eg. 'OF')
      * @param nodeConnectorId
      *            NodeConnector Identifier as specified by
      *            {@link org.opendaylight.controller.sal.core.NodeConnector}
-     * @param propName
+     *            (Eg. '2')
+     * @param propertyName
      *            Name of the Property specified by
      *            {@link org.opendaylight.controller.sal.core.Property} and its
      *            extended classes
-     * @param propValue
+     *            Property that can be configured is bandwidth
+     * @param propertyValue
      *            Value of the Property specified by
      *            {@link org.opendaylight.controller.sal.core.Property} and its
      *            extended classes
      * @return Response as dictated by the HTTP Response Status code
+     *
+     * <pre>
+     *
+     * Example:
+     *
+     * RequestURL:
+     * http://.../default/nodeconnector/OF/00:00:00:00:00:00:00:01/OF/2/property/bandwidth/1
+     *
+     * </pre>
      */
 
-    @Path("/{containerName}/nodeconnector/{nodeType}/{nodeId}/{nodeConnectorType}/{nodeConnectorId}/property/{propName}/{propValue}")
+    @Path("/{containerName}/nodeconnector/{nodeType}/{nodeId}/{nodeConnectorType}/{nodeConnectorId}/property/{propertyName}/{propertyValue}")
     @PUT
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({
-            @ResponseCode(code = 200, condition = "Operation successful"),
+            @ResponseCode(code = 201, condition = "Operation successful"),
+            @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
             @ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
+            @ResponseCode(code = 409, condition = "Unable to add property due to cluster conflict"),
             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
     public Response addNodeConnectorProperty(
             @PathParam("containerName") String containerName,
@@ -420,8 +529,8 @@ public class SwitchNorthbound {
             @PathParam("nodeId") String nodeId,
             @PathParam("nodeConnectorType") String nodeConnectorType,
             @PathParam("nodeConnectorId") String nodeConnectorId,
-            @PathParam("propName") String propName,
-            @PathParam("propValue") String propValue) {
+            @PathParam("propertyName") String propertyName,
+            @PathParam("propertyValue") String propertyValue) {
 
         if (!isValidContainer(containerName)) {
             throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
@@ -447,7 +556,7 @@ public class SwitchNorthbound {
         NodeConnector nc = NodeConnector
                 .fromStringNoNode(nodeConnectorType, nodeConnectorId, node);
 
-        Property prop = switchManager.createProperty(propName, propValue);
+        Property prop = switchManager.createProperty(propertyName, propertyValue);
         if (prop == null) {
             throw new ResourceNotFoundException(
                     RestMessages.INVALIDDATA.toString());
@@ -464,22 +573,33 @@ public class SwitchNorthbound {
      * Delete a property of a node connector
      *
      * @param containerName
-     *            Name of the Container
+     *            Name of the Container (Eg. 'default')
      * @param nodeType
-     *            Type of the node being programmed
+     *            Type of the node being programmed (Eg. 'OF')
      * @param nodeId
      *            Node Identifier as specified by
      *            {@link org.opendaylight.controller.sal.core.Node}
+     *            (Eg. '00:00:00:00:00:00:00:01')
      * @param nodeConnectorType
-     *            Type of the node connector being programmed
+     *            Type of the node connector being programmed (Eg. 'OF')
      * @param nodeConnectorId
      *            NodeConnector Identifier as specified by
      *            {@link org.opendaylight.controller.sal.core.NodeConnector}
+     *            (Eg. '1')
      * @param propertyName
      *            Name of the Property specified by
      *            {@link org.opendaylight.controller.sal.core.Property} and its
-     *            extended classes
+     *            extended classes. Property that can be deleted is bandwidth
      * @return Response as dictated by the HTTP Response Status code
+     *
+     * <pre>
+     *
+     * Example:
+     *
+     * RequestURL:
+     * http://.../default/nodeconnector/OF/00:00:00:00:00:00:00:01/OF/2/property/bandwidth
+     *
+     * </pre>
      */
 
     @Path("/{containerName}/nodeconnector/{nodeType}/{nodeId}/{nodeConnectorType}/{nodeConnectorId}/property/{propertyName}")
@@ -487,6 +607,7 @@ public class SwitchNorthbound {
     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({
             @ResponseCode(code = 200, condition = "Operation successful"),
+            @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
             @ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
     public Response deleteNodeConnectorProperty(
@@ -528,125 +649,30 @@ public class SwitchNorthbound {
         throw new ResourceNotFoundException(ret.getDescription());
     }
 
-    /*    *//**
-     * Retrieve a list of Span ports that were configured previously.
-     *
-     * @param containerName
-     *            Name of the Container
-     * @return list of
-     *         {@link org.opendaylight.controller.switchmanager.SpanConfig}
-     *         resources
-     */
-    /*
-     * @Path("/span-config/{containerName}")
-     *
-     * @GET
-     *
-     * @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
-     *
-     * @StatusCodes( {
-     *
-     * @ResponseCode(code = 200, condition = "Operation successful"),
-     *
-     * @ResponseCode(code = 404, condition = "The containerName is not found"),
-     *
-     * @ResponseCode(code = 503, condition =
-     * "One or more of Controller Services are unavailable") }) public
-     * List<SpanConfig> getSpanConfigList(@PathParam("containerName") String
-     * containerName) { ISwitchManager switchManager = (ISwitchManager)
-     * getIfSwitchManagerService(containerName); if (switchManager == null) {
-     * throw new ServiceUnavailableException("Switch Manager " +
-     * RestMessages.SERVICEUNAVAILABLE.toString()); }
-     *
-     * return switchManager.getSpanConfigList(); }
-     *//**
-     * Add a span configuration
-     *
-     * @param containerName
-     *            Name of the Container
-     * @param config
-     *            {@link org.opendaylight.controller.switchmanager.SpanConfig}
-     *            in JSON or XML format
-     * @return Response as dictated by the HTTP Response Status code
-     */
-    /*
-     * @Path("/span-config/{containerName}")
-     *
-     * @PUT
-     *
-     * @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
-     *
-     * @StatusCodes( {
-     *
-     * @ResponseCode(code = 200, condition = "Operation successful"),
-     *
-     * @ResponseCode(code = 404, condition = "The containerName is not found"),
-     *
-     * @ResponseCode(code = 503, condition =
-     * "One or more of Controller Services are unavailable") }) public Response
-     * addSpanConfig(@PathParam("containerName") String containerName,
-     *
-     * @TypeHint(SubnetConfig.class) JAXBElement<SpanConfig> config) {
-     * ISwitchManager switchManager = (ISwitchManager)
-     * getIfSwitchManagerService(containerName); if (switchManager == null) {
-     * throw new ServiceUnavailableException("Switch Manager " +
-     * RestMessages.SERVICEUNAVAILABLE.toString()); }
-     *
-     * String ret = switchManager.addSpanConfig(config.getValue()); if
-     * (ret.equals(ReturnString.SUCCESS.toString())) { return
-     * Response.status(Response.Status.CREATED).build(); } throw new
-     * InternalServerErrorException(ret); }
-     *//**
-     * Delete a span configuration
+    /**
+     * Save the current switch configurations
      *
      * @param containerName
-     *            Name of the Container
-     * @param config
-     *            {@link org.opendaylight.controller.switchmanager.SpanConfig}
-     *            in JSON or XML format
+     *            Name of the Container (Eg. 'default')
      * @return Response as dictated by the HTTP Response Status code
-     */
-    /*
-     * @Path("/span-config/{containerName}")
-     *
-     * @DELETE
-     *
-     * @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
      *
-     * @StatusCodes( {
-     *
-     * @ResponseCode(code = 200, condition = "Operation successful"),
-     *
-     * @ResponseCode(code = 404, condition = "The containerName is not found"),
+     * <pre>
      *
-     * @ResponseCode(code = 503, condition =
-     * "One or more of Controller Services are unavailable") }) public Response
-     * deleteSpanConfig(@PathParam("containerName") String containerName,
+     * Example:
      *
-     * @TypeHint(SubnetConfig.class) JAXBElement<SpanConfig> config) {
-     * ISwitchManager switchManager = (ISwitchManager)
-     * getIfSwitchManagerService(containerName); if (switchManager == null) {
-     * throw new ServiceUnavailableException("Switch Manager " +
-     * RestMessages.SERVICEUNAVAILABLE.toString()); }
+     * RequestURL:
+     * http://.../default/switch-config
      *
-     * String ret = switchManager.removeSpanConfig(config.getValue()); if
-     * (ret.equals(ReturnString.SUCCESS.toString())) { return
-     * Response.ok().build(); } throw new ResourceNotFoundException(ret); }
-     */
-
-    /**
-     * Save the current switch configurations
-     *
-     * @param containerName
-     *            Name of the Container
-     * @return Response as dictated by the HTTP Response Status code
+     * </pre>
      */
     @Path("/{containerName}/switch-config")
     @POST
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @StatusCodes({
             @ResponseCode(code = 200, condition = "Operation successful"),
+            @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
             @ResponseCode(code = 404, condition = "The containerName is not found"),
+            @ResponseCode(code = 500, condition = "Failed to save switch configuration. Failure Reason included in HTTP Error response"),
             @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
     public Response saveSwitchConfig(
             @PathParam("containerName") String containerName) {
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", "");
index 9444360eecb7d7ae4a6e084a4fde8512c7b5ed71..a96d3efcec698d80a243969fd2c1c3df9ee1e2cf 100644 (file)
@@ -8,6 +8,7 @@
 
 package org.opendaylight.controller.flows.web;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -77,13 +78,16 @@ public class Flows implements IDaylightWeb {
 
     @RequestMapping(value = "/main")
     @ResponseBody
-    public Map<String, Object> getFlows(HttpServletRequest request, @RequestParam(required = false) String container) {
-        String containerName = (container == null) ? GlobalConstants.DEFAULT.toString() : container;
+    public Map<String, Object> getFlows(HttpServletRequest request,
+            @RequestParam(required = false) String container) {
+        String containerName = (container == null) ? GlobalConstants.DEFAULT
+                .toString() : container;
 
         // Derive the privilege this user has on the current container
         String userName = request.getUserPrincipal().getName();
-        Privilege privilege = DaylightWebUtil.getContainerPrivilege(userName, containerName, this);
-        if (privilege  == Privilege.NONE) {
+        Privilege privilege = DaylightWebUtil.getContainerPrivilege(userName,
+                containerName, this);
+        if (privilege == Privilege.NONE) {
             return null;
         }
 
@@ -114,7 +118,7 @@ public class Flows implements IDaylightWeb {
             flowSet.add(entry);
         }
 
-        Map <String, Object> output = new HashMap<String, Object>(2);
+        Map<String, Object> output = new HashMap<String, Object>(2);
         output.put("flows", flowSet);
         output.put("privilege", privilege);
         return output;
@@ -122,12 +126,15 @@ public class Flows implements IDaylightWeb {
 
     @RequestMapping(value = "/node-ports")
     @ResponseBody
-    public Map<String, Object> getNodePorts(HttpServletRequest request, @RequestParam(required = false) String container) {
-        String containerName = (container == null) ? GlobalConstants.DEFAULT.toString() : container;
+    public Map<String, Object> getNodePorts(HttpServletRequest request,
+            @RequestParam(required = false) String container) {
+        String containerName = (container == null) ? GlobalConstants.DEFAULT
+                .toString() : container;
 
         // Derive the privilege this user has on the current container
         String userName = request.getUserPrincipal().getName();
-        if (DaylightWebUtil.getContainerPrivilege(userName, containerName, this) == Privilege.NONE) {
+        if (DaylightWebUtil
+                .getContainerPrivilege(userName, containerName, this) == Privilege.NONE) {
             return null;
         }
 
@@ -171,12 +178,15 @@ public class Flows implements IDaylightWeb {
 
     @RequestMapping(value = "/node-flows")
     @ResponseBody
-    public Map<String, Object> getNodeFlows(HttpServletRequest request, @RequestParam(required = false) String container) {
-        String containerName = (container == null) ? GlobalConstants.DEFAULT.toString() : container;
+    public Map<String, Object> getNodeFlows(HttpServletRequest request,
+            @RequestParam(required = false) String container) {
+        String containerName = (container == null) ? GlobalConstants.DEFAULT
+                .toString() : container;
 
         // Derive the privilege this user has on the current container
         String userName = request.getUserPrincipal().getName();
-        if (DaylightWebUtil.getContainerPrivilege(userName, containerName, this) == Privilege.NONE) {
+        if (DaylightWebUtil
+                .getContainerPrivilege(userName, containerName, this) == Privilege.NONE) {
             return null;
         }
 
@@ -199,10 +209,12 @@ public class Flows implements IDaylightWeb {
             List<FlowConfig> flows = frm.getStaticFlows(node);
 
             String nodeDesc = node.toString();
-            SwitchConfig config = switchManager.getSwitchConfig(node
-                    .toString());
-            if ((config != null) && (config.getProperty(Description.propertyName) != null)) {
-                nodeDesc = ((Description) config.getProperty(Description.propertyName)).getValue();
+            SwitchConfig config = switchManager
+                    .getSwitchConfig(node.toString());
+            if ((config != null)
+                    && (config.getProperty(Description.propertyName) != null)) {
+                nodeDesc = ((Description) config
+                        .getProperty(Description.propertyName)).getValue();
             }
 
             nodes.put(nodeDesc, flows.size());
@@ -216,12 +228,15 @@ public class Flows implements IDaylightWeb {
     public String actionFlow(@RequestParam(required = true) String action,
             @RequestParam(required = false) String body,
             @RequestParam(required = true) String nodeId,
-            HttpServletRequest request, @RequestParam(required = false) String container) {
-        String containerName = (container == null) ? GlobalConstants.DEFAULT.toString() : container;
+            HttpServletRequest request,
+            @RequestParam(required = false) String container) {
+        String containerName = (container == null) ? GlobalConstants.DEFAULT
+                .toString() : container;
 
         // Authorization check
         String userName = request.getUserPrincipal().getName();
-        if (DaylightWebUtil.getContainerPrivilege(userName, containerName, this) != Privilege.WRITE) {
+        if (DaylightWebUtil
+                .getContainerPrivilege(userName, containerName, this) != Privilege.WRITE) {
             return "Operation not authorized";
         }
 
@@ -238,7 +253,8 @@ public class Flows implements IDaylightWeb {
         Status result = new Status(StatusCode.BADREQUEST, "Invalid request");
         if (action.equals("add")) {
             result = frm.addStaticFlow(flow);
-            DaylightWebUtil.auditlog("Flow", userName, "added", flow.getName(), containerName);
+            DaylightWebUtil.auditlog("Flow", userName, "added", flow.getName(),
+                    containerName);
         }
 
         return (result.isSuccess()) ? StatusCode.SUCCESS.toString() : result
@@ -250,12 +266,15 @@ public class Flows implements IDaylightWeb {
     public String removeFlow(@PathVariable("nodeId") String nodeId,
             @PathVariable("name") String name,
             @RequestParam(required = true) String action,
-            HttpServletRequest request, @RequestParam(required = false) String container) {
-        String containerName = (container == null) ? GlobalConstants.DEFAULT.toString() : container;
+            HttpServletRequest request,
+            @RequestParam(required = false) String container) {
+        String containerName = (container == null) ? GlobalConstants.DEFAULT
+                .toString() : container;
 
         // Authorization check
         String userName = request.getUserPrincipal().getName();
-        if (DaylightWebUtil.getContainerPrivilege(userName, containerName, this) != Privilege.WRITE) {
+        if (DaylightWebUtil
+                .getContainerPrivilege(userName, containerName, this) != Privilege.WRITE) {
             return "Operation not authorized";
         }
 
@@ -272,13 +291,15 @@ public class Flows implements IDaylightWeb {
         }
         if (action.equals("remove")) {
             result = frm.removeStaticFlow(name, node);
-            if(result.isSuccess()) {
-                DaylightWebUtil.auditlog("Flow", userName, "removed", name, containerName);
+            if (result.isSuccess()) {
+                DaylightWebUtil.auditlog("Flow", userName, "removed", name,
+                        containerName);
             }
         } else if (action.equals("toggle")) {
             result = frm.toggleStaticFlowStatus(name, node);
-            if(result.isSuccess()) {
-                DaylightWebUtil.auditlog("Flow", userName, "toggled", name, containerName);
+            if (result.isSuccess()) {
+                DaylightWebUtil.auditlog("Flow", userName, "toggled", name,
+                        containerName);
             }
         } else {
             result = new Status(StatusCode.BADREQUEST, "Unknown action");
@@ -288,10 +309,58 @@ public class Flows implements IDaylightWeb {
                 .getDescription();
     }
 
+    @SuppressWarnings("unchecked")
+    @RequestMapping(value = "/flow/deleteFlows", method = RequestMethod.POST)
+    @ResponseBody
+    public String removeSelectedFlows(
+            @RequestParam(required = false) String body,
+            HttpServletRequest request,
+            @RequestParam(required = false) String container) {
+        String containerName = (container == null) ? GlobalConstants.DEFAULT
+                .toString() : container;
+
+        // Authorization check
+        String userName = request.getUserPrincipal().getName();
+        if (DaylightWebUtil
+                .getContainerPrivilege(userName, containerName, this) != Privilege.WRITE) {
+            return "Operation not authorized";
+        }
+
+        IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper
+                .getInstance(IForwardingRulesManager.class, containerName, this);
+        if (frm == null) {
+            return "Forwarding Rules Manager is not available";
+        }
+
+        Gson gson = new Gson();
+        List<Map<String, String>> flowList = new ArrayList<Map<String, String>>();
+        flowList = gson.fromJson(body, flowList.getClass());
+        Status result = new Status(StatusCode.BADREQUEST, "Invalid request");
+        String status = "";
+        for (Map<String, String> flowEntry : flowList) {
+            Node node = Node.fromString(flowEntry.get("node"));
+            result = frm.removeStaticFlow(flowEntry.get("name"), node);
+            if (result.isSuccess()) {
+                DaylightWebUtil.auditlog("Flow", userName, "removed",
+                        flowEntry.get("name"), containerName);
+            } else {
+                status = flowEntry.get("name") + ", " + status;
+            }
+        }
+        if (!status.equals("")) {
+            return "Could not remove "
+                    + status.substring(0, status.length() - 2) + " Flow(s)";
+        } else {
+            return "Success";
+        }
+    }
+
     private String getNodeDesc(Node node, ISwitchManager switchManager) {
-        Description desc = (Description) switchManager.getNodeProp(node, Description.propertyName);
+        Description desc = (Description) switchManager.getNodeProp(node,
+                Description.propertyName);
         String description = (desc == null) ? "" : desc.getValue();
-        return (description.isEmpty() || description.equalsIgnoreCase("none")) ? node.toString() : description;
+        return (description.isEmpty() || description.equalsIgnoreCase("none")) ? node
+                .toString() : description;
     }
 
 }
index 0f1eabb85929dde53cecacf939b69c4b1e651edb..3dcf9e3006914daa3824476bf25ec1667024d455 100644 (file)
@@ -48,7 +48,8 @@ one.f.address = {
         main : "/main",
         flows : "/node-flows",
         nodes : "/node-ports",
-        flow : "/flow"
+        flow : "/flow",
+        deleteFlows:"/flow/deleteFlows"
     }
 }
 
@@ -251,9 +252,11 @@ one.f.flows = {
     id : {
         dashlet : {
             add : "one_f_flows_id_dashlet_add",
+            removeMultiple : "one_f_flows_id_dashlet_removeMultiple",
             remove : "one_f_flows_id_dashlet_remove",
             toggle : "one_f_flows_id_dashlet_toggle",
-            datagrid: "one_f_flows_id_dashlet_datagrid"
+            datagrid : "one_f_flows_id_dashlet_datagrid",
+            selectAllFlows : "one_f_flows_id_dashlet_selectAllFlows"
         },
         modal : {
             install : "one_f_flows_id_modal_install",
@@ -327,6 +330,36 @@ one.f.flows = {
                     $modal.modal();
                 });
                 $dashlet.append($button);
+                var button = one.lib.dashlet.button.single("Remove Flow Entry", one.f.flows.id.dashlet.removeMultiple, "btn-primary", "btn-mini");
+                var $button = one.lib.dashlet.button.button(button);
+
+                $button.click(function() {
+                    var checkedCheckBoxes = $('.flowEntry[type=checkbox]:checked');
+                    
+                    var requestData = [];
+                    
+                    var resource = {};
+                    checkedCheckBoxes.each(function(index, value) {
+                        var flowEntry = {};
+                        flowEntry['name'] = checkedCheckBoxes[index].name;
+                        flowEntry['node'] = checkedCheckBoxes[index].getAttribute("node");
+                        requestData.push(flowEntry);  
+                    });
+                    resource['body'] = JSON.stringify(requestData);
+
+                    $.post(one.f.address.root+one.f.address.flows.deleteFlows, resource, function(response) {
+                        if(response == "Success") {
+                            one.lib.alert("Flow(s) successfully removed");                            
+                        } else {
+                            one.lib.alert(response);
+                        }
+                        one.main.dashlet.right.bottom.empty();
+                        one.f.detail.dashlet(one.main.dashlet.right.bottom);
+                        one.main.dashlet.left.top.empty();
+                        one.f.flows.dashlet(one.main.dashlet.left.top);
+                    });                    
+                });
+                $dashlet.append($button);
 
             }
 
@@ -339,7 +372,12 @@ one.f.flows = {
             $dashlet.append($gridHTML);
             var dataSource = one.f.flows.data.flowsDataGrid(data);
             $("#" + one.f.flows.id.dashlet.datagrid).datagrid({dataSource: dataSource}).on("loaded", function() {
-                $("#" + one.f.flows.id.dashlet.datagrid).find("tbody tr").each(function(index, tr) {
+                    $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).click(function() {
+                                $("#" + one.f.flows.id.dashlet.datagrid).find(':checkbox').prop('checked',
+                                        $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).is(':checked'));
+                    });
+                    
+                    $("#" + one.f.flows.id.dashlet.datagrid).find("tbody tr").each(function(index, tr) {
                     $tr = $(tr);
                     $span = $("td span", $tr);
                     var flowstatus = $span.data("flowstatus");
@@ -357,10 +395,22 @@ one.f.flows = {
                     });
                     // attach click event
                     $tr.click(function() {
-                        var $td = $($(this).find("td")[0]);
+                        var $td = $($(this).find("td")[1]);
                         var id = $td.text();
                         var node = $td.find("span").data("nodeid");
-                        one.f.flows.detail(id, node);   
+                        one.f.flows.detail(id, node);
+                    });
+                    $(".flowEntry").click(function(){
+                                if (!$('.flowEntry[type=checkbox]:not(:checked)').length) {
+                            $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
+                                .prop("checked",
+                              true);
+                        } else {
+                            $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
+                                .prop("checked",
+                             false);
+                        }
+                        event.stopPropagation();
                     });
                 });
             });
@@ -1193,6 +1243,11 @@ one.f.flows = {
         flowsDataGrid: function(data) {
             var source = new StaticDataSource({
                     columns: [
+                        {
+                            property: 'selector',
+                            label: "<input type='checkbox' id='"+one.f.flows.id.dashlet.datagrid.selectAllFlows+"'/>",
+                            sortable: false
+                        },
                         {
                             property: 'name',
                             label: 'Flow Name',
@@ -1207,7 +1262,13 @@ one.f.flows = {
                     data: data.flows,
                     formatter: function(items) {
                         $.each(items, function(index, item) {
-                            item["name"] = '<span data-installInHw=' + item["flow"]["installInHw"] + 
+                                    var $checkbox = document.createElement("input");
+                            $checkbox.setAttribute("type", "checkbox");
+                            $checkbox.setAttribute("name", item.name);
+                            $checkbox.setAttribute("node", item.node);
+                            $checkbox.setAttribute('class','flowEntry')
+                            item.selector = $checkbox.outerHTML;
+                                  item["name"] = '<span data-installInHw=' + item["flow"]["installInHw"] + 
                                 ' data-flowstatus=' + item["flow"]["status"] + 
                                 ' data-nodeId=' + item["nodeId"] + '>' + item["name"] + '</span>';
                         });
@@ -1222,6 +1283,8 @@ one.f.flows = {
             $(data).each(function(index, value) {
                 var tr = {};
                 var entry = [];
+
+                
                 entry.push(value['name']);
                 entry.push(value['node']);
                 if (value['flow']['installInHw'] == 'true' && value['flow']['status'] == 'Success')
@@ -1244,12 +1307,12 @@ one.f.flows = {
             var $table = one.lib.dashlet.table.table(attributes);
 
             var headers = ['Flow Name', 'Node'];
+                
             var $thead = one.lib.dashlet.table.header(headers);
             $table.append($thead);
 
             var $tbody = one.lib.dashlet.table.body(body);
             $table.append($tbody);
-
             return $table;
         }
     }
index 337d721696b8003713fe3805563c091760d1852b..71ba687f2d4b16e22c43de7586f6d75c7119b7ac 100644 (file)
@@ -208,7 +208,7 @@ public class Troubleshoot implements IDaylightWeb {
                 List<NodeConnectorStatistics> statistics = statisticsManager
                         .getNodeConnectorStatistics(node);
                 for (NodeConnectorStatistics stats : statistics) {
-                    cells.add(this.convertPortsStatistics(stats));
+                    cells.add(this.convertPortsStatistics(stats, containerName));
                 }
             }
         }
@@ -220,11 +220,19 @@ public class Troubleshoot implements IDaylightWeb {
     }
 
     private Map<String, String> convertPortsStatistics(
-            NodeConnectorStatistics ncStats) {
+            NodeConnectorStatistics ncStats, String containerName) {
         Map<String, String> row = new HashMap<String, String>();
 
+        ISwitchManager switchManager = (ISwitchManager) ServiceHelper
+                .getInstance(ISwitchManager.class, containerName, this);
+        NodeConnector nodeConnector = ncStats.getNodeConnector();
+        Description description = (Description) switchManager.getNodeProp(nodeConnector.getNode(), Description.propertyName);
+        String desc = (description == null) ? "" : description.getValue();
+        String nodeName = desc.equalsIgnoreCase("none") ? nodeConnector.getNode().getNodeIDString() : desc;
+        String nodeConnectorDisplayName = nodeConnector.getType() + "|" + nodeConnector.getID() + "@" + nodeName;
         row.put("nodeConnector",
-                String.valueOf(ncStats.getNodeConnector().toString()));
+                String.valueOf(nodeConnectorDisplayName));
+
         row.put("rxPkts", String.valueOf(ncStats.getReceivePacketCount()));
         row.put("txPkts", String.valueOf(ncStats.getTransmitPacketCount()));
         row.put("rxBytes", String.valueOf(ncStats.getReceiveByteCount()));