2 * Copyright (c) 2014 Cisco Systems, Inc. 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
9 package org.opendaylight.netconf.ssh;
11 import com.google.common.base.Preconditions;
12 import io.netty.bootstrap.Bootstrap;
13 import io.netty.channel.Channel;
14 import io.netty.channel.ChannelFuture;
15 import io.netty.channel.ChannelInitializer;
16 import io.netty.channel.EventLoopGroup;
17 import io.netty.channel.local.LocalAddress;
18 import io.netty.channel.local.LocalChannel;
19 import io.netty.util.concurrent.GenericFutureListener;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.net.InetSocketAddress;
24 import java.net.SocketAddress;
25 import org.apache.sshd.common.NamedFactory;
26 import org.apache.sshd.common.io.IoInputStream;
27 import org.apache.sshd.common.io.IoOutputStream;
28 import org.apache.sshd.server.AsyncCommand;
29 import org.apache.sshd.server.Command;
30 import org.apache.sshd.server.Environment;
31 import org.apache.sshd.server.ExitCallback;
32 import org.apache.sshd.server.SessionAware;
33 import org.apache.sshd.server.session.ServerSession;
34 import org.opendaylight.netconf.api.messages.NetconfHelloMessageAdditionalHeader;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
39 * This command handles all netconf related rpc and forwards to delegate server.
40 * Uses netty to make a local connection to delegate server.
43 * Command is Apache Mina SSH terminology for objects handling ssh data.
45 public class RemoteNetconfCommand implements AsyncCommand, SessionAware {
47 private static final Logger LOG = LoggerFactory.getLogger(RemoteNetconfCommand.class);
49 private final EventLoopGroup clientEventGroup;
50 private final LocalAddress localAddress;
52 private IoInputStream in;
53 private IoOutputStream out;
54 private ExitCallback callback;
55 private NetconfHelloMessageAdditionalHeader netconfHelloMessageAdditionalHeader;
57 private Channel clientChannel;
58 private ChannelFuture clientChannelFuture;
60 public RemoteNetconfCommand(final EventLoopGroup clientEventGroup, final LocalAddress localAddress) {
61 this.clientEventGroup = clientEventGroup;
62 this.localAddress = localAddress;
66 @SuppressWarnings("checkstyle:hiddenField")
67 public void setIoInputStream(final IoInputStream in) {
72 @SuppressWarnings("checkstyle:hiddenField")
73 public void setIoOutputStream(final IoOutputStream out) {
78 public void setIoErrorStream(final IoOutputStream err) {
79 // TODO do we want to use error stream in some way ?
83 @SuppressWarnings("checkstyle:hiddenField")
84 public void setInputStream(final InputStream in) {
85 throw new UnsupportedOperationException("Synchronous IO is unsupported");
89 @SuppressWarnings("checkstyle:hiddenField")
90 public void setOutputStream(final OutputStream out) {
91 throw new UnsupportedOperationException("Synchronous IO is unsupported");
96 public void setErrorStream(final OutputStream err) {
97 throw new UnsupportedOperationException("Synchronous IO is unsupported");
102 @SuppressWarnings("checkstyle:hiddenField")
103 public void setExitCallback(final ExitCallback callback) {
104 this.callback = callback;
108 public void start(final Environment env) throws IOException {
109 LOG.trace("Establishing internal connection to netconf server for client: {}", getClientAddress());
111 final Bootstrap clientBootstrap = new Bootstrap();
112 clientBootstrap.group(clientEventGroup).channel(LocalChannel.class);
114 clientBootstrap.handler(new ChannelInitializer<LocalChannel>() {
116 public void initChannel(final LocalChannel ch) throws Exception {
118 .addLast(new SshProxyClientHandler(in, out, netconfHelloMessageAdditionalHeader, callback));
121 clientChannelFuture = clientBootstrap.connect(localAddress);
122 clientChannelFuture.addListener(new GenericFutureListener<ChannelFuture>() {
125 public void operationComplete(final ChannelFuture future) throws Exception {
126 if (future.isSuccess()) {
127 clientChannel = clientChannelFuture.channel();
129 LOG.warn("Unable to establish internal connection to netconf server for client: {}",
131 Preconditions.checkNotNull(callback, "Exit callback must be set");
132 callback.onExit(1, "Unable to establish internal connection to netconf server for client: "
133 + getClientAddress());
140 public void destroy() {
141 LOG.trace("Releasing internal connection to netconf server for client: {} on channel: {}",
142 getClientAddress(), clientChannel);
144 clientChannelFuture.cancel(true);
145 if (clientChannel != null) {
146 clientChannel.close().addListener(new GenericFutureListener<ChannelFuture>() {
149 public void operationComplete(final ChannelFuture future) throws Exception {
150 if (!future.isSuccess()) {
151 LOG.warn("Unable to release internal connection to netconf server on channel: {}",
159 private String getClientAddress() {
160 return netconfHelloMessageAdditionalHeader.getAddress();
164 public void setSession(final ServerSession session) {
165 final SocketAddress remoteAddress = session.getIoSession().getRemoteAddress();
166 String hostName = "";
168 if (remoteAddress instanceof InetSocketAddress) {
169 hostName = ((InetSocketAddress) remoteAddress).getAddress().getHostAddress();
170 port = Integer.toString(((InetSocketAddress) remoteAddress).getPort());
172 netconfHelloMessageAdditionalHeader = new NetconfHelloMessageAdditionalHeader(
173 session.getUsername(), hostName, port, "ssh", "client");
176 public static class NetconfCommandFactory implements NamedFactory<Command> {
178 public static final String NETCONF = "netconf";
180 private final EventLoopGroup clientBootstrap;
181 private final LocalAddress localAddress;
183 public NetconfCommandFactory(final EventLoopGroup clientBootstrap, final LocalAddress localAddress) {
185 this.clientBootstrap = clientBootstrap;
186 this.localAddress = localAddress;
190 public String getName() {
195 public RemoteNetconfCommand create() {
196 return new RemoteNetconfCommand(clientBootstrap, localAddress);