[ONOS-7566] Implementation of NetconfProxySession

Change-Id: I01cbe0b10ac36cb6db53127555b551f405acdeb1
diff --git a/protocols/netconf/api/src/main/java/org/onosproject/netconf/AbstractNetconfSession.java b/protocols/netconf/api/src/main/java/org/onosproject/netconf/AbstractNetconfSession.java
index 2254572..7843f7d 100644
--- a/protocols/netconf/api/src/main/java/org/onosproject/netconf/AbstractNetconfSession.java
+++ b/protocols/netconf/api/src/main/java/org/onosproject/netconf/AbstractNetconfSession.java
@@ -80,6 +80,21 @@
     @Override
     public abstract CompletableFuture<String> rpc(String request) throws NetconfException;
 
+    protected CompletableFuture<CharSequence> executeRpc(String rpcString) throws NetconfException {
+        return rpc(rpcString)
+                .thenApply(msg -> {
+                    // crude way of removing rpc-reply envelope
+                    int begin = msg.indexOf("<data>");
+                    int end = msg.lastIndexOf("</data>");
+                    if (begin != -1 && end != -1) {
+                        return msg.subSequence(begin, end + "</data>".length());
+                    } else {
+                        // FIXME probably should exceptionally fail here.
+                        return msg;
+                    }
+                });
+    }
+
     @Override
     public CompletableFuture<CharSequence> asyncGetConfig(DatastoreId datastore) throws NetconfException {
         StringBuilder rpc = new StringBuilder();
@@ -102,7 +117,6 @@
     @Override
     public CompletableFuture<CharSequence> asyncGet() throws NetconfException {
         StringBuilder rpc = new StringBuilder();
-
         rpc.append(RPC_OPEN);
         rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
         rpc.append(GET_OPEN).append(NEW_LINE);
@@ -113,22 +127,6 @@
         return executeRpc(rpc.toString());
     }
 
-    protected CompletableFuture<CharSequence> executeRpc(String rpcString) throws NetconfException {
-        return rpc(rpcString)
-                .thenApply(msg -> {
-                    // crude way of removing rpc-reply envelope
-                    int begin = msg.indexOf("<data>");
-                    int end = msg.lastIndexOf("</data>");
-                    if (begin != -1 && end != -1) {
-                        return msg.subSequence(begin, end + "</data>".length());
-                    } else {
-                        // FIXME probably should exceptionally fail here.
-                        return msg;
-                    }
-                });
-
-    }
-
     @Override
     public String get(String request) throws NetconfException {
         return requestSync(request);
@@ -362,10 +360,14 @@
     public abstract Set<String> getDeviceCapabilitiesSet();
 
     @Override
-    public abstract void addDeviceOutputListener(NetconfDeviceOutputEventListener listener);
+    public void addDeviceOutputListener(NetconfDeviceOutputEventListener listener) throws NetconfException {
+        throw new NetconfException("Only master session can call addDeviceOutputListener");
+    }
 
     @Override
-    public abstract void removeDeviceOutputListener(NetconfDeviceOutputEventListener listener);
+    public void removeDeviceOutputListener(NetconfDeviceOutputEventListener listener) throws NetconfException {
+        throw new NetconfException("Only master session can call removeDeviceOutputListener");
+    }
 
     /**
      * Checks errors in reply from the session.
diff --git a/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfController.java b/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfController.java
index 041c379..45d81d2 100644
--- a/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfController.java
+++ b/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfController.java
@@ -21,6 +21,7 @@
 
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
 
 /**
  * Abstraction of an NETCONF controller. Serves as a one stop shop for obtaining
@@ -53,6 +54,20 @@
     NetconfDevice connectDevice(DeviceId deviceId) throws NetconfException;
 
     /**
+     * Tries to connect to a specific NETCONF device, if the connection is succesful
+     * it creates and adds the device to the ONOS core as a NetconfDevice.
+     * If isMaster true: Will create two sessions for a device : secure transport session and proxy session.
+     * If isMaster false: Will create only proxy session.
+     * @param deviceId deviceId of the device to connect
+     * @param isMaster if the controller is master for the device
+     * @return NetconfDevice Netconf device
+     * @throws NetconfException when device is not available
+     */
+    default NetconfDevice connectDevice(DeviceId deviceId, boolean isMaster) throws NetconfException {
+        return connectDevice(deviceId);
+    }
+
+    /**
      * Disconnects a Netconf device and removes it from the core.
      *
      * @param deviceId id of the device to remove
@@ -107,4 +122,20 @@
      * @return NetconfDevice Netconf device
      */
     NetconfDevice getNetconfDevice(IpAddress ip, int port, String path);
+
+    /**
+     * If master, will execute the call locally else will use
+     * clusterCommunicationManager to execute at master controller.
+     * Meant only for internal synchronization and not to be used by applications.
+     *
+     * @param proxyMessage proxy message
+     * @param <T> return type
+     * @return Completable future of class T
+     * @throws NetconfException netconf exception
+     */
+    default <T> CompletableFuture<T> executeAtMaster(NetconfProxyMessage proxyMessage) throws NetconfException {
+        CompletableFuture<T> errorFuture = new CompletableFuture<>();
+        errorFuture.completeExceptionally(new NetconfException("Method executeAtMaster not implemented"));
+        return errorFuture;
+    }
 }
diff --git a/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfDevice.java b/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfDevice.java
index 639f0a8..8c756e5 100644
--- a/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfDevice.java
+++ b/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfDevice.java
@@ -24,13 +24,22 @@
 
     /**
      * Returns whether a device is a NETCONF device with a capabilities list
-     * and is accessible.
+     * and is accessible, through a secure transport session or a proxy session.
      *
      * @return true if device is accessible, false otherwise
      */
     boolean isActive();
 
     /**
+     * Returns whether the device has secure transport session.
+     *
+     * @return true if secure transport session exists, false otherwise
+     */
+    default boolean isMasterSession() {
+        return isActive();
+    }
+
+    /**
      * Returns a NETCONF session context for this device.
      *
      * @return netconf session
diff --git a/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfDeviceFactory.java b/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfDeviceFactory.java
index 312c7c3..53e5db7 100644
--- a/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfDeviceFactory.java
+++ b/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfDeviceFactory.java
@@ -16,6 +16,8 @@
 
 package org.onosproject.netconf;
 
+import com.google.common.annotations.Beta;
+
 /**
  * Abstract interface for the creation of a NETCONF device.
  */
@@ -31,4 +33,19 @@
      */
     NetconfDevice createNetconfDevice(NetconfDeviceInfo netconfDeviceInfo)
             throws NetconfException;
+
+    /**
+     * Creates a new NETCONF device based on the supplied information.
+     * @param netconfDeviceInfo information of the device to create.
+     * @param isMaster if true create secure transport session with the device,
+     *                 else just create a proxy session
+     * @return Instance of NetconfDevice.
+     * @throws NetconfException when problems arise creating the device and establishing
+     * the connection.
+     */
+    @Beta
+    default NetconfDevice createNetconfDevice(NetconfDeviceInfo netconfDeviceInfo,
+                                              boolean isMaster) throws NetconfException {
+        return createNetconfDevice(netconfDeviceInfo);
+    }
 }
