2 * Copyright (c) 2023 PANTHEON.tech s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netconf.client;
10 import static java.util.Objects.requireNonNull;
11 import static org.opendaylight.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol.SSH;
12 import static org.opendaylight.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol.TCP;
13 import static org.opendaylight.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol.TLS;
15 import com.google.common.collect.ImmutableSet;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import com.google.common.util.concurrent.SettableFuture;
18 import javax.annotation.PreDestroy;
19 import javax.inject.Singleton;
20 import org.opendaylight.netconf.api.TransportConstants;
21 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
22 import org.opendaylight.netconf.common.NetconfTimer;
23 import org.opendaylight.netconf.transport.api.TransportChannel;
24 import org.opendaylight.netconf.transport.api.TransportChannelListener;
25 import org.opendaylight.netconf.transport.api.UnsupportedConfigurationException;
26 import org.opendaylight.netconf.transport.ssh.SSHTransportStackFactory;
27 import org.opendaylight.netconf.transport.tcp.TCPClient;
28 import org.opendaylight.netconf.transport.tls.TLSClient;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
30 import org.osgi.service.component.annotations.Activate;
31 import org.osgi.service.component.annotations.Component;
32 import org.osgi.service.component.annotations.Deactivate;
33 import org.osgi.service.component.annotations.Reference;
36 // FIXME: drop the property and use lazy activation
37 @Component(immediate = true, service = NetconfClientFactory.class, property = "type=netconf-client-factory")
38 public class NetconfClientFactoryImpl implements NetconfClientFactory {
39 private final SSHTransportStackFactory factory;
40 private final NetconfTimer timer;
42 public NetconfClientFactoryImpl(final NetconfTimer timer, final SSHTransportStackFactory factory) {
43 this.timer = requireNonNull(timer);
44 this.factory = requireNonNull(factory);
48 public NetconfClientFactoryImpl(@Reference final NetconfTimer timer) {
49 // FIXME: make factory component configurable for OSGi
50 this(timer, new SSHTransportStackFactory("odl-netconf-client", 0));
61 public ListenableFuture<NetconfClientSession> createClient(final NetconfClientConfiguration configuration)
62 throws UnsupportedConfigurationException {
63 final var protocol = configuration.getProtocol();
64 final var future = SettableFuture.<NetconfClientSession>create();
65 final var channelInitializer = new ClientChannelInitializer(createNegotiatorFactory(configuration),
66 () -> configuration.getSessionListener());
67 final var bootstrap = factory.newBootstrap();
69 if (TCP.equals(protocol)) {
70 TCPClient.connect(new ClientTransportChannelListener(future, channelInitializer), bootstrap,
71 configuration.getTcpParameters());
72 } else if (TLS.equals(protocol)) {
73 if (configuration.getTlsParameters() != null) {
74 TLSClient.connect(new ClientTransportChannelListener(future, channelInitializer), bootstrap,
75 configuration.getTcpParameters(), configuration.getTlsParameters());
77 TLSClient.connect(new ClientTransportChannelListener(future, channelInitializer), bootstrap,
78 configuration.getTcpParameters(), configuration.getSslHandlerFactory());
80 } else if (SSH.equals(protocol)) {
81 factory.connectClient(TransportConstants.SSH_SUBSYSTEM,
82 new ClientTransportChannelListener(future, channelInitializer), configuration.getTcpParameters(),
83 configuration.getSshParameters(), configuration.getSshConfigurator());
88 private NetconfClientSessionNegotiatorFactory createNegotiatorFactory(
89 final NetconfClientConfiguration configuration) {
90 final var capabilities = configuration.getOdlHelloCapabilities();
91 if (capabilities == null || capabilities.isEmpty()) {
92 return new NetconfClientSessionNegotiatorFactory(timer, configuration.getAdditionalHeader(),
93 configuration.getConnectionTimeoutMillis(), configuration.getMaximumIncomingChunkSize());
95 final var stringCapabilities = capabilities.stream().map(Uri::getValue)
96 .collect(ImmutableSet.toImmutableSet());
97 return new NetconfClientSessionNegotiatorFactory(timer, configuration.getAdditionalHeader(),
98 configuration.getConnectionTimeoutMillis(), stringCapabilities);
101 private record ClientTransportChannelListener(
102 SettableFuture<NetconfClientSession> future,
103 ClientChannelInitializer initializer) implements TransportChannelListener {
104 ClientTransportChannelListener {
105 requireNonNull(future);
106 requireNonNull(initializer);
110 public void onTransportChannelEstablished(final TransportChannel channel) {
111 final var nettyChannel = channel.channel();
112 final var promise = nettyChannel.eventLoop().<NetconfClientSession>newPromise();
113 initializer.initialize(nettyChannel, promise);
114 promise.addListener(ignored -> {
115 final var cause = promise.cause();
117 future.setException(cause);
119 future.set(promise.getNow());
125 public void onTransportChannelFailed(final Throwable cause) {
126 future.setException(cause);