Patch for ONOS-6840 NETCONF timeouts per device

Change-Id: Ia2e578245b97e0f68ea720cefe783e708e255ca7
diff --git a/protocols/netconf/ctl/src/main/java/org/onosproject/netconf/ctl/NetconfSessionImpl.java b/protocols/netconf/ctl/src/main/java/org/onosproject/netconf/ctl/NetconfSessionImpl.java
index 0f98c77..a2c6a3d 100644
--- a/protocols/netconf/ctl/src/main/java/org/onosproject/netconf/ctl/NetconfSessionImpl.java
+++ b/protocols/netconf/ctl/src/main/java/org/onosproject/netconf/ctl/NetconfSessionImpl.java
@@ -104,6 +104,9 @@
     private boolean subscriptionConnected = false;
     private String notificationFilterSchema = null;
 
+    private int connectTimeout;
+    private int replyTimeout;
+
 
     public NetconfSessionImpl(NetconfDeviceInfo deviceInfo) throws NetconfException {
         this.deviceInfo = deviceInfo;
@@ -112,13 +115,18 @@
         connectionActive = false;
         replies = new ConcurrentHashMap<>();
         errorReplies = new ArrayList<>();
+        connectTimeout = deviceInfo.getConnectTimeoutSec().orElse(
+                                    NetconfControllerImpl.netconfConnectTimeout);
+        replyTimeout = deviceInfo.getReplyTimeoutSec().orElse(
+                                    NetconfControllerImpl.netconfReplyTimeout);
+        log.info("Connecting to {} with timeouts C:{}, R:{}. idle=connect", deviceInfo,
+                connectTimeout, replyTimeout);
         startConnection();
     }
 
     private void startConnection() throws NetconfException {
         if (!connectionActive) {
             netconfConnection = new Connection(deviceInfo.ip().toString(), deviceInfo.port());
-            int connectTimeout = NetconfControllerImpl.netconfConnectTimeout;
 
             try {
                 netconfConnection.connect(null, 1000 * connectTimeout, 1000 * connectTimeout);
@@ -312,7 +320,6 @@
         request = formatRequestMessageId(request, messageId);
         request = formatXmlHeader(request);
         CompletableFuture<String> futureReply = request(request, messageId);
-        int replyTimeout = NetconfControllerImpl.netconfReplyTimeout;
         String rp;
         try {
             rp = futureReply.get(replyTimeout, TimeUnit.SECONDS);
@@ -622,6 +629,24 @@
     }
 
     @Override
+    public int timeoutConnectSec() {
+        return connectTimeout;
+    }
+
+    @Override
+    public int timeoutReplySec() {
+        return replyTimeout;
+    }
+
+    /**
+     * Idle timeout is not settable on ETZ_SSH - the valuse used is the connect timeout.
+     */
+    @Override
+    public int timeoutIdleSec() {
+        return connectTimeout;
+    }
+
+    @Override
     public void addDeviceOutputListener(NetconfDeviceOutputEventListener listener) {
         streamHandler.addDeviceEventListener(listener);
     }
diff --git a/protocols/netconf/ctl/src/main/java/org/onosproject/netconf/ctl/impl/NetconfControllerImpl.java b/protocols/netconf/ctl/src/main/java/org/onosproject/netconf/ctl/impl/NetconfControllerImpl.java
index 2265234..5e6d7f9 100644
--- a/protocols/netconf/ctl/src/main/java/org/onosproject/netconf/ctl/impl/NetconfControllerImpl.java
+++ b/protocols/netconf/ctl/src/main/java/org/onosproject/netconf/ctl/impl/NetconfControllerImpl.java
@@ -29,6 +29,7 @@
 import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.config.NetworkConfigRegistry;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.key.DeviceKey;
 import org.onosproject.net.key.DeviceKeyId;
@@ -42,6 +43,8 @@
 import org.onosproject.netconf.NetconfDeviceOutputEvent;
 import org.onosproject.netconf.NetconfDeviceOutputEventListener;
 import org.onosproject.netconf.NetconfException;
+import org.onosproject.netconf.config.NetconfDeviceConfig;
+import org.onosproject.netconf.config.NetconfSshClientLib;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -55,8 +58,8 @@
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
-import static com.google.common.base.Strings.isNullOrEmpty;
 import static org.onlab.util.Tools.get;
+import static org.onlab.util.Tools.getIntegerProperty;
 import static org.onlab.util.Tools.groupedThreads;
 
 /**
@@ -87,10 +90,10 @@
     protected static int netconfIdleTimeout = DEFAULT_IDLE_TIMEOUT_SECONDS;
 
     private static final String SSH_LIBRARY = "sshLibrary";
-    private static final String APACHE_MINA = "apache_mina";
-    @Property(name = SSH_LIBRARY, value = APACHE_MINA,
+    private static final String APACHE_MINA_STR = "apache-mina";
+    @Property(name = SSH_LIBRARY, value = APACHE_MINA_STR,
             label = "Ssh Library instead of apache_mina (i.e. ethz-ssh2")
-    protected static String sshLibrary = APACHE_MINA;
+    protected static NetconfSshClientLib sshLibrary = NetconfSshClientLib.APACHE_MINA;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ComponentConfigService cfgService;
@@ -101,6 +104,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DeviceKeyService deviceKeyService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigRegistry netCfgService;
+
     public static final Logger log = LoggerFactory
             .getLogger(NetconfControllerImpl.class);
 
@@ -139,36 +145,24 @@
         if (context == null) {
             netconfReplyTimeout = DEFAULT_REPLY_TIMEOUT_SECONDS;
             netconfConnectTimeout = DEFAULT_CONNECT_TIMEOUT_SECONDS;
-            sshLibrary = APACHE_MINA;
+            netconfIdleTimeout = DEFAULT_IDLE_TIMEOUT_SECONDS;
+            sshLibrary = NetconfSshClientLib.APACHE_MINA;
             log.info("No component configuration");
             return;
         }
 
         Dictionary<?, ?> properties = context.getProperties();
 
-        int newNetconfReplyTimeout;
-        int newNetconfConnectTimeout;
-        int newNetconfIdleTimeout;
         String newSshLibrary;
-        try {
-            String s = get(properties, PROP_NETCONF_REPLY_TIMEOUT);
-            newNetconfReplyTimeout = isNullOrEmpty(s) ?
-                    netconfReplyTimeout : Integer.parseInt(s.trim());
 
-            s = get(properties, PROP_NETCONF_CONNECT_TIMEOUT);
-            newNetconfConnectTimeout = isNullOrEmpty(s) ?
-                    netconfConnectTimeout : Integer.parseInt(s.trim());
+        int newNetconfReplyTimeout = getIntegerProperty(
+                properties, PROP_NETCONF_REPLY_TIMEOUT, netconfReplyTimeout);
+        int newNetconfConnectTimeout = getIntegerProperty(
+                properties, PROP_NETCONF_CONNECT_TIMEOUT, netconfConnectTimeout);
+        int newNetconfIdleTimeout = getIntegerProperty(
+                properties, PROP_NETCONF_IDLE_TIMEOUT, netconfIdleTimeout);
 
-            s = get(properties, PROP_NETCONF_IDLE_TIMEOUT);
-            newNetconfIdleTimeout = isNullOrEmpty(s) ?
-                    netconfIdleTimeout : Integer.parseInt(s.trim());
-
-            newSshLibrary = get(properties, SSH_LIBRARY);
-
-        } catch (NumberFormatException e) {
-            log.warn("Component configuration had invalid value", e);
-            return;
-        }
+        newSshLibrary = get(properties, SSH_LIBRARY);
 
         if (newNetconfConnectTimeout < 0) {
             log.warn("netconfConnectTimeout is invalid - less than 0");
@@ -184,8 +178,10 @@
         netconfReplyTimeout = newNetconfReplyTimeout;
         netconfConnectTimeout = newNetconfConnectTimeout;
         netconfIdleTimeout = newNetconfIdleTimeout;
-        sshLibrary = newSshLibrary;
-        log.info("Settings: {} = {}, {} = {}, {} = {}",
+        if (newSshLibrary != null) {
+            sshLibrary = NetconfSshClientLib.getEnum(newSshLibrary);
+        }
+        log.info("Settings: {} = {}, {} = {}, {} = {}, {} = {}",
                  PROP_NETCONF_REPLY_TIMEOUT, netconfReplyTimeout,
                  PROP_NETCONF_CONNECT_TIMEOUT, netconfConnectTimeout,
                  PROP_NETCONF_IDLE_TIMEOUT, netconfIdleTimeout,
@@ -221,9 +217,17 @@
 
     @Override
     public NetconfDevice connectDevice(DeviceId deviceId) throws NetconfException {
+        NetconfDeviceConfig netCfg  = netCfgService.getConfig(
+                deviceId, NetconfDeviceConfig.class);
+        NetconfDeviceInfo deviceInfo = null;
+
         if (netconfDeviceMap.containsKey(deviceId)) {
             log.debug("Device {} is already present", deviceId);
             return netconfDeviceMap.get(deviceId);
+        } else if (netCfg != null) {
+            log.debug("Device {} is present in NetworkConfig", deviceId);
+            deviceInfo = new NetconfDeviceInfo(netCfg);
+
         } else {
             log.debug("Creating NETCONF device {}", deviceId);
             Device device = deviceService.getDevice(deviceId);
@@ -249,7 +253,6 @@
             try {
                 DeviceKey deviceKey = deviceKeyService.getDeviceKey(
                         DeviceKeyId.deviceKeyId(deviceId.toString()));
-                NetconfDeviceInfo deviceInfo = null;
                 if (deviceKey.type() == DeviceKey.Type.USERNAME_PASSWORD) {
                     UsernamePassword usernamepasswd = deviceKey.asUsernamePassword();
 
@@ -271,13 +274,13 @@
                 } else {
                     log.error("Unknown device key for device {}", deviceId);
                 }
-                NetconfDevice netconfDevicedevice = createDevice(deviceInfo);
-                netconfDevicedevice.getSession().addDeviceOutputListener(downListener);
-                return netconfDevicedevice;
             } catch (NullPointerException e) {
                 throw new NetconfException("No Device Key for device " + deviceId, e);
             }
         }
+        NetconfDevice netconfDevicedevice = createDevice(deviceInfo);
+        netconfDevicedevice.getSession().addDeviceOutputListener(downListener);
+        return netconfDevicedevice;
     }
 
     @Override
@@ -341,10 +344,15 @@
         @Override
         public NetconfDevice createNetconfDevice(NetconfDeviceInfo netconfDeviceInfo)
                 throws NetconfException {
-            if (sshLibrary.equals(ETHZ_SSH2)) {
+            if (NetconfSshClientLib.ETHZ_SSH2.equals(netconfDeviceInfo.sshClientLib()) ||
+                    NetconfSshClientLib.ETHZ_SSH2.equals(sshLibrary)) {
+                log.info("Creating NETCONF session to {} with {}",
+                            netconfDeviceInfo.name(), NetconfSshClientLib.ETHZ_SSH2);
                 return new DefaultNetconfDevice(netconfDeviceInfo,
-                                                new NetconfSessionImpl.SshNetconfSessionFactory());
+                            new NetconfSessionImpl.SshNetconfSessionFactory());
             }
+            log.info("Creating NETCONF session to {} with {}",
+                    netconfDeviceInfo.getDeviceId(), NetconfSshClientLib.APACHE_MINA);
             return new DefaultNetconfDevice(netconfDeviceInfo);
         }
     }
@@ -372,8 +380,8 @@
 
                     } catch (NetconfException e) {
                         log.error("The SSH connection with device {} couldn't be " +
-                                          "reestablished due to {}. " +
-                                          "Marking the device as unreachable", e.getMessage());
+                                "reestablished due to {}. " +
+                                "Marking the device as unreachable", e.getMessage());
                         log.debug("Complete exception: ", e);
                         removeDevice(did);
                     }
diff --git a/protocols/netconf/ctl/src/main/java/org/onosproject/netconf/ctl/impl/NetconfSessionImpl.java b/protocols/netconf/ctl/src/main/java/org/onosproject/netconf/ctl/impl/NetconfSessionImpl.java
index 99ad5f2..959c807 100644
--- a/protocols/netconf/ctl/src/main/java/org/onosproject/netconf/ctl/impl/NetconfSessionImpl.java
+++ b/protocols/netconf/ctl/src/main/java/org/onosproject/netconf/ctl/impl/NetconfSessionImpl.java
@@ -136,6 +136,8 @@
     private final Collection<NetconfSession> children =
             new CopyOnWriteArrayList<>();
 
+    private int connectTimeout;
+    private int replyTimeout;
 
     public NetconfSessionImpl(NetconfDeviceInfo deviceInfo) throws NetconfException {
         this.deviceInfo = deviceInfo;
@@ -144,6 +146,7 @@
         connectionActive = false;
         replies = new ConcurrentHashMap<>();
         errorReplies = new ArrayList<>();
+
         startConnection();
     }
 
@@ -160,9 +163,15 @@
     }
 
     private void startConnection() throws NetconfException {
+        connectTimeout = deviceInfo.getConnectTimeoutSec().orElse(
+                                    NetconfControllerImpl.netconfConnectTimeout);
+        replyTimeout = deviceInfo.getReplyTimeoutSec().orElse(
+                                    NetconfControllerImpl.netconfReplyTimeout);
+        log.debug("Connecting to {} with timeouts C:{}, R:{}. I:connect-timeout", deviceInfo,
+                connectTimeout, replyTimeout);
+
         if (!connectionActive) {
             netconfConnection = new Connection(deviceInfo.ip().toString(), deviceInfo.port());
-            int connectTimeout = NetconfControllerImpl.netconfConnectTimeout;
 
             try {
                 netconfConnection.connect(null, 1000 * connectTimeout, 1000 * connectTimeout);
@@ -431,16 +440,18 @@
         request = formatXmlHeader(request);
         request = formatRequestMessageId(request, messageId);
         CompletableFuture<String> futureReply = request(request, messageId);
-        int replyTimeout = NetconfControllerImpl.netconfReplyTimeout;
         String rp;
         try {
+            log.debug("Sending request to NETCONF with timeout {} for {}",
+                    replyTimeout, deviceInfo.name());
             rp = futureReply.get(replyTimeout, TimeUnit.SECONDS);
             replies.remove(messageId);
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();
             throw new NetconfException("Interrupted waiting for reply for request" + request, e);
         } catch (TimeoutException e) {
-            throw new NetconfException("Timed out waiting for reply for request " + request, e);
+            throw new NetconfException("Timed out waiting for reply for request " +
+                    request + " after " + replyTimeout + " sec.", e);
         } catch (ExecutionException e) {
             log.warn("Closing session {} for {} due to unexpected Error", sessionID, deviceInfo, e);
 
@@ -790,7 +801,6 @@
         onosCapabilities = capabilities;
     }
 
-
     @Override
     public void addDeviceOutputListener(NetconfDeviceOutputEventListener listener) {
         streamHandler.addDeviceEventListener(listener);
@@ -798,6 +808,24 @@
     }
 
     @Override
+    public int timeoutConnectSec() {
+        return connectTimeout;
+    }
+
+    @Override
+    public int timeoutReplySec() {
+        return replyTimeout;
+    }
+
+    /**
+     * Idle timeout is not settable on ETZ_SSH - the valuse used is the connect timeout.
+     */
+    @Override
+    public int timeoutIdleSec() {
+        return connectTimeout;
+    }
+
+    @Override
     public void removeDeviceOutputListener(NetconfDeviceOutputEventListener listener) {
         primaryListeners.remove(listener);
         streamHandler.removeDeviceEventListener(listener);
@@ -902,4 +930,4 @@
             return new NetconfSessionImpl(netconfDeviceInfo);
         }
     }
-}
\ No newline at end of file
+}
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 0e2e3ea..6d30314 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
@@ -143,6 +143,10 @@
     private final Collection<NetconfSession> children =
             new CopyOnWriteArrayList<>();
 
