Northbound support for inserting table rows 81/2681/2
authorMadhu Venugopal <mavenugo@gmail.com>
Mon, 11 Nov 2013 00:23:23 +0000 (16:23 -0800)
committerMadhu Venugopal <mavenugo@gmail.com>
Wed, 13 Nov 2013 15:12:09 +0000 (07:12 -0800)
Change-Id: Ifc006389046028c814ebb4d2090d4b5882745bed
Signed-off-by: Madhu Venugopal <mavenugo@gmail.com>
northbound/ovsdb/enunciate.xml [new file with mode: 0644]
northbound/ovsdb/pom.xml [new file with mode: 0644]
northbound/ovsdb/src/main/java/org/opendaylight/ovsdb/northbound/OVSDBNorthbound.java [new file with mode: 0644]
northbound/ovsdb/src/main/java/org/opendaylight/ovsdb/northbound/OVSDBRow.java [new file with mode: 0644]
northbound/ovsdb/src/main/resources/WEB-INF/web.xml [new file with mode: 0644]
ovsdb/pom.xml
ovsdb/src/main/java/org/opendaylight/ovsdb/lib/table/internal/Table.java
ovsdb/src/main/java/org/opendaylight/ovsdb/plugin/Activator.java
ovsdb/src/main/java/org/opendaylight/ovsdb/plugin/ConfigurationService.java
ovsdb/src/main/java/org/opendaylight/ovsdb/plugin/OVSDBConfigService.java [new file with mode: 0644]

