Convert mdsal-netconf-ssh to OSGi DS 14/104314/2
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 8 Feb 2023 16:38:30 +0000 (17:38 +0100)
committerRobert Varga <nite@hq.sk>
Wed, 8 Feb 2023 17:55:04 +0000 (17:55 +0000)
This is a rather simple blueprint, just convert the single component we
have here. Since Declarative Services shutdown dependents before
deactivating the reference, we can also squash the unbind() method.

JIRA: NETCONF-956
Change-Id: If3c5af7bd212583d0f37af085a33468a8d905802
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
netconf/mdsal-netconf-ssh/pom.xml
netconf/mdsal-netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/NetconfNorthboundSshServer.java
netconf/mdsal-netconf-ssh/src/main/resources/OSGI-INF/blueprint/netconf-ssh.xml [deleted file]
netconf/mdsal-netconf-ssh/src/test/java/org/opendaylight/netconf/ssh/authentication/SSHServerTest.java

index 47adb1af3d39b00fc2e7fda94a5cccaeb2dcd40c..e64540da21999308334c8547f65037b2c1e54e11 100644 (file)
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
     </dependency>
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-buffer</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-common</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-transport</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.mdsal.binding.model.ietf</groupId>
       <artifactId>rfc6991-ietf-inet-types</artifactId>
     </dependency>
     <dependency>
-      <groupId>${project.groupId}</groupId>
+      <groupId>org.opendaylight.netconf</groupId>
       <artifactId>netconf-api</artifactId>
     </dependency>
     <dependency>
-      <groupId>${project.groupId}</groupId>
+      <groupId>org.opendaylight.netconf</groupId>
       <artifactId>netconf-auth</artifactId>
     </dependency>
     <dependency>
-      <groupId>${project.groupId}</groupId>
-      <artifactId>netconf-util</artifactId>
+      <groupId>org.opendaylight.netconf</groupId>
+      <artifactId>netconf-netty-util</artifactId>
     </dependency>
     <dependency>
-      <groupId>${project.groupId}</groupId>
-      <artifactId>netconf-netty-util</artifactId>
+      <groupId>org.opendaylight.netconf</groupId>
+      <artifactId>shaded-sshd</artifactId>
     </dependency>
     <dependency>
       <groupId>org.osgi</groupId>
-      <artifactId>org.osgi.framework</artifactId>
+      <artifactId>org.osgi.service.component.annotations</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.service.metatype.annotations</artifactId>
     </dependency>
-  </dependencies>
 
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-handler</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.netconf</groupId>
+      <artifactId>netconf-util</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
 </project>
index b0c73e013f4f825620595ae972402b64c39a57b6..80916d391858c776dba0d5433af0127dc63bcd07 100644 (file)
@@ -12,59 +12,75 @@ import io.netty.channel.EventLoopGroup;
 import io.netty.channel.local.LocalAddress;
 import io.netty.util.concurrent.EventExecutor;
 import java.io.IOException;
-import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.util.concurrent.Executors;
 import org.opendaylight.netconf.api.NetconfServerDispatcher;
 import org.opendaylight.netconf.auth.AuthProvider;
 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.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
-import org.osgi.framework.ServiceReference;
+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;
 
