Northbound Netconf servers moved to new transport implementation 89/106789/33
authorRuslan Kashapov <ruslan.kashapov@pantheon.tech>
Mon, 3 Jul 2023 14:51:49 +0000 (17:51 +0300)
committerRobert Varga <robert.varga@pantheon.tech>
Thu, 21 Sep 2023 12:26:00 +0000 (14:26 +0200)
Deprecated NetconfServerDispatcher is replaced with usage of new
NetconfServerFactory. Netty thread group provider was added as fix
for incompatible netty-threadgroup-config (nio only, fails with epoll)
from controller project.

JIRA: NETCONF-1106
Change-Id: I076020565b844ac7ddc4dfecc4b23154b825e4bf
Signed-off-by: Ruslan Kashapov <ruslan.kashapov@pantheon.tech>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
14 files changed:
apps/netconf-nb/pom.xml
apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/DefaultNetconfServerDispatcher.java [deleted file]
apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/NetconfNorthboundSshServer.java [deleted file]
apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/NetconfNorthboundTcpServer.java [deleted file]
apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/OSGiNetconfServer.java
apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/SshServerTransport.java [new file with mode: 0644]
apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/TcpServerTransport.java [new file with mode: 0644]
apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/TransportFactoryHolder.java [new file with mode: 0644]
apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/ssh/ExecutorServiceFacade.java
apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/ssh/RemoteNetconfCommand.java
apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/ssh/SshProxyClientHandler.java
apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/ssh/SshProxyServer.java
apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/ssh/SshProxyServerConfiguration.java
apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/ssh/SshProxyServerConfigurationBuilder.java

index d0a7b0854c8801ee8c6bd8081c9274caf50be3f8..fe57f154243972df289cd0d378bfda755e0189de 100644 (file)
             <groupId>org.opendaylight.netconf</groupId>
             <artifactId>shaded-sshd</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.guicedee.services</groupId>