diff --git a/northbound/ovsdb/enunciate.xml b/northbound/ovsdb/enunciate.xml
new file mode 100644 (file)
index 0000000..e6faef2
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<enunciate label="full" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:noNamespaceSchemaLocation="http://enunciate.codehaus.org/schemas/enunciate-1.26.xsd">
+
+  <services>
+    <rest defaultRestSubcontext="/ovsdb/nb/v2"/>
+  </services>
+
+  <modules>
+    <docs docsDir="rest" title="OVSDB Table operations over REST" includeExampleXml="true" includeExampleJson="true"/>
+  </modules>
+</enunciate>
diff --git a/northbound/ovsdb/pom.xml b/northbound/ovsdb/pom.xml
new file mode 100644 (file)
index 0000000..6d4807f
--- /dev/null
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.ovsdb</groupId>
+    <artifactId>commons.ovsdb</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <relativePath>../../commons/parent/</relativePath>
+  </parent>
+  <scm>
+    <connection>scm:git:ssh://git.opendaylight.org:29418/ovsdb.git</connection>
+    <developerConnection>scm:git:ssh://git.opendaylight.org:29418/ovsdt.git</developerConnection>
+    <url>https://wiki.opendaylight.org/view/OVSDB_Integration:Main</url>
+    <tag>HEAD</tag>
+  </scm>
+
+  <artifactId>ovsdb.northbound</artifactId>
+  <version>0.5.0-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.enunciate</groupId>
+        <artifactId>maven-enunciate-plugin</artifactId>
+        <version>${enunciate.version}</version>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>${bundle.plugin.version}</version>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Export-Package>
+            </Export-Package>
+            <Import-Package>
+              org.opendaylight.controller.sal.utils,
+              org.opendaylight.controller.northbound.commons,
+              org.opendaylight.controller.northbound.commons.exception,
+              org.opendaylight.controller.northbound.commons.utils,
+              com.sun.jersey.spi.container.servlet,
+              org.opendaylight.controller.sal.core,
+              org.opendaylight.controller.sal.authorization,
+              org.opendaylight.ovsdb.lib.table,
+              org.opendaylight.ovsdb.lib.table.internal,
+              org.opendaylight.ovsdb.plugin,
+              javax.ws.rs,
+              javax.ws.rs.core,
+              javax.xml.bind,
+              javax.xml.bind.annotation,
+              org.slf4j,
+              org.apache.catalina.filters,
+              org.codehaus.jackson.jaxrs,
+              org.codehaus.jackson.annotate,
+              !org.codehaus.enunciate.jaxrs
+            </Import-Package>
+            <Export-Package>
+            </Export-Package>
+            <Web-ContextPath>/ovsdb/nb/v2</Web-ContextPath>
+            <Jaxrs-Resources>,${classes;ANNOTATION;javax.ws.rs.Path}</Jaxrs-Resources>
+          </instructions>
+          <manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal</artifactId>
+      <version>0.5.1-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.ovsdb</groupId>
+      <artifactId>ovsdb</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>commons.northbound</artifactId>
+      <version>0.4.1-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.enunciate</groupId>
+      <artifactId>enunciate-core-annotations</artifactId>
+      <version>${enunciate.version}</version>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/northbound/ovsdb/src/main/java/org/opendaylight/ovsdb/northbound/OVSDBNorthbound.java b/northbound/ovsdb/src/main/java/org/opendaylight/ovsdb/northbound/OVSDBNorthbound.java
new file mode 100644 (file)
index 0000000..aea65df
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * 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.ovsdb.northbound;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
+
+import org.codehaus.enunciate.jaxrs.ResponseCode;
+import org.codehaus.enunciate.jaxrs.StatusCodes;
+import org.codehaus.enunciate.jaxrs.TypeHint;
+import org.opendaylight.controller.northbound.commons.RestMessages;
+import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
+import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
+import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
+import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
+import org.opendaylight.controller.sal.authorization.Privilege;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.utils.ServiceHelper;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.ovsdb.plugin.OVSDBConfigService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class provides REST APIs to Create, Read, Update and Delete OVSDB Row in any of the ovsdb table
+ * database one at a time.
+ */
+
+@Path("/")
+public class OVSDBNorthbound {
+    protected static final Logger logger = LoggerFactory.getLogger(OVSDBNorthbound.class);
+
+    @Context
+    private UriInfo _uriInfo;
+    private String username;
+
+    @Context
+    public void setSecurityContext(SecurityContext context) {
+        if (context != null && context.getUserPrincipal() != null) {
+            username = context.getUserPrincipal().getName();
+        }
+    }
+
+    protected String getUserName() {
+        return username;
+    }
+
+    private void handleNameMismatch(String name, String nameinURL) {
+        if (name == null || nameinURL == null) {
+            throw new BadRequestException(RestMessages.INVALIDDATA.toString() + " : Name is null");
+        }
+
+        if (name.equalsIgnoreCase(nameinURL)) {
+            return;
+        }
+        throw new ResourceConflictException(RestMessages.INVALIDDATA.toString()
+                + " : Table Name in URL does not match the row name in request body");
+    }
+
+    /**
+     * Create a Row
+     *
+     * @param tableName name of the ovsdb table
+     * @param row the {@link OVSDBRow} Row that is being inserted
+     *
+     * @return Response as dictated by the HTTP Response Status code
+     *
+     *         <pre>
+     * Example:
+     *
+     * Request URL:
+     * https://localhost/controller/nb/v2/ovsdb/tables/bridge/rows
+     * </pre>
+     */
+
+    @Path("/node/{nodeType}/{nodeId}/tables/{tableName}/rows")
+    @POST
+    @StatusCodes({ @ResponseCode(code = 201, condition = "Row Inserted successfully"),
+        @ResponseCode(code = 400, condition = "Invalid data passed"),
+        @ResponseCode(code = 401, condition = "User not authorized to perform this operation")})
+    @Consumes({ MediaType.APPLICATION_JSON})
+    public Response addRow(@PathParam("nodeType") String nodeType, @PathParam("nodeId") String nodeId,
+                           @PathParam("tableName") String tableName, @TypeHint(OVSDBRow.class) OVSDBRow row) {
+
+        if (!NorthboundUtils.isAuthorized(getUserName(), "default", Privilege.WRITE, this)) {
+            throw new UnauthorizedException("User is not authorized to perform this operation");
+        }
+
+        OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class,
+                                                                                            this);
+        if (ovsdbTable == null) {
+            throw new ServiceUnavailableException("UserManager " + RestMessages.SERVICEUNAVAILABLE.toString());
+        }
+
+        if (row != null && row.getRow() != null) {
+            handleNameMismatch(tableName, row.getRow().getTableName().getName());
+            Node node = Node.fromString(nodeType, nodeId);
+            Status statusWithUUID = ovsdbTable.insertRow(node, tableName, row.getParent_uuid(), row.getRow());
+
+            if (statusWithUUID.isSuccess()) {
+                return Response.status(Response.Status.CREATED)
+                        .header("Location", String.format("%s/%s", _uriInfo.getAbsolutePath().toString(),
+                                                                    statusWithUUID.getDescription()))
+                        .entity(statusWithUUID.getDescription())
+                        .build();
+            } else {
+                return NorthboundUtils.getResponse(statusWithUUID);
+            }
+        }
+        return Response.status(Response.Status.BAD_REQUEST).build();
+    }
+
+    /**
+     * Update a Row
+     *
+     * @param node OVSDB Node identifier
+     * @param tableName name of the ovsdb table
+     * @param rowUuid UUID of the row being updated
+     * @param row the {@link OVSDBRow} Row that is being inserted
+     *
+     * @return Response as dictated by the HTTP Response Status code
+     *
+     * <pre>
+     * Example:
+     *
+     * Request URL:
+     * https://localhost/controller/nb/v2/ovsdb/tables/bridge/rows
+     * </pre>
+     */
+
+    @Path("/node/{nodeType}/{nodeId}/tables/{tableName}/rows/{rowUuid}")
+    @PUT
+    @StatusCodes({ @ResponseCode(code = 200, condition = "Row Updated successfully"),
+        @ResponseCode(code = 400, condition = "Invalid data passed"),
+        @ResponseCode(code = 401, condition = "User not authorized to perform this operation")})
+    @Consumes({ MediaType.APPLICATION_JSON})
+    public Response updateRow(@PathParam("nodeType") String nodeType, @PathParam("nodeId") String nodeId,
+                           @PathParam("tableName") String tableName, @PathParam("rowUuid") String rowUuid,
+                           @TypeHint(OVSDBRow.class) OVSDBRow row) {
+
+        if (!NorthboundUtils.isAuthorized(getUserName(), "default", Privilege.WRITE, this)) {
+            throw new UnauthorizedException("User is not authorized to perform this operation");
+        }
+
+        OVSDBConfigService ovsdbTable = (OVSDBConfigService)ServiceHelper.getGlobalInstance(OVSDBConfigService.class,
+                                                                                            this);
+        if (ovsdbTable == null) {
+            throw new ServiceUnavailableException("UserManager " + RestMessages.SERVICEUNAVAILABLE.toString());
+        }
+
+        if (row != null && row.getRow() != null) {
+            handleNameMismatch(tableName, row.getRow().getTableName().getName());
+            Node node = Node.fromString(nodeType, nodeId);
+            Status status = ovsdbTable.updateRow(node, rowUuid, row.getRow());
+            return NorthboundUtils.getResponse(status);
+        }
+        return Response.status(Response.Status.BAD_REQUEST).build();
+    }
+
+    /**
+     * Delete a row
+     *
+     * @param tableName name of the ovsdb table
+     * @param uuid UUID of the Row to be removed
+     * @return Response as dictated by the HTTP Response Status code
+     *
+     * <pre>
+     * Example:
+     *
+     * Request URL:
+     * https://localhost/controller/nb/v2/ovsdb/tables/bridge/rows/41ab15a9-0dab-4675-a579-63019d4bcbec
+     *
+     * </pre>
+     */
+    @Path("/node/{nodeType}/{nodeId}/tables/{tableName}/rows/{uuid}")
+    @DELETE
+    @StatusCodes({ @ResponseCode(code = 204, condition = "User Deleted Successfully"),
+        @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
+        @ResponseCode(code = 404, condition = "The userName passed was not found"),
+        @ResponseCode(code = 500, condition = "Internal Server Error : Removal of user failed"),
+        @ResponseCode(code = 503, condition = "Service unavailable") })
+    public Response removeRow(@PathParam("nodeType") String nodeType, @PathParam("nodeId") String nodeId,
+                              @PathParam("tableName") String tableName, @PathParam("uuid") String uuid) {
+        if (!NorthboundUtils.isAuthorized(getUserName(), "default", Privilege.WRITE, this)) {
+            throw new UnauthorizedException("User is not authorized to perform this operation");
+        }
+
+        OVSDBConfigService ovsdbTable = (OVSDBConfigService) ServiceHelper.getGlobalInstance(OVSDBConfigService.class, this);
+        if (ovsdbTable == null) {
+            throw new ServiceUnavailableException("UserManager " + RestMessages.SERVICEUNAVAILABLE.toString());
+        }
+        Node node = Node.fromString(nodeType, nodeId);
+        Status status = ovsdbTable.deleteRow(node, tableName, uuid);
+        if (status.isSuccess()) {
+            return Response.noContent().build();
+        }
+        return NorthboundUtils.getResponse(status);
+    }
+}
diff --git a/northbound/ovsdb/src/main/java/org/opendaylight/ovsdb/northbound/OVSDBRow.java b/northbound/ovsdb/src/main/java/org/opendaylight/ovsdb/northbound/OVSDBRow.java
new file mode 100644 (file)
index 0000000..24e849a
--- /dev/null
@@ -0,0 +1,68 @@
+package org.opendaylight.ovsdb.northbound;
+
+import org.codehaus.jackson.annotate.JsonSubTypes;
+import org.codehaus.jackson.annotate.JsonTypeInfo;
+import org.opendaylight.ovsdb.lib.table.Bridge;
+import org.opendaylight.ovsdb.lib.table.Capability;
+import org.opendaylight.ovsdb.lib.table.Controller;
+import org.opendaylight.ovsdb.lib.table.Interface;
+import org.opendaylight.ovsdb.lib.table.Manager;
+import org.opendaylight.ovsdb.lib.table.Mirror;
+import org.opendaylight.ovsdb.lib.table.NetFlow;
+import org.opendaylight.ovsdb.lib.table.Open_vSwitch;
+import org.opendaylight.ovsdb.lib.table.Port;
+import org.opendaylight.ovsdb.lib.table.Qos;
+import org.opendaylight.ovsdb.lib.table.Queue;
+import org.opendaylight.ovsdb.lib.table.SFlow;
+import org.opendaylight.ovsdb.lib.table.SSL;
+import org.opendaylight.ovsdb.lib.table.internal.Table;
+
+public class OVSDBRow {
+    String parent_uuid;
+    /*
+     * MINIMAL_CLASS Directive expects a leading "." character on the class name and is lame.
+     * Hence going with NAME directive even though it calls for SubTypes.
+     * Since we are using fixed table types for the Hydrogen release, this is acceptable.
+     * When we move towards Schema driven table definition, this is anyways not required.
+
+    @JsonTypeInfo(
+            use = JsonTypeInfo.Id.MINIMAL_CLASS,
+            include = JsonTypeInfo.As.PROPERTY,
+            property = "@class")
+    */
+
+    @JsonTypeInfo(
+            use = JsonTypeInfo.Id.NAME,
+            include = JsonTypeInfo.As.WRAPPER_OBJECT)
+    @JsonSubTypes({
+        @JsonSubTypes.Type(value=Bridge.class, name="Bridge"),
+        @JsonSubTypes.Type(value=Capability.class, name="Capbility"),
+        @JsonSubTypes.Type(value=Controller.class, name="Controller"),
+        @JsonSubTypes.Type(value=Interface.class, name="Interface"),
+        @JsonSubTypes.Type(value=Manager.class, name="Manager"),
+        @JsonSubTypes.Type(value=Mirror.class, name="Mirror"),
+        @JsonSubTypes.Type(value=NetFlow.class, name="NetFlow"),
+        @JsonSubTypes.Type(value=Open_vSwitch.class, name="Open_vSwitch"),
+        @JsonSubTypes.Type(value=Port.class, name="Port"),
+        @JsonSubTypes.Type(value=Qos.class, name="QoS"),
+        @JsonSubTypes.Type(value=Queue.class, name="Queue"),
+        @JsonSubTypes.Type(value=SFlow.class, name="sFlow"),
+        @JsonSubTypes.Type(value=SSL.class, name="SSL")
+        })
+    Table row;
+
+    public OVSDBRow() {
+    }
+    public String getParent_uuid() {
+        return parent_uuid;
+    }
+    public void setParent_uuid(String parent_uuid) {
+        this.parent_uuid = parent_uuid;
+    }
+    public Table getRow() {
+        return row;
+    }
+    public void setRow(Table row) {
+        this.row = row;
+    }
+}
diff --git a/northbound/ovsdb/src/main/resources/WEB-INF/web.xml b/northbound/ovsdb/src/main/resources/WEB-INF/web.xml
new file mode 100644 (file)
index 0000000..61f9014
--- /dev/null
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+        version="3.0">
+  <servlet>
+    <servlet-name>OVSDB</servlet-name>
+    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
+    <init-param>
+      <param-name>javax.ws.rs.Application</param-name>
+      <param-value> org.opendaylight.controller.northbound.commons.NorthboundApplication</param-value>
+    </init-param>
+    <init-param>
+        <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
+        <param-value>true</param-value>
+    </init-param>
+    <load-on-startup>1</load-on-startup>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>OVSDB</servlet-name>
+    <url-pattern>/*</url-pattern>
+  </servlet-mapping>
+        <filter>
+          <filter-name>CorsFilter</filter-name>
+          <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
+          <init-param>
+            <param-name>cors.allowed.origins</param-name>
+            <param-value>*</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.methods</param-name>
+            <param-value>GET,POST,HEAD,OPTIONS,PUT,DELETE</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.headers</param-name>
+            <param-value>Content-Type,X-Requested-With,accept,authorization, origin,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.exposed.headers</param-name>
+            <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.support.credentials</param-name>
+            <param-value>true</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.preflight.maxage</param-name>
+            <param-value>10</param-value>
+          </init-param>
+        </filter>
+        <filter-mapping>
+          <filter-name>CorsFilter</filter-name>
+          <url-pattern>/*</url-pattern>
+        </filter-mapping>
+
+        <security-constraint>
+          <web-resource-collection>
+            <web-resource-name>NB api</web-resource-name>
+            <url-pattern>/*</url-pattern>
+            <http-method>POST</http-method>
+            <http-method>GET</http-method>
+            <http-method>PUT</http-method>
+            <http-method>PATCH</http-method>
+            <http-method>DELETE</http-method>
+            <http-method>HEAD</http-method>
+          </web-resource-collection>
+          <auth-constraint>
+            <role-name>System-Admin</role-name>
+            <role-name>Network-Admin</role-name>
+          </auth-constraint>
+        </security-constraint>
+
+        <security-role>
+                <role-name>System-Admin</role-name>
+        </security-role>
+        <security-role>
+                <role-name>Network-Admin</role-name>
+        </security-role>
+
+        <login-config>
+                <auth-method>BASIC</auth-method>
+                <realm-name>opendaylight</realm-name>
+        </login-config>
+</web-app>
index 62566c2e814e96fb5601ec382cc3fb825ba1ff1f..0ce328da981de386c0791db2e2bfe312e66de016 100755 (executable)
@@ -7,6 +7,12 @@
         <version>1.0.0-SNAPSHOT</version>
         <relativePath>../commons/parent/</relativePath>
     </parent>
+    <scm>
+      <connection>scm:git:ssh://git.opendaylight.org:29418/ovsdb.git</connection>
+      <developerConnection>scm:git:ssh://git.opendaylight.org:29418/ovsdt.git</developerConnection>
+      <url>https://wiki.opendaylight.org/view/OVSDB_Integration:Main</url>
+      <tag>HEAD</tag>
+    </scm>
     <artifactId>ovsdb</artifactId>
     <version>0.5.0-SNAPSHOT</version>
     <packaging>bundle</packaging>
@@ -65,6 +71,9 @@
                         <Bundle-Activator>
                             org.opendaylight.ovsdb.plugin.Activator
                         </Bundle-Activator>
+                        <Export-Package>
+                            org.opendaylight.ovsdb.lib.table, org.opendaylight.ovsdb.lib.table.internal, org.opendaylight.ovsdb.plugin
+                        </Export-Package>
                     </instructions>
                     <manifestLocation>${project.basedir}/META-INF</manifestLocation>
                 </configuration>
index a3b885bf624b183e8be1ba52daf8c0872e5afe73..429f59a5e5f1d35ef2ffeb83fd68c0f7f1a3315b 100644 (file)
@@ -1,8 +1,8 @@
 package org.opendaylight.ovsdb.lib.table.internal;
 
 public abstract class Table<E extends Table> {
-
     public abstract Name<E> getTableName();
+    @Override
     public abstract String toString();
     public Column<E> getColumns() {
         return null;
index ebab6599cb1e4efd6cb2b38df330d82f3d587bd1..c8f81f67d8b7cf33f6af17b0736f2948c39b42a7 100755 (executable)
@@ -5,16 +5,16 @@ import java.util.Hashtable;
 
 import org.apache.felix.dm.Component;
 import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
-import org.opendaylight.controller.sal.networkconfig.bridgedomain.IPluginInBridgeDomainConfigService;
 import org.opendaylight.controller.sal.connection.IPluginInConnectionService;
 import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
-import org.opendaylight.controller.sal.utils.INodeConnectorFactory;
-import org.opendaylight.controller.sal.utils.INodeFactory;
 import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.core.NodeConnector;
 import org.opendaylight.controller.sal.inventory.IPluginInInventoryService;
 import org.opendaylight.controller.sal.inventory.IPluginOutInventoryService;
+import org.opendaylight.controller.sal.networkconfig.bridgedomain.IPluginInBridgeDomainConfigService;
 import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.INodeConnectorFactory;
+import org.opendaylight.controller.sal.utils.INodeFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -33,6 +33,7 @@ public class Activator extends ComponentActivatorAbstractBase {
      * Here it registers the node Type
      *
      */
