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.appc.ccadaptor;
27 import com.att.eelf.configuration.EELFLogger;
28 import com.att.eelf.configuration.EELFManager;
29 import com.jcraft.jsch.Channel;
30 import com.jcraft.jsch.ChannelSftp;
31 import com.jcraft.jsch.ChannelShell;
32 import com.jcraft.jsch.ChannelSubsystem;
33 import com.jcraft.jsch.JSch;
34 import com.jcraft.jsch.JSchException;
35 import com.jcraft.jsch.Session;
36 import com.jcraft.jsch.SftpException;
37 import com.jcraft.jsch.UIKeyboardInteractive;
38 import com.jcraft.jsch.UserInfo;
39 import java.io.BufferedInputStream;
40 import java.io.BufferedReader;
41 import java.io.BufferedWriter;
42 import java.io.ByteArrayInputStream;
43 import java.io.ByteArrayOutputStream;
44 import java.io.DataInputStream;
45 import java.io.DataOutputStream;
47 import java.io.FileWriter;
48 import java.io.IOException;
49 import java.io.InputStream;
50 import java.io.InputStreamReader;
51 import java.io.OutputStream;
52 import java.io.RandomAccessFile;
53 import java.text.DateFormat;
54 import java.text.SimpleDateFormat;
55 import java.util.Calendar;
56 import java.util.Date;
57 import java.util.StringTokenizer;
58 import java.util.concurrent.TimeUnit;
59 import javax.annotation.Nonnull;
60 import org.apache.commons.lang.StringUtils;
61 import org.onap.appc.i18n.Msg;
63 public class SshJcraftWrapper {
65 private static final EELFLogger log = EELFManager.getInstance().getLogger(SshJcraftWrapper.class);
66 static final int DEFAULT_PORT = 22;
67 static final String EOL = "\n";
68 static final String CHANNEL_SHELL_TYPE = "shell";
69 static final String CHANNEL_SUBSYSTEM_TYPE = "subsystem";
70 private static final String TERMINAL_BASIC_MODE = "vt102";
71 static final String STRICT_HOST_CHECK_KEY = "StrictHostKeyChecking";
72 static final String STRICT_HOST_CHECK_VALUE = "no";
73 static final String DELIMITERS_SEPARATOR = "|";
75 private TelnetListener listener = null;
76 private String routerLogFileName = null;
77 private BufferedReader reader = null;
78 private BufferedWriter out = null;
79 private File tmpFile = null;
80 private JSch jsch = null;
81 private Session session = null;
82 private Channel channel = null;
83 private String aggregatedReceivedString = "";
84 private String routerCmdType = "XML";
85 private String routerFileName = null;
86 private File jcraftReadSwConfigFileFromDisk = new File("/tmp/jcraftReadSwConfigFileFromDisk");
87 private String equipNameCode = null;
88 private String routerName = null;
89 private String hostName = null;
90 private String userName = null;
91 private String passWord = null;
92 private int readIntervalMs = 500;
93 private int readBufferSizeBytes = 512_000;
94 private int charsChunkSize = 300_000;
95 private int sessionTimeoutMs = 9_000;
96 private char[] charBuffer;
97 private Runtime runtime = Runtime.getRuntime();
99 public SshJcraftWrapper() {
100 this.jsch = new JSch();
101 this.charBuffer = new char[readBufferSizeBytes];
104 SshJcraftWrapper(JSch jsch, int readIntervalMs, int readBufferSizeBytes) {
105 this.readIntervalMs = readIntervalMs;
107 this.readBufferSizeBytes = readBufferSizeBytes;
108 this.charBuffer = new char[readBufferSizeBytes];
111 public void connect(String hostname, String username, String password, String prompt, int timeOut)
113 log.debug("Attempting to connect to {0} username={1} prompt='{2}' timeOut={3}",
114 hostname, username, prompt, timeOut);
115 routerName = hostname;
120 channel = provideSessionChannel(CHANNEL_SHELL_TYPE, DEFAULT_PORT, timeOut);
121 ((ChannelShell) channel).setPtyType(TERMINAL_BASIC_MODE);
122 reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())),
123 readBufferSizeBytes);
125 log.info("Successfully connected. Flushing input buffer.");
127 receiveUntil(prompt, 3000, "No cmd was sent, just waiting");
128 } catch (IOException e) {
129 log.warn("Caught an Exception: Nothing to flush out.", e);
131 } catch (JSchException e) {
132 log.error(Msg.CANNOT_ESTABLISH_CONNECTION, hostname, String.valueOf(DEFAULT_PORT), username);
133 throw new IOException(e.toString());
137 // User specifies the port number.
138 public void connect(String hostname, String username, String password, String prompt, int timeOut, int portNum)
140 log.debug("Attempting to connect to {0} username={1} prompt='{2}' timeOut={3} portNum={4}",
141 hostname, username, prompt, timeOut, portNum);
142 routerName = hostname;
147 channel = provideSessionChannel(CHANNEL_SHELL_TYPE, portNum, timeOut);
148 ((ChannelShell) channel).setPtyType(TERMINAL_BASIC_MODE);
149 reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())),
150 readBufferSizeBytes);
152 log.info("Successfully connected. Flushing input buffer.");
154 if ("]]>]]>".equals(prompt)) {
155 receiveUntil("]]>]]>", 10000, "No cmd was sent, just waiting");
157 receiveUntil(":~#", 5000, "No cmd was sent, just waiting");
159 } catch (IOException e) {
160 log.warn("Caught an Exception: Nothing to flush out.", e);
162 } catch (JSchException e) {
163 log.error(Msg.CANNOT_ESTABLISH_CONNECTION, hostname, String.valueOf(portNum), username);
164 throw new IOException(e.toString());
169 public String receiveUntil(String delimeters, int timeout, String cmdThatWasSent) throws IOException {
171 boolean match = false;
172 boolean cliPromptCmd = false;
173 StringBuilder sb = new StringBuilder();
174 StringBuilder sbReceive = new StringBuilder();
175 log.debug("delimeters='{0}' timeout={1} cmdThatWasSent='{2}'", delimeters, timeout, cmdThatWasSent);
177 aggregatedReceivedString = "";
178 FileWriter fileWriter = null;
180 long deadline = new Date().getTime() + timeout;
182 session.setTimeout(timeout); // This is the socket timeout value.
184 if (new Date().getTime() > deadline) {
185 String formattedCmd = removeWhiteSpaceAndNewLineCharactersAroundString(cmdThatWasSent);
186 log.error(Msg.SSH_CONNECTION_TIMEOUT, routerName, formattedCmd);
187 throw new TimedOutException("Routine has timed out");
189 sleep(readIntervalMs);
190 int len = reader.read(charBuffer, 0, readBufferSizeBytes);
191 log.trace("After reader. Read command len={0}", len);
193 log.error(Msg.SSH_CONNECTION_TIMEOUT, routerName, cmdThatWasSent);
194 throw new TimedOutException("Received a SocketTimeoutException router=" + routerName);
197 if (cmdThatWasSent.indexOf("IOS_XR_uploadedSwConfigCmd") != -1) {
199 // This is a IOS XR sw config file. We will write it to the disk.
200 timeout = timeout * 2;
201 deadline = new Date().getTime() + timeout;
202 log.debug("IOS XR upload for software config: timeout={0}", timeout);
203 StringTokenizer st = new StringTokenizer(cmdThatWasSent);
205 routerFileName = st.nextToken();
206 fileWriter = new FileWriter(routerFileName);
207 out = new BufferedWriter(fileWriter);
208 routerLogFileName = "/tmp/" + routerName;
209 tmpFile = new File(routerLogFileName);
210 log.debug("Prepared for writing swConfigFile to disk, routerFileName=" + routerFileName);
212 out.write(charBuffer, 0, len);
214 log.debug("{0} bytes has been written to the disk", len);
215 if (tmpFile.exists()) {
216 appendToRouterFile(routerLogFileName, len);
218 match = checkIfReceivedStringMatchesDelimeter(len, "\nXML>");
227 log.debug("Reader read {0} of data within {1} read iteration", len, readCounts);
230 for (int i = 0; i < len; i++) {
232 if ((c != 7) && (c != 13) && (c != 0) && (c != 27)) {
233 sbReceive.append(charBuffer[i]);
234 sb.append(charBuffer[i]);
237 appendToRouterFile("/tmp/" + routerName, len);
238 if (listener != null) {
239 listener.receivedString(sb.toString());
241 match = checkIfReceivedStringMatchesDelimeter(delimeters, sb.toString(), cmdThatWasSent);
243 log.trace("Match was true, breaking the loop.");
248 log.trace("cliPromptCmd");
250 for (int i = 0; i < len; i++) {
251 sbReceive.append(charBuffer[i]);
252 sb.append(charBuffer[i]);
254 appendToRouterFile("/tmp/" + routerName, sb);
255 if (listener != null) {
256 listener.receivedString(sb.toString());
258 log.debug("sb2={0} delimiters={1}", sb.toString(), delimeters);
259 if (sb.toString().contains("\nariPrompt>")) {
260 log.debug("Found ari prompt");
265 } catch (JSchException | IOException e) {
266 log.error(Msg.SSH_DATA_EXCEPTION, e.getMessage());
267 throw new TimedOutException(e.getMessage());
270 if (fileWriter != null) {
273 } catch (IOException ex) {
274 log.warn("Failed to close fileWriter output stream", ex);
277 return stripOffCmdFromRouterResponse(sbReceive.toString());
280 private void sleep(long timeoutMs) {
282 TimeUnit.MILLISECONDS.sleep(timeoutMs);
283 } catch (java.lang.InterruptedException ee) {
284 Thread.currentThread().interrupt();
288 private void checkConnection() {
290 if (!isConnected() || !reader.ready()) {
291 throw new IllegalStateException("Connection not established. Cannot perform action.");
293 } catch (IOException e) {
294 throw new IllegalStateException("Reader stream is closed. Cannot perform action.", e);
298 public boolean checkIfReceivedStringMatchesDelimeter(String delimeters, String receivedString,
299 String cmdThatWasSent) {
300 // The delimeters are in a '|' seperated string. Return true on the first match.
301 log.debug("Entered checkIfReceivedStringMatchesDelimeter: delimeters={0} cmdThatWasSent={1} receivedString={2}",
302 delimeters, cmdThatWasSent, receivedString);
303 StringTokenizer st = new StringTokenizer(delimeters, DELIMITERS_SEPARATOR);
305 if ((delimeters.contains("#$")) || ("CLI".equals(routerCmdType))) // This would be an IOS XR, CLI command.
307 int x = receivedString.lastIndexOf('#');
308 int y = receivedString.length() - 1;
309 log.debug("IOS XR, CLI command");
310 if (log.isTraceEnabled()) {
311 log.trace("cmdThatWasSent={0}, lastIndexOf hash delimiter={1}, maxIndexNum={2}", cmdThatWasSent, x, y);
313 return (x != -1) && (y == x);
315 if (cmdThatWasSent.contains("show config")) {
316 log.trace("In the block for 'show config'");
317 while (st.hasMoreTokens()) {
318 String delimeter = st.nextToken();
319 // Make sure we don't get faked out by a response of " #".
321 // # signaling-local-address ipv6 FD00:F4D5:EA06:1::110:136:254
323 int x = receivedString.lastIndexOf(delimeter);
324 if ((receivedString.lastIndexOf(delimeter) != -1) && (receivedString.lastIndexOf(" #") != x - 1)) {
325 log.debug("receivedString={0}", receivedString);
326 log.trace("Found ending for 'show config' command, exiting.");
331 aggregatedReceivedString = aggregatedReceivedString + receivedString;
332 appendToFile("/tmp/aggregatedReceivedString.debug", aggregatedReceivedString);
334 log.debug("receivedString={0}", receivedString);
335 while (st.hasMoreTokens()) {
336 String delimeter = st.nextToken();
337 log.debug("Looking for an delimiter of:{0}", delimeter);
338 if (aggregatedReceivedString.indexOf(delimeter) != -1) {
339 log.debug("Found delimiter={0}, exiting", delimeter);
340 aggregatedReceivedString = "";
348 public boolean checkIfReceivedStringMatchesDelimeter(int len, String delimeter) {
351 String str = StringUtils.EMPTY;
353 if (jcraftReadSwConfigFileFromDisk()) {
354 log.trace("jcraftReadSwConfigFileFromDisk block");
355 File fileName = new File(routerFileName);
356 log.debug("jcraftReadSwConfigFileFromDisk::: Will read the tail end of the file from the disk");
358 str = getLastFewLinesOfFile(fileName, 3);
359 } catch (IOException e) {
360 log.warn("IOException occurred, while reading file=" + fileName, e);
363 // When looking at the end of the charBuffer, don't include any linefeeds or spaces. We only want to make the smallest string possible.
364 for (x = len - 1; x >= 0; x--) {
366 if ((c != 10) && (c != 32)) // Not a line feed nor a space.
371 if ((x + 1 - 13) >= 0) {
372 str = new String(charBuffer, x + 1 - 13, 13);
373 log.debug("str:{0}", str);
375 File fileName = new File(routerFileName);
376 log.debug("Will read the tail end of the file from the disk, x={0} len={1} str={2} routerFileName={3}",
377 x, len, str, routerFileName);
379 str = getLastFewLinesOfFile(fileName, 3);
380 } catch (IOException e) {
381 log.warn("IOException occurred, while reading file=" + fileName, e);
386 log.debug("Parsed string was str='{0}', searched delimiter was {1}");
387 return str.contains(delimeter);
390 public void closeConnection() {
391 log.info("Closing connection");
393 if (reader != null) {
396 } catch (IOException ex) {
397 log.warn("Could not close reader instance", ex);
400 channel.disconnect();
401 session.disconnect();
409 boolean isConnected() {
410 return channel != null && session != null;
413 public void send(String cmd) throws IOException {
414 try (OutputStream os = channel.getOutputStream(); DataOutputStream dos = new DataOutputStream(os)) {
415 String command = enhanceCommandWithEOL(cmd);
416 int length = command.length();
417 log.debug("Sending ssh command: length={0}, payload: {1}", command.length(), command);
418 if(isCmdLengthEnoughToSendInChunks(length, charsChunkSize)) {
419 sendSshCommandInChunks(command, dos);
421 sendSshCommand(command, dos);
423 } catch (IOException e) {
424 log.error(Msg.SSH_DATA_EXCEPTION, e.getMessage());
429 public void sendChar(int v) throws IOException {
430 try (OutputStream os = channel.getOutputStream(); DataOutputStream dos = new DataOutputStream(os)) {
431 if (log.isTraceEnabled()) {
432 log.trace("Sending charCode: {0}", v);
436 } catch (IOException e) {
437 log.error(Msg.SSH_DATA_EXCEPTION, e.getMessage());
442 public void send(byte[] b, int off, int len) throws IOException {
443 try (OutputStream os = channel.getOutputStream(); DataOutputStream dos = new DataOutputStream(os)) {
444 dos.write(b, off, len);
446 } catch (IOException e) {
447 log.error(Msg.SSH_DATA_EXCEPTION, e.getMessage());
452 public static class MyUserInfo implements UserInfo, UIKeyboardInteractive {
455 public String getPassword() {
460 public boolean promptYesNo(String str) {
465 public String getPassphrase() {
470 public boolean promptPassphrase(String message) {
475 public boolean promptPassword(String message) {
480 public void showMessage(String message) {
485 public String[] promptKeyboardInteractive(String destination,
490 return new String[0];
494 public void addListener(TelnetListener listener) {
495 this.listener = listener;
498 private void appendToFile(String fileName, String dataToWrite) {
499 File outputFile = new File(fileName);
500 if (outputFile.exists()) {
501 try (FileWriter fw = new FileWriter(fileName, true); BufferedWriter ow = new BufferedWriter(fw)) {
502 ow.write(dataToWrite);
504 } catch (IOException e) {
505 log.warn("IOException occurred while writing to file=" + fileName, e);
510 public String getTheDate() {
511 DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy H:mm:ss ");
512 return dateFormat.format(Calendar.getInstance().getTime());
516 public void appendToRouterFile(String fileName, StringBuilder dataToWrite) {
517 appendToFile(fileName, dataToWrite.toString());
520 public void appendToRouterFile(String fileName, int len) {
521 File outputFile = new File(fileName);
522 if (outputFile.exists()) {
523 try (FileWriter fw = new FileWriter(fileName, true); BufferedWriter ow = new BufferedWriter(fw)) {
524 ow.write(charBuffer, 0, len);
526 } catch (IOException e) {
527 log.warn("Could not write data to router file:" + fileName, e);
532 public String removeWhiteSpaceAndNewLineCharactersAroundString(String str) {
533 if (str != null && !StringUtils.EMPTY.equals(str)) {
534 StringTokenizer strTok = new StringTokenizer(str, EOL);
535 StringBuilder sb = new StringBuilder();
537 while (strTok.hasMoreTokens()) {
538 String line = strTok.nextToken();
541 return sb.toString().trim();
543 return StringUtils.EMPTY;
546 public String stripOffCmdFromRouterResponse(String routerResponse) {
547 // The session of SSH will echo the command sent to the router, in the router's response.
548 // Since all our commands are terminated by a '\n', strip off the first line
549 // of the response from the router. This first line contains the orginal command.
551 String[] responseTokens = routerResponse.split(EOL, 2);
552 return responseTokens[responseTokens.length - 1];
555 public void setRouterCommandType(String type) {
556 this.routerCmdType = type;
557 log.debug("Router command type is set to: {0}", type);
560 public String getLastFewLinesOfFile(File file, int linesToRead) throws IOException {
562 try(RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r")) {
564 StringBuilder builder = new StringBuilder();
565 long length = file.length();
567 randomAccessFile.seek(length);
568 for (long seek = length; seek >= 0; --seek) {
569 randomAccessFile.seek(seek);
570 char c = (char) randomAccessFile.read();
573 builder = builder.reverse();
574 tail = builder.append(tail).toString();
576 builder.setLength(0);
577 if (lines == linesToRead) {
583 if (log.isDebugEnabled()) {
584 log.debug("Content read from file={0} was tail={1}", file.getName(), tail);
589 public boolean jcraftReadSwConfigFileFromDisk() {
590 return jcraftReadSwConfigFileFromDisk.exists();
593 public String getEquipNameCode() {
594 return equipNameCode;
597 public void setEquipNameCode(String equipNameCode) {
598 this.equipNameCode = equipNameCode;
601 public String getRouterName() {
605 // Routine does reads until it has read 'nchars' or times out.
606 public String receiveUntilBufferFlush(int ncharsSent, int timeout, String command) throws IOException {
607 log.debug("ncharsSent={0}, timeout={1}, message={2}", ncharsSent, timeout, command);
608 int ncharsTotalReceived = 0;
610 StringBuilder received = new StringBuilder();
612 long deadline = new Date().getTime() + timeout;
615 session.setTimeout(timeout); // This is the socket timeout value.
617 if (new Date().getTime() > deadline) {
618 log.error(Msg.SSH_CONNECTION_TIMEOUT, routerName, command);
619 throw new TimedOutException("Routine has timed out");
621 ncharsRead = reader.read(charBuffer, 0, readBufferSizeBytes);
623 received.append(charBuffer, 0, ncharsRead);
625 if (listener != null) {
626 listener.receivedString(String.copyValueOf(charBuffer, 0, ncharsRead));
628 appendToRouterFile("/tmp/" + routerName, ncharsRead);
629 ncharsTotalReceived = ncharsTotalReceived + ncharsRead;
630 if (ncharsTotalReceived >= ncharsSent) {
631 log.debug("Received the correct number of characters, ncharsSent={0}, ncharsTotalReceived={1}",
632 ncharsSent, ncharsTotalReceived);
634 return received.toString();
637 } catch (JSchException e) {
638 log.error(Msg.SSH_SESSION_CONFIG_ERROR, e.getMessage());
639 log.debug("ncharsSent={0}, ncharsTotalReceived={1}, ncharsRead={2} until error occurred",
640 ncharsSent, ncharsTotalReceived, ncharsRead);
641 throw new TimedOutException(e.getMessage());
645 public String getHostName() {
649 public String getUserName() {
653 public String getPassWord() {
657 public void sftpPutFile(String sourcePath, String destDirectory) throws IOException {
659 Session sftpSession = jsch.getSession(userName, hostName, DEFAULT_PORT);
660 UserInfo ui = new MyUserInfo();
661 sftpSession.setPassword(passWord);
662 sftpSession.setUserInfo(ui);
663 sftpSession.connect(30 * 1000);
664 ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
666 log.debug("Sending via sftp from source: {0} to destination: {1}", sourcePath, destDirectory);
667 sftp.put(sourcePath, destDirectory, ChannelSftp.OVERWRITE);
668 sftpSession.disconnect();
669 } catch (JSchException ex) {
670 log.error(Msg.CANNOT_ESTABLISH_CONNECTION, hostName, String.valueOf(DEFAULT_PORT), userName);
671 throw new IOException(ex.getMessage());
672 } catch (SftpException ex) {
673 log.error(Msg.SFTP_TRANSFER_FAILED, hostName, userName, "PUT", ex.getMessage());
674 throw new IOException(ex.getMessage());
678 public void sftpPutStringData(String stringOfData, String fullPathDest) throws IOException {
680 Session sftpSession = jsch.getSession(userName, hostName, DEFAULT_PORT);
681 UserInfo ui = new MyUserInfo();
682 sftpSession.setPassword(passWord);
683 sftpSession.setUserInfo(ui);
684 sftpSession.connect(30 * 1000);
685 ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
687 InputStream is = new ByteArrayInputStream(stringOfData.getBytes());
688 log.debug("Sending via sftp stringOfData to destination: {0}", fullPathDest);
689 sftp.put(is, fullPathDest, ChannelSftp.OVERWRITE);
690 sftpSession.disconnect();
691 } catch (JSchException ex) {
692 log.error(Msg.CANNOT_ESTABLISH_CONNECTION, hostName, String.valueOf(DEFAULT_PORT), userName);
693 throw new IOException(ex.getMessage());
694 } catch (SftpException ex) {
695 log.error(Msg.SFTP_TRANSFER_FAILED, hostName, userName, "PUT", ex.getMessage());
696 throw new IOException(ex.getMessage());
700 public String sftpGet(String fullFilePathName) throws IOException {
702 Session sftpSession = jsch.getSession(userName, hostName, DEFAULT_PORT);
703 UserInfo ui = new MyUserInfo();
704 sftpSession.setPassword(passWord);
705 sftpSession.setUserInfo(ui);
706 sftpSession.connect(30 * 1000);
707 ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
709 InputStream in = sftp.get(fullFilePathName);
710 String sftpFileString = readInputStreamAsString(in);
711 log.debug("Received data via sftp connection sftpFileString={0} from fullFilePathName={1}",
712 sftpFileString, fullFilePathName);
713 sftpSession.disconnect();
714 return sftpFileString;
715 } catch (JSchException ex) {
716 log.error(Msg.CANNOT_ESTABLISH_CONNECTION, hostName, String.valueOf(DEFAULT_PORT), userName);
717 throw new IOException(ex.getMessage());
718 } catch (SftpException ex) {
719 log.error(Msg.SFTP_TRANSFER_FAILED, hostName, userName, "GET", ex.getMessage());
720 throw new IOException(ex.getMessage());
724 public static String readInputStreamAsString(InputStream in) throws IOException {
725 BufferedInputStream bis = new BufferedInputStream(in);
726 ByteArrayOutputStream buf = new ByteArrayOutputStream();
727 int result = bis.read();
728 while (result != -1) {
729 byte b = (byte) result;
733 return buf.toString();
737 public void logMemoryUsage() {
738 int mb = 1024 * 1024;
740 long maxMemoryAvailable;
741 long memoryLeftOnHeap;
742 maxMemoryAvailable = runtime.maxMemory() / mb;
743 usedMemory = (runtime.totalMemory() / mb) - (runtime.freeMemory() / mb);
744 memoryLeftOnHeap = maxMemoryAvailable - usedMemory;
745 log.info("Memory usage: maxMemoryAvailable={0}, usedMemory={1}, memoryLeftOnHeap={2}",
746 maxMemoryAvailable, usedMemory, memoryLeftOnHeap);
749 public void connect(String hostname, String username, String password, int timeOut, int portNum,
750 String subsystem) throws IOException {
752 if (log.isDebugEnabled()) {
754 "Attempting to connect to {0} username={1} timeOut={2} portNum={3} subsystem={4}",
755 hostname, username, timeOut, portNum, subsystem);
757 this.routerName = hostname;
758 this.hostName = hostname;
759 this.userName = username;
760 this.passWord = password;
762 channel = provideSessionChannel(CHANNEL_SUBSYSTEM_TYPE, portNum, timeOut);
763 ((ChannelSubsystem) channel).setSubsystem(subsystem);
764 ((ChannelSubsystem) channel).setPty(true); //expected ptyType vt102
765 reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())),
766 readBufferSizeBytes);
767 channel.connect(5000);
768 } catch (JSchException e) {
769 log.error(Msg.CANNOT_ESTABLISH_CONNECTION, hostname, String.valueOf(portNum), username);
770 throw new IOException(e.getMessage());
774 public void connect(String hostName, String username, String password) throws IOException {
775 log.debug("Attempting to connect to {0} username={1} portNumber={2}", hostName, username, DEFAULT_PORT);
776 this.routerName = hostName;
777 this.hostName = hostName;
778 this.userName = username;
779 this.passWord = password;
781 channel = provideSessionChannel(CHANNEL_SHELL_TYPE, DEFAULT_PORT, 30000);
782 ((ChannelShell) channel).setPtyType(TERMINAL_BASIC_MODE);
783 reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())),
784 readBufferSizeBytes);
787 receiveUntil(":~#", 9000, "No cmd was sent, just waiting, but we can stop on a '~#'");
788 } catch (Exception e) {
789 log.warn("Caught an Exception: Nothing to flush out.", e);
792 } catch (JSchException e) {
793 log.error(Msg.CANNOT_ESTABLISH_CONNECTION, hostName, String.valueOf(DEFAULT_PORT), username);
794 throw new IOException(e.getMessage());
799 public void put(String sourcePath, String destDirectory) throws IOException {
801 Session sftpSession = jsch.getSession(userName, hostName, DEFAULT_PORT);
802 UserInfo ui = new MyUserInfo();
803 sftpSession.setPassword(passWord);
804 sftpSession.setUserInfo(ui);
805 sftpSession.connect(30 * 1000);
806 ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
808 log.debug("Sending via sftp from source: {0} to destination: {1}", sourcePath, destDirectory);
809 sftp.put(sourcePath, destDirectory, ChannelSftp.OVERWRITE);
810 sftpSession.disconnect();
811 } catch (JSchException ex) {
812 log.error(Msg.CANNOT_ESTABLISH_CONNECTION, hostName, String.valueOf(DEFAULT_PORT), userName);
813 throw new IOException(ex.getMessage());
814 } catch (SftpException ex) {
815 log.error(Msg.SFTP_TRANSFER_FAILED, hostName, userName, "PUT", ex.getMessage());
816 throw new IOException(ex.getMessage());
820 public void put(InputStream is, String fullPathDest, String hostName, String userName, String passWord)
822 Session sftpSession = null;
824 log.debug("Sftp put invoked, connection details: username={1} hostname={2}",
827 java.util.Properties config = new java.util.Properties();
828 config.put("StrictHostKeyChecking", "no");
829 sftpSession = jsch.getSession(userName, hostName, DEFAULT_PORT);
830 UserInfo ui = new MyUserInfo();
831 sftpSession.setPassword(passWord);
832 sftpSession.setUserInfo(ui);
833 sftpSession.setConfig(config);
834 sftpSession.connect(30 * 1000);
835 ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
837 String oldFiles = fullPathDest + "*";
838 log.debug("Deleting old files: {0}", oldFiles);
841 } catch (SftpException ex) {
842 String exp = "No such file";
843 if (ex.getMessage() != null && ex.getMessage().contains(exp)) {
844 log.warn("No files found, continue");
846 log.error(Msg.SFTP_TRANSFER_FAILED, hostName, userName, "RM", ex.getMessage());
850 log.debug("Sending stringOfData to destination {0}", fullPathDest);
851 sftp.put(is, fullPathDest, ChannelSftp.OVERWRITE);
852 } catch (JSchException ex) {
853 log.error(Msg.CANNOT_ESTABLISH_CONNECTION, hostName, String.valueOf(DEFAULT_PORT), userName);
854 throw new IOException(ex.getMessage());
855 } catch (SftpException ex) {
856 log.error(Msg.SFTP_TRANSFER_FAILED, hostName, userName, "PUT", ex.getMessage());
857 throw new IOException(ex.getMessage());
859 if (sftpSession != null) {
860 sftpSession.disconnect();
865 public String get(String fullFilePathName, String hostName, String userName, String passWord) throws IOException {
866 Session sftpSession = null;
868 log.debug("Sftp get invoked, connection details: username={1} hostname={2}",
871 sftpSession = jsch.getSession(userName, hostName, DEFAULT_PORT);
872 java.util.Properties config = new java.util.Properties();
873 config.put("StrictHostKeyChecking", "no");
874 UserInfo ui = new MyUserInfo();
875 sftpSession.setPassword(passWord);
876 sftpSession.setUserInfo(ui);
877 sftpSession.setConfig(config);
878 sftpSession.connect(30 * 1000);
879 ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
881 InputStream in = sftp.get(fullFilePathName);
882 return readInputStreamAsString(in);
883 } catch (JSchException ex) {
884 log.error(Msg.CANNOT_ESTABLISH_CONNECTION, hostName, String.valueOf(DEFAULT_PORT), userName);
885 throw new IOException(ex.getMessage());
886 } catch (SftpException ex) {
887 log.error(Msg.SFTP_TRANSFER_FAILED, hostName, userName, "GET", ex.getMessage());
888 throw new IOException(ex.getMessage());
890 if (sftpSession != null) {
891 sftpSession.disconnect();
896 public String send(String cmd, String delimiter) throws IOException {
897 try (OutputStream os = channel.getOutputStream(); DataOutputStream dos = new DataOutputStream(os)) {
898 String command = enhanceCommandWithEOL(cmd);
899 int length = command.length();
900 log.debug("Sending ssh command: length={0}, payload: {1}", command.length(), command);
901 if(isCmdLengthEnoughToSendInChunks(length, charsChunkSize)) {
902 return sendSshCommandInChunks(command, dos);
904 sendSshCommand(command, dos);
905 return receiveUntil(delimiter, 300000, cmd);
910 private void sendSshCommand(@Nonnull String command, @Nonnull DataOutputStream channelOutputStream)
912 channelOutputStream.writeBytes(command);
913 channelOutputStream.flush();
916 private String sendSshCommandInChunks(@Nonnull String command, @Nonnull DataOutputStream channelOutputStream) throws IOException {
917 StringBuilder received = new StringBuilder();
918 int charsTotalSent = 0;
919 int length = command.length();
920 for (int i = 0; i < length; i += charsChunkSize) {
921 String commandChunk = command.substring(i, Math.min(length, i + charsChunkSize));
922 int numCharsSentInChunk = commandChunk.length();
923 charsTotalSent = charsTotalSent + commandChunk.length();
924 log.debug("Iteration nr:{0}, sending command chunk: {1}", i, numCharsSentInChunk);
925 channelOutputStream.writeBytes(commandChunk);
926 channelOutputStream.flush();
928 if (numCharsSentInChunk < length) {
929 received.append(receiveUntilBufferFlush(numCharsSentInChunk, sessionTimeoutMs, command));
931 log.trace("i={0}, flush immediately", i);
932 channelOutputStream.flush();
934 } catch (IOException ex) {
935 log.warn("IOException occurred: nothing to flush out", ex);
938 return received.toString();
941 public void setSessionTimeoutMs(int sessionTimeoutMs) {
942 this.sessionTimeoutMs = sessionTimeoutMs;
945 void setCharsChunkSize(int charsChunkSize) {
946 this.charsChunkSize = charsChunkSize;
949 private boolean isCmdLengthEnoughToSendInChunks(int length, int chunkSize) {
950 return length > 2 * chunkSize;
953 private String enhanceCommandWithEOL(@Nonnull String originalCommand) {
954 char commandEnding = originalCommand.charAt(originalCommand.length() - 1);
955 if (commandEnding != '\n' && commandEnding != '\r') {
956 return originalCommand + EOL;
958 return originalCommand;
961 private Channel provideSessionChannel(String channelType, int port, int timeout) throws JSchException {
962 session = jsch.getSession(this.userName, this.hostName, port);
963 session.setPassword(this.passWord);
964 session.setUserInfo(new MyUserInfo()); //needed?
965 session.setConfig(STRICT_HOST_CHECK_KEY, STRICT_HOST_CHECK_VALUE);
966 session.connect(timeout);
967 session.setServerAliveCountMax(
968 0); // If this is not set to '0', then socket timeout on all reads will not work!!!!
969 return session.openChannel(channelType);