diff --git a/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfProxyMessage.java b/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfProxyMessage.java
new file mode 100644
index 0000000..8ebfbe3
--- /dev/null
+++ b/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfProxyMessage.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.netconf;
+
+import org.onosproject.net.DeviceId;
+
+import java.util.List;
+
+/**
+ * Interface representing a NETCONF proxy message.
+ */
+public interface NetconfProxyMessage {
+
+    enum SubjectType {
+        RPC,
+        // FIXME in the final form there should only be (async) RPC
+        // and REQUEST, REQUEST_SYNC should go away
+        // once NetconfSession methods got cleaned up.
+        REQUEST,
+        REQUEST_SYNC,
+        START_SUBSCRIPTION,
+        END_SUBSCRIPTION,
+        GET_SESSION_ID,
+        GET_DEVICE_CAPABILITIES_SET,
+        SET_ONOS_CAPABILITIES
+    }
+
+    /**
+     * Returns the subject of the message.
+     *
+     * @return subject in enum subject type
+     */
+    NetconfProxyMessage.SubjectType subjectType();
+
+    /**
+     * Returns the device id of the device to which the message is intended.
+     * @return device id
+     */
+    DeviceId deviceId();
+
+    /**
+     * Returns the arguments of the intended method call in order.
+     * @return arguments
+     */
+    List<String> arguments();
+}
diff --git a/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfProxyMessageHandler.java b/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfProxyMessageHandler.java
new file mode 100644
index 0000000..a1d4d74
--- /dev/null
+++ b/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfProxyMessageHandler.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.netconf;
+
+/**
+ * Abstract interface for the implementation of proxy message handler.
+ */
+public interface NetconfProxyMessageHandler {
+    /**
+     * Will decode the message on case basis and
+     * call the actual method in Netconf Session implementation bound to secure transport.
+     * @param proxyMessage incoming proxy message
+     * @param <T> return type
+     * @return the value returned by session call
+     * @throws NetconfException netconf exception
+     */
+    <T> T handleIncomingMessage(NetconfProxyMessage proxyMessage) throws NetconfException;
+}
\ No newline at end of file
diff --git a/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfSession.java b/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfSession.java
index bc04118..d0c80de 100644
--- a/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfSession.java
+++ b/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfSession.java
@@ -122,6 +122,19 @@
     String requestSync(String request) throws NetconfException;
 
     /**
+     * Executes an synchronous RPC to the server with specific reply TIMEOUT.
+     *
+     * @param request the XML containing the RPC for the server.
+     * @param timeout the reply timeout.
+     * @return Server response or ERROR
+     * @throws NetconfException when there is a problem in the communication process on
+     * the underlying connection
+     */
+    default String requestSync(String request, int timeout) throws NetconfException {
+        return "";
+    }
+
+    /**
      * Retrieves the specified configuration.
      *
      * @param netconfTargetConfig the type of configuration to retrieve.
@@ -341,15 +354,17 @@
      * Add a listener to the underlying stream handler implementation.
      *
      * @param listener event listener.
+     * @throws NetconfException when this method will be called by STANDBY or NONE node.
      */