+            <artifactId>javax.inject</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.annotation</groupId>
+            <artifactId>jakarta.annotation-api</artifactId>
+            <scope>provided</scope>
+            <optional>true</optional>
+        </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.framework</artifactId>
diff --git a/apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/DefaultNetconfServerDispatcher.java b/apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/DefaultNetconfServerDispatcher.java
deleted file mode 100644 (file)
index c2c6f4f..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.netconf.northbound;
-
-import static java.util.Objects.requireNonNull;
-
-import io.netty.channel.EventLoopGroup;
-import java.util.Map;
-import org.opendaylight.netconf.server.NetconfServerDispatcherImpl;
-import org.opendaylight.netconf.server.ServerChannelInitializer;
-import org.opendaylight.netconf.server.api.NetconfServerDispatcher;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-
-@Component(factory = DefaultNetconfServerDispatcher.FACTORY_NAME, service = NetconfServerDispatcher.class)
-public final class DefaultNetconfServerDispatcher extends NetconfServerDispatcherImpl {
-    static final String FACTORY_NAME = "org.opendaylight.netconf.impl.mdsal.DefaultNetconfServerDispatcher";
-
-    private static final String BOSS_PROP = ".bossGroup";
-    private static final String WORKER_PROP = ".workerGroup";
-    private static final String INITIALIZER_PROP = ".initializer";
-
-    @Activate
-    public DefaultNetconfServerDispatcher(final Map<String, ?> properties) {
-        super(OSGiNetconfServer.extractProp(properties, INITIALIZER_PROP, ServerChannelInitializer.class),
-            OSGiNetconfServer.extractProp(properties, BOSS_PROP, EventLoopGroup.class),
-            OSGiNetconfServer.extractProp(properties, WORKER_PROP, EventLoopGroup.class));
-    }
-
-    static Map<String, ?> props(final EventLoopGroup bossGroup, final EventLoopGroup workerGroup,
-            final ServerChannelInitializer initializer) {
-        return Map.of(
-            "type", "netconf-server-dispatcher",
-            BOSS_PROP, requireNonNull(bossGroup),
-            WORKER_PROP, requireNonNull(workerGroup),
-            INITIALIZER_PROP, requireNonNull(initializer));
-    }
-}
diff --git a/apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/NetconfNorthboundSshServer.java b/apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/NetconfNorthboundSshServer.java
deleted file mode 100644 (file)
index 32acf74..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (c) 2016 Inocybe Technologies and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.netconf.northbound;
-
-import io.netty.channel.ChannelFuture;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.local.LocalAddress;
-import io.netty.util.concurrent.EventExecutor;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.util.concurrent.Executors;
-import org.opendaylight.netconf.auth.AuthProvider;
-import org.opendaylight.netconf.northbound.ssh.SshProxyServer;
-import org.opendaylight.netconf.northbound.ssh.SshProxyServerConfigurationBuilder;
-import org.opendaylight.netconf.server.api.NetconfServerDispatcher;
-import org.opendaylight.netconf.shaded.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Deactivate;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.Designate;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * NETCONF server for MD-SAL (listening by default on port 2830).
- */
-@Component(service = { }, configurationPid = "org.opendaylight.netconf.ssh")
-@Designate(ocd = NetconfNorthboundSshServer.Configuration.class)
-public final class NetconfNorthboundSshServer implements AutoCloseable {
-    @ObjectClassDefinition
-    public @interface Configuration {
-        @AttributeDefinition
-        String bindingAddress() default "0.0.0.0";
-        @AttributeDefinition(min = "1", max = "65535")
-        int portNumber() default 2830;
-    }
-
-    private static final Logger LOG = LoggerFactory.getLogger(NetconfNorthboundSshServer.class);
-
-    private final ChannelFuture localServer;
-    private final SshProxyServer sshProxyServer;
-
-    @Activate
-    public NetconfNorthboundSshServer(
-            @Reference final NetconfServerDispatcher netconfServerDispatcher,
-            @Reference(target = "(type=global-worker-group)") final EventLoopGroup workerGroup,
-            @Reference(target = "(type=global-event-executor)") final EventExecutor eventExecutor,
-            @Reference(target = "(type=netconf-auth-provider)") final AuthProvider authProvider,
-            final Configuration configuration) {
-        this(netconfServerDispatcher, workerGroup, eventExecutor, authProvider, configuration.bindingAddress(),
-            configuration.portNumber());
-    }
-
-    public NetconfNorthboundSshServer(final NetconfServerDispatcher netconfServerDispatcher,
-            final EventLoopGroup workerGroup, final EventExecutor eventExecutor, final AuthProvider authProvider,
-            final String bindingAddress, final int portNumber) {
-        final LocalAddress localAddress = new LocalAddress(String.valueOf(portNumber));
-        final var sshProxyServerConfiguration = new SshProxyServerConfigurationBuilder()
-            .setBindingAddress(getInetAddress(bindingAddress, portNumber))
-            .setLocalAddress(localAddress)
-            .setAuthenticator(authProvider)
-            .setIdleTimeout(Integer.MAX_VALUE)
-            .setKeyPairProvider(new SimpleGeneratorHostKeyProvider())
-            .createSshProxyServerConfiguration();
-
-        localServer = netconfServerDispatcher.createLocalServer(localAddress);
-        sshProxyServer = new SshProxyServer(Executors.newScheduledThreadPool(1), workerGroup, eventExecutor);
-
-        localServer.addListener(future -> {
-            if (future.isDone() && !future.isCancelled()) {
-                try {
-                    sshProxyServer.bind(sshProxyServerConfiguration);
-                } catch (final IOException e) {
-                    throw new IllegalStateException("Unable to start SSH netconf server", e);
-                }
-                LOG.info("Netconf SSH endpoint started successfully at {}", bindingAddress);
-            } else {
-                LOG.warn("Unable to start SSH netconf server at {}", bindingAddress, future.cause());
-                throw new IllegalStateException("Unable to start SSH netconf server", future.cause());
-            }
-        });
-    }
-
-    @Deactivate
-    @Override
-    public void close() throws IOException {
-        sshProxyServer.close();
-
-        if (localServer.isDone()) {
-            localServer.channel().close();
-        } else {
-            localServer.cancel(true);
-        }
-    }
-
-    private static InetSocketAddress getInetAddress(final String bindingAddress, final int portNumber) {
-        final var ipAddress = IetfInetUtil.ipAddressFor(bindingAddress);
-        return new InetSocketAddress(IetfInetUtil.inetAddressFor(ipAddress), portNumber);
-    }
-}
diff --git a/apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/NetconfNorthboundTcpServer.java b/apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/NetconfNorthboundTcpServer.java
deleted file mode 100644 (file)
index bc35f81..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2016 Inocybe Technologies and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.netconf.northbound;
-
-import io.netty.channel.ChannelFuture;
-import java.net.InetSocketAddress;
-import org.opendaylight.netconf.server.api.NetconfServerDispatcher;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.Designate;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Create an MD-SAL NETCONF server using TCP.
- */
-@Component(service = { }, configurationPid = "org.opendaylight.netconf.tcp", enabled = false)
-@Designate(ocd = NetconfNorthboundTcpServer.Configuration.class)
-public final class NetconfNorthboundTcpServer implements AutoCloseable {
-    @ObjectClassDefinition
-    public @interface Configuration {
-        @AttributeDefinition
-        String bindingAddress() default "0.0.0.0";
-        @AttributeDefinition(min = "1", max = "65535")
-        int portNumber() default 2831;
-    }
-
-    private static final Logger LOG = LoggerFactory.getLogger(NetconfNorthboundTcpServer.class);
-
-    private final ChannelFuture tcpServer;
-
-    @Activate
-    public NetconfNorthboundTcpServer(
-            @Reference(target = "(type=netconf-server-dispatcher)") final NetconfServerDispatcher serverDispatcher,
-            final Configuration configuration) {
-        this(serverDispatcher, configuration.bindingAddress(), configuration.portNumber());
-    }
-
-    public NetconfNorthboundTcpServer(final NetconfServerDispatcher serverDispatcher, final String address,
-            final int port) {
-        final InetSocketAddress inetAddress = getInetAddress(address, port);
-        tcpServer = serverDispatcher.createServer(inetAddress);
-        tcpServer.addListener(future -> {
-            if (future.isDone() && future.isSuccess()) {
-                LOG.info("Netconf TCP endpoint started successfully at {}", inetAddress);
-            } else {
-                LOG.warn("Unable to start TCP netconf server at {}", inetAddress, future.cause());
-                throw new IllegalStateException("Unable to start TCP netconf server", future.cause());
-            }
-        });
-    }
-
-    @Override
-    public void close() {
-        if (tcpServer.isDone()) {
-            tcpServer.channel().close();
-        } else {
-            tcpServer.cancel(true);
-        }
-    }
-
-    private static InetSocketAddress getInetAddress(final String bindingAddress, final int portNumber) {
-        final var inetAd = IetfInetUtil.inetAddressFor(IetfInetUtil.ipAddressFor(bindingAddress));
-        return new InetSocketAddress(inetAd, portNumber);
-    }
-}
index 644ba53a3b4902f99de91bc4ab4b1205fe69519d..fca737a3e87d0fb9c4067430a17efec886697248 100644 (file)
@@ -10,7 +10,6 @@ package org.opendaylight.netconf.northbound;
 import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
