2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.netconf.cli;
10 import static com.google.common.base.Throwables.getStackTraceAsString;
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;
32 * Parse arguments, start remote device connection and start CLI after the
33 * connection is fully up
37 public static void main(final String[] args) {
38 final CliArgumentParser cliArgs = new CliArgumentParser();
41 } catch (final ArgumentParserException e) {
42 // Just end the cli, exception was handled by the CliArgumentParser
46 final ConsoleIO consoleIO;
48 consoleIO = new ConsoleIOImpl();
49 } catch (final IOException e) {
50 handleStartupException(e);
54 final SchemaContext localSchema = CommandDispatcher.parseSchema(CommandDispatcher.LOCAL_SCHEMA_PATHS);
55 final SchemaContextRegistry schemaContextRegistry = new SchemaContextRegistry(localSchema);
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);
63 commandDispatcher.addLocalCommands(connectionManager, localSchema, cliArgs.getConnectionTimeoutMs());
65 switch (cliArgs.connectionArgsPresent()) {
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"));
72 writeStatus(consoleIO, "Connecting to %s via SSH. Please wait.", cliArgs.getAddress());
73 connectionManager.connectBlocking(cliArgs.getAddress(), cliArgs.getServerAddress(), getClientSshConfig(cliArgs));
76 case NONE: {/* Do not connect initially */
77 writeStatus(consoleIO, "No initial connection. To connect use the connect command");
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);
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);
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);
105 private static void handleStartupException(final IOException e) {
106 handleException(e, "Unable to initialize CLI");
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())));
113 private static void writeStatus(final ConsoleIO io, final String blueprint, final Object... args) {
115 io.formatLn(blueprint, args);
116 } catch (final IOException e) {
117 handleStartupException(e);
121 private static void handleRunningException(final Exception e) {
122 handleException(e, "Unexpected CLI runtime exception");
125 private static final class CliArgumentParser {
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";
132 public static final String CONNECT_TIMEOUT = "connectionTimeout";
133 public static final int DEFAULT_CONNECTION_TIMEOUT_MS = 50000;
135 private final ArgumentParser parser;
136 private Namespace parsed;
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");
146 final ArgumentGroup tcpGroup = parser.addArgumentGroup("TCP")
147 .description("Base arguments to initiate TCP connection right away");
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)
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");
157 final ArgumentGroup sshGroup = parser.addArgumentGroup("SSH")
158 .description("SSH credentials, if provided, initial connection will be attempted using SSH");
160 sshGroup.addArgument("--" + USERNAME).help("Username for SSH connection");
161 sshGroup.addArgument("--" + PASSWORD).help("Password for SSH connection");
164 public void parse(final String[] args) throws ArgumentParserException {
166 this.parsed = parser.parseArgs(args);
167 } catch (final ArgumentParserException e) {
168 parser.handleError(e);
173 public InetSocketAddress getServerAddress() {
175 return new InetSocketAddress(InetAddress.getByName(getAddress()), getPort());
176 } catch (final UnknownHostException e) {
177 throw new IllegalArgumentException(e);
181 private Integer getPort() {
183 return parsed.getInt(PORT);
186 private String getAddress() {
188 return getString(SERVER);
191 private Integer getConnectionTimeoutMs() {
193 return parsed.getInt(CONNECT_TIMEOUT);
196 private void checkParsed() {
197 Preconditions.checkState(parsed != null, "No arguments were parsed yet");
200 public String getUsername() {
202 return getString(USERNAME);
205 private String getString(final String key) {
206 return parsed.getString(key);
209 public LoginPassword getCredentials() {
210 return new LoginPassword(getUsername(), getPassword());
213 public String getPassword() {
215 return getString(PASSWORD);
218 public InitialConnectionType connectionArgsPresent() {
219 if(getAddress() != null && getPort() != null) {
220 if(getUsername() != null && getPassword() != null) {
221 return InitialConnectionType.SSH;
223 return InitialConnectionType.TCP;
225 return InitialConnectionType.NONE;
228 enum InitialConnectionType {