Flatten callhome-provider
[netconf.git] / apps / callhome-provider / src / main / java / org / opendaylight / netconf / topology / callhome / CallHomeTransportChannelListener.java
1 /*
2  * Copyright (c) 2023 PANTHEON.tech s.r.o. and others. All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netconf.topology.callhome;
9
10 import static java.util.Objects.requireNonNull;
11
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;
21
22 public class CallHomeTransportChannelListener implements TransportChannelListener {
23     private static final Logger LOG = LoggerFactory.getLogger(CallHomeTransportChannelListener.class);
24
25     private final @NonNull NetconfClientSessionNegotiatorFactory negotiationFactory;
26     private final CallHomeSessionContextManager<?> contextManager;
27     private final CallHomeStatusRecorder statusRecorder;
28
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);
34     }
35
36     @Override
37     public void onTransportChannelEstablished(final TransportChannel transportChannel) {
38         final var channel = transportChannel.channel();
39
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());
45             channel.close();
46             return;
47         }
48
49         LOG.info("Starting netconf negotiation for context: {}", context);
50
51         // init NETCONF negotiation
52         final var promise = channel.eventLoop().<NetconfClientSession>newPromise();
53         promise.addListener(ignored -> {
54             final var cause = promise.cause();
55             if (cause != null) {
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);
60             } else {
61                 statusRecorder.reportSuccess(context.id());
62                 context.settableFuture().set(promise.getNow());
63                 LOG.info("Netconf session established for context: {}", context);
64             }
65         });
66         new ClientChannelInitializer(negotiationFactory, context::netconfSessionListener)
67             .initialize(channel, promise);
68
69         // this is required to trigger NETCONF negotiation on TLS
70         if (channel.pipeline().get(SslHandler.class) != null) {
71             channel.pipeline().fireChannelActive();
72         }
73     }
74
75     @Override
76     public void onTransportChannelFailed(final Throwable cause) {
77         statusRecorder.onTransportChannelFailure(cause);
78     }
79 }