-import io.netty.channel.EventLoopGroup;
 import io.netty.util.Timer;
 import java.util.Map;
 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
@@ -30,7 +29,7 @@ import org.osgi.service.metatype.annotations.AttributeDefinition;
 import org.osgi.service.metatype.annotations.Designate;
 import org.osgi.service.metatype.annotations.ObjectClassDefinition;
 
-@Component(service = { }, configurationPid = "org.opendaylight.netconf.impl")
+@Component(service = { OSGiNetconfServer.class }, configurationPid = "org.opendaylight.netconf.impl")
 @Designate(ocd = OSGiNetconfServer.Configuration.class)
 public final class OSGiNetconfServer {
     @ObjectClassDefinition
@@ -43,39 +42,36 @@ public final class OSGiNetconfServer {
 
     private final AggregatedNetconfOperationServiceFactory mappers = new AggregatedNetconfOperationServiceFactory();
     private final ComponentInstance<DefaultNetconfMonitoringService> monitoring;
-    private final ComponentInstance<DefaultNetconfServerDispatcher> dispatcher;
+    private final ServerChannelInitializer serverChannelInitializer;
 
     @Activate
     public OSGiNetconfServer(
             @Reference(target = "(component.factory=" + DefaultNetconfMonitoringService.FACTORY_NAME + ")")
             final ComponentFactory<DefaultNetconfMonitoringService> monitoringFactory,
-            @Reference(target = "(component.factory=" + DefaultNetconfServerDispatcher.FACTORY_NAME + ")")
-            final ComponentFactory<DefaultNetconfServerDispatcher> dispatcherFactory,
             @Reference(target = "(type=mapper-aggregator-registry)")
             final NetconfOperationServiceFactory mapperAggregatorRegistry,
             @Reference(target = "(type=global-netconf-ssh-scheduled-executor)")
             final ScheduledThreadPool sshScheduledExecutor,
-            @Reference(target = "(type=global-boss-group)") final EventLoopGroup bossGroup,
-            @Reference(target = "(type=global-boss-group)") final EventLoopGroup workerGroup,
             @Reference(target = "(type=global-timer)") final Timer timer,
             @Reference final SessionIdProvider sessionIdProvider,
             final Configuration configuration) {
         mappers.onAddNetconfOperationServiceFactory(mapperAggregatorRegistry);
         monitoring = monitoringFactory.newInstance(FrameworkUtil.asDictionary(DefaultNetconfMonitoringService.props(
             mapperAggregatorRegistry, sshScheduledExecutor, configuration.monitoring$_$update$_$interval())));
-        dispatcher = dispatcherFactory.newInstance(FrameworkUtil.asDictionary(DefaultNetconfServerDispatcher.props(
-            bossGroup, workerGroup, new ServerChannelInitializer(new NetconfServerSessionNegotiatorFactory(timer,
-                mappers, sessionIdProvider, configuration.connection$_$timeout$_$millis(),
-                monitoring.getInstance())))));
+        serverChannelInitializer =  new ServerChannelInitializer(new NetconfServerSessionNegotiatorFactory(timer,
+            mappers, sessionIdProvider, configuration.connection$_$timeout$_$millis(), monitoring.getInstance()));
     }
 
     @Deactivate
     public void deactivate() {
-        dispatcher.dispose();
         monitoring.dispose();
         mappers.close();
     }
 
+    ServerChannelInitializer serverChannelInitializer() {
+        return serverChannelInitializer;
+    }
+
     static <T> T extractProp(final Map<String, ?> properties, final String key, final Class<T> valueType) {
         return valueType.cast(verifyNotNull(properties.get(requireNonNull(key))));
     }
diff --git a/apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/SshServerTransport.java b/apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/SshServerTransport.java
new file mode 100644 (file)
index 0000000..aa40dc5
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2016 Inocybe Technologies and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netconf.northbound;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.opendaylight.netconf.auth.AuthProvider;
+import org.opendaylight.netconf.server.BaseTransportChannelListener;
+import org.opendaylight.netconf.server.NetconfSubsystemFactory;
+import org.opendaylight.netconf.server.ServerChannelInitializer;
+import org.opendaylight.netconf.shaded.sshd.server.auth.password.UserAuthPasswordFactory;
+import org.opendaylight.netconf.shaded.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+import org.opendaylight.netconf.transport.api.UnsupportedConfigurationException;
+import org.opendaylight.netconf.transport.ssh.SSHServer;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.server.rev230417.netconf.server.listen.stack.grouping.transport.ssh.ssh.TcpServerParametersBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tcp.server.rev230417.TcpServerGrouping;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * NETCONF server for MD-SAL (listening by default on port 2830).
+ */
+@Component(service = { }, configurationPid = "org.opendaylight.netconf.ssh")
+@Designate(ocd = SshServerTransport.Configuration.class)
+public final class SshServerTransport extends BaseTransportChannelListener implements AutoCloseable {
+    @ObjectClassDefinition
+    public @interface Configuration {
+        @AttributeDefinition
+        String bindingAddress() default "0.0.0.0";
+        @AttributeDefinition(min = "1", max = "65535")
+        int portNumber() default 2830;
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(SshServerTransport.class);
+
+    private final SSHServer sshServer;
+
+    @Activate
+    public SshServerTransport(@Reference final TransportFactoryHolder factoryHolder,
+            @Reference final OSGiNetconfServer backend,
+            @Reference(target = "(type=netconf-auth-provider)") final AuthProvider authProvider,
+            final Configuration configuration) {
+        this(factoryHolder, backend.serverChannelInitializer(), authProvider, new TcpServerParametersBuilder()
+            .setLocalAddress(IetfInetUtil.ipAddressFor(configuration.bindingAddress()))
+            .setLocalPort(new PortNumber(Uint16.valueOf(configuration.portNumber())))
+            .build());
+    }
+
+    public SshServerTransport(final TransportFactoryHolder factoryHolder, final ServerChannelInitializer initializer,
+            final AuthProvider authProvider, final TcpServerGrouping listenParams) {
+        final var localAddr = listenParams.requireLocalAddress().stringValue();
+        final var localPort = listenParams.requireLocalPort().getValue();
+
+        try {
+            sshServer = factoryHolder.factory().listenServer(this, new NetconfSubsystemFactory(initializer),
+                listenParams, null, factoryMgr -> {
+                    factoryMgr.setUserAuthFactories(List.of(UserAuthPasswordFactory.INSTANCE));
+                    factoryMgr.setPasswordAuthenticator(
+                        (username, password, session) -> authProvider.authenticated(username, password));
+                    factoryMgr.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
+                })
+                .get();
+        } catch (UnsupportedConfigurationException | ExecutionException | InterruptedException e) {
+            LOG.warn("Could not start SSH NETCONF server at {}:{}", localAddr, localPort, e);
+            throw new IllegalStateException("Unable to start SSH netconf server", e);
+        }
+
+        LOG.info("SSH NETCONF server at {}:{} started", localAddr, localPort);
+    }
+
+    @Deactivate
+    @Override
+    public void close() throws IOException {
+        try {
+            sshServer.shutdown().get();
+        } catch (ExecutionException | InterruptedException e) {
+            LOG.warn("Could not stop SSH NETCONF server {}", sshServer, e);
+        }
+    }
+}
diff --git a/apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/TcpServerTransport.java b/apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/TcpServerTransport.java
new file mode 100644 (file)
index 0000000..d6819cc
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2016 Inocybe Technologies and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netconf.northbound;
+
+import java.util.concurrent.ExecutionException;
+import org.opendaylight.netconf.server.BaseServerTransport;
+import org.opendaylight.netconf.server.ServerChannelInitializer;
+import org.opendaylight.netconf.transport.api.UnsupportedConfigurationException;
+import org.opendaylight.netconf.transport.tcp.TCPServer;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.server.rev230417.netconf.server.listen.stack.grouping.transport.tls.tls.TcpServerParametersBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tcp.server.rev230417.TcpServerGrouping;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Create an MD-SAL NETCONF server using TCP.
+ */
+@Component(service = {}, configurationPid = "org.opendaylight.netconf.tcp", enabled = false)
+@Designate(ocd = TcpServerTransport.Configuration.class)
+public final class TcpServerTransport extends BaseServerTransport implements AutoCloseable {
+    @ObjectClassDefinition
+    public @interface Configuration {
+        @AttributeDefinition
+        String bindingAddress() default "0.0.0.0";
+
+        @AttributeDefinition(min = "1", max = "65535")
+        int portNumber() default 2831;
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(TcpServerTransport.class);
+
+    private final TCPServer tcpServer;
+
+    @Activate
+    public TcpServerTransport(@Reference final TransportFactoryHolder factoryHolder,
+            @Reference final OSGiNetconfServer backend, final Configuration configuration) {
+        // FIXME: create an instantiation and do not use TLS
+        this(factoryHolder, backend.serverChannelInitializer(), new TcpServerParametersBuilder()
+            .setLocalAddress(IetfInetUtil.ipAddressFor(configuration.bindingAddress()))
+            .setLocalPort(new PortNumber(Uint16.valueOf(configuration.portNumber())))
+            .build());
+    }
+
+    public TcpServerTransport(final TransportFactoryHolder factoryHolder, final ServerChannelInitializer initializer,
+            final TcpServerGrouping listenParams) {
+        super(initializer);
+
+        final var localAddr = listenParams.requireLocalAddress().stringValue();
+        final var localPort = listenParams.requireLocalPort().getValue();
+
+        try {
+            tcpServer = TCPServer.listen(this, factoryHolder.factory().newServerBootstrap(), listenParams).get();
+        } catch (UnsupportedConfigurationException | ExecutionException | InterruptedException e) {
+            LOG.warn("Could not start TCP NETCONF server at {}:{}", localAddr, localPort, e);
+            throw new IllegalStateException("Could not start TCP NETCONF server", e);
+        }
+
+        LOG.info("TCP NETCONF server at {}:{} started", localAddr, localPort);
+    }
+
+    @Deactivate
+    @Override
+    public void close() {
+        try {
+            tcpServer.shutdown().get();
+        } catch (ExecutionException | InterruptedException e) {
+            LOG.warn("Could not stop TCP NETCONF server {}", tcpServer, e);
+        }
+    }
+}
diff --git a/apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/TransportFactoryHolder.java b/apps/netconf-nb/src/main/java/org/opendaylight/netconf/northbound/TransportFactoryHolder.java
new file mode 100644 (file)
index 0000000..860f25a
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2023 PANTHEON.tech s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netconf.northbound;
+
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import org.opendaylight.netconf.transport.ssh.SSHTransportStackFactory;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A simple holder of {@link SSHTransportStackFactory}, bridging to configuration. The factory encapsulates Netty
+ * event loop groups, which is suitable for use with TLS/TCP transports as well.
+ */
+@Singleton
+@Component(service = TransportFactoryHolder.class, configurationPid = "org.opendaylight.netconf.northbound.netty")
+@Designate(ocd = TransportFactoryHolder.Configuration.class)
+public final class TransportFactoryHolder implements AutoCloseable {
+    /**
+     * Configuration of the NETCONF northbound.
+     */
+    @ObjectClassDefinition()
+    public @interface Configuration {
+        @AttributeDefinition(name = "Number of Netty boss threads", min = "0")
+        int boss$_$threads() default 0;
+
+        @AttributeDefinition(name = "Number of Netty worker threads", min = "0")
+        int worker$_$threads() default 0;
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(TransportFactoryHolder.class);
+
+    private final SSHTransportStackFactory factory;
+
+    @Inject
+    public TransportFactoryHolder() {
+        this(0, 0);
+    }
+
+    @Activate
+    public TransportFactoryHolder(final Configuration configuration) {
+        this(configuration.boss$_$threads(), configuration.worker$_$threads());
+    }
+
+    public TransportFactoryHolder(final int bossThreads, final int workerThreads) {
+        factory = new SSHTransportStackFactory("odl-netconf-nb-worker", workerThreads,
+            "odl-netconf-nb-boss", bossThreads);
+        LOG.info("NETCONF Northbound Netty context initialized");
+    }
+
+    @PreDestroy
+    @Deactivate
+    @Override
+    public void close() {
+        LOG.info("NETCONF Northbound Netty context shutting down");
+        factory.close();
+        LOG.info("NETCONF Northbound Netty context shut down");
+    }
+
+    SSHTransportStackFactory factory() {
+        return factory;
+    }
+}
index bfb93d8d1623b16274112d6a94f62959928e5d12..78ca7bd4f0940d4dac078a4d36617e180440b4e6 100644 (file)
@@ -16,6 +16,7 @@ import java.util.concurrent.ExecutorService;
  * Facade for guarding against {@link #shutdown()} invocations. This is necessary as SSHD wants to shutdown the executor
  * when the server shuts down.
  */
+@Deprecated(since = "7.0.0", forRemoval = true)
 final class ExecutorServiceFacade extends ForwardingExecutorService {
     private final ExecutorService delegate;
 
index 495085d448f58ef793ef40fd6a60095b0c389583..0ebcb0373b826de150bbb9362ea9b694f81a5945 100644 (file)
@@ -40,6 +40,7 @@ import org.slf4j.LoggerFactory;
  * <p>
  * Command is Apache Mina SSH terminology for objects handling ssh data.
  */
+@Deprecated(since = "7.0.0", forRemoval = true)
 final class RemoteNetconfCommand implements AsyncCommand {
     private static final Logger LOG = LoggerFactory.getLogger(RemoteNetconfCommand.class);
 
index 6e53fe5b0b2b9705f2d657c92cbfd157182e33e3..b9e5fe200e3ddd9165b8ddced1f33461e66017c1 100644 (file)
@@ -24,6 +24,7 @@ import org.slf4j.LoggerFactory;
  * Netty handler that reads SSH from remote client and writes to delegate server
  * and reads from delegate server and writes to remote client.
  */
+@Deprecated(since = "7.0.0", forRemoval = true)
 final class SshProxyClientHandler extends ChannelInboundHandlerAdapter {
     private static final Logger LOG = LoggerFactory.getLogger(SshProxyClientHandler.class);
 
index f5fbee75c44d09e969fa7409be3546c3499390d7..421e231c87fcdd282a700304e3639bed31775b99 100644 (file)
@@ -45,6 +45,7 @@ import org.opendaylight.netconf.shaded.sshd.server.SshServer;
  * Proxy SSH server that just delegates decrypted content to a delegate server within same VM.
  * Implemented using Apache Mina SSH lib.
  */
+@Deprecated(since = "7.0.0", forRemoval = true)
 public class SshProxyServer implements AutoCloseable {
     private final SshServer sshServer;
     private final ScheduledExecutorService minaTimerExecutor;
index c54111ec35bb31ecbc4b91fed29c653517bcdd8e..876a17d0c3fa382cd575d8740e17f78c539c2eb3 100644 (file)
@@ -17,6 +17,7 @@ import org.opendaylight.netconf.auth.AuthProvider;
 import org.opendaylight.netconf.shaded.sshd.common.keyprovider.KeyPairProvider;
 import org.opendaylight.netconf.shaded.sshd.server.auth.pubkey.PublickeyAuthenticator;
 
+@Deprecated(since = "7.0.0", forRemoval = true)
 public final class SshProxyServerConfiguration {
     private final InetSocketAddress bindingAddress;
     private final LocalAddress localAddress;
index 7fe2cd51d33fab5cdac48f238c890a6aa21b894c..dd46774d8d2e67a0648bd8795d7e0782efeabbb5 100644 (file)
@@ -13,6 +13,7 @@ import org.opendaylight.netconf.auth.AuthProvider;
 import org.opendaylight.netconf.shaded.sshd.common.keyprovider.KeyPairProvider;
 import org.opendaylight.netconf.shaded.sshd.server.auth.pubkey.PublickeyAuthenticator;
 
+@Deprecated(since = "7.0.0", forRemoval = true)
 public final class SshProxyServerConfigurationBuilder {
     private InetSocketAddress bindingAddress;
     private LocalAddress localAddress;