3d91f3aeb4ab35cd1fee451e869860968f584cf0
[netconf.git] / netconf / netconf-ssh / src / main / java / org / opendaylight / netconf / ssh / RemoteNetconfCommand.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. 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
9 package org.opendaylight.netconf.ssh;
10
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;
37
38 /**
39  * This command handles all netconf related rpc and forwards to delegate server.
40  * Uses netty to make a local connection to delegate server.
41  *
42  * <p>
43  * Command is Apache Mina SSH terminology for objects handling ssh data.
44  */
45 public class RemoteNetconfCommand implements AsyncCommand, SessionAware {
46
47     private static final Logger LOG = LoggerFactory.getLogger(RemoteNetconfCommand.class);
48
49     private final EventLoopGroup clientEventGroup;
50     private final LocalAddress localAddress;
51
52     private IoInputStream in;
53     private IoOutputStream out;
54     private ExitCallback callback;
55     private NetconfHelloMessageAdditionalHeader netconfHelloMessageAdditionalHeader;
56
57     private Channel clientChannel;
58     private ChannelFuture clientChannelFuture;
59
60     public RemoteNetconfCommand(final EventLoopGroup clientEventGroup, final LocalAddress localAddress) {
61         this.clientEventGroup = clientEventGroup;
62         this.localAddress = localAddress;
63     }
64
65     @Override
66     @SuppressWarnings("checkstyle:hiddenField")
67     public void setIoInputStream(final IoInputStream in) {
68         this.in = in;
69     }
70
71     @Override
72     @SuppressWarnings("checkstyle:hiddenField")
73     public void setIoOutputStream(final IoOutputStream out) {
74         this.out = out;
75     }
76
77     @Override
78     public void setIoErrorStream(final IoOutputStream err) {
79         // TODO do we want to use error stream in some way ?
80     }
81
82     @Override
83     @SuppressWarnings("checkstyle:hiddenField")
84     public void setInputStream(final InputStream in) {
85         throw new UnsupportedOperationException("Synchronous IO is unsupported");
86     }
87
88     @Override
89     @SuppressWarnings("checkstyle:hiddenField")
90     public void setOutputStream(final OutputStream out) {
91         throw new UnsupportedOperationException("Synchronous IO is unsupported");
92
93     }
94
95     @Override
96     public void setErrorStream(final OutputStream err) {
97         throw new UnsupportedOperationException("Synchronous IO is unsupported");
98
99     }
100
101     @Override
102     @SuppressWarnings("checkstyle:hiddenField")
103     public void setExitCallback(final ExitCallback callback) {
104         this.callback = callback;
105     }
106
107     @Override
108     public void start(final Environment env) throws IOException {
109         LOG.trace("Establishing internal connection to netconf server for client: {}", getClientAddress());
110
111         final Bootstrap clientBootstrap = new Bootstrap();
112         clientBootstrap.group(clientEventGroup).channel(LocalChannel.class);
113
114         clientBootstrap.handler(new ChannelInitializer<LocalChannel>() {
115             @Override
116             public void initChannel(final LocalChannel ch) throws Exception {
117                 ch.pipeline()
118                         .addLast(new SshProxyClientHandler(in, out, netconfHelloMessageAdditionalHeader, callback));
119             }
120         });
121         clientChannelFuture = clientBootstrap.connect(localAddress);
122         clientChannelFuture.addListener(new GenericFutureListener<ChannelFuture>() {
123
124             @Override
125             public void operationComplete(final ChannelFuture future) throws Exception {
126                 if (future.isSuccess()) {
127                     clientChannel = clientChannelFuture.channel();
128                 } else {
129                     LOG.warn("Unable to establish internal connection to netconf server for client: {}",
130                             getClientAddress());
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());
134                 }
135             }
136         });
137     }
138
139     @Override
140     public void destroy() {
141         LOG.trace("Releasing internal connection to netconf server for client: {} on channel: {}",
142                 getClientAddress(), clientChannel);
143
144         clientChannelFuture.cancel(true);
145         if (clientChannel != null) {
146             clientChannel.close().addListener(new GenericFutureListener<ChannelFuture>() {
147
148                 @Override
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: {}",
152                                 clientChannel);
153                     }
154                 }
155             });
156         }
157     }
158
159     private String getClientAddress() {
160         return netconfHelloMessageAdditionalHeader.getAddress();
161     }
162
163     @Override
164     public void setSession(final ServerSession session) {
165         final SocketAddress remoteAddress = session.getIoSession().getRemoteAddress();
166         String hostName = "";
167         String port = "";
168         if (remoteAddress instanceof InetSocketAddress) {
169             hostName = ((InetSocketAddress) remoteAddress).getAddress().getHostAddress();
170             port = Integer.toString(((InetSocketAddress) remoteAddress).getPort());
171         }
172         netconfHelloMessageAdditionalHeader = new NetconfHelloMessageAdditionalHeader(
173                 session.getUsername(), hostName, port, "ssh", "client");
174     }
175
176     public static class NetconfCommandFactory implements NamedFactory<Command> {
177
178         public static final String NETCONF = "netconf";
179
180         private final EventLoopGroup clientBootstrap;
181         private final LocalAddress localAddress;
182
183         public NetconfCommandFactory(final EventLoopGroup clientBootstrap, final LocalAddress localAddress) {
184
185             this.clientBootstrap = clientBootstrap;
186             this.localAddress = localAddress;
187         }
188
189         @Override
190         public String getName() {
191             return NETCONF;
192         }
193
194         @Override
195         public RemoteNetconfCommand create() {
196             return new RemoteNetconfCommand(clientBootstrap, localAddress);
197         }
198     }
199
200 }