/* * Copyright (c) 2015 Huawei, 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 */ package org.opendaylight.usc.client.netconf; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.local.LocalChannel; import io.netty.channel.local.LocalEventLoopGroup; import io.netty.util.Timer; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GlobalEventExecutor; import io.netty.util.concurrent.Promise; import java.io.Closeable; import java.net.InetSocketAddress; import org.opendaylight.netconf.client.NetconfClientDispatcher; import org.opendaylight.netconf.client.NetconfClientDispatcherImpl; import org.opendaylight.netconf.client.NetconfClientSession; import org.opendaylight.netconf.client.NetconfClientSessionNegotiatorFactory; import org.opendaylight.netconf.client.conf.NetconfClientConfiguration; import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration; import org.opendaylight.protocol.framework.ReconnectStrategy; import org.opendaylight.protocol.framework.SessionNegotiatorFactory; import org.opendaylight.usc.manager.UscManagerService; import org.opendaylight.usc.plugin.UscPluginTcp; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class extends the NetconfClientDispatcherImpl with USC. */ public class UscNetconfClientDispatcherImpl implements NetconfClientDispatcher, Closeable { private static final GlobalEventExecutor executor = GlobalEventExecutor.INSTANCE; private static final Logger LOG = LoggerFactory.getLogger(UscNetconfClientDispatcherImpl.class); private final UscPluginTcp plugin; private final LocalEventLoopGroup group = new LocalEventLoopGroup(); private final Timer timer; private final NetconfClientDispatcherImpl fallbackDispatcher; protected interface PipelineInitializer<S> { /** * Initializes channel by specifying the handlers in its pipeline. Handlers are protocol specific, therefore * this method needs to be implemented in protocol specific Dispatchers. * * @param channel * whose pipeline should be defined, also to be passed to {@link SessionNegotiatorFactory} * @param promise * to be passed to {@link SessionNegotiatorFactory} */ void initializeChannel(Channel channel, Promise<S> promise); } /** * Constructs a new UscNetconfClientDispatcherImpl * * @param bossGroup * @param workerGroup * @param timer */ public UscNetconfClientDispatcherImpl(final EventLoopGroup bossGroup, final EventLoopGroup workerGroup, final Timer timer) { LOG.warn("UscNetconfClientDispatcherImpl constructor"); this.timer = timer; //initializing USC Manager Service for USC Plugin even the USC feature is not installed. UscManagerService.getInstance().init(); plugin = UscManagerService.getInstance().getPluginTcp(); this.fallbackDispatcher = new NetconfClientDispatcherImpl(bossGroup, workerGroup, timer); } @Override public Future<NetconfClientSession> createClient(final NetconfClientConfiguration clientConfiguration) { switch (clientConfiguration.getProtocol()) { case TCP: if (plugin.isChannelAvailable(clientConfiguration.getAddress())) { return createTcpClient(clientConfiguration); } else { LOG.warn("UscNetconfClientDispatcherImpl createClient using fallback for TCP"); return fallbackDispatcher.createClient(clientConfiguration); } case SSH: // SSH doesn't support LocalChannel addresses, so use fallback LOG.warn("UscNetconfClientDispatcherImpl createClient using fallback for SSH"); return fallbackDispatcher.createClient(clientConfiguration); default: throw new IllegalArgumentException("Unknown client protocol " + clientConfiguration.getProtocol()); } } @Override public Future<Void> createReconnectingClient(final NetconfReconnectingClientConfiguration clientConfiguration) { switch (clientConfiguration.getProtocol()) { case TCP: if (plugin.isChannelAvailable(clientConfiguration.getAddress())) { // SSH doesn't support LocalChannel addresses, so only TCP is supported return createReconnectingTcpClient(clientConfiguration); } else { LOG.warn("UscNetconfClientDispatcherImpl createReconnectingClient using fallback for TCP"); return fallbackDispatcher.createReconnectingClient(clientConfiguration); } case SSH: // SSH doesn't support LocalChannel addresses, so use fallback LOG.warn("UscNetconfClientDispatcherImpl createReconnectingClient using fallback for SSH"); return fallbackDispatcher.createReconnectingClient(clientConfiguration); default: throw new IllegalArgumentException("Unknown client protocol " + clientConfiguration.getProtocol()); } } /** * Create a client but use a pre-configured bootstrap. This method however replaces the ChannelInitializer in the * bootstrap. All other configuration is preserved. * * @param address * remote address */ protected Future<NetconfClientSession> createClient(final InetSocketAddress address, final ReconnectStrategy strategy, final Bootstrap bootstrap, final PipelineInitializer<NetconfClientSession> initializer) { final ProtocolSessionPromise<NetconfClientSession> p = new ProtocolSessionPromise<>(plugin, executor, address, strategy, bootstrap); bootstrap.handler(new ChannelInitializer<Channel>() { @Override protected void initChannel(final Channel ch) { initializer.initializeChannel(ch, p); } }); p.connect(); LOG.debug("Client created."); return p; } private Future<NetconfClientSession> createTcpClient(final NetconfClientConfiguration currentConfiguration) { LOG.debug("Creating TCP client with configuration: {}", currentConfiguration); LOG.warn("UscNetconfClientDispatcherImpl createTcpClient"); final Bootstrap b = new Bootstrap(); b.group(group); b.channel(LocalChannel.class); return createClient(currentConfiguration.getAddress(), currentConfiguration.getReconnectStrategy(), b, new PipelineInitializer<NetconfClientSession>() { @Override public void initializeChannel(final Channel ch, final Promise<NetconfClientSession> promise) { new NetconfTcpClientChannelInitializer(getNegotiatorFactory(currentConfiguration), currentConfiguration.getSessionListener()).initialize(ch, promise); } }); } private Future<Void> createReconnectingTcpClient(final NetconfReconnectingClientConfiguration currentConfiguration) { LOG.debug("Creating reconnecting TCP client with configuration: {}", currentConfiguration); LOG.warn("UscNetconfClientDispatcherImpl createReconnectingTcpClient"); final Bootstrap b = new Bootstrap(); b.group(group); b.channel(LocalChannel.class); final ReconnectPromise p = new ReconnectPromise(executor, this, currentConfiguration.getAddress(), currentConfiguration.getConnectStrategyFactory(), b, new PipelineInitializer<NetconfClientSession>() { @Override public void initializeChannel(final Channel ch, final Promise<NetconfClientSession> promise) { new NetconfTcpClientChannelInitializer(getNegotiatorFactory(currentConfiguration), currentConfiguration.getSessionListener()).initialize(ch, promise); } }); p.connect(); return p; } private Future<NetconfClientSession> createSshClient(final NetconfClientConfiguration currentConfiguration) { LOG.debug("Creating SSH client with configuration: {}", currentConfiguration); LOG.warn("UscNetconfClientDispatcherImpl createSshClient"); final Bootstrap b = new Bootstrap(); b.group(group); b.channel(LocalChannel.class); return createClient(currentConfiguration.getAddress(), currentConfiguration.getReconnectStrategy(), b, new PipelineInitializer<NetconfClientSession>() { @Override public void initializeChannel(final Channel ch, final Promise<NetconfClientSession> promise) { new NetconfSshClientChannelInitializer(currentConfiguration.getAuthHandler(), getNegotiatorFactory(currentConfiguration), currentConfiguration.getSessionListener()) .initialize(ch, promise); } }); } private Future<Void> createReconnectingSshClient(final NetconfReconnectingClientConfiguration currentConfiguration) { LOG.debug("Creating reconnecting SSH client with configuration: {}", currentConfiguration); LOG.warn("UscNetconfClientDispatcherImpl createReconnectingSshClient"); final Bootstrap b = new Bootstrap(); b.group(group); b.channel(LocalChannel.class); final ReconnectPromise p = new ReconnectPromise(GlobalEventExecutor.INSTANCE, this, currentConfiguration.getAddress(), currentConfiguration.getConnectStrategyFactory(), b, new PipelineInitializer<NetconfClientSession>() { @Override public void initializeChannel(final Channel ch, final Promise<NetconfClientSession> promise) { new NetconfSshClientChannelInitializer(currentConfiguration.getAuthHandler(), getNegotiatorFactory(currentConfiguration), currentConfiguration.getSessionListener()) .initialize(ch, promise); } }); p.connect(); return p; } @Override public void close() { group.shutdownGracefully(); } private NetconfClientSessionNegotiatorFactory getNegotiatorFactory(final NetconfClientConfiguration cfg) { return new NetconfClientSessionNegotiatorFactory(timer, cfg.getAdditionalHeader(), cfg.getConnectionTimeoutMillis()); } }