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