-public class NetconfNorthboundSshServer {
+/**
+ * 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);
 
-    // Do not store unencrypted private key
-    private static final String DEFAULT_PRIVATE_KEY_PATH = null;
-    private static final String DEFAULT_ALGORITHM = "RSA";
-    private static final int DEFAULT_KEY_SIZE = 4096;
-
     private final ChannelFuture localServer;
     private final SshProxyServer sshProxyServer;
 
-    public NetconfNorthboundSshServer(final NetconfServerDispatcher netconfServerDispatcher,
-                                      final EventLoopGroup workerGroup,
-                                      final EventExecutor eventExecutor,
-                                      final String bindingAddress,
-                                      final String portNumber,
-                                      final AuthProvider authProvider) {
+    @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());
+    }
 
-        final LocalAddress localAddress = new LocalAddress(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);
 
-        final InetSocketAddress inetAddress = getInetAddress(bindingAddress, portNumber);
-        final SshProxyServerConfigurationBuilder sshProxyServerConfigurationBuilder =
-                new SshProxyServerConfigurationBuilder();
-        sshProxyServerConfigurationBuilder.setBindingAddress(inetAddress);
-        sshProxyServerConfigurationBuilder.setLocalAddress(localAddress);
-        sshProxyServerConfigurationBuilder.setAuthenticator(authProvider);
-        sshProxyServerConfigurationBuilder.setIdleTimeout(Integer.MAX_VALUE);
-        sshProxyServerConfigurationBuilder.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
-
         localServer.addListener(future -> {
             if (future.isDone() && !future.isCancelled()) {
                 try {
-                    sshProxyServer.bind(sshProxyServerConfigurationBuilder.createSshProxyServerConfiguration());
-                    LOG.info("Netconf SSH endpoint started successfully at {}", bindingAddress);
+                    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());
@@ -72,12 +88,8 @@ public class NetconfNorthboundSshServer {
         });
     }
 
-    private static InetSocketAddress getInetAddress(final String bindingAddress, final String portNumber) {
-        final IpAddress ipAddress = IetfInetUtil.ipAddressFor(bindingAddress);
-        final InetAddress inetAd = IetfInetUtil.INSTANCE.inetAddressFor(ipAddress);
-        return new InetSocketAddress(inetAd, Integer.parseInt(portNumber));
-    }
-
+    @Deactivate
+    @Override
     public void close() throws IOException {
         sshProxyServer.close();
 
@@ -88,17 +100,8 @@ public class NetconfNorthboundSshServer {
         }
     }
 
-    /*
-     * Called when the underlying reference to EventExecutor is about to be removed from the container allowing
-     * us to close the ssh server while it still exists.
-     */
-    public void unbind(final ServiceReference<?> reference) {
-        LOG.debug("EventExecutor is being removed, closing netconf ssh server. {}", reference);
-
-        try {
-            close();
-        } catch (final IOException e) {
-            LOG.error("Closing of ssh server failed while unbinding reference listener.", e);
-        }
+    private static InetSocketAddress getInetAddress(final String bindingAddress, final int portNumber) {
+        final var ipAddress = IetfInetUtil.ipAddressFor(bindingAddress);
+        return new InetSocketAddress(IetfInetUtil.INSTANCE.inetAddressFor(ipAddress), portNumber);
     }
 }
diff --git a/netconf/mdsal-netconf-ssh/src/main/resources/OSGI-INF/blueprint/netconf-ssh.xml b/netconf/mdsal-netconf-ssh/src/main/resources/OSGI-INF/blueprint/netconf-ssh.xml
deleted file mode 100644 (file)
index 1b1278e..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (c) 2016 Inocybe Technologies Inc. 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
--->
-<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
-           xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
-           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.3.0"
-           odl:restart-dependents-on-updates="true">
-
-    <reference id="netconfServerDispatcher" interface="org.opendaylight.netconf.api.NetconfServerDispatcher"/>
-    <reference id="globalWorkerGroup" interface="io.netty.channel.EventLoopGroup" odl:type="global-worker-group"/>
-    <reference id="authProvider" interface="org.opendaylight.netconf.auth.AuthProvider" odl:type="netconf-auth-provider"/>
-
-    <reference id="executor" interface="io.netty.util.concurrent.EventExecutor" odl:type="global-event-executor">
-        <reference-listener ref="netconfMdsalServer" unbind-method="unbind"/>
-    </reference>
-
-    <!--    NETCONF server for MD-SAL (listening by default on port 2830)-->
-
-    <cm:property-placeholder persistent-id="org.opendaylight.netconf.ssh" update-strategy="none">
-        <cm:default-properties>
-            <cm:property name="bindingAddress" value="0.0.0.0"/>
-            <cm:property name="portNumber" value="2830"/>
-        </cm:default-properties>
-    </cm:property-placeholder>
-
-    <bean id="netconfMdsalServer"
-          class="org.opendaylight.netconf.ssh.NetconfNorthboundSshServer"
-          destroy-method="close">
-        <argument ref="netconfServerDispatcher"/>
-        <argument ref="globalWorkerGroup"/>
-        <argument ref="executor"/>
-        <argument value="${bindingAddress}"/>
-        <argument value="${portNumber}"/>
-        <argument ref="authProvider"/>
-    </bean>
-
-</blueprint>
index 0ca1fb81163b73bb5d4b80e285784fba0f2eb828..e040304b32a9853f0fc2c415c18eeb05d789664b 100644 (file)
@@ -18,9 +18,6 @@ import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
 import org.opendaylight.netconf.shaded.sshd.client.SshClient;
 import org.opendaylight.netconf.shaded.sshd.client.future.AuthFuture;
 import org.opendaylight.netconf.shaded.sshd.client.future.ConnectFuture;
@@ -29,11 +26,9 @@ import org.opendaylight.netconf.shaded.sshd.common.util.security.SecurityUtils;
 import org.opendaylight.netconf.ssh.SshProxyServer;
 import org.opendaylight.netconf.ssh.SshProxyServerConfigurationBuilder;
 import org.opendaylight.netconf.util.NetconfConfiguration;
-import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-@RunWith(MockitoJUnitRunner.StrictStubs.class)
 public class SSHServerTest {
     private static final String USER = "netconf";
     private static final String PASSWORD = "netconf";
@@ -44,8 +39,6 @@ public class SSHServerTest {
     private File sshKeyPair;
     private SshProxyServer server;
 
-    @Mock
-    private BundleContext mockedContext;
     private final ExecutorService nioExec = Executors.newFixedThreadPool(1);
     private final EventLoopGroup clientGroup = new NioEventLoopGroup();
     private final ScheduledExecutorService minaTimerEx = Executors.newScheduledThreadPool(1);
@@ -88,5 +81,4 @@ public class SSHServerTest {
             nioExec.shutdownNow();
         }
     }
-
 }