2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Copyright (C) 2017 Amdocs
8 * =============================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
21 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 * ============LICENSE_END=========================================================
25 package org.onap.ccsdk.sli.adaptors.saltstack.impl;
27 import com.att.eelf.configuration.EELFLogger;
28 import com.att.eelf.configuration.EELFManager;
29 import org.apache.sshd.ClientChannel;
30 import org.apache.sshd.ClientSession;
31 import org.apache.sshd.SshClient;
32 import org.apache.sshd.client.channel.ChannelExec;
33 import org.apache.sshd.client.future.AuthFuture;
34 import org.apache.sshd.client.future.OpenFuture;
35 import org.apache.sshd.common.KeyPairProvider;
36 import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
37 import org.onap.appc.encryption.EncryptionTool;
38 import org.onap.ccsdk.sli.adaptors.saltstack.model.SaltstackResult;
39 import org.onap.ccsdk.sli.adaptors.saltstack.model.SaltstackResultCodes;
41 import java.io.OutputStream;
42 import java.security.KeyPair;
45 * Implementation of SshConnection interface based on Apache MINA SSHD library.
49 public static final int DEFAULT_CONNECTION_RETRY_DELAY = 60;
50 public static final int DEFAULT_CONNECTION_RETRY_COUNT = 5;
51 private static final EELFLogger logger = EELFManager.getInstance().getApplicationLogger();
52 private static final long AUTH_TIMEOUT = 60000;
53 //TODO : change back to 120000
54 private static final long EXEC_TIMEOUT = 120000;
57 private String username;
58 private String password;
59 private long timeout = EXEC_TIMEOUT;
60 private String keyFile;
61 private SshClient sshClient;
62 private ClientSession clientSession;
64 public SshConnection(String host, int port, String username, String password, String keyFile) {
67 this.username = username;
68 this.password = password;
69 this.keyFile = keyFile;
72 public SshConnection(String host, int port, String username, String password) {
73 this(host, port, username, password, null);
76 public SshConnection(String host, int port, String keyFile) {
77 this(host, port, null, null, keyFile);
80 public SaltstackResult connect() {
81 SaltstackResult result = new SaltstackResult();
82 sshClient = SshClient.setUpDefaultClient();
86 sshClient.connect(EncryptionTool.getInstance().decrypt(username), host, port).await().getSession();
87 if (password != null) {
88 clientSession.addPasswordIdentity(EncryptionTool.getInstance().decrypt(password));
90 if (keyFile != null) {
91 KeyPairProvider keyPairProvider = new FileKeyPairProvider(new String[]{
94 KeyPair keyPair = keyPairProvider.loadKeys().iterator().next();
95 clientSession.addPublicKeyIdentity(keyPair);
97 AuthFuture authFuture = clientSession.auth();
98 authFuture.await(AUTH_TIMEOUT);
99 if (!authFuture.isSuccess()) {
100 String errMessage = "Error establishing ssh connection to [" + username + "@" + host + ":" + port
101 + "]. Authentication failed.";
102 result.setStatusCode(SaltstackResultCodes.USER_UNAUTHORIZED.getValue());
103 result.setStatusMessage(errMessage);
105 } catch (RuntimeException e) {
106 String errMessage = "Error establishing ssh connection to [" + username + "@" + host + ":" + port + "]." +
107 "Runtime Exception : " + e.getMessage();
108 result.setStatusCode(SaltstackResultCodes.UNKNOWN_EXCEPTION.getValue());
109 result.setStatusMessage(errMessage);
110 } catch (Exception e) {
111 String errMessage = "Error establishing ssh connection to [" + username + "@" + host + ":" + port + "]." +
112 "Host Unknown : " + e.getMessage();
113 result.setStatusCode(SaltstackResultCodes.HOST_UNKNOWN.getValue());
114 result.setStatusMessage(errMessage);
116 if (logger.isDebugEnabled()) {
117 logger.debug("SSH: connected to [" + toString() + "]");
119 result.setStatusCode(SaltstackResultCodes.SUCCESS.getValue());
123 public SaltstackResult connectWithRetry(int retryCount, int retryDelay) {
125 SaltstackResult result = new SaltstackResult();
126 if (retryCount == 0) {
127 retryCount = DEFAULT_CONNECTION_RETRY_COUNT;
129 if (retryDelay == 0) {
130 retryDelay = DEFAULT_CONNECTION_RETRY_DELAY;
132 retriesLeft = retryCount + 1;
135 result = this.connect();
137 } catch (RuntimeException e) {
138 if (retriesLeft > 1) {
139 logger.debug("SSH Connection failed. Waiting for change in server's state.");
140 waitForConnection(retryDelay);
142 logger.debug("Retrying SSH connection. Attempt [" + Integer.toString(retryCount - retriesLeft + 1)
143 + "] out of [" + retryCount + "]");
148 } while (retriesLeft > 0);
152 public void disconnect() {
154 if (logger.isDebugEnabled()) {
155 logger.debug("SSH: disconnecting from [" + toString() + "]");
157 clientSession.close(false);
159 if (sshClient != null) {
165 public void setExecTimeout(long timeout) {
166 this.timeout = timeout;
169 public SaltstackResult execCommand(String cmd, OutputStream out, OutputStream err, SaltstackResult result ) {
170 return execCommand(cmd, out, err, false, result);
173 public SaltstackResult execCommandWithPty(String cmd, OutputStream out, SaltstackResult result ) {
174 return execCommand(cmd, out, out, true, result);
177 private SaltstackResult execCommand(String cmd, OutputStream out, OutputStream err,
178 boolean usePty, SaltstackResult result ) {
181 if (logger.isDebugEnabled()) {
182 logger.debug("SSH: executing command");
184 ChannelExec client = clientSession.createExecChannel(cmd);
185 client.setUsePty(usePty); // use pseudo-tty?
188 OpenFuture openFuture = client.open();
191 client.waitFor(ClientChannel.CLOSED, timeout);
193 Integer exitStatusI = client.getExitStatus();
194 if (exitStatusI == null) {
195 String errMessage = "Error executing command [" + cmd + "] over SSH [" + username + "@" + host
196 + ":" + port + "]. SSH operation timed out.";
197 result.setStatusCode(SaltstackResultCodes.OPERATION_TIMEOUT.getValue());
198 result.setStatusMessage(errMessage);
201 exitStatus = exitStatusI;
205 result.setSshExitStatus(exitStatus);
207 } catch (RuntimeException e) {
208 String errMessage = "Error establishing ssh connection to [" + username + "@" + host + ":" + port + "]." +
209 "Runtime Exception : " + e.getMessage();
210 result.setStatusCode(SaltstackResultCodes.UNKNOWN_EXCEPTION.getValue());
211 result.setStatusMessage(errMessage);
212 } catch (Exception e1) {
213 String errMessage = "Error executing command [" + cmd + "] over SSH [" + username + "@" + host + ":" +
214 port + "]" + e1.getMessage();
215 result.setStatusCode(SaltstackResultCodes.UNKNOWN_EXCEPTION.getValue());
216 result.setStatusMessage(errMessage);
218 result.setStatusCode(SaltstackResultCodes.SUCCESS.getValue());
222 private void waitForConnection(int retryDelay) {
223 long time = retryDelay * 1000L;
224 long future = System.currentTimeMillis() + time;
226 while (System.currentTimeMillis() < future && time > 0) {
229 } catch (InterruptedException e) {
231 * This is rare, but it can happen if another thread interrupts us while we are sleeping. In that
232 * case, the thread is resumed before the delay time has actually expired, so re-calculate the
233 * amount of delay time needed and reenter the sleep until we get to the future time.
235 time = future - System.currentTimeMillis();
242 public String toString() {
243 String address = host;
244 if (username != null) {
245 address = username + '@' + address + ':' + port;