Merge "BUG-2679 Workaround for wrong nagasena encode/decode with reused transmogrifier"
[controller.git] / opendaylight / netconf / netconf-cli / src / main / java / org / opendaylight / controller / netconf / cli / Main.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.controller.netconf.cli;
9
10 import static com.google.common.base.Throwables.getStackTraceAsString;
11
12 import com.google.common.base.Preconditions;
13 import java.io.IOException;
14 import java.net.InetAddress;
15 import java.net.InetSocketAddress;
16 import java.net.UnknownHostException;
17 import net.sourceforge.argparse4j.ArgumentParsers;
18 import net.sourceforge.argparse4j.inf.ArgumentGroup;
19 import net.sourceforge.argparse4j.inf.ArgumentParser;
20 import net.sourceforge.argparse4j.inf.ArgumentParserException;
21 import net.sourceforge.argparse4j.inf.Namespace;
22 import org.opendaylight.controller.netconf.cli.commands.CommandDispatcher;
23 import org.opendaylight.controller.netconf.cli.commands.local.Connect;
24 import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
25 import org.opendaylight.controller.netconf.cli.io.ConsoleIOImpl;
26 import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
27 import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder;
28 import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
29 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
30
31 /**
32  * Parse arguments, start remote device connection and start CLI after the
33  * connection is fully up
34  */
35 public class Main {
36
37     public static void main(final String[] args) {
38         final CliArgumentParser cliArgs = new CliArgumentParser();
39         try {
40             cliArgs.parse(args);
41         } catch (final ArgumentParserException e) {
42             // Just end the cli, exception was handled by the CliArgumentParser
43             return;
44         }
45
46         final ConsoleIO consoleIO;
47         try {
48             consoleIO = new ConsoleIOImpl();
49         } catch (final IOException e) {
50             handleStartupException(e);
51             return;
52         }
53
54         final SchemaContext localSchema = CommandDispatcher.parseSchema(CommandDispatcher.LOCAL_SCHEMA_PATHS);
55         final SchemaContextRegistry schemaContextRegistry = new SchemaContextRegistry(localSchema);
56
57         final CommandDispatcher commandDispatcher = new CommandDispatcher();
58         final CommandArgHandlerRegistry argumentHandlerRegistry = new CommandArgHandlerRegistry(consoleIO,
59                 schemaContextRegistry);
60         final NetconfDeviceConnectionManager connectionManager = new NetconfDeviceConnectionManager(commandDispatcher,
61                 argumentHandlerRegistry, schemaContextRegistry, consoleIO);
62
63         commandDispatcher.addLocalCommands(connectionManager, localSchema, cliArgs.getConnectionTimeoutMs());
64
65         switch (cliArgs.connectionArgsPresent()) {
66         case TCP: {
67             // FIXME support pure TCP
68             handleRunningException(new UnsupportedOperationException("PURE TCP CONNECTIONS ARE NOT SUPPORTED YET, USE SSH INSTEAD BY PROVIDING USERNAME AND PASSWORD AS WELL"));
69             return;
70         }
71         case SSH: {
72             writeStatus(consoleIO, "Connecting to %s via SSH. Please wait.", cliArgs.getAddress());
73             connectionManager.connectBlocking(cliArgs.getAddress(), cliArgs.getServerAddress(), getClientSshConfig(cliArgs));
74             break;
75         }
76         case NONE: {/* Do not connect initially */
77             writeStatus(consoleIO, "No initial connection. To connect use the connect command");
78         }
79         }
80
81         try {
82             new Cli(consoleIO, commandDispatcher, argumentHandlerRegistry, schemaContextRegistry).run();
83         } catch (final Exception e) {
84             // TODO Running exceptions have to be handled properly
85             handleRunningException(e);
86             System.exit(0);
87         }
88     }
89
90     private static NetconfClientConfigurationBuilder getClientConfig(final CliArgumentParser cliArgs) {
91         return NetconfClientConfigurationBuilder.create().withAddress(cliArgs.getServerAddress())
92                 .withConnectionTimeoutMillis(cliArgs.getConnectionTimeoutMs())
93                 .withReconnectStrategy(Connect.getReconnectStrategy())
94                 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TCP);
95     }
96
97     private static NetconfClientConfigurationBuilder getClientSshConfig(final CliArgumentParser cliArgs) {
98         return NetconfClientConfigurationBuilder.create().withAddress(cliArgs.getServerAddress())
99                 .withConnectionTimeoutMillis(cliArgs.getConnectionTimeoutMs())
100                 .withReconnectStrategy(Connect.getReconnectStrategy())
101                 .withAuthHandler(cliArgs.getCredentials())
102                 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH);
103     }
104
105     private static void handleStartupException(final IOException e) {
106         handleException(e, "Unable to initialize CLI");
107     }
108
109     private static void handleException(final Exception e, final String message) {
110         System.console().writer().println(String.format("Error %s cause %s", message, getStackTraceAsString(e.fillInStackTrace())));
111     }
112
113     private static void writeStatus(final ConsoleIO io, final String blueprint, final Object... args) {
114         try {
115             io.formatLn(blueprint, args);
116         } catch (final IOException e) {
117             handleStartupException(e);
118         }
119     }
120
121     private static void handleRunningException(final Exception e) {
122         handleException(e, "Unexpected CLI runtime exception");
123     }
124
125     private static final class CliArgumentParser {
126
127         public static final String USERNAME = "username";
128         public static final String PASSWORD = "password";
129         public static final String SERVER = "server";
130         public static final String PORT = "port";
131
132         public static final String CONNECT_TIMEOUT = "connectionTimeout";
133         public static final int DEFAULT_CONNECTION_TIMEOUT_MS = 50000;
134
135         private final ArgumentParser parser;
136         private Namespace parsed;
137
138         private CliArgumentParser() {
139             parser = ArgumentParsers.newArgumentParser("Netconf cli").defaultHelp(true)
140                     .description("Generic cli for netconf devices")
141                     .usage("Submit address + port for initial TCP connection (PURE TCP CONNECTIONS ARE NOT SUPPORTED YET)\n" +
142                             "Submit username + password in addition to address + port for initial SSH connection\n" +
143                             "If no arguments(or unexpected combination) is submitted, cli will be started without initial connection\n" +
144                             "To use with ODL controller, run with: java -jar netconf-cli-0.2.5-SNAPSHOT-executable.jar  --server localhost --port 1830 --username admin --password admin");
145
146             final ArgumentGroup tcpGroup = parser.addArgumentGroup("TCP")
147                     .description("Base arguments to initiate TCP connection right away");
148
149             tcpGroup.addArgument("--" + SERVER).help("Netconf device ip-address/domain name");
150             tcpGroup.addArgument("--" + PORT).type(Integer.class).help("Netconf device port");
151             tcpGroup.addArgument("--" + CONNECT_TIMEOUT)
152                     .type(Integer.class)
153                     .setDefault(DEFAULT_CONNECTION_TIMEOUT_MS)
154                     .help("Timeout(in ms) for connection to succeed, if the connection is not fully established by the time is up, " +
155                             "connection attempt is considered a failure. This attribute is not working as expected yet");
156
157             final ArgumentGroup sshGroup = parser.addArgumentGroup("SSH")
158                     .description("SSH credentials, if provided, initial connection will be attempted using SSH");
159
160             sshGroup.addArgument("--" + USERNAME).help("Username for SSH connection");
161             sshGroup.addArgument("--" + PASSWORD).help("Password for SSH connection");
162         }
163
164         public void parse(final String[] args) throws ArgumentParserException {
165             try {
166                 this.parsed = parser.parseArgs(args);
167             } catch (final ArgumentParserException e) {
168                 parser.handleError(e);
169                 throw e;
170             }
171         }
172
173         public InetSocketAddress getServerAddress() {
174             try {
175                 return new InetSocketAddress(InetAddress.getByName(getAddress()), getPort());
176             } catch (final UnknownHostException e) {
177                 throw new IllegalArgumentException(e);
178             }
179         }
180
181         private Integer getPort() {
182             checkParsed();
183             return parsed.getInt(PORT);
184         }
185
186         private String getAddress() {
187             checkParsed();
188             return getString(SERVER);
189         }
190
191         private Integer getConnectionTimeoutMs() {
192             checkParsed();
193             return parsed.getInt(CONNECT_TIMEOUT);
194         }
195
196         private void checkParsed() {
197             Preconditions.checkState(parsed != null, "No arguments were parsed yet");
198         }
199
200         public String getUsername() {
201             checkParsed();
202             return getString(USERNAME);
203         }
204
205         private String getString(final String key) {
206             return parsed.getString(key);
207         }
208
209         public LoginPassword getCredentials() {
210             return new LoginPassword(getUsername(), getPassword());
211         }
212
213         public String getPassword() {
214             checkParsed();
215             return getString(PASSWORD);
216         }
217
218         public InitialConnectionType connectionArgsPresent() {
219             if(getAddress() != null && getPort() != null) {
220                 if(getUsername() != null && getPassword() != null) {
221                     return InitialConnectionType.SSH;
222                 }
223                 return InitialConnectionType.TCP;
224             }
225             return InitialConnectionType.NONE;
226         }
227
228         enum InitialConnectionType {
229             TCP, SSH, NONE
230         }
231     }
232 }