-    void addDeviceOutputListener(NetconfDeviceOutputEventListener listener);
+    void addDeviceOutputListener(NetconfDeviceOutputEventListener listener) throws NetconfException;
 
     /**
      * Remove a listener from the underlying stream handler implementation.
      *
      * @param listener event listener.
+     * @throws NetconfException when this method will be called by STANDBY or NONE node.
      */
-    void removeDeviceOutputListener(NetconfDeviceOutputEventListener listener);
+    void removeDeviceOutputListener(NetconfDeviceOutputEventListener listener) throws NetconfException;
 
     /**
      * Read the connect timeout that this session was created with.
diff --git a/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfSessionFactory.java b/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfSessionFactory.java
index 806eccb..95f32a4 100644
--- a/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfSessionFactory.java
+++ b/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfSessionFactory.java
@@ -25,9 +25,11 @@
     /**
      * Creates a new NETCONF session for the specified device.
      * @param netconfDeviceInfo information of the device to create the session for.
+     * @param netconfController netconf controller object
      * @return Instance of NetconfSession.
      * @throws NetconfException when problems arise establishing the connection.
      */
-    NetconfSession createNetconfSession(NetconfDeviceInfo netconfDeviceInfo)
+    NetconfSession createNetconfSession(NetconfDeviceInfo netconfDeviceInfo,
+                                        NetconfController netconfController)
             throws NetconfException;
 }