Adds a REST interface application to ovsdb

Change-Id: Ife8adac92ed8dc3fda6ffd97bdd783ecc3af9f35
diff --git a/ovsdb-rest/README.md b/ovsdb-rest/README.md
new file mode 100644
index 0000000..393e63d
--- /dev/null
+++ b/ovsdb-rest/README.md
@@ -0,0 +1,87 @@
+# OVSDBREST ONOS APPLICATION
+
+This application provides a ***minimal*** interface to an ovsdb device by exposing REST APIs.
+The API allows to create/delete a bridge, attach/remove ports from an existing bridge, create peer patch and setup GRE tunnels.
+
+## Install
+To install the application on a running onos instance run the following steps.
+
+- first of all, if it is not ready installed, you need to install the ovsdb driver provided by onos. On your onos root directory run:
+
+        cd drivers/ovsdb/
+        onos-app {onos-address} reinstall target/onos-drivers-ovsdb-1.7.0-SNAPSHOT.oar
+
+- then build the source code of the ovsdbrest application through maven:
+
+        git clone https://github.com/netgroup-polito/onos-applications
+        cd onos-applications/ovsdbrest
+        mvn clean install
+
+- Finally you can install the application through the command:
+
+        onos-app {onos-address} reinstall target/onos-app-ovsdbrest-1.7.0-SNAPSHOT.oar
+
+(onos-address is the ip-address of onos server, for example 192.168.123.1)
+
+
+## Activate
+After installing the application, you can activate it through the onos cli by typing:
+
+        app activate org.onosproject.ovsdbrest
+
+To check that the app has been activated type log:tail from the onos cli.
+
+
+## Configure
+After activating the application you need to configure the ovsdb node IP. This is done by using the onos Network Configuration system.
+
+- Send a REST request as follows:
+
+    **POST http://{onos-address}:8181/onos/v1/network/configuration/**
+
+    ```json
+    {
+    	"apps": {
+    		"org.onosproject.ovsdbrest": {
+    			"ovsdbrest": {
+    				"nodes": [
+    					{
+    						"ovsdbIp": "192.168.123.2",
+    						"ovsdbPort": "6632"
+    					}
+    				]
+    			}
+    		}
+    	}
+    }
+  ```
+
+Check your ovsdb configuration to get the correct ip and port for the ovsdb node.
+The request uses basic HTTP authentication, so you need to provide onos username and password.
+To verify that the configuration has been correctly pushed you can type log:tail from the onos cli.
+The app will start contacting the ovsdb nodes and you should see some related logs from the onos cli.
+
+
+## API
+
+- Create/Delete bridge:
+
+    **POST http://{onos-address}:8181/onos/ovsdb/{ovsdb-ip}/bridge/{bridge-name}**
+
+    **DELETE http://{onos-address}:8181/onos/ovsdb/{ovsdb-ip}/bridge/{bridge-name}**
+
+- Add/Remove a port in a bridge:
+
+    **POST http://{onos-address}:8181/onos/ovsdb/{ovsdb-ip}/bridge/{bridge-name}/port/{port-name}**
+
+    **DELETE http://{onos-address}:8181/onos/ovsdb/{ovsdb-ip}/bridge/{bridge-name}/port/{port-name}**
+
+- Create patch port:
+
+    **POST http://{onos-address}:8181/onos/ovsdb/{ovsdb-ip}/bridge/{bridge-name}/port/{port-name}/patch_peer/{peer-port}**
+
+- Create/Delete a GRE tunnel:
+
+    **POST http://{onos-address}:8181/onos/ovsdb/{ovsdb-ip}/bridge/{bridge-name}/port/{port-name}/gre/{local-ip}/{remote-ip}/{key}**
+
+    **DELETE http://{onos-address}:8181/onos/ovsdb/{ovsdb-ip}/bridge/{bridge-name}/port/{port-name}/gre**
diff --git a/ovsdb-rest/pom.xml b/ovsdb-rest/pom.xml
new file mode 100644
index 0000000..20ee632
--- /dev/null
+++ b/ovsdb-rest/pom.xml
@@ -0,0 +1,139 @@
+<?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/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-dependencies</artifactId>
+        <version>1.9.0-rc2</version>
+        <relativePath/><!-- parent is remote -->
+    </parent>
+
+    <artifactId>ovsdb-rest</artifactId>
+    <version>1.9.0-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <description>REST apis for bridge and GRE port setup in ovs</description>
+
+    <properties>
+        <onos.app.name>org.onosproject.ovsdbrest</onos.app.name>
+        <web.context>/onos/ovsdb</web.context>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <onos.app.url>http://onosproject.org</onos.app.url>
+        <onos.app.title>REST apis for bridge and GRE port setup in ovs</onos.app.title>
+        <onos.app.requires>
+            org.onosproject.ovsdb-base,
+            org.onosproject.drivers.ovsdb
+        </onos.app.requires>
+        <onos.version>1.9.0-rc2</onos.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-core-serializers</artifactId>
+            <version>${onos.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-core-common</artifactId>
+            <version>${onos.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-misc</artifactId>
+            <version>${onos.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+            <version>${onos.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-ovsdb-rfc</artifactId>
+            <version>1.6.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-ovsdb-api</artifactId>
+            <version>1.6.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <version>2.0.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-osgi</artifactId>
+            <version>${onos.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-rest</artifactId>
+            <version>${onos.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <_wab>src/main/webapp/</_wab>
+                        <Bundle-SymbolicName>
+                            ${project.groupId}.${project.artifactId}
+                        </Bundle-SymbolicName>
+                        <Import-Package>
+                            *,org.glassfish.jersey.servlet
+                        </Import-Package>
+                        <Web-ContextPath>${web.context}</Web-ContextPath>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <repositories>
+        <repository>
+            <id>ovsdb-api</id>
+            <url>https://oss.sonatype.org/content/repositories/snapshots/org/onosproject/onos-ovsdb-api/1.8.0-SNAPSHOT/</url>
+        </repository>
+        <repository>
+            <id>ovsdb-rfc</id>
+            <url>https://oss.sonatype.org/content/repositories/snapshots/org/onosproject/onos-ovsdb-rfc/1.8.0-SNAPSHOT/</url>
+        </repository>
+    </repositories>
+</project>
diff --git a/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/ConnectionHandler.java b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/ConnectionHandler.java
new file mode 100644
index 0000000..e00eb28
--- /dev/null
+++ b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/ConnectionHandler.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.ovsdbrest;
+
+/**
+ * Entity capable of handling a subject connected and disconnected situation.
+ */
+public interface ConnectionHandler<T> {
+
+    /**
+     * Processes the connected subject.
+     *
+     * @param subject subject
+     */
+    void connected(T subject);
+
+    /**
+     * Processes the disconnected subject.
+     *
+     * @param subject subject.
+     */
+    void disconnected(T subject);
+}
+
diff --git a/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/OvsdbBridgeManager.java b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/OvsdbBridgeManager.java
new file mode 100644
index 0000000..85eef31
--- /dev/null
+++ b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/OvsdbBridgeManager.java
@@ -0,0 +1,612 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.ovsdbrest;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Reference;
+import org.onlab.packet.IpAddress;
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.BridgeConfig;
+import org.onosproject.net.behaviour.BridgeDescription;
+import org.onosproject.net.behaviour.BridgeName;
+import org.onosproject.net.behaviour.ControllerConfig;
+import org.onosproject.net.behaviour.ControllerInfo;
+import org.onosproject.net.behaviour.DefaultBridgeDescription;
+import org.onosproject.net.behaviour.DefaultPatchDescription;
+import org.onosproject.net.behaviour.DefaultTunnelDescription;
+import org.onosproject.net.behaviour.InterfaceConfig;
+import org.onosproject.net.behaviour.PatchDescription;
+import org.onosproject.net.behaviour.TunnelDescription;
+import org.onosproject.net.behaviour.TunnelEndPoints;
+import org.onosproject.net.behaviour.TunnelKey;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.basics.SubjectFactories;
+import org.onosproject.net.device.DeviceAdminService;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.ovsdb.controller.OvsdbClientService;
+import org.onosproject.ovsdb.controller.OvsdbController;
+import org.onosproject.ovsdb.controller.OvsdbNodeId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.Optional;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.onosproject.ovsdbrest.OvsdbNodeConfig.OvsdbNode;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.ovsdbrest.OvsdbRestException.BridgeAlreadyExistsException;
+import static org.onosproject.ovsdbrest.OvsdbRestException.BridgeNotFoundException;
+import static org.onosproject.ovsdbrest.OvsdbRestException.OvsdbDeviceException;
+
+/**
+ * Bridge and port controller.
+ */
+@Component(immediate = true)
+@Service
+public class OvsdbBridgeManager implements OvsdbBridgeService {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private ApplicationId appId;
+    private static final int DPID_BEGIN = 4;
+    private static final int OFPORT = 6653;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ClusterService clusterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigRegistry configRegistry;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigService configService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OvsdbController controller;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceAdminService adminService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DriverService driverService;
+
+    private Set<OvsdbNode> ovsdbNodes;
+
+    // {bridgeName: datapathId} structure to manage the creation/deletion of bridges
+    private Map<String, DeviceId> bridgeIds = Maps.newConcurrentMap();
+
+    private Map<OvsdbNode, Set<DeviceId>> ovsdbNodeDevIdsSetMap = Maps.newConcurrentMap();
+
+    private final ExecutorService eventExecutor =
+            newSingleThreadExecutor(groupedThreads("onos/ovsdb-rest-ctl", "event-handler", log));
+    private final NetworkConfigListener configListener = new InternalConfigListener();
+    private final AtomicLong datapathId = new AtomicLong(DPID_BEGIN);
+
+
+    private final ConfigFactory configFactory =
+            new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, OvsdbNodeConfig.class, "ovsdbrest") {
+                @Override
+                public OvsdbNodeConfig createConfig() {
+                    return new OvsdbNodeConfig();
+                }
+            };
+
+    @Activate
+    protected void activate() {
+        appId = coreService.getAppId("org.onosproject.ovsdbrest");
+        configService.addListener(configListener);
+        configRegistry.registerConfigFactory(configFactory);
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        configService.removeListener(configListener);
+        configRegistry.unregisterConfigFactory(configFactory);
+        eventExecutor.shutdown();
+        log.info("Stopped");
+    }
+
+    @Override
+    public void createBridge(IpAddress ovsdbAddress, String bridgeName)
+            throws OvsdbDeviceException, BridgeAlreadyExistsException {
+
+        OvsdbNode ovsdbNode;
+        log.debug("Creating bridge {} at {}", bridgeName, ovsdbAddress);
+        try {
+            //  gets the target ovsdb node
+            ovsdbNode = ovsdbNodes.stream().filter(node -> node.ovsdbIp().equals(ovsdbAddress)).findFirst().get();
+        } catch (NoSuchElementException nsee) {
+            log.info(nsee.getMessage());
+            throw new OvsdbDeviceException(nsee.getMessage());
+        }
+
+        // construct a unique dev id'
+        DeviceId dpid = getNextUniqueDatapathId(datapathId);
+
+
+        if (isBridgeCreated(bridgeName)) {
+            log.warn("A bridge with this name already exists, aborting.");
+            throw new BridgeAlreadyExistsException();
+        }
+        List<ControllerInfo> controllers = new ArrayList<>();
+        Sets.newHashSet(clusterService.getNodes()).forEach(controller -> {
+            ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
+            controllers.add(ctrlInfo);
+            log.info("controller {}:{} added", ctrlInfo.ip().toString(), ctrlInfo.port());
+        });
+        try {
+            Device device = deviceService.getDevice(ovsdbNode.ovsdbId());
+            if (device == null) {
+                log.warn("Ovsdb device not found, aborting.");
+                throw new OvsdbDeviceException("Ovsdb device not found");
+            }
+            if (device.is(BridgeConfig.class)) {
+                BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
+                BridgeDescription bridgeDescription = DefaultBridgeDescription.builder()
+                        .name(bridgeName)
+                        .datapathId(dpid.toString())
+                        .controllers(controllers)
+                        .build();
+                bridgeConfig.addBridge(bridgeDescription);
+                bridgeIds.put(bridgeName, bridgeDescription.deviceId().get());
+                log.info("Correctly created bridge {} at {}", bridgeName, ovsdbAddress);
+            } else {
+                log.warn("The bridging behaviour is not supported in device {}", device.id());
+                throw new OvsdbDeviceException(
+                        "The bridging behaviour is not supported in device " + device.id()
+                );
+            }
+        } catch (ItemNotFoundException e) {
+            log.warn("Failed to create integration bridge on {}", ovsdbNode.ovsdbIp());
+            throw new OvsdbDeviceException("Error with ovsdb device: item not found");
+        }
+    }
+
+    @Override
+    public void deleteBridge(IpAddress ovsdbAddress, String bridgeName)
+            throws OvsdbDeviceException, BridgeNotFoundException {
+
+        OvsdbNode ovsdbNode;
+        log.debug("Deleting bridge {} at {}", bridgeName, ovsdbAddress);
+
+        try {
+            // gets the target ovsdb node
+            ovsdbNode = ovsdbNodes.stream().filter(node -> node.ovsdbIp().equals(ovsdbAddress)).findFirst().get();
+        } catch (NoSuchElementException nsee) {
+            log.warn(nsee.getMessage());
+            throw new OvsdbDeviceException(nsee.getMessage());
+        }
+
+        DeviceId deviceId = bridgeIds.get(bridgeName);
+        if (deviceId == null) {
+            log.warn("No bridge with this name, aborting.");
+            throw new BridgeNotFoundException();
+        }
+
+        log.debug("Device id is: " + deviceId.toString());
+
+        try {
+            Device device = deviceService.getDevice(ovsdbNode.ovsdbId());
+            if (device == null) {
+                log.warn("Ovsdb device not found, aborting.");
+                throw new OvsdbDeviceException("Ovsdb device not found");
+            }
+            if (device.is(BridgeConfig.class)) {
+
+                // unregister bridge from its controllers
+                deviceId = DeviceId.deviceId(deviceId.uri());
+                DriverHandler h = driverService.createHandler(deviceId);
+                ControllerConfig controllerConfig = h.behaviour(ControllerConfig.class);
+                controllerConfig.setControllers(new ArrayList<>());
+
+                // remove bridge from ovsdb
+                BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
+                bridgeConfig.deleteBridge(BridgeName.bridgeName(bridgeName));
+                bridgeIds.remove(bridgeName);
+
+                // remove bridge from onos devices
+                adminService.removeDevice(deviceId);
+
+                log.info("Correctly deleted bridge {} at {}", bridgeName, ovsdbAddress);
+            } else {
+                log.warn("The bridging behaviour is not supported in device {}", device.id());
+                throw new OvsdbDeviceException(
+                        "The bridging behaviour is not supported in device " + device.id()
+                );
+            }
+        } catch (ItemNotFoundException e) {
+            log.warn("Failed to delete bridge on {}", ovsdbNode.ovsdbIp());
+            throw new OvsdbDeviceException("Error with ovsdb device: item not found");
+        }
+    }
+
+    @Override
+    public void addPort(IpAddress ovsdbAddress, String bridgeName, String portName)
+            throws OvsdbDeviceException, BridgeNotFoundException {
+
+        OvsdbNode ovsdbNode;
+        log.debug("Adding port {} to bridge {} at {}", portName, bridgeName, ovsdbAddress);
+
+        try {
+            // gets the target ovsdb node
+            ovsdbNode = ovsdbNodes.stream().filter(node -> node.ovsdbIp().equals(ovsdbAddress)).findFirst().get();
+        } catch (NoSuchElementException nsee) {
+            log.warn(nsee.getMessage());
+            throw new OvsdbDeviceException(nsee.getMessage());
+        }
+
+        try {
+            Device device = deviceService.getDevice(ovsdbNode.ovsdbId());
+            log.debug("OvsdbNode.ovsdbId = " + ovsdbNode.ovsdbId());
+            if (device == null) {
+                log.warn("Ovsdb device not found, aborting.");
+                throw new OvsdbDeviceException("Ovsdb device not found");
+            }
+            if (device.is(BridgeConfig.class)) {
+                // add port to bridge through ovsdb
+                BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
+                bridgeConfig.addPort(BridgeName.bridgeName(bridgeName), portName);
+                log.info("Correctly added port {} to bridge {} at {}", portName, bridgeName, ovsdbAddress);
+            } else {
+                log.warn("The bridging behaviour is not supported in device {}", device.id());
+                throw new OvsdbDeviceException(
+                        "The bridging behaviour is not supported in device " + device.id()
+                );
+            }
+        } catch (ItemNotFoundException e) {
+            log.warn("Failed to delete bridge on {}", ovsdbNode.ovsdbIp());
+            throw new OvsdbDeviceException("Error with ovsdb device: item not found");
+        }
+    }
+
+    @Override
+    public void removePort(IpAddress ovsdbAddress, String bridgeName, String portName)
+            throws OvsdbDeviceException, BridgeNotFoundException {
+
+        OvsdbNode ovsdbNode;
+        log.debug("Deleting port {} to bridge {} at {}", portName, bridgeName, ovsdbAddress);
+
+        try {
+            // gets the target ovsdb node
+            ovsdbNode = ovsdbNodes.stream().filter(node -> node.ovsdbIp().equals(ovsdbAddress)).findFirst().get();
+
+        } catch (NoSuchElementException nsee) {
+            log.warn(nsee.getMessage());
+            throw new OvsdbDeviceException(nsee.getMessage());
+        }
+
+        try {
+            Device device = deviceService.getDevice(ovsdbNode.ovsdbId());
+            if (device == null) {
+                log.warn("Ovsdb device not found, aborting.");
+                throw new OvsdbDeviceException("Ovsdb device not found");
+            }
+            if (device.is(BridgeConfig.class)) {
+
+                // delete port from bridge through ovsdb
+                BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
+                bridgeConfig.deletePort(BridgeName.bridgeName(bridgeName), portName);
+
+                log.info("Correctly deleted port {} from bridge {} at {}", portName, bridgeName, ovsdbAddress);
+
+            } else {
+                log.warn("The bridging behaviour is not supported in device {}", device.id());
+                throw new OvsdbDeviceException(
+                        "The bridging behaviour is not supported in device " + device.id()
+                );
+            }
+        } catch (ItemNotFoundException e) {
+            log.warn("Failed to delete bridge on {}", ovsdbNode.ovsdbIp());
+            throw new OvsdbDeviceException("Error with ovsdb device: item not found");
+        }
+    }
+
+    @Override
+    public void createPatchPeerPort(IpAddress ovsdbAddress, String bridgeName, String portName, String patchPeer)
+            throws OvsdbDeviceException {
+
+        OvsdbNode ovsdbNode;
+        log.debug("Setting port {} as peer of port {}", portName, patchPeer);
+
+        try {
+            // gets the target ovsdb node
+            ovsdbNode = ovsdbNodes.stream().filter(node -> node.ovsdbIp().equals(ovsdbAddress)).findFirst().get();
+        } catch (NoSuchElementException nsee) {
+            log.warn(nsee.getMessage());
+            throw new OvsdbDeviceException(nsee.getMessage());
+        }
+
+        Device device = deviceService.getDevice(ovsdbNode.ovsdbId());
+        log.debug("OvsdbNode.ovsdbId = " + ovsdbNode.ovsdbId());
+        if (device == null) {
+            log.warn("Ovsdb device not found, aborting.");
+            throw new OvsdbDeviceException("Ovsdb device not found");
+        }
+
+        if (device.is(InterfaceConfig.class)) {
+            InterfaceConfig interfaceConfig = device.as(InterfaceConfig.class);
+
+            // prepare patch
+            PatchDescription.Builder builder = DefaultPatchDescription.builder();
+            PatchDescription patchDescription = builder
+                    .deviceId(bridgeName)
+                    .ifaceName(portName)
+                    .peer(patchPeer)
+                    .build();
+            // add patch to port through ovsdb
+            interfaceConfig.addPatchMode(portName, patchDescription);
+            log.info("Correctly created port {} on device {} as peer of port {}", portName, bridgeName, patchPeer);
+        } else {
+            log.warn("The interface behaviour is not supported in device {}", device.id());
+            throw new OvsdbDeviceException(
+                    "The interface behaviour is not supported in device " + device.id()
+            );
+        }
+    }
+
+    @Override
+    public void createGreTunnel(IpAddress ovsdbAddress, String bridgeName, String portName, IpAddress localIp,
+                                IpAddress remoteIp, String key)
+            throws OvsdbDeviceException, BridgeNotFoundException {
+
+        OvsdbNode ovsdbNode;
+        log.debug("Setting up tunnel GRE from {} to {} with key {}",
+                localIp, remoteIp, key);
+
+        try {
+            // gets the target ovsdb node
+            ovsdbNode = ovsdbNodes.stream().filter(node -> node.ovsdbIp().equals(ovsdbAddress)).findFirst().get();
+        } catch (NoSuchElementException nsee) {
+            log.warn(nsee.getMessage());
+            throw new OvsdbDeviceException(nsee.getMessage());
+        }
+
+        try {
+            Device device = deviceService.getDevice(ovsdbNode.ovsdbId());
+            log.debug("OvsdbNode.ovsdbId = " + ovsdbNode.ovsdbId());
+            if (device == null) {
+                log.warn("Ovsdb device not found, aborting.");
+                throw new OvsdbDeviceException("Ovsdb device not found");
+            }
+
+            if (device.is(InterfaceConfig.class)) {
+                InterfaceConfig interfaceConfig = device.as(InterfaceConfig.class);
+
+                // prepare tunnel
+                TunnelDescription tunnelDescription = DefaultTunnelDescription.builder()
+                        .deviceId(bridgeName)
+                        .ifaceName(portName)
+                        .type(TunnelDescription.Type.GRE)
+                        .local(TunnelEndPoints.ipTunnelEndpoint(localIp))
+                        .remote(TunnelEndPoints.ipTunnelEndpoint(remoteIp))
+                        .key(new TunnelKey<>(key))
+                        .build();
+                // create tunnel to port through ovsdb
+                interfaceConfig.addTunnelMode(portName, tunnelDescription);
+                log.info("Correctly added tunnel GRE from {} to {} with key {}",
+                        localIp, remoteIp, key);
+            } else {
+                log.warn("The interface behaviour is not supported in device {}", device.id());
+                throw new OvsdbDeviceException(
+                        "The interface behaviour is not supported in device " + device.id()
+                );
+            }
+        } catch (ItemNotFoundException e) {
+            log.warn("Failed to delete bridge on {}", ovsdbNode.ovsdbIp());
+            throw new OvsdbDeviceException("Error with ovsdb device: item not found");
+        }
+    }
+
+    @Override
+    public void deleteGreTunnel(IpAddress ovsdbAddress, String bridgeName, String portName)
+            throws OvsdbDeviceException {
+
+        OvsdbNode ovsdbNode;
+        log.debug("Deleting tunnel GRE from interface {}",
+                portName);
+
+        try {
+            // gets the target ovsdb node
+            ovsdbNode = ovsdbNodes.stream().filter(node -> node.ovsdbIp().equals(ovsdbAddress)).findFirst().get();
+        } catch (NoSuchElementException nsee) {
+            log.warn(nsee.getMessage());
+            throw new OvsdbDeviceException(nsee.getMessage());
+        }
+
+        try {
+            Device device = deviceService.getDevice(ovsdbNode.ovsdbId());
+            if (device == null) {
+                log.warn("Ovsdb device not found, aborting.");
+                throw new OvsdbDeviceException("Ovsdb device not found");
+            }
+
+            if (device.is(InterfaceConfig.class)) {
+                InterfaceConfig interfaceConfig = device.as(InterfaceConfig.class);
+                // remove tunnel through ovsdb
+                interfaceConfig.removeTunnelMode(portName);
+                log.info("Correctly deleted tunnel GRE from interface {}", portName);
+            } else {
+                log.warn("The interface behaviour is not supported in device {}", device.id());
+                throw new OvsdbDeviceException(
+                        "The interface behaviour is not supported in device " + device.id()
+                );
+            }
+        } catch (ItemNotFoundException e) {
+            log.warn("Failed to delete bridge on {}", ovsdbNode.ovsdbIp());
+            throw new OvsdbDeviceException("Error with ovsdb device: item not found");
+        }
+
+    }
+
+    /**
+     * Performs the connection to ovsdb.
+     *
+     * @param node the ovsdb node, with IP address and port
+     */
+    private void connectOvsdb(OvsdbNode node) {
+        if (!isOvsdbConnected(node)) {
+            log.info("connecting ovsdb at {}:{}", node.ovsdbIp(), node.ovsdbPort());
+            controller.connect(node.ovsdbIp(), node.ovsdbPort());
+        }
+    }
+
+    /**
+     * Gets an available datapath id for the new bridge.
+     *
+     * @param datapathId the integer used to generate ids
+     * @return the datapath id
+     */
+    private DeviceId getNextUniqueDatapathId(AtomicLong datapathId) {
+        DeviceId dpid;
+        do {
+            String stringId = String.format("%16X", datapathId.getAndIncrement()).replace(' ', '0');
+            log.info("String id is: " + stringId);
+            dpid = DeviceId.deviceId(stringId);
+        } while (deviceService.getDevice(dpid) != null);
+        return dpid;
+    }
+
+    /**
+     * Checks if the bridge exists and is available.
+     *
+     * @return true if the bridge is available, false otherwise
+     */
+    private boolean isBridgeCreated(String bridgeName) {
+        DeviceId deviceId = bridgeIds.get(bridgeName);
+        return (deviceId != null
+                && deviceService.getDevice(deviceId) != null
+                && deviceService.isAvailable(deviceId));
+    }
+
+    /**
+     * Returns connection state of OVSDB server for a given node.
+     *
+     * @return true if it is connected, false otherwise
+     */
+    private boolean isOvsdbConnected(OvsdbNode node) {
+
+        OvsdbClientService ovsdbClient = getOvsdbClient(node);
+        return deviceService.isAvailable(node.ovsdbId()) &&
+                ovsdbClient != null && ovsdbClient.isConnected();
+    }
+
+    /**
+     * Returns OVSDB client for a given node.
+     *
+     * @return OVSDB client, or null if it fails to get OVSDB client
+     */
+    private OvsdbClientService getOvsdbClient(OvsdbNode node) {
+
+        OvsdbClientService ovsdbClient = controller.getOvsdbClient(
+                new OvsdbNodeId(node.ovsdbIp(), node.ovsdbPort().toInt()));
+        if (ovsdbClient == null) {
+            log.trace("Couldn't find OVSDB client for {}", node.ovsdbId().toString());
+        }
+        return ovsdbClient;
+    }
+
+    /**
+     * Returns an ovsdb node associated with a given OVSDB device.
+     *
+     * @param ovsdbId OVSDB device id
+     * @return cordvtn node, null if it fails to find the node
+     */
+    private OvsdbNode nodeByOvsdbId(DeviceId ovsdbId) {
+        return ovsdbNodes.stream()
+                .filter(node -> node.ovsdbId().equals(ovsdbId))
+                .findFirst().orElse(null);
+    }
+
+    /**
+     * Returns ovsdb node associated with a given integration bridge.
+     *
+     * @param bridgeId device id of the bridge
+     * @return ovsdb node, null if it fails to find the node
+     */
+    private OvsdbNode nodeByBridgeId(DeviceId bridgeId) {
+        final  Set<OvsdbNode> nodes = new HashSet<>();
+        ovsdbNodeDevIdsSetMap.forEach((node, set) -> {
+            if (set.contains(bridgeId)) {
+                 nodes.add(node);
+            }
+        });
+        Optional<OvsdbNode> opt = nodes.stream().findAny();
+        if (opt.isPresent()) {
+            return opt.get();
+        } else {
+            return null;
+        }
+    }
+
+    private void readConfiguration() {
+        OvsdbNodeConfig config = configRegistry.getConfig(appId, OvsdbNodeConfig.class);
+        if (config == null) {
+            log.debug("No configuration found");
+            return;
+        }
+        ovsdbNodes = config.getNodes();
+        ovsdbNodes.forEach(this::connectOvsdb);
+    }
+
+    private class InternalConfigListener implements NetworkConfigListener {
+
+        @Override
+        public void event(NetworkConfigEvent event) {
+            if (!event.configClass().equals(OvsdbNodeConfig.class)) {
+                return;
+            }
+            switch (event.type()) {
+                case CONFIG_ADDED:
+                case CONFIG_UPDATED:
+                    eventExecutor.execute(OvsdbBridgeManager.this::readConfiguration);
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+}
diff --git a/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/OvsdbBridgeService.java b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/OvsdbBridgeService.java
new file mode 100644
index 0000000..6f69f69
--- /dev/null
+++ b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/OvsdbBridgeService.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.ovsdbrest;
+
+import org.onlab.packet.IpAddress;
+
+/**
+ * APIs for ovsdb driver access.
+ */
+public interface OvsdbBridgeService {
+
+    /**
+     * Creates a new bridge.
+     * @param ovsdbAddress the ovsdb IP address
+     * @param bridgeName the bridge identifier
+     */
+    void createBridge(IpAddress ovsdbAddress, String bridgeName) throws OvsdbRestException.OvsdbDeviceException,
+            OvsdbRestException.BridgeAlreadyExistsException;
+
+    /**
+     * Deletes a bridge.
+     * @param ovsdbAddress the ovsdb IP address
+     * @param bridgeName the bridge identifier
+     */
+    void deleteBridge(IpAddress ovsdbAddress, String bridgeName) throws OvsdbRestException.OvsdbDeviceException,
+            OvsdbRestException.BridgeNotFoundException;
+
+    /**
+     * Adds a port to a bridge.
+     * @param ovsdbAddress the ovsdb IP address
+     * @param bridgeName the bridge identifier
+     * @param portName the name of the port to attach to the bridge
+     */
+    void addPort(IpAddress ovsdbAddress, String bridgeName, String portName)
+            throws OvsdbRestException.OvsdbDeviceException, OvsdbRestException.BridgeNotFoundException;
+
+    /**
+     * Removes a port from a bridge.
+     * @param ovsdbAddress the ovsdb IP address
+     * @param bridgeName the bridge identifier
+     * @param portName the name of the port to remove from the bridge
+     */
+    void removePort(IpAddress ovsdbAddress, String bridgeName, String portName)
+            throws OvsdbRestException.OvsdbDeviceException, OvsdbRestException.BridgeNotFoundException;
+
+    /**
+     * Adds a patch port to a bridge setting it as peer of an other port.
+     * @param ovsdbAddress the ovsdb IP address
+     * @param bridgeName the bridge identifier
+     * @param portName the port name
+     * @param patchPeer the name of the peer port
+     */
+    void createPatchPeerPort(IpAddress ovsdbAddress, String bridgeName, String portName, String patchPeer)
+            throws OvsdbRestException.OvsdbDeviceException;
+
+    /**
+     * Creates a GRE tunnel from a bridge to a remote destination.
+     * @param ovsdbAddress the ovsdb IP address
+     * @param bridgeName the bridge identifier
+     * @param portName the name of the new GRE port
+     * @param localIp local end point of the GRE tunnel
+     * @param remoteIp remote end point of GRE tunnel
+     * @param key the tunnel key, should represent a 32 bit hexadecimal number
+     */
+    void createGreTunnel(IpAddress ovsdbAddress, String bridgeName, String portName, IpAddress localIp,
+                         IpAddress remoteIp, String key)
+            throws OvsdbRestException.OvsdbDeviceException, OvsdbRestException.BridgeNotFoundException;
+
+    /**
+     * Deletes a GRE tunnel given the port name.
+     * @param ovsdbAddress the ovsdb IP address
+     * @param bridgeName the bridge identifier
+     * @param portName the name of the GRE
+     */
+    void deleteGreTunnel(IpAddress ovsdbAddress, String bridgeName, String portName)
+            throws OvsdbRestException.OvsdbDeviceException;
+}
diff --git a/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/OvsdbNodeConfig.java b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/OvsdbNodeConfig.java
new file mode 100644
index 0000000..5caa9d3
--- /dev/null
+++ b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/OvsdbNodeConfig.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.ovsdbrest;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.collect.Sets;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.TpPort;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.config.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Set;
+
+/**
+ * Configuration info to reach the OVSDB server.
+ */
+public class OvsdbNodeConfig extends Config<ApplicationId> {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private static final String NODES = "nodes";
+    private static final String OVSDB_PORT = "ovsdbPort";
+    private static final String OVSDB_IP = "ovsdbIp";
+
+    public Set<OvsdbNode> getNodes() {
+        Set<OvsdbNode> nodes = Sets.newConcurrentHashSet();
+
+        JsonNode jsnoNodes = object.path(NODES);
+        jsnoNodes.forEach(node -> {
+            IpAddress ovsdbIp = IpAddress.valueOf(node.path(OVSDB_IP).textValue());
+            TpPort port = TpPort.tpPort(Integer.parseInt(node.path(OVSDB_PORT).asText()));
+            log.info("Ovsdb port: " + port.toString());
+            nodes.add(new OvsdbNode(ovsdbIp, port));
+        });
+        return nodes;
+    }
+
+    public static class OvsdbNode {
+        private final IpAddress ovsdbIp;
+        private final TpPort ovsdbPort;
+
+        public OvsdbNode(IpAddress ovsdbIp, TpPort ovsdbPort) {
+            this.ovsdbIp = ovsdbIp;
+            this.ovsdbPort = ovsdbPort;
+        }
+
+        public IpAddress ovsdbIp() {
+            return ovsdbIp;
+        }
+
+        public TpPort ovsdbPort() {
+            return ovsdbPort;
+        }
+
+        public DeviceId ovsdbId() {
+            return DeviceId.deviceId("ovsdb:" + ovsdbIp.toString());
+        }
+    }
+}
diff --git a/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/OvsdbRestException.java b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/OvsdbRestException.java
new file mode 100644
index 0000000..c6aa621
--- /dev/null
+++ b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/OvsdbRestException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.ovsdbrest;
+
+/**
+ * Custom exception class for OVSDB device.
+ */
+public class OvsdbRestException {
+
+    /**
+     * Thrown for problems related to a device entity representing an ovsdb node.
+     */
+    public static class OvsdbDeviceException extends Exception {
+        public OvsdbDeviceException(String message) {
+            super(message);
+        }
+    }
+
+    /**
+     * Thrown when the an ovs bridge already exists with a given name.
+     */
+    public static class BridgeAlreadyExistsException extends Exception { }
+
+    /**
+     * Thrown when an ovs bridge is not found.
+     */
+    public static class BridgeNotFoundException extends Exception { }
+}
diff --git a/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/package-info.java b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/package-info.java
new file mode 100644
index 0000000..a15b6a3
--- /dev/null
+++ b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Ovsdb interaction implementation.
+ */
+package org.onosproject.ovsdbrest;
\ No newline at end of file
diff --git a/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/rest/OvsdbBridgeWebResource.java b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/rest/OvsdbBridgeWebResource.java
new file mode 100644
index 0000000..f00bd0d
--- /dev/null
+++ b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/rest/OvsdbBridgeWebResource.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.ovsdbrest.rest;
+
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.packet.IpAddress;
+import org.onosproject.ovsdbrest.OvsdbRestException;
+import org.onosproject.ovsdbrest.OvsdbBridgeService;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import java.io.InputStream;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * REST APIs for create/delete a bridge and create a port.
+ */
+
+@Path("/")
+public class OvsdbBridgeWebResource extends AbstractWebResource {
+    private final Logger log = getLogger(getClass());
+
+    @GET
+    @Path("/test")
+    public Response getTest() {
+        ObjectNode responseBody = new ObjectNode(JsonNodeFactory.instance);
+        responseBody.put("message", "it works!");
+        return Response.status(200).entity(responseBody).build();
+    }
+
+    @POST
+    @Path("/{ovsdb-ip}/bridge/{bridge-name}")
+    @Produces(MediaType.TEXT_PLAIN)
+    public Response addBridge(InputStream stream,
+                              @PathParam("ovsdb-ip") String ovsdbIp,
+                              @PathParam("bridge-name") String bridgeName) {
+        try {
+            IpAddress ovsdbAddress = IpAddress.valueOf(ovsdbIp);
+            OvsdbBridgeService ovsdbBridgeService = get(OvsdbBridgeService.class);
+            ovsdbBridgeService.createBridge(ovsdbAddress, bridgeName);
+            return Response.status(200).build();
+        } catch (OvsdbRestException.BridgeAlreadyExistsException ex) {
+            return Response.status(Response.Status.CONFLICT).entity("A bridge with this name already exists").build();
+        } catch (OvsdbRestException.OvsdbDeviceException ex) {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
+        }
+    }
+
+    @DELETE
+    @Path("/{ovsdb-ip}/bridge/{bridge-name}")
+    @Produces(MediaType.TEXT_PLAIN)
+    public Response deleteBridge(InputStream stream,
+                                 @PathParam("ovsdb-ip") String ovsdbIp,
+                                 @PathParam("bridge-name") String bridgeName) {
+        try {
+
+            IpAddress ovsdbAddress = IpAddress.valueOf(ovsdbIp);
+            OvsdbBridgeService ovsdbBridgeService = get(OvsdbBridgeService.class);
+            ovsdbBridgeService.deleteBridge(ovsdbAddress, bridgeName);
+            return Response.status(200).build();
+        } catch (OvsdbRestException.BridgeNotFoundException ex) {
+            return Response.status(Response.Status.NOT_FOUND).entity("No bridge found with the specified name").build();
+        } catch (OvsdbRestException.OvsdbDeviceException ex) {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
+        }
+    }
+
+    @POST
+    @Path("/{ovsdb-ip}/bridge/{bridge-name}/port/{port-name}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.TEXT_PLAIN)
+    public Response addPort(InputStream stream,
+                            @PathParam("ovsdb-ip") String ovsdbIp,
+                            @PathParam("bridge-name") String bridgeName,
+                            @PathParam("port-name") String portName) {
+        try {
+            IpAddress ovsdbAddress = IpAddress.valueOf(ovsdbIp);
+            OvsdbBridgeService ovsdbBridgeService = get(OvsdbBridgeService.class);
+            ovsdbBridgeService.addPort(ovsdbAddress, bridgeName, portName);
+            return Response.status(200).build();
+        } catch (OvsdbRestException.BridgeNotFoundException ex) {
+            return Response.status(Response.Status.NOT_FOUND).entity("No bridge found with the specified name").build();
+        } catch (OvsdbRestException.OvsdbDeviceException ex) {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
+        }
+    }
+
+    @DELETE
+    @Path("/{ovsdb-ip}/bridge/{bridge-name}/port/{port-name}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.TEXT_PLAIN)
+    public Response deletePort(InputStream stream,
+                               @PathParam("ovsdb-ip") String ovsdbIp,
+                               @PathParam("bridge-name") String bridgeName,
+                               @PathParam("port-name") String portName) {
+        try {
+            IpAddress ovsdbAddress = IpAddress.valueOf(ovsdbIp);
+            OvsdbBridgeService ovsdbBridgeService = get(OvsdbBridgeService.class);
+            ovsdbBridgeService.removePort(ovsdbAddress, bridgeName, portName);
+            return Response.status(200).build();
+        } catch (OvsdbRestException.BridgeNotFoundException ex) {
+            return Response.status(Response.Status.NOT_FOUND).entity("No bridge found with the specified name").build();
+        } catch (OvsdbRestException.OvsdbDeviceException ex) {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
+        }
+    }
+
+    @POST
+    @Path("/{ovsdb-ip}/bridge/{bridge-name}/port/{port-name}/patch_peer/{patch-peer}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.TEXT_PLAIN)
+    public Response createPatchPeerPort(InputStream stream,
+                                        @PathParam("ovsdb-ip") String ovsdbIp,
+                                        @PathParam("bridge-name") String bridgeName,
+                                        @PathParam("port-name") String portName,
+                                        @PathParam("patch-peer") String patchPeer) {
+        try {
+            IpAddress ovsdbAddress = IpAddress.valueOf(ovsdbIp);
+            OvsdbBridgeService ovsdbBridgeService = get(OvsdbBridgeService.class);
+            ovsdbBridgeService.createPatchPeerPort(ovsdbAddress, bridgeName, portName, patchPeer);
+            return Response.status(200).build();
+        } catch (OvsdbRestException.OvsdbDeviceException ex) {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
+        }
+    }
+
+    @POST
+    @Path("/{ovsdb-ip}/bridge/{bridge-name}/port/{port-name}/gre/{local-ip}/{remote-ip}/{key}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.TEXT_PLAIN)
+    public Response addGreTunnel(InputStream stream,
+                                 @PathParam("ovsdb-ip") String ovsdbIp,
+                                 @PathParam("bridge-name") String bridgeName,
+                                 @PathParam("port-name") String portName,
+                                 @PathParam("local-ip") String localIp,
+                                 @PathParam("remote-ip") String remoteIp,
+                                 @PathParam("key") String key) {
+        try {
+            IpAddress ovsdbAddress = IpAddress.valueOf(ovsdbIp);
+            IpAddress tunnelLocalIp = IpAddress.valueOf(localIp);
+            IpAddress tunnelRemoteIp = IpAddress.valueOf(remoteIp);
+            OvsdbBridgeService ovsdbBridgeService = get(OvsdbBridgeService.class);
+            ovsdbBridgeService.createGreTunnel(ovsdbAddress, bridgeName, portName, tunnelLocalIp, tunnelRemoteIp, key);
+            return Response.status(200).build();
+        } catch (OvsdbRestException.BridgeNotFoundException ex) {
+            return Response.status(Response.Status.NOT_FOUND).entity("No bridge found with the specified name").build();
+        } catch (OvsdbRestException.OvsdbDeviceException ex) {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
+        }
+    }
+
+    @DELETE
+    @Path("/{ovsdb-ip}/bridge/{bridge-name}/port/{port-name}/gre")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.TEXT_PLAIN)
+    public Response deleteGreTunnel(InputStream stream,
+                                    @PathParam("ovsdb-ip") String ovsdbIp,
+                                    @PathParam("bridge-name") String bridgeName,
+                                    @PathParam("port-name") String portName) {
+        try {
+            IpAddress ovsdbAddress = IpAddress.valueOf(ovsdbIp);
+            OvsdbBridgeService ovsdbBridgeService = get(OvsdbBridgeService.class);
+            ovsdbBridgeService.deleteGreTunnel(ovsdbAddress, bridgeName, portName);
+            return Response.status(200).build();
+        } catch (OvsdbRestException.OvsdbDeviceException ex) {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build();
+        }
+    }
+}
diff --git a/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/rest/OvsdbRestApp.java b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/rest/OvsdbRestApp.java
new file mode 100644
index 0000000..a308409
--- /dev/null
+++ b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/rest/OvsdbRestApp.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.ovsdbrest.rest;
+
+import org.onlab.rest.AbstractWebApplication;
+
+import java.util.Set;
+
+/**
+ * Loaders of web resource classes.
+ */
+public class OvsdbRestApp extends AbstractWebApplication {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return getClasses(OvsdbBridgeWebResource.class);
+    }
+}
diff --git a/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/rest/package-info.java b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/rest/package-info.java
new file mode 100644
index 0000000..32a6dc2
--- /dev/null
+++ b/ovsdb-rest/src/main/java/org/onosproject/ovsdbrest/rest/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * REST interfaces implementation.
+ */
+package org.onosproject.ovsdbrest.rest;
\ No newline at end of file
diff --git a/ovsdb-rest/src/main/webapp/WEB-INF/web.xml b/ovsdb-rest/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..8725ab5
--- /dev/null
+++ b/ovsdb-rest/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2016-present Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         id="ONOS" version="2.5">
+    <display-name>ONOS REST API v1.0</display-name>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Secured</web-resource-name>
+            <url-pattern>/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>admin</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <security-role>
+        <role-name>admin</role-name>
+    </security-role>
+
+    <login-config>
+        <auth-method>BASIC</auth-method>
+        <realm-name>karaf</realm-name>
+    </login-config>
+
+    <servlet>
+        <servlet-name>JAX-RS Service</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.onosproject.ovsdbrest.rest.OvsdbRestApp</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>JAX-RS Service</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/ovsdb-rest/tools/addBridge.json b/ovsdb-rest/tools/addBridge.json
new file mode 100644
index 0000000..60ac33d
--- /dev/null
+++ b/ovsdb-rest/tools/addBridge.json
@@ -0,0 +1 @@
+{"ovsdbIp" : "127.0.0.1", "bridgeName" : "br-test"}
diff --git a/ovsdb-rest/tools/ovsdbrest.json b/ovsdb-rest/tools/ovsdbrest.json
new file mode 100644
index 0000000..da79f40
--- /dev/null
+++ b/ovsdb-rest/tools/ovsdbrest.json
@@ -0,0 +1,15 @@
+{
+	"apps": {
+		"org.onosproject.ovsdbrest": {
+			"ovsdbrest": {
+				"nodes": [
+					{
+						"ovsdbIp": "127.0.0.1",
+						"ovsdbPort": "6632"
+					}
+				]
+			}
+		}
+	}
+}
+
diff --git a/pom.xml b/pom.xml
index aae10b6..accaeaf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -47,6 +47,7 @@
         <module>mef-sca-api</module>
         <module>icona</module>
         <module>patchpanel</module>
+        <module>ovsdb-rest</module>
     </modules>
 
 </project>