Refresh IETF client/server models
[netconf.git] / transport / transport-ssh / src / main / java / org / opendaylight / netconf / transport / ssh / TransportSshClient.java
index 9b680f8cb9e52c2e67b681ab9d9a46676cf14276..6888fbfe4a8b111a705742872cf1353d3edcddd0 100644 (file)
@@ -11,8 +11,8 @@ import static java.util.Objects.requireNonNull;
 
 import com.google.common.collect.ImmutableList;
 import com.google.errorprone.annotations.DoNotCall;
-import io.netty.channel.EventLoopGroup;
 import java.security.cert.Certificate;
+import java.util.concurrent.ScheduledExecutorService;
 import org.opendaylight.netconf.shaded.sshd.client.ClientBuilder;
 import org.opendaylight.netconf.shaded.sshd.client.SshClient;
 import org.opendaylight.netconf.shaded.sshd.client.auth.UserAuthFactory;
@@ -23,12 +23,13 @@ import org.opendaylight.netconf.shaded.sshd.client.auth.password.UserAuthPasswor
 import org.opendaylight.netconf.shaded.sshd.client.auth.pubkey.UserAuthPublicKeyFactory;
 import org.opendaylight.netconf.shaded.sshd.client.keyverifier.ServerKeyVerifier;
 import org.opendaylight.netconf.shaded.sshd.common.keyprovider.KeyIdentityProvider;
+import org.opendaylight.netconf.shaded.sshd.netty.NettyIoServiceFactoryFactory;
 import org.opendaylight.netconf.transport.api.UnsupportedConfigurationException;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.crypto.types.rev230417.password.grouping.password.type.CleartextPassword;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev230417.ssh.client.grouping.ClientIdentity;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev230417.ssh.client.grouping.Keepalives;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev230417.ssh.client.grouping.ServerAuthentication;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.common.rev230417.TransportParamsGrouping;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.crypto.types.rev231228.password.grouping.password.type.CleartextPassword;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev231228.ssh.client.grouping.ClientIdentity;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev231228.ssh.client.grouping.Keepalives;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev231228.ssh.client.grouping.ServerAuthentication;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.common.rev231228.TransportParamsGrouping;
 
 /**
  * Our internal-use {@link SshClient}. We reuse all the properties and logic of an {@link SshClient}, but we never allow
@@ -68,13 +69,16 @@ final class TransportSshClient extends SshClient {
      * {@code ietf-netconf-client.yang} configuration.
      */
     static final class Builder extends ClientBuilder {
-        private final EventLoopGroup group;
+        private final NettyIoServiceFactoryFactory ioServiceFactory;
+        private final ScheduledExecutorService executorService;
 
+        private ClientFactoryManagerConfigurator configurator;
         private Keepalives keepAlives;
         private ClientIdentity clientIdentity;
 
-        Builder(final EventLoopGroup group) {
-            this.group = requireNonNull(group);
+        Builder(final NettyIoServiceFactoryFactory ioServiceFactory, final ScheduledExecutorService executorService) {
+            this.ioServiceFactory = requireNonNull(ioServiceFactory);
+            this.executorService = requireNonNull(executorService);
         }
 
         Builder transportParams(final TransportParamsGrouping params) throws UnsupportedConfigurationException {
@@ -82,8 +86,8 @@ final class TransportSshClient extends SshClient {
             return this;
         }
 
-        Builder keepAlives(final Keepalives newkeepAlives) {
-            keepAlives = newkeepAlives;
+        Builder keepAlives(final Keepalives newKeepAlives) {
+            keepAlives = newKeepAlives;
             return this;
         }
 
@@ -114,6 +118,11 @@ final class TransportSshClient extends SshClient {
             return this;
         }
 
+        Builder configurator(final ClientFactoryManagerConfigurator newConfigurator) {
+            configurator = newConfigurator;
+            return this;
+        }
+
         TransportSshClient buildChecked() throws UnsupportedConfigurationException {
             final var ret = (TransportSshClient) super.build(true);
             if (keepAlives != null) {
@@ -121,16 +130,30 @@ final class TransportSshClient extends SshClient {
             } else {
                 ConfigUtils.setKeepAlives(ret, null, null);
             }
+            if (clientIdentity == null) {
+                throw new UnsupportedConfigurationException("Client parameters are required");
+            }
+            final var username = clientIdentity.getUsername();
+            if (username == null) {
+                throw new UnsupportedConfigurationException("Client parameters are missing username");
+            }
+
             if (clientIdentity != null && clientIdentity.getNone() == null) {
-                setClientIdentity(ret, clientIdentity);
+                setClientIdentity(ret, clientIdentity, configurator == null);
+            }
+            if (configurator != null) {
+                configurator.configureClientFactoryManager(ret);
             }
-            ret.setScheduledExecutorService(group);
+            ret.setIoServiceFactoryFactory(ioServiceFactory);
+            ret.setScheduledExecutorService(executorService);
 
             try {
                 ret.checkConfig();
             } catch (IllegalArgumentException e) {
                 throw new UnsupportedConfigurationException("Inconsistent client configuration", e);
             }
+
+            ret.setSessionFactory(new TransportClientSessionFactory(ret, username));
             return ret;
         }
 
@@ -166,8 +189,8 @@ final class TransportSshClient extends SshClient {
             return super.fillWithDefaultValues();
         }
 
-        private static void setClientIdentity(final TransportSshClient client, final ClientIdentity clientIdentity)
-                throws UnsupportedConfigurationException {
+        private static void setClientIdentity(final TransportSshClient client, final ClientIdentity clientIdentity,
+                final boolean throwExceptionIfNoAuthMethodDefined) throws UnsupportedConfigurationException {
             final var authFactoriesListBuilder = ImmutableList.<UserAuthFactory>builder();
             final var password = clientIdentity.getPassword();
             if (password != null) {
@@ -184,7 +207,8 @@ final class TransportSshClient extends SshClient {
                 var factory = new UserAuthHostBasedFactory();
                 factory.setClientHostKeys(HostKeyIdentityProvider.wrap(keyPair));
                 factory.setClientUsername(clientIdentity.getUsername());
-                factory.setClientHostname(null); // not provided via config
+                // not provided via config
+                factory.setClientHostname(null);
                 factory.setSignatureFactories(client.getSignatureFactories());
                 authFactoriesListBuilder.add(factory);
             }
@@ -197,11 +221,13 @@ final class TransportSshClient extends SshClient {
                 authFactoriesListBuilder.add(factory);
             }
             // FIXME implement authentication using X509 certificate
+
             final var userAuthFactories = authFactoriesListBuilder.build();
-            if (userAuthFactories.isEmpty()) {
+            if (!userAuthFactories.isEmpty()) {
+                client.setUserAuthFactories(userAuthFactories);
+            } else if (throwExceptionIfNoAuthMethodDefined) {
                 throw new UnsupportedConfigurationException("Client Identity has no authentication mechanism defined");
             }
-            client.setUserAuthFactories(userAuthFactories);
         }
     }
 }