[ODTN] Netconf inject capabilities and Nokia openconfig device discovery

Author:    Quan PHAM VAN <quan.pham_van@nokia.com>

Change-Id: I6bb639f0af3d1083922c4e631b8958a94d822886
diff --git a/protocols/netconf/ctl/src/main/java/org/onosproject/netconf/ctl/impl/NetconfSessionMinaImpl.java b/protocols/netconf/ctl/src/main/java/org/onosproject/netconf/ctl/impl/NetconfSessionMinaImpl.java
index 2385f94..8a086b5 100644
--- a/protocols/netconf/ctl/src/main/java/org/onosproject/netconf/ctl/impl/NetconfSessionMinaImpl.java
+++ b/protocols/netconf/ctl/src/main/java/org/onosproject/netconf/ctl/impl/NetconfSessionMinaImpl.java
@@ -21,6 +21,7 @@
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.channel.ClientChannel;
 import org.apache.sshd.client.future.ConnectFuture;
@@ -29,10 +30,15 @@
 import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.openssl.PEMParser;
 import org.bouncycastle.openssl.PEMKeyPair;
+import org.bouncycastle.openssl.PEMParser;
 import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.osgi.ServiceDirectory;
 import org.onlab.util.SharedExecutors;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.net.driver.DriverService;
 import org.onosproject.netconf.AbstractNetconfSession;
 import org.onosproject.netconf.NetconfDeviceInfo;
 import org.onosproject.netconf.NetconfDeviceOutputEvent;
@@ -43,8 +49,6 @@
 import org.onosproject.netconf.NetconfSessionFactory;
 import org.onosproject.netconf.NetconfTransportException;
 import org.slf4j.Logger;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.slf4j.LoggerFactory.getLogger;
 
 import java.io.CharArrayReader;
 import java.io.IOException;
@@ -54,14 +58,15 @@
 import java.security.PublicKey;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.X509EncodedKeySpec;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
 import java.util.Optional;
-import java.util.ArrayList;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -72,6 +77,9 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.slf4j.LoggerFactory.getLogger;
+
 /**
  * Implementation of a NETCONF session to talk to a device.
  */
@@ -110,6 +118,9 @@
     private static final String MSGLEN_REGEX_PATTERN = "\n#\\d+\n";
     private static final String NETCONF_10_CAPABILITY = "urn:ietf:params:netconf:base:1.0";
     private static final String NETCONF_11_CAPABILITY = "urn:ietf:params:netconf:base:1.1";
+    private static final String NETCONF_CLIENT_CAPABILITY = "netconfClientCapability";
+
+    private static ServiceDirectory directory = new DefaultServiceDirectory();
 
     private String sessionID;
     private final AtomicInteger messageIdInteger = new AtomicInteger(1);
@@ -137,17 +148,19 @@
     private int replyTimeout;
     private int idleTimeout;
 
-
     private ClientChannel channel = null;
     private ClientSession session = null;
     private SshClient client = null;
 
-
     public NetconfSessionMinaImpl(NetconfDeviceInfo deviceInfo) throws NetconfException {
         this.deviceInfo = deviceInfo;
         replies = new ConcurrentHashMap<>();
         errorReplies = new ArrayList<>();
-
+        Set<String> capabilities = getClientCapabilites(deviceInfo.getDeviceId());
+        if (!capabilities.isEmpty()) {
+            capabilities.addAll(Sets.newHashSet(onosCapabilities));
+            setOnosCapabilities(capabilities);
+        }
         // FIXME should not immediately start session on construction
         // setOnosCapabilities() is useless due to this behavior
         startConnection();
@@ -163,6 +176,28 @@
         startConnection();
     }
 
+    /**
+     * Get the list of the netconf client capabilities from device driver property.
+     *
+     * @param deviceId the deviceID for which to recover the capabilities from the driver.
+     * @return the String list of clientCapability property, or null if it is not configured
+     */
+    public Set<String> getClientCapabilites(DeviceId deviceId) {
+        Set<String> capabilities = new LinkedHashSet<>();
+        DriverService driverService = directory.get(DriverService.class);
+        Driver driver = driverService.getDriver(deviceId);
+        if (driver == null) {
+            return capabilities;
+        }
+        String clientCapabilities = driver.getProperty(NETCONF_CLIENT_CAPABILITY);
+        if (clientCapabilities == null) {
+            return capabilities;
+        }
+        String[] textStr = clientCapabilities.split("\\|");
+        capabilities.addAll(Arrays.asList(textStr));
+        return capabilities;
+    }
+
     private void startConnection() throws NetconfException {
         connectTimeout = deviceInfo.getConnectTimeoutSec().orElse(
                                 NetconfControllerImpl.netconfConnectTimeout);
diff --git a/protocols/netconf/ctl/src/test/java/org/onosproject/netconf/ctl/impl/NetconfSessionMinaImplTest.java b/protocols/netconf/ctl/src/test/java/org/onosproject/netconf/ctl/impl/NetconfSessionMinaImplTest.java
index 02c4214..d6cf94b 100644
--- a/protocols/netconf/ctl/src/test/java/org/onosproject/netconf/ctl/impl/NetconfSessionMinaImplTest.java
+++ b/protocols/netconf/ctl/src/test/java/org/onosproject/netconf/ctl/impl/NetconfSessionMinaImplTest.java
@@ -27,11 +27,16 @@
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.onlab.junit.TestTools;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.packet.Ip4Address;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.driver.DriverServiceAdapter;
+import org.onosproject.netconf.DatastoreId;
 import org.onosproject.netconf.NetconfDeviceInfo;
 import org.onosproject.netconf.NetconfException;
 import org.onosproject.netconf.NetconfSession;
-import org.onosproject.netconf.DatastoreId;
-import org.onlab.packet.Ip4Address;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -49,11 +54,11 @@
 import java.util.regex.Pattern;
 
 import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.onosproject.netconf.DatastoreId.CANDIDATE;
 import static org.onosproject.netconf.DatastoreId.RUNNING;
 import static org.onosproject.netconf.DatastoreId.STARTUP;
@@ -116,6 +121,9 @@
             .add("urn:ietf:params:netconf:capability:validate:1.1")
             .build();
 
+    private static final ServiceDirectory TEST_DIRECTORY =
+            new TestServiceDirectory()
+                    .add(DriverService.class, new DriverServiceAdapter());
 
     private static NetconfSession session1;
     private static NetconfSession session2;
@@ -138,6 +146,9 @@
                         return TEST_USERNAME.equals(username) && TEST_PASSWORD.equals(password);
                     }
                 });
+
+        TestUtils.setField(NetconfSessionMinaImpl.class, "directory", TEST_DIRECTORY);
+
         sshServerNetconf.setPort(portNumber);
         SimpleGeneratorHostKeyProvider provider = new SimpleGeneratorHostKeyProvider();
         provider.setFile(new File(TEST_SERFILE));
@@ -192,6 +203,7 @@
             session4.close();
         }
 
+        TestUtils.setField(NetconfSessionMinaImpl.class, "directory", null);
         sshServerNetconf.stop();
     }