Enable REST to manage devices under a proxy

This commit integrates the REST proxy within the standard REST
protocol and provider, supporting multiple devices managed by
a single REST end point (e.g. a REST-based second level controller).

It introduces the concept of RestSBServer, that represents
the REST service exposed by a single or a set of devices.

Change-Id: I0b187151848c20bc1fd8c39a33d57a500f667533
diff --git a/protocols/rest/api/src/main/java/org/onosproject/protocol/http/ctl/HttpSBControllerImpl.java b/protocols/rest/api/src/main/java/org/onosproject/protocol/http/ctl/HttpSBControllerImpl.java
index 2c2d1a2..b162b56 100644
--- a/protocols/rest/api/src/main/java/org/onosproject/protocol/http/ctl/HttpSBControllerImpl.java
+++ b/protocols/rest/api/src/main/java/org/onosproject/protocol/http/ctl/HttpSBControllerImpl.java
@@ -63,8 +63,8 @@
             LoggerFactory.getLogger(HttpSBControllerImpl.class);
     private static final String XML = "xml";
     private static final String JSON = "json";
-    private static final String DOUBLESLASH = "//";
-    private static final String COLON = ":";
+    protected static final String DOUBLESLASH = "//";
+    protected static final String COLON = ":";
     private static final int STATUS_OK = Response.Status.OK.getStatusCode();
     private static final int STATUS_CREATED = Response.Status.CREATED.getStatusCode();
     private static final int STATUS_ACCEPTED = Response.Status.ACCEPTED.getStatusCode();
@@ -279,7 +279,7 @@
                                       .build()).build();
     }
 
