Support maximum wait time of connection attempts
[netconf.git] / apps / netconf-console / src / main / java / org / opendaylight / netconf / console / commands / NetconfConnectDeviceCommand.java
1 /*
2  * Copyright (c) 2016 Inocybe Technologies 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.console.commands;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.VisibleForTesting;
13 import com.google.common.base.Strings;
14 import com.google.common.collect.ImmutableSet;
15 import org.apache.karaf.shell.api.action.Action;
16 import org.apache.karaf.shell.api.action.Command;
17 import org.apache.karaf.shell.api.action.Option;
18 import org.apache.karaf.shell.api.action.lifecycle.Reference;
19 import org.apache.karaf.shell.api.action.lifecycle.Service;
20 import org.opendaylight.netconf.console.api.NetconfCommands;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240104.connection.parameters.Protocol.Name;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240104.connection.parameters.ProtocolBuilder;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240104.connection.parameters.protocol.specification.TlsCaseBuilder;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240104.connection.parameters.protocol.specification.tls._case.TlsBuilder;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240104.credentials.credentials.LoginPwUnencryptedBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240104.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencryptedBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev231121.NetconfNodeBuilder;
32 import org.opendaylight.yangtools.yang.common.Uint16;
33
34 @Service
35 @Command(name = "connect-device", scope = "netconf", description = "Connect to a netconf device.")
36 public class NetconfConnectDeviceCommand implements Action {
37     @Reference
38     private NetconfCommands service;
39
40     @Option(name = "-i",
41             aliases = { "--ipaddress" },
42             description = "IP address of the netconf device",
43             required = true,
44             multiValued = false)
45     String deviceIp;
46
47     @Option(name = "-p",
48             aliases = { "--port" },
49             description = "Port of the netconf device",
50             required = true,
51             multiValued = false)
52     String devicePort;
53
54     @Option(name = "-U",
55             aliases = { "--username" },
56             description = "Username for netconf connection",
57             required = false,
58             censor = true,
59             multiValued = false)
60     String username;
61
62     @Option(name = "-P",
63             aliases = { "--password" },
64             description = "Password for netconf connection",
65             required = false,
66             censor = true,
67             multiValued = false)
68     String password;
69
70     @Option(name = "-t",
71             aliases = { "--tcp-only" },
72             description = "Type of connection, true for tcp only",
73             required = false,
74             multiValued = false)
75     String connectionType = "false";
76
77     @Option(name = "-pr",
78             aliases = { "--protocol" },
79             description = "Which protocol to be used, ssh or tls",
80             required = false,
81             multiValued = false)
82     String protocol = "ssh";
83
84     @Option(name = "-ev",
85             aliases = { "--excluded-versions" },
86             description = "TLS versions not supported by target device",
87             required = false,
88             multiValued = false)
89     String excludedTlsVersions;
90
91     @Option(name = "-sl",
92             aliases = { "--schemaless" },
93             description = "Schemaless surpport, true for schemaless",
94             required = false,
95             multiValued = false)
96     String schemaless = "false";
97
98     @Option(name = "-id",
99             aliases = { "--identifier" },
100             description = "Node Identifier of the netconf device",
101             required = false,
102             multiValued = false)
103     private String deviceId;
104
105     public NetconfConnectDeviceCommand() {
106         // Nothing here, uses injection
107     }
108
109     @VisibleForTesting
110     NetconfConnectDeviceCommand(final NetconfCommands service) {
111         this.service = requireNonNull(service);
112     }
113
114     @VisibleForTesting
115     NetconfConnectDeviceCommand(final NetconfCommands service, final String deviceIp, final String devicePort,
116             final String username, final String password) {
117         this.service = requireNonNull(service);
118         this.deviceIp = requireNonNull(deviceIp);
119         this.devicePort = requireNonNull(devicePort);
120         this.username = requireNonNull(username);
121         this.password = requireNonNull(password);
122     }
123
124     @Override
125     public String execute() {
126         if (!NetconfCommandUtils.isIpValid(deviceIp) || !NetconfCommandUtils.isPortValid(devicePort)) {
127             return "Invalid IP:" + deviceIp + " or Port:" + devicePort + "Please enter a valid entry to proceed.";
128         }
129
130         final boolean isTcpOnly = connectionType.equals("true");
131         final boolean isSchemaless = schemaless.equals("true");
132
133         final var netconfNodeBuilder = new NetconfNodeBuilder()
134             .setHost(new Host(new IpAddress(new Ipv4Address(deviceIp))))
135             .setPort(new PortNumber(Uint16.valueOf(Integer.decode(devicePort))))
136             .setTcpOnly(isTcpOnly)
137             .setSchemaless(isSchemaless);
138
139         if (isTcpOnly || protocol.equalsIgnoreCase("ssh")) {
140             if (Strings.isNullOrEmpty(username) || Strings.isNullOrEmpty(password)) {
141                 return "Empty Username:" + username + " or Password:" + password + ". "
142                     + "In TCP or SSH mode, you must provide valid username and password.";
143             }
144             netconfNodeBuilder.setCredentials(new LoginPwUnencryptedBuilder()
145                 .setLoginPasswordUnencrypted(new LoginPasswordUnencryptedBuilder()
146                     .setUsername(username)
147                     .setPassword(password)
148                     .build())
149                 .build());
150             if (!isTcpOnly) {
151                 netconfNodeBuilder.setProtocol(new ProtocolBuilder().setName(Name.SSH).build());
152             }
153         } else if (protocol.equalsIgnoreCase("tls")) {
154             final var tlsCase = Strings.isNullOrEmpty(excludedTlsVersions) ? null : new TlsCaseBuilder()
155                 .setTls(new TlsBuilder()
156                     .setExcludedVersions(ImmutableSet.copyOf(excludedTlsVersions.split(",")))
157                     .build())
158                 .build();
159             netconfNodeBuilder.setProtocol(new ProtocolBuilder().setName(Name.TLS).setSpecification(tlsCase).build());
160         } else {
161             return "Invalid protocol: " + protocol + ". Only SSH and TLS are supported.";
162         }
163
164         service.connectDevice(netconfNodeBuilder.build(), deviceId);
165         return "Netconf connector added succesfully";
166     }
167 }