+    private int connectTimeout;
+    private int replyTimeout;
+    private int idleTimeout;
+
 
     private ClientChannel channel = null;
     private ClientSession session = null;
@@ -153,6 +157,7 @@
         this.deviceInfo = deviceInfo;
         replies = new ConcurrentHashMap<>();
         errorReplies = new ArrayList<>();
+
         startConnection();
     }
 
@@ -165,6 +170,15 @@
     }
 
     private void startConnection() throws NetconfException {
+        connectTimeout = deviceInfo.getConnectTimeoutSec().orElse(
+                                NetconfControllerImpl.netconfConnectTimeout);
+        replyTimeout = deviceInfo.getReplyTimeoutSec().orElse(
+                                NetconfControllerImpl.netconfReplyTimeout);
+        idleTimeout = deviceInfo.getIdleTimeoutSec().orElse(
+                                NetconfControllerImpl.netconfIdleTimeout);
+        log.info("Connecting to {} with timeouts C:{}, R:{}, I:{}", deviceInfo,
+                connectTimeout, replyTimeout, idleTimeout);
+
         try {
             startClient();
         } catch (IOException e) {
@@ -174,11 +188,10 @@
 
     private void startClient() throws IOException {
         client = SshClient.setUpDefaultClient();
-        int replyTimeoutSec = NetconfControllerImpl.netconfIdleTimeout;
         client.getProperties().putIfAbsent(FactoryManager.IDLE_TIMEOUT,
-                TimeUnit.SECONDS.toMillis(replyTimeoutSec));
+                TimeUnit.SECONDS.toMillis(idleTimeout));
         client.getProperties().putIfAbsent(FactoryManager.NIO2_READ_TIMEOUT,
-                TimeUnit.SECONDS.toMillis(replyTimeoutSec + 15L));
+                TimeUnit.SECONDS.toMillis(idleTimeout + 15L));
         client.start();
         client.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
         startSession();
@@ -189,7 +202,7 @@
         connectFuture = client.connect(deviceInfo.name(),
                 deviceInfo.ip().toString(),
                 deviceInfo.port())
-                .verify(NetconfControllerImpl.netconfConnectTimeout, TimeUnit.SECONDS);
+                .verify(connectTimeout, TimeUnit.SECONDS);
         session = connectFuture.getSession();
         //Using the device ssh key if possible
         if (deviceInfo.getKey() != null) {
@@ -213,7 +226,7 @@
         } else {
             session.addPasswordIdentity(deviceInfo.password());
         }
-        session.auth().verify(NetconfControllerImpl.netconfConnectTimeout, TimeUnit.SECONDS);
+        session.auth().verify(connectTimeout, TimeUnit.SECONDS);
         Set<ClientSession.ClientSessionEvent> event = session.waitFor(
                 ImmutableSet.of(ClientSession.ClientSessionEvent.WAIT_AUTH,
                         ClientSession.ClientSessionEvent.CLOSED,
@@ -239,7 +252,7 @@
     private void openChannel() throws IOException {
         channel = session.createSubsystemChannel("netconf");
         OpenFuture channelFuture = channel.open();
-        if (channelFuture.await(NetconfControllerImpl.netconfConnectTimeout, TimeUnit.SECONDS)) {
+        if (channelFuture.await(connectTimeout, TimeUnit.SECONDS)) {
             if (channelFuture.isOpened()) {
                 streamHandler = new NetconfStreamThread(channel.getInvertedOut(), channel.getInvertedIn(),
                         channel.getInvertedErr(), deviceInfo,
@@ -456,6 +469,21 @@
         return streamHandler.sendMessage(request);
     }
 
+    @Override
+    public int timeoutConnectSec() {
+        return connectTimeout;
+    }
+
+    @Override
+    public int timeoutReplySec() {
+        return replyTimeout;
+    }
+
+    @Override
+    public int timeoutIdleSec() {
+        return idleTimeout;
+    }
+
     private CompletableFuture<String> request(String request, int messageId) {
         return streamHandler.sendMessage(request, messageId);
     }
@@ -474,16 +502,18 @@
         request = formatXmlHeader(request);
         request = formatRequestMessageId(request, messageId);
         CompletableFuture<String> futureReply = request(request, messageId);
-        int replyTimeout = NetconfControllerImpl.netconfReplyTimeout;
         String rp;
         try {
+            log.debug("Sending request to NETCONF with timeout {} for {}",
+                    replyTimeout, deviceInfo.name());
             rp = futureReply.get(replyTimeout, TimeUnit.SECONDS);
             replies.remove(messageId); // Why here???
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();
             throw new NetconfException("Interrupted waiting for reply for request" + request, e);
         } catch (TimeoutException e) {
-            throw new NetconfException("Timed out waiting for reply for request " + request, e);
+            throw new NetconfException("Timed out waiting for reply for request " +
+                    request + " after " + replyTimeout + " sec.", e);
         } catch (ExecutionException e) {
             log.warn("Closing session {} for {} due to unexpected Error", sessionID, deviceInfo, e);
             try {
@@ -951,4 +981,4 @@
             return new NetconfSessionMinaImpl(netconfDeviceInfo);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/protocols/netconf/ctl/src/test/java/org/onosproject/netconf/ctl/impl/NetconfControllerImplTest.java b/protocols/netconf/ctl/src/test/java/org/onosproject/netconf/ctl/impl/NetconfControllerImplTest.java
index eab21fd..ed4eabf 100644
--- a/protocols/netconf/ctl/src/test/java/org/onosproject/netconf/ctl/impl/NetconfControllerImplTest.java
+++ b/protocols/netconf/ctl/src/test/java/org/onosproject/netconf/ctl/impl/NetconfControllerImplTest.java
@@ -15,6 +15,8 @@
  */
 package org.onosproject.netconf.ctl.impl;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import org.easymock.EasyMock;
 import org.junit.After;
 import org.junit.Before;
@@ -24,6 +26,12 @@
 import org.onosproject.cfg.ComponentConfigAdapter;
 import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.ConfigApplyDelegate;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.NetworkConfigRegistryAdapter;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.key.DeviceKeyService;
 import org.onosproject.netconf.NetconfDevice;
@@ -34,8 +42,12 @@
 import org.onosproject.netconf.NetconfDeviceOutputEventListener;
 import org.onosproject.netconf.NetconfException;
 import org.onosproject.netconf.NetconfSession;
+import org.onosproject.netconf.config.NetconfDeviceConfig;
+import org.onosproject.netconf.config.NetconfSshClientLib;
 import org.osgi.service.component.ComponentContext;
 
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
 import java.lang.reflect.Field;
 import java.util.Dictionary;
 import java.util.Enumeration;
@@ -51,6 +63,9 @@
  * Unit tests for the Netconf controller implementation test.
  */
 public class NetconfControllerImplTest {
+    private final Set<ConfigFactory> cfgFactories = new HashSet<>();
+    private final Set<NetworkConfigListener> netCfgListeners = new HashSet<>();
+    private boolean available = false;
 
     NetconfControllerImpl ctrl;
 
@@ -60,6 +75,9 @@
     NetconfDeviceInfo badDeviceInfo3;
     NetconfDeviceInfo deviceInfoIpV6;
 
+    NetconfDeviceConfig deviceConfig10;
+    DeviceId deviceConfig10Id;
+
     //Devices & DeviceId
     NetconfDevice device1;
     DeviceId deviceId1;
@@ -77,16 +95,25 @@
     private static final String DEVICE_1_IP = "10.10.10.11";
     private static final String DEVICE_2_IP = "10.10.10.12";
     private static final String BAD_DEVICE_IP = "10.10.10.13";
+    private static final String DEVICE_10_IP = "10.10.10.10";
     private static final String DEVICE_IPV6 = "2001:db8::1";
 
     private static final int DEVICE_1_PORT = 11;
     private static final int DEVICE_2_PORT = 12;
     private static final int BAD_DEVICE_PORT = 13;
     private static final int IPV6_DEVICE_PORT = 14;
+    private static final int DEVICE_10_PORT = 10;
+
+    private static final String DEVICE_10_USERNAME = "device10";
+    private static final String DEVICE_10_PASSWORD = "010";
+    private static final int DEVICE_10_CONNECT_TIMEOUT = 10;
+    private static final int DEVICE_10_REPLY_TIMEOUT = 11;
+    private static final int DEVICE_10_IDLE_TIMEOUT = 12;
 
     private static ComponentConfigService cfgService = new ComponentConfigAdapter();
     private static DeviceService deviceService = new NetconfDeviceServiceMock();
     private static DeviceKeyService deviceKeyService = new NetconfDeviceKeyServiceMock();
+    private final NetworkConfigRegistry netCfgService = new MockNetworkConfigRegistry();
 
     private final ComponentContext context = new MockComponentContext();
 
@@ -97,13 +124,35 @@
         ctrl.cfgService = cfgService;
         ctrl.deviceService = deviceService;
         ctrl.deviceKeyService = deviceKeyService;
+        ctrl.netCfgService = netCfgService;
 
         //Creating mock devices
         deviceInfo1 = new NetconfDeviceInfo("device1", "001", IpAddress.valueOf(DEVICE_1_IP), DEVICE_1_PORT);
         deviceInfo2 = new NetconfDeviceInfo("device2", "002", IpAddress.valueOf(DEVICE_2_IP), DEVICE_2_PORT);
+        deviceInfo2.setSshClientLib(Optional.of(NetconfSshClientLib.ETHZ_SSH2));
         badDeviceInfo3 = new NetconfDeviceInfo("device3", "003", IpAddress.valueOf(BAD_DEVICE_IP), BAD_DEVICE_PORT);
         deviceInfoIpV6 = new NetconfDeviceInfo("deviceIpv6", "004", IpAddress.valueOf(DEVICE_IPV6), IPV6_DEVICE_PORT);
 
+        deviceConfig10Id = DeviceId.deviceId("netconf:" + DEVICE_10_IP + ":" + DEVICE_10_PORT);
+        //Create a JSON entry just like Network Config accepts
+        ObjectMapper mapper = new ObjectMapper();
+        String jsonMessage = "{\n" +
+                "  \"ip\":\"" + DEVICE_10_IP + "\",\n" +
+                "  \"port\":" + DEVICE_10_PORT + ",\n" +
+                "  \"username\":\"" + DEVICE_10_USERNAME + "\",\n" +
+                "  \"password\":\"" + DEVICE_10_PASSWORD + "\",\n" +
+                "  \"" + NetconfDeviceConfig.CONNECT_TIMEOUT + "\":" + DEVICE_10_CONNECT_TIMEOUT + ",\n" +
+                "  \"" + NetconfDeviceConfig.REPLY_TIMEOUT + "\":" + DEVICE_10_REPLY_TIMEOUT + ",\n" +
+                "  \"" + NetconfDeviceConfig.IDLE_TIMEOUT + "\":" + DEVICE_10_IDLE_TIMEOUT + ",\n" +
+                "  \"" + NetconfDeviceConfig.SSHCLIENT + "\":\"" + NetconfSshClientLib.ETHZ_SSH2.toString() + "\"\n" +
+                "}";
+        InputStream jsonStream = new ByteArrayInputStream(jsonMessage.getBytes());
+        JsonNode jsonNode = mapper.readTree(jsonStream);
+        jsonStream.close();
+        ConfigApplyDelegate delegate = new MockDelegate();
+        deviceConfig10 = new NetconfDeviceConfig();
+        deviceConfig10.init(deviceConfig10Id, "netconf", jsonNode, mapper, delegate);
+
         device1 = new TestNetconfDevice(deviceInfo1);
         deviceId1 = deviceInfo1.getDeviceId();
         device2 = new TestNetconfDevice(deviceInfo2);
@@ -162,7 +211,7 @@
                      2, ctrl.netconfConnectTimeout);
         assertEquals("Incorrect NetConf session timeout",
                      1, ctrl.netconfReplyTimeout);
-        assertEquals("ethz-ssh2", ctrl.sshLibrary);
+        assertEquals("ethz-ssh2", ctrl.sshLibrary.toString());
     }
 
     /**
@@ -228,13 +277,41 @@
     }
 
     /**
+     * Check for connection by netconfDeviceConfig.
+     */
+    @Test
+    public void testConnectDeviceNetConfig10() throws Exception {
+        NetconfDevice fetchedDevice10 = ctrl.connectDevice(deviceConfig10Id);
+        assertEquals("Incorrect device fetched - ip",
+                fetchedDevice10.getDeviceInfo().ip().toString(), DEVICE_10_IP);
+        assertEquals("Incorrect device fetched - port",
+                fetchedDevice10.getDeviceInfo().port(), DEVICE_10_PORT);
+        assertEquals("Incorrect device fetched - username",
+                fetchedDevice10.getDeviceInfo().name(), DEVICE_10_USERNAME);
+        assertEquals("Incorrect device fetched - password",
+                fetchedDevice10.getDeviceInfo().password(), DEVICE_10_PASSWORD);
+        assertEquals("Incorrect device fetched - connectTimeout",
+                fetchedDevice10.getDeviceInfo().getConnectTimeoutSec().getAsInt(),
+                DEVICE_10_CONNECT_TIMEOUT);
+        assertEquals("Incorrect device fetched - replyTimeout",
+                fetchedDevice10.getDeviceInfo().getReplyTimeoutSec().getAsInt(),
+                DEVICE_10_REPLY_TIMEOUT);
+        assertEquals("Incorrect device fetched - idleTimeout",
+                fetchedDevice10.getDeviceInfo().getIdleTimeoutSec().getAsInt(),
+                DEVICE_10_IDLE_TIMEOUT);
+        assertEquals("Incorrect device fetched - sshClient",
+                fetchedDevice10.getDeviceInfo().sshClientLib().get(),
+                NetconfSshClientLib.ETHZ_SSH2);
+    }
+
+    /**
      * Check for correct device connection. In this case the device map get modified.
      */
     @Test
     public void testConnectCorrectDevice() throws Exception {
         reflectedDeviceMap.clear();
-        ctrl.connectDevice(deviceInfo1.getDeviceId());
-        ctrl.connectDevice(deviceInfo2.getDeviceId());
+        NetconfDevice device1 = ctrl.connectDevice(deviceInfo1.getDeviceId());
+        NetconfDevice device2 = ctrl.connectDevice(deviceInfo2.getDeviceId());
         assertTrue("Incorrect device connection", ctrl.getDevicesMap().containsKey(deviceId1));
         assertTrue("Incorrect device connection", ctrl.getDevicesMap().containsKey(deviceId2));
         assertEquals("Incorrect device connection", 2, ctrl.getDevicesMap().size());
@@ -416,4 +493,46 @@
             return null;
         }
     }
+
+    private class MockNetworkConfigRegistry extends NetworkConfigRegistryAdapter {
+        NetconfDeviceConfig cfg = null;
+
+        @Override
+        public void registerConfigFactory(ConfigFactory configFactory) {
+            cfgFactories.add(configFactory);
+        }
+
+        @Override
+        public void unregisterConfigFactory(ConfigFactory configFactory) {
+            cfgFactories.remove(configFactory);
+        }
+
+        @Override
+        public void addListener(NetworkConfigListener listener) {
+            netCfgListeners.add(listener);
+        }
+
+        @Override
+        public void removeListener(NetworkConfigListener listener) {
+            netCfgListeners.remove(listener);
+        }
+
+
+        @Override
+        public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
+            DeviceId did = (DeviceId) subject;
+            if (configClass.equals(NetconfDeviceConfig.class)
+                    && did.equals(deviceConfig10Id)) {
+                return (C) deviceConfig10;
+            }
+            return null;
+        }
+
+    }
+
+    private class MockDelegate implements ConfigApplyDelegate {
+        @Override
+        public void onApply(Config configFile) {
+        }
+    }
 }
diff --git a/protocols/netconf/ctl/src/test/java/org/onosproject/netconf/ctl/impl/NetconfSessionImplTest.java b/protocols/netconf/ctl/src/test/java/org/onosproject/netconf/ctl/impl/NetconfSessionImplTest.java
index 1b5e7fa..842bafd 100644
--- a/protocols/netconf/ctl/src/test/java/org/onosproject/netconf/ctl/impl/NetconfSessionImplTest.java
+++ b/protocols/netconf/ctl/src/test/java/org/onosproject/netconf/ctl/impl/NetconfSessionImplTest.java
@@ -16,6 +16,7 @@
 package org.onosproject.netconf.ctl.impl;
 
 import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
@@ -28,6 +29,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Optional;
+import java.util.OptionalInt;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -46,10 +48,11 @@
 import org.junit.Test;
 import org.onlab.junit.TestTools;
 import org.onlab.packet.Ip4Address;
+import org.onosproject.netconf.DatastoreId;
+import org.onosproject.netconf.NetconfController;
 import org.onosproject.netconf.NetconfDeviceInfo;
 import org.onosproject.netconf.NetconfException;
 import org.onosproject.netconf.NetconfSession;
-import org.onosproject.netconf.DatastoreId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -116,28 +119,36 @@
         sshServerNetconf.open();
         log.info("SSH Server opened on port {}", PORT_NUMBER);
 
-        NetconfDeviceInfo deviceInfo = new NetconfDeviceInfo(
+        NetconfController netconfCtl = new NetconfControllerImpl();
+
+        NetconfDeviceInfo deviceInfo1 = new NetconfDeviceInfo(
                 TEST_USERNAME, TEST_PASSWORD, Ip4Address.valueOf(TEST_HOSTNAME), PORT_NUMBER);
 
-        session1 = new NetconfSessionImpl(deviceInfo, ImmutableList.of("urn:ietf:params:netconf:base:1.0"));
+        session1 = new NetconfSessionImpl(deviceInfo1, ImmutableList.of("urn:ietf:params:netconf:base:1.0"));
         log.info("Started NETCONF Session {} with test SSHD server in Unit Test", session1.getSessionId());
         assertTrue("Incorrect sessionId", !session1.getSessionId().equalsIgnoreCase("-1"));
         assertTrue("Incorrect sessionId", !session1.getSessionId().equalsIgnoreCase("0"));
         assertThat(session1.getDeviceCapabilitiesSet(), containsInAnyOrder(
                 NetconfSessionMinaImplTest.DEFAULT_CAPABILITIES.toArray()));
-        session2 = new NetconfSessionImpl(deviceInfo, ImmutableList.of("urn:ietf:params:netconf:base:1.0"));
+
+        NetconfDeviceInfo deviceInfo2 = new NetconfDeviceInfo(
+                TEST_USERNAME, TEST_PASSWORD, Ip4Address.valueOf(TEST_HOSTNAME), PORT_NUMBER);
+        deviceInfo2.setConnectTimeoutSec(OptionalInt.of(11));
+        deviceInfo2.setReplyTimeoutSec(OptionalInt.of(10));
+        deviceInfo2.setIdleTimeoutSec(OptionalInt.of(12));
+        session2 = new NetconfSessionMinaImpl(deviceInfo2, ImmutableList.of("urn:ietf:params:netconf:base:1.0"));
         log.info("Started NETCONF Session {} with test SSHD server in Unit Test", session2.getSessionId());
         assertTrue("Incorrect sessionId", !session2.getSessionId().equalsIgnoreCase("-1"));
         assertTrue("Incorrect sessionId", !session2.getSessionId().equalsIgnoreCase("0"));
         assertThat(session2.getDeviceCapabilitiesSet(), containsInAnyOrder(
                 NetconfSessionMinaImplTest.DEFAULT_CAPABILITIES.toArray()));
-        session3 = new NetconfSessionImpl(deviceInfo);
+        session3 = new NetconfSessionImpl(deviceInfo1);
         log.info("Started NETCONF Session {} with test SSHD server in Unit Test", session3.getSessionId());
         assertTrue("Incorrect sessionId", !session3.getSessionId().equalsIgnoreCase("-1"));
         assertTrue("Incorrect sessionId", !session3.getSessionId().equalsIgnoreCase("0"));
         assertThat(session3.getDeviceCapabilitiesSet(), containsInAnyOrder(
                 NetconfSessionMinaImplTest.DEFAULT_CAPABILITIES_1_1.toArray()));
-        session4 = new NetconfSessionImpl(deviceInfo);
+        session4 = new NetconfSessionImpl(deviceInfo1);
         log.info("Started NETCONF Session {} with test SSHD server in Unit Test", session4.getSessionId());
         assertTrue("Incorrect sessionId", !session4.getSessionId().equalsIgnoreCase("-1"));
         assertTrue("Incorrect sessionId", !session4.getSessionId().equalsIgnoreCase("0"));
@@ -572,6 +583,18 @@
         fail("NETCONF test failed to complete.");
     }
 
+    @Test
+    public void testSessionTimeouts() {
+        assertTrue("SSH Client wrong", session1 instanceof NetconfSessionImpl);
+        assertEquals("Timeout wrong", 5, session1.timeoutConnectSec());
+        assertEquals("Timeout wrong", 5, session1.timeoutReplySec());
+        assertEquals("Timeout wrong", 5, session1.timeoutIdleSec());
+
+        assertTrue("SSH Client wrong", session2 instanceof NetconfSessionMinaImpl);
+        assertEquals("Timeout wrong", 11, session2.timeoutConnectSec());
+        assertEquals("Timeout wrong", 10, session2.timeoutReplySec());
+        assertEquals("Timeout wrong", 12, session2.timeoutIdleSec());
+    }
 
     public static String getTestHelloReply(Optional<Long> sessionId, boolean useChunkedFraming) {
         if (useChunkedFraming) {
@@ -736,4 +759,4 @@
             return session.copyConfig(target, source);
         }
     }
-}
\ No newline at end of file
+}