-    private String getUrlString(DeviceId device, String request) {
+    protected String getUrlString(DeviceId device, String request) {
         if (deviceMap.get(device).url() != null) {
             return deviceMap.get(device).protocol() + COLON + DOUBLESLASH
                     + deviceMap.get(device).url() + request;
diff --git a/protocols/rest/api/src/main/java/org/onosproject/protocol/http/ctl/package-info.java b/protocols/rest/api/src/main/java/org/onosproject/protocol/http/ctl/package-info.java
index e6fe54d..6401451 100644
--- a/protocols/rest/api/src/main/java/org/onosproject/protocol/http/ctl/package-info.java
+++ b/protocols/rest/api/src/main/java/org/onosproject/protocol/http/ctl/package-info.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Copyright 2016-present Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,8 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 /**
- * @author onos
- *
+ * REST southbound protocol controller.
  */
 package org.onosproject.protocol.http.ctl;
diff --git a/protocols/rest/api/src/main/java/org/onosproject/protocol/http/package-info.java b/protocols/rest/api/src/main/java/org/onosproject/protocol/http/package-info.java
index c54b035..e4e6cb7 100644
--- a/protocols/rest/api/src/main/java/org/onosproject/protocol/http/package-info.java
+++ b/protocols/rest/api/src/main/java/org/onosproject/protocol/http/package-info.java
@@ -1,20 +1,20 @@
-/**
+/*
  * Copyright 2016-present Open Networking Laboratory
- *
+ * <p>
  * 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
- *
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
  * 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.
  */
+
 /**
- * @author onos
- *
+ * REST southbound protocol controller.
  */
 package org.onosproject.protocol.http;
diff --git a/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/DefaultRestSBDevice.java b/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/DefaultRestSBDevice.java
index 40816a4..53ae0dc 100644
--- a/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/DefaultRestSBDevice.java
+++ b/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/DefaultRestSBDevice.java
@@ -16,16 +16,16 @@
 
 package org.onosproject.protocol.rest;
 
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Objects;
-
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
 import org.apache.commons.lang3.StringUtils;
 import org.onlab.packet.IpAddress;
 import org.onosproject.net.DeviceId;
 
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Objects;
+import java.util.Optional;
 
 /**
  * Default implementation for Rest devices.
@@ -40,9 +40,21 @@
     private boolean isActive;
     private String protocol;
     private String url;
+    private boolean isProxy;
+    private final Optional<String> testUrl;
+    private final Optional<String> manufacturer;
+    private final Optional<String> hwVersion;
+    private final Optional<String> swVersion;
 
     public DefaultRestSBDevice(IpAddress ip, int port, String name, String password,
                                String protocol, String url, boolean isActive) {
+        this(ip, port, name, password, protocol, url, isActive, "", "", "", "");
+    }
+
+    public DefaultRestSBDevice(IpAddress ip, int port, String name, String password,
+                               String protocol, String url, boolean isActive, String testUrl, String manufacturer,
+                               String hwVersion,
+                               String swVersion) {
         Preconditions.checkNotNull(ip, "IP address cannot be null");
         Preconditions.checkArgument(port > 0, "Port address cannot be negative");
         Preconditions.checkNotNull(protocol, "protocol address cannot be null");
@@ -53,6 +65,21 @@
         this.isActive = isActive;
         this.protocol = protocol;
         this.url = StringUtils.isEmpty(url) ? null : url;
+        this.manufacturer = StringUtils.isEmpty(manufacturer) ?
+                Optional.empty() : Optional.ofNullable(manufacturer);
+        this.hwVersion = StringUtils.isEmpty(hwVersion) ?
+                Optional.empty() : Optional.ofNullable(hwVersion);
+        this.swVersion = StringUtils.isEmpty(swVersion) ?
+                Optional.empty() : Optional.ofNullable(swVersion);
+        this.testUrl = StringUtils.isEmpty(testUrl) ?
+                Optional.empty() : Optional.ofNullable(testUrl);
+        if (this.manufacturer.isPresent()
+                && this.hwVersion.isPresent()
+                && this.swVersion.isPresent()) {
+            this.isProxy = true;
+        } else {
+            this.isProxy = false;
+        }
     }
 
     @Override
@@ -107,14 +134,45 @@
     }
 
     @Override
+    public boolean isProxy() {
+        return isProxy;
+    }
+
+    @Override
+    public Optional<String> testUrl() {
+        return testUrl;
+    }
+
+    @Override
+    public Optional<String> manufacturer() {
+        return manufacturer;
+    }
+
+    @Override
+    public Optional<String> hwVersion() {
+        return hwVersion;
+    }
+
+    @Override
+    public Optional<String> swVersion() {
+        return swVersion;
+    }
+
+    @Override
     public String toString() {
         return MoreObjects.toStringHelper(this)
+                .omitNullValues()
                 .add("url", url)
+                .add("testUrl", testUrl)
                 .add("protocol", protocol)
                 .add("username", username)
                 .add("port", port)
                 .add("ip", ip)
+                .add("manufacturer", manufacturer.orElse(null))
+                .add("hwVersion", hwVersion.orElse(null))
+                .add("swVersion", swVersion.orElse(null))
                 .toString();
+
     }
 
     @Override
diff --git a/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/RestSBController.java b/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/RestSBController.java
index 2fe3791..bca4a6c 100644
--- a/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/RestSBController.java
+++ b/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/RestSBController.java
@@ -16,11 +16,44 @@
 
 package org.onosproject.protocol.rest;
 
+import org.onosproject.net.DeviceId;
 import org.onosproject.protocol.http.HttpSBController;
 
+import java.util.Set;
+
 /**
  * Abstraction of an REST controller. Serves as a one stop shop for obtaining
- * Rest southbound devices and (un)register listeners.
+ * Rest southbound devices.
  */
 public interface RestSBController extends HttpSBController {
+
+    /**
+     * Add a new association between a proxied device exposed to ONOS and
+     * a REST proxy server.
+     * @param deviceId REST device identifier
+     * @param proxy REST proxy device
+     */
+    void addProxiedDevice(DeviceId deviceId, RestSBDevice proxy);
+
+    /**
+     * Remove the association between a proxied device exposed to ONOS
+     * and a REST proxy server.
+     * @param deviceId REST device identifier
+     */
+    void removeProxiedDevice(DeviceId deviceId);
+
+    /**
+     * Get all the proxied device exposed to ONOS ids under the same
+     * REST proxy server.
+     * @param proxyId REST proxy device identifier
+     * @return set of device ids under same proxy
+     */
+    Set<DeviceId> getProxiedDevices(DeviceId proxyId);
+
+    /**
+     * Get a REST proxied server given a device id.
+     * @param deviceId the id of proxied device exposed to ONOS
+     * @return the corresponding REST proxied device
+     */
+    RestSBDevice getProxySBDevice(DeviceId deviceId);
 }
diff --git a/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/RestSBDevice.java b/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/RestSBDevice.java
index b1f61ac..bacbbfe 100644
--- a/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/RestSBDevice.java
+++ b/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/RestSBDevice.java
@@ -19,6 +19,8 @@
 import org.onlab.packet.IpAddress;
 import org.onosproject.net.DeviceId;
 
+import java.util.Optional;
+
 /**
  * Represents an abstraction of a Rest Device in ONOS.
  */
@@ -85,4 +87,39 @@
      * @return url
      */
     String url();
+
+    /**
+     * Returns the proxy state of this device
+     * (if true, the device is proxying multiple ONOS devices).
+     * @return proxy state
+     */
+    boolean isProxy();
+
+    /**
+     * Returns the url for the REST TEST requests.
+     *
+     * @return testUrl
+     */
+    Optional<String> testUrl();
+
+    /**
+     * The manufacturer of the rest device.
+     *
+     * @return the name of the manufacturer
+     */
+    Optional<String> manufacturer();
+
+    /**
+     * The hardware version of the rest device.
+     *
+     * @return the hardware version
+     */
+    Optional<String> hwVersion();
+
+    /**
+     * The software version of rest device.
+     *
+     * @return the software version.
+     */
+    Optional<String> swVersion();
 }