Merge "Netconf-cli compilable and included in project"
[controller.git] / opendaylight / netconf / netconf-ssh / src / main / java / org / opendaylight / controller / 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.controller.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.controller.netconf.util.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  * 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     public void setIoInputStream(final IoInputStream in) {
66         this.in = in;
67     }
68
69     @Override
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     public void setInputStream(final InputStream in) {
81         throw new UnsupportedOperationException("Synchronous IO is unsupported");
82     }
83
84     @Override
85     public void setOutputStream(final OutputStream out) {
86         throw new UnsupportedOperationException("Synchronous IO is unsupported");
87
88     }
89
90     @Override
91     public void setErrorStream(final OutputStream err) {
92         throw new UnsupportedOperationException("Synchronous IO is unsupported");
93
94     }
95
96     @Override
97     public void setExitCallback(final ExitCallback callback) {
98         this.callback = callback;
99     }
100
101     @Override
102     public void start(final Environment env) throws IOException {
103         LOG.trace("Establishing internal connection to netconf server for client: {}", getClientAddress());
104
105         final Bootstrap clientBootstrap = new Bootstrap();
106         clientBootstrap.group(clientEventGroup).channel(LocalChannel.class);
107
108         clientBootstrap
109                 .handler(new ChannelInitializer<LocalChannel>() {
110                     @Override
111                     public void initChannel(final LocalChannel ch) throws Exception {
112                         ch.pipeline().addLast(new SshProxyClientHandler(in, out, netconfHelloMessageAdditionalHeader, callback));
113                     }
114                 });
115         clientChannelFuture = clientBootstrap.connect(localAddress);
116         clientChannelFuture.addListener(new GenericFutureListener<ChannelFuture>() {
117
118             @Override
119             public void operationComplete(final ChannelFuture future) throws Exception {
120                 if(future.isSuccess()) {
121                     clientChannel = clientChannelFuture.channel();
122                 } else {
123                     LOG.warn("Unable to establish internal connection to netconf server for client: {}", getClientAddress());
124                     Preconditions.checkNotNull(callback, "Exit callback must be set");
125                     callback.onExit(1, "Unable to establish internal connection to netconf server for client: "+ getClientAddress());
126                 }
127             }
128         });
129     }
130
131     @Override
132     public void destroy() {
133         LOG.trace("Releasing internal connection to netconf server for client: {} on channel: {}",
134                 getClientAddress(), clientChannel);
135
136         clientChannelFuture.cancel(true);
137         if(clientChannel != null) {
138             clientChannel.close().addListener(new GenericFutureListener<ChannelFuture>() {
139
140                 @Override
141                 public void operationComplete(final ChannelFuture future) throws Exception {
142                     if (future.isSuccess() == false) {
143                         LOG.warn("Unable to release internal connection to netconf server on channel: {}", clientChannel);
144                     }
145                 }
146             });
147         }
148     }
149
150     private String getClientAddress() {
151         return netconfHelloMessageAdditionalHeader.getAddress();
152     }
153
154     @Override
155     public void setSession(final ServerSession session) {
156         final SocketAddress remoteAddress = session.getIoSession().getRemoteAddress();
157         String hostName = "";
158         String port = "";
159         if(remoteAddress instanceof InetSocketAddress) {
160             hostName = ((InetSocketAddress) remoteAddress).getAddress().getHostAddress();
161             port = Integer.toString(((InetSocketAddress) remoteAddress).getPort());
162         }
163         netconfHelloMessageAdditionalHeader = new NetconfHelloMessageAdditionalHeader(
164                 session.getUsername(), hostName, port, "ssh", "client");
165     }
166
167     public static class NetconfCommandFactory implements NamedFactory<Command> {
168
169         public static final String NETCONF = "netconf";
170
171         private final EventLoopGroup clientBootstrap;
172         private final LocalAddress localAddress;
173
174         public NetconfCommandFactory(final EventLoopGroup clientBootstrap, final LocalAddress localAddress) {
175
176             this.clientBootstrap = clientBootstrap;
177             this.localAddress = localAddress;
178         }
179
180         @Override
181         public String getName() {
182             return NETCONF;
183         }
184
185         @Override
186         public RemoteNetconfCommand create() {
187             return new RemoteNetconfCommand(clientBootstrap, localAddress);
188         }
189     }
190
191 }