+    @Override
     public void init() {
         Node.NodeIDType.registerIDType("OVS", String.class);
         NodeConnector.NodeConnectorIDType.registerIDType("OVS", String.class, "OVS");
@@ -43,15 +44,18 @@ public class Activator extends ComponentActivatorAbstractBase {
      * ComponentActivatorAbstractBase
      *
      */
+    @Override
     public void destroy() {
         Node.NodeIDType.unRegisterIDType("OVS");
         NodeConnector.NodeConnectorIDType.unRegisterIDType("OVS");
     }
+    @Override
     public Object[] getGlobalImplementations() {
         Object[] res = { ConnectionService.class, ConfigurationService.class, NodeFactory.class, NodeConnectorFactory.class, InventoryService.class };
         return res;
     }
 
+    @Override
     public void configureGlobalInstance(Component c, Object imp){
         if (imp.equals(ConfigurationService.class)) {
             // export the service to be used by SAL
@@ -59,7 +63,8 @@ public class Activator extends ComponentActivatorAbstractBase {
             // Set the protocolPluginType property which will be used
             // by SAL
             props.put(GlobalConstants.PROTOCOLPLUGINTYPE.toString(), "OVS");
-            c.setInterface(IPluginInBridgeDomainConfigService.class.getName(), props);
+            c.setInterface(new String[] { IPluginInBridgeDomainConfigService.class.getName(),
+                                          OVSDBConfigService.class.getName()}, props);
 
             c.add(createServiceDependency()
                     .setService(IConnectionServiceInternal.class)
index 34da1cc3ff140627fdefcca3b2451756975f5c39..5680e415bfdacb28c560b77e709fa457278ad9b6 100755 (executable)
@@ -4,10 +4,26 @@ import java.math.BigInteger;
 import java.net.InetAddress;
 import java.net.NetworkInterface;
 import java.net.SocketException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 
 import org.eclipse.osgi.framework.console.CommandInterpreter;
 import org.eclipse.osgi.framework.console.CommandProvider;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.sal.connection.ConnectionConstants;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.networkconfig.bridgedomain.ConfigConstants;
+import org.opendaylight.controller.sal.networkconfig.bridgedomain.IPluginInBridgeDomainConfigService;
+import org.opendaylight.controller.sal.utils.NetUtils;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
 import org.opendaylight.ovsdb.lib.database.OVSInstance;
 import org.opendaylight.ovsdb.lib.database.OvsdbType;
 import org.opendaylight.ovsdb.lib.message.TransactBuilder;
@@ -23,20 +39,19 @@ import org.opendaylight.ovsdb.lib.notation.OvsDBMap;
 import org.opendaylight.ovsdb.lib.notation.OvsDBSet;
 import org.opendaylight.ovsdb.lib.notation.UUID;
 import org.opendaylight.ovsdb.lib.table.Bridge;
+import org.opendaylight.ovsdb.lib.table.Capability;
 import org.opendaylight.ovsdb.lib.table.Controller;
 import org.opendaylight.ovsdb.lib.table.Interface;
+import org.opendaylight.ovsdb.lib.table.Manager;
+import org.opendaylight.ovsdb.lib.table.Mirror;
+import org.opendaylight.ovsdb.lib.table.NetFlow;
 import org.opendaylight.ovsdb.lib.table.Open_vSwitch;
 import org.opendaylight.ovsdb.lib.table.Port;
+import org.opendaylight.ovsdb.lib.table.Qos;
+import org.opendaylight.ovsdb.lib.table.Queue;
+import org.opendaylight.ovsdb.lib.table.SFlow;
+import org.opendaylight.ovsdb.lib.table.SSL;
 import org.opendaylight.ovsdb.lib.table.internal.Table;
-import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
-import org.opendaylight.controller.sal.connection.ConnectionConstants;
-import org.opendaylight.controller.sal.core.Node;
-import org.opendaylight.controller.sal.core.NodeConnector;
-import org.opendaylight.controller.sal.networkconfig.bridgedomain.ConfigConstants;
-import org.opendaylight.controller.sal.networkconfig.bridgedomain.IPluginInBridgeDomainConfigService;
-import org.opendaylight.controller.sal.utils.NetUtils;
-import org.opendaylight.controller.sal.utils.Status;
-import org.opendaylight.controller.sal.utils.StatusCode;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.FrameworkUtil;
 import org.slf4j.Logger;
@@ -44,7 +59,8 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
-public class ConfigurationService implements IPluginInBridgeDomainConfigService, CommandProvider
+public class ConfigurationService implements IPluginInBridgeDomainConfigService, OVSDBConfigService,
+                                             CommandProvider
 {
     private static final Logger logger = LoggerFactory
             .getLogger(ConfigurationService.class);
@@ -215,7 +231,11 @@ public class ConfigurationService implements IPluginInBridgeDomainConfigService,
 
             TransactBuilder transaction = new TransactBuilder();
             transaction.addOperations(new ArrayList<Operation>(
-                                      Arrays.asList(addSwitchRequest, addIntfRequest, addPortRequest, addBridgeRequest, updateCfgVerRequest)));
+                                      Arrays.asList(addSwitchRequest,
+                                                    addIntfRequest,
+                                                    addPortRequest,
+                                                    addBridgeRequest,
+                                                    updateCfgVerRequest)));
 
             ListenableFuture<List<OperationResult>> transResponse = connection.getRpc().transact(transaction);
             List<OperationResult> tr = transResponse.get();
@@ -798,6 +818,279 @@ public class ConfigurationService implements IPluginInBridgeDomainConfigService,
         return true;
     }
 
+    @Override
+    public Status insertRow(Node node, String tableName, String parent_uuid, Table<?> row) {
+        logger.error("tableName : {}, parent_uuid : {} Row : {}", tableName, parent_uuid, row.toString());
+        Status statusWithUUID = null;
+
+        // Schema based Table handling will help fix this static Table handling.
+
+        if (row.getTableName().getName().equalsIgnoreCase("Bridge")) {
+            statusWithUUID = insertBridgeRow(node, parent_uuid, (Bridge)row);
+        }
+        else if (row.getTableName().getName().equalsIgnoreCase("Capbility")) {
+            statusWithUUID = insertCapabilityRow(node, parent_uuid, (Capability)row);
+        }
+        else if (row.getTableName().getName().equalsIgnoreCase("Controller")) {
+            statusWithUUID = insertControllerRow(node, parent_uuid, (Controller)row);
+        }
+        else if (row.getTableName().getName().equalsIgnoreCase("Interface")) {
+            statusWithUUID = insertInterfaceRow(node, parent_uuid, (Interface)row);
+        }
+        else if (row.getTableName().getName().equalsIgnoreCase("Manager")) {
+            statusWithUUID = insertManagerRow(node, parent_uuid, (Manager)row);
+        }
+        else if (row.getTableName().getName().equalsIgnoreCase("Mirror")) {
+            statusWithUUID = insertMirrorRow(node, parent_uuid, (Mirror)row);
+        }
+        else if (row.getTableName().getName().equalsIgnoreCase("NetFlow")) {
+            statusWithUUID = insertNetFlowRow(node, parent_uuid, (NetFlow)row);
+        }
+        else if (row.getTableName().getName().equalsIgnoreCase("Open_vSwitch")) {
+            statusWithUUID = insertOpen_vSwitchRow(node, (Open_vSwitch)row);
+        }
+        else if (row.getTableName().getName().equalsIgnoreCase("Port")) {
+            statusWithUUID = insertPortRow(node, parent_uuid, (Port)row);
+        }
+        else if (row.getTableName().getName().equalsIgnoreCase("QoS")) {
+            statusWithUUID = insertQosRow(node, parent_uuid, (Qos)row);
+        }
+        else if (row.getTableName().getName().equalsIgnoreCase("Queue")) {
+            statusWithUUID = insertQueueRow(node, parent_uuid, (Queue)row);
+        }
+        else if (row.getTableName().getName().equalsIgnoreCase("sFlow")) {
+            statusWithUUID = insertSflowRow(node, parent_uuid, (SFlow)row);
+        }
+        else if (row.getTableName().getName().equalsIgnoreCase("SSL")) {
+            statusWithUUID = insertSSLRow(node, parent_uuid, (SSL)row);
+        }
+        return statusWithUUID;
+    }
+
+    private Status insertBridgeRow(Node node, String open_VSwitch_uuid, Bridge bridgeRow) {
+        try{
+            if (connectionService == null) {
+                logger.error("Couldn't refer to the ConnectionService");
+                return new Status(StatusCode.NOSERVICE);
+            }
+
+            Connection connection = this.getConnection(node);
+            if (connection == null) {
+                return new Status(StatusCode.NOSERVICE, "Connection to ovsdb-server not available");
+            }
+
+            Map<String, Table<?>> ovsTable = inventoryServiceInternal.getTableCache(node, Open_vSwitch.NAME.getName());
+
+            if (ovsTable == null) {
+                return new Status(StatusCode.NOTFOUND, "There are no Open_vSwitch instance in the Open_vSwitch table");
+            }
+
+            String newBridge = "new_bridge";
+
+            Operation addSwitchRequest = null;
+
+            String ovsTableUUID = open_VSwitch_uuid;
+            if (ovsTableUUID == null) ovsTableUUID = (String) ovsTable.keySet().toArray()[0];
+            UUID bridgeUuidPair = new UUID(newBridge);
+            Mutation bm = new Mutation("bridges", Mutator.INSERT, bridgeUuidPair);
+            List<Mutation> mutations = new ArrayList<Mutation>();
+            mutations.add(bm);
+
+            UUID uuid = new UUID(ovsTableUUID);
+            Condition condition = new Condition("_uuid", Function.EQUALS, uuid);
+            List<Condition> where = new ArrayList<Condition>();
+            where.add(condition);
+            addSwitchRequest = new MutateOperation(Open_vSwitch.NAME.getName(), where, mutations);
+
+            InsertOperation addBridgeRequest = new InsertOperation(Bridge.NAME.getName(), newBridge, bridgeRow);
+
+            TransactBuilder transaction = new TransactBuilder();
+            transaction.addOperations(new ArrayList<Operation>(
+                                      Arrays.asList(addSwitchRequest,
+                                                    addBridgeRequest)));
+
+            int bridgeInsertIndex = transaction.getRequests().indexOf(addBridgeRequest);
+
+            ListenableFuture<List<OperationResult>> transResponse = connection.getRpc().transact(transaction);
+            List<OperationResult> tr = transResponse.get();
+            List<Operation> requests = transaction.getRequests();
+            Status status = new Status(StatusCode.SUCCESS);
+            for (int i = 0; i < tr.size() ; i++) {
+                if (i < requests.size()) requests.get(i).setResult(tr.get(i));
+                if (tr.get(i) != null && tr.get(i).getError() != null && tr.get(i).getError().trim().length() > 0) {
+                    OperationResult result = tr.get(i);
+                    status = new Status(StatusCode.BADREQUEST, result.getError() + " : " + result.getDetails());
+                }
+            }
+
+            if (tr.size() > requests.size()) {
+                OperationResult result = tr.get(tr.size()-1);
+                logger.error("Error creating Bridge : {}\n Error : {}\n Details : {}", bridgeRow.getName(),
+                                                                                       result.getError(),
+                                                                                       result.getDetails());
+                status = new Status(StatusCode.BADREQUEST, result.getError() + " : " + result.getDetails());
+            }
+            if (status.isSuccess()) {
+                UUID bridgeUUID = tr.get(bridgeInsertIndex).getUuid();
+                status = new Status(StatusCode.SUCCESS, bridgeUUID.toString());
+            }
+            return status;
+        } catch(Exception e){
+            e.printStackTrace();
+        }
+        return new Status(StatusCode.INTERNALERROR);
+    }
+
+    private Status insertPortRow(Node node, String bridge_uuid, Port portRow) {
+        try{
+            if (connectionService == null) {
+                logger.error("Couldn't refer to the ConnectionService");
+                return new Status(StatusCode.NOSERVICE);
+            }
+            Connection connection = this.getConnection(node);
+            if (connection == null) {
+                return new Status(StatusCode.NOSERVICE, "Connection to ovsdb-server not available");
+            }
+
+            Map<String, Table<?>> brTable = inventoryServiceInternal.getTableCache(node, Bridge.NAME.getName());
+            if (brTable == null ||  brTable.get(bridge_uuid) == null) {
+                return new Status(StatusCode.NOTFOUND, "Bridge with UUID "+bridge_uuid+" Not found");
+            }
+            String newPort = "new_port";
+            UUID portUUID = new UUID(newPort);
+            Mutation bm = new Mutation("ports", Mutator.INSERT, portUUID);
+            List<Mutation> mutations = new ArrayList<Mutation>();
+            mutations.add(bm);
+
+            UUID uuid = new UUID(bridge_uuid);
+            Condition condition = new Condition("_uuid", Function.EQUALS, uuid);
+            List<Condition> where = new ArrayList<Condition>();
+            where.add(condition);
+            Operation addBrMutRequest = new MutateOperation(Bridge.NAME.getName(), where, mutations);
+
+            // Default OVS schema is to have 1 or more interface part of Bridge. Hence it is mandatory to
+            // Insert an Interface in a Port add case :-(.
+
+            String newInterface = "new_interface";
+            Interface interfaceRow = new Interface();
+            interfaceRow.setName(portRow.getName());
+            InsertOperation addIntfRequest = new InsertOperation(Interface.NAME.getName(),
+                    newInterface, interfaceRow);
+
+            OvsDBSet<UUID> interfaces = new OvsDBSet<UUID>();
+            UUID interfaceid = new UUID(newInterface);
+            interfaces.add(interfaceid);
+            portRow.setInterfaces(interfaces);
+
+            InsertOperation addPortRequest = new InsertOperation(Port.NAME.getName(), newPort, portRow);
+
+            TransactBuilder transaction = new TransactBuilder();
+            transaction.addOperations(new ArrayList<Operation>
+            (Arrays.asList(addBrMutRequest, addPortRequest, addIntfRequest)));
+            int portInsertIndex = transaction.getRequests().indexOf(addPortRequest);
+            ListenableFuture<List<OperationResult>> transResponse = connection.getRpc().transact(transaction);
+            List<OperationResult> tr = transResponse.get();
+            List<Operation> requests = transaction.getRequests();
+            Status status = new Status(StatusCode.SUCCESS);
+            for (int i = 0; i < tr.size() ; i++) {
+                if (i < requests.size()) requests.get(i).setResult(tr.get(i));
+                if (tr.get(i) != null && tr.get(i).getError() != null && tr.get(i).getError().trim().length() > 0) {
+                    OperationResult result = tr.get(i);
+                    status = new Status(StatusCode.BADREQUEST, result.getError() + " : " + result.getDetails());
+                }
+            }
+
+            if (tr.size() > requests.size()) {
+                OperationResult result = tr.get(tr.size()-1);
+                logger.error("Error creating port : {}\n Error : {}\n Details : {}", portRow.getName(),
+                        result.getError(),
+                        result.getDetails());
+                status = new Status(StatusCode.BADREQUEST, result.getError() + " : " + result.getDetails());
+            }
+            if (status.isSuccess()) {
+                uuid = tr.get(portInsertIndex).getUuid();
+                status = new Status(StatusCode.SUCCESS, uuid.toString());
+            }
+
+            return status;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return new Status(StatusCode.INTERNALERROR);
+    }
+
+    private Status insertInterfaceRow(Node node, String port_uuid, Interface row) {
+        return new Status(StatusCode.NOTIMPLEMENTED, "Insert operation for this Table is not implemented yet.");
+    }
+
+    private Status insertOpen_vSwitchRow(Node node, Open_vSwitch row) {
+        return new Status(StatusCode.NOTIMPLEMENTED, "Insert operation for this Table is not implemented yet.");
+    }
+
+    private Status insertControllerRow(Node node, String bridge_uuid, Controller row) {
+        return new Status(StatusCode.NOTIMPLEMENTED, "Insert operation for this Table is not implemented yet.");
+    }
+
+    private Status insertSSLRow(Node node, String parent_uuid, SSL row) {
+        return new Status(StatusCode.NOTIMPLEMENTED, "Insert operation for this Table is not implemented yet.");
+    }
+
+    private Status insertSflowRow(Node node, String parent_uuid, SFlow row) {
+        return new Status(StatusCode.NOTIMPLEMENTED, "Insert operation for this Table is not implemented yet.");
+    }
+
+    private Status insertQueueRow(Node node, String parent_uuid, Queue row) {
+        return new Status(StatusCode.NOTIMPLEMENTED, "Insert operation for this Table is not implemented yet.");
+    }
+
+    private Status insertQosRow(Node node, String parent_uuid, Qos row) {
+        return new Status(StatusCode.NOTIMPLEMENTED, "Insert operation for this Table is not implemented yet.");
+    }
+
+    private Status insertNetFlowRow(Node node, String parent_uuid, NetFlow row) {
+        return new Status(StatusCode.NOTIMPLEMENTED, "Insert operation for this Table is not implemented yet.");
+    }
+
+    private Status insertMirrorRow(Node node, String parent_uuid, Mirror row) {
+        return new Status(StatusCode.NOTIMPLEMENTED, "Insert operation for this Table is not implemented yet.");
+    }
+
+    private Status insertManagerRow(Node node, String parent_uuid, Manager row) {
+        return new Status(StatusCode.NOTIMPLEMENTED, "Insert operation for this Table is not implemented yet.");
+    }
+
+    private Status insertCapabilityRow(Node node, String parent_uuid, Capability row) {
+        return new Status(StatusCode.NOTIMPLEMENTED, "Insert operation for this Table is not implemented yet.");
+    }
+
+    @Override
+    public Status updateRow(Node node, String rowUUID, Table<?> row) {
+        return new Status(StatusCode.NOTIMPLEMENTED, "Update Row functionality is not implemented yet.");
+    }
+
+    @Override
+    public Status deleteRow(Node node, String tableName, String uuid) {
+        return new Status(StatusCode.NOTIMPLEMENTED, "Delete Row functionality is not implemented yet.");
+    }
+
+    @Override
+    public List<Table<?>> getRows(Node node, String tableName) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Table<?> getRow(Node node, String tableName, String uuid) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public List<String> getTables(Node node) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
     public void _ovsconnect (CommandInterpreter ci) {
         String bridgeName = ci.nextArgument();
         if (bridgeName == null) {
diff --git a/ovsdb/src/main/java/org/opendaylight/ovsdb/plugin/OVSDBConfigService.java b/ovsdb/src/main/java/org/opendaylight/ovsdb/plugin/OVSDBConfigService.java
new file mode 100644 (file)
index 0000000..e2e9c37
--- /dev/null
@@ -0,0 +1,16 @@
+package org.opendaylight.ovsdb.plugin;
+
+import java.util.List;
+
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.ovsdb.lib.table.internal.Table;
+
+public interface OVSDBConfigService {
+    public Status insertRow (Node node, String tableName, String parentUUID, Table<?> row);
+    public Status deleteRow (Node node, String tableName, String uuid);
+    public Status updateRow (Node node, String rowUUID, Table<?> row);
+    public Table<?> getRow(Node node, String tableName, String uuid);
+    public List<Table<?>> getRows(Node node, String tableName);
+    public List<String> getTables(Node node);
+}