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