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.topology.callhome;
10 import static java.util.Objects.requireNonNull;
12 import io.netty.handler.ssl.SslHandler;
13 import org.eclipse.jdt.annotation.NonNull;
14 import org.opendaylight.netconf.client.ClientChannelInitializer;
15 import org.opendaylight.netconf.client.NetconfClientSession;
16 import org.opendaylight.netconf.client.NetconfClientSessionNegotiatorFactory;
17 import org.opendaylight.netconf.transport.api.TransportChannel;
18 import org.opendaylight.netconf.transport.api.TransportChannelListener;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
22 public class CallHomeTransportChannelListener implements TransportChannelListener {
23 private static final Logger LOG = LoggerFactory.getLogger(CallHomeTransportChannelListener.class);
25 private final @NonNull NetconfClientSessionNegotiatorFactory negotiationFactory;
26 private final CallHomeSessionContextManager<?> contextManager;
27 private final CallHomeStatusRecorder statusRecorder;
29 public CallHomeTransportChannelListener(final NetconfClientSessionNegotiatorFactory negotiationFactory,
30 final CallHomeSessionContextManager<?> contextManager, final CallHomeStatusRecorder statusRecorder) {
31 this.negotiationFactory = requireNonNull(negotiationFactory);
32 this.contextManager = requireNonNull(contextManager);
33 this.statusRecorder = requireNonNull(statusRecorder);
37 public void onTransportChannelEstablished(final TransportChannel transportChannel) {
38 final var channel = transportChannel.channel();
40 // identify or create session context associated with current connection
41 final var context = contextManager.findByChannel(channel);
42 if (context == null) {
43 LOG.error("No valid context found for incoming connection from {}. Connection rejected.",
44 channel.remoteAddress());
49 LOG.info("Starting netconf negotiation for context: {}", context);
51 // init NETCONF negotiation
52 final var promise = channel.eventLoop().<NetconfClientSession>newPromise();
53 promise.addListener(ignored -> {
54 final var cause = promise.cause();
56 contextManager.remove(context.id());
57 context.settableFuture().setException(cause);
58 statusRecorder.reportNetconfFailure(context.id());
59 LOG.error("Netconf session failed for context: {}", context, cause);
61 statusRecorder.reportSuccess(context.id());
62 context.settableFuture().set(promise.getNow());
63 LOG.info("Netconf session established for context: {}", context);
66 new ClientChannelInitializer(negotiationFactory, context::netconfSessionListener)
67 .initialize(channel, promise);
69 // this is required to trigger NETCONF negotiation on TLS
70 if (channel.pipeline().get(SslHandler.class) != null) {
71 channel.pipeline().fireChannelActive();
76 public void onTransportChannelFailed(final Throwable cause) {
77 statusRecorder.onTransportChannelFailure(cause);