70b762bc74220ad1788769c6909f0c7c4ef31c4c
[appc.git] / appc-config / appc-config-adaptor / provider / src / main / java / org / onap / appc / ccadaptor / SshJcraftWrapper.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : APPC
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
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
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.
20  *
21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  * ============LICENSE_END=========================================================
23  */
24
25 package org.onap.appc.ccadaptor;
26
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;
46 import java.io.File;
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
62 public class SshJcraftWrapper {
63
64     private static final EELFLogger log = EELFManager.getInstance().getLogger(SshJcraftWrapper.class);
65
66     private static final int BUFFER_SIZE = 512000;
67     private InputStream inputStream = null;
68     private TelnetListener listener = null;
69     private String routerLogFileName = null;
70     private String routerName = null;
71     private char[] charBuffer = new char[BUFFER_SIZE];
72     private DataInputStream dis = null;
73     private BufferedReader reader = null;
74     private BufferedWriter out = null;
75     private File tmpFile = null;
76     private JSch jsch = null;
77     private Session session = null;
78     private Channel channel = null;
79     private String aggregatedReceivedString = "";
80     private String routerCmdType = "XML";
81     private String routerFileName = null;
82     private File jcraftReadSwConfigFileFromDisk = new File("/tmp/jcraftReadSwConfigFileFromDisk");
83     private String equipNameCode = null;
84     private String hostName = null;
85     private String userName = null;
86     private String passWord = null;
87     private Runtime runtime = Runtime.getRuntime();
88
89     public void connect(String hostname, String username, String password, String prompt, int timeOut)
90         throws IOException {
91         jsch = new JSch();
92         log.debug("Attempting to connect to {0} username={1} prompt='{2}' timeOut={3}",
93             hostname, username, prompt, timeOut);
94         routerName = hostname;
95         hostName = hostname;
96         userName = username;
97         passWord = password;
98         try {
99             session = jsch.getSession(username, hostname, 22);
100             UserInfo ui = new MyUserInfo();
101             session.setPassword(password);
102             session.setUserInfo(ui);
103             session.connect(timeOut);
104             channel = session.openChannel("shell");
105             session.setServerAliveCountMax(
106                 0); // If this is not set to '0', then socket timeout on all reads will not work!!!!
107             ((ChannelShell) channel).setPtyType("vt102");
108             inputStream = channel.getInputStream();
109             dis = new DataInputStream(inputStream);
110             reader = new BufferedReader(new InputStreamReader(dis), BUFFER_SIZE);
111             channel.connect();
112             log.info("Successfully connected. Flushing input buffer.");
113             try {
114                 receiveUntil(prompt, 3000, "No cmd was sent, just waiting");
115             } catch (Exception e) {
116                 log.warn("Caught an Exception: Nothing to flush out.", e);
117             }
118         } catch (Exception e) {
119             log.error("Could not connect to host=" + hostname, e);
120             throw new IOException(e.toString());
121         }
122     }
123
124     // User specifies the port number.
125     public void connect(String hostname, String username, String password, String prompt, int timeOut, int portNum)
126         throws IOException {
127         log.debug("Attempting to connect to {0} username={1} prompt='{2}' timeOut={3} portNum={4}",
128             hostname, username, prompt, timeOut, portNum);
129         routerName = hostname;
130         hostName = hostname;
131         userName = username;
132         passWord = password;
133         jsch = new JSch();
134         try {
135             session = jsch.getSession(username, hostname, portNum);
136             UserInfo ui = new MyUserInfo();
137             session.setPassword(password);
138             session.setUserInfo(ui);
139             session.setConfig("StrictHostKeyChecking", "no");
140             log.debug("StrictHostKeyChecking set to 'no'");
141
142             session.connect(timeOut);
143             session.setServerAliveCountMax(
144                 0); // If this is not set to '0', then socket timeout on all reads will not work!!!!
145             channel = session.openChannel("shell");
146             ((ChannelShell) channel).setPtyType("vt102");
147             inputStream = channel.getInputStream();
148             dis = new DataInputStream(inputStream);
149             reader = new BufferedReader(new InputStreamReader(dis), BUFFER_SIZE);
150             channel.connect();
151             log.info("Successfully connected. Flushing input buffer.");
152             try {
153                 if ("]]>]]>".equals(prompt)) {
154                     receiveUntil("]]>]]>", 10000, "No cmd was sent, just waiting");
155                 } else {
156                     receiveUntil(":~#", 5000, "No cmd was sent, just waiting");
157                 }
158             } catch (Exception e) {
159                 log.warn("Caught an Exception: Nothing to flush out.", e);
160             }
161         } catch (Exception e) {
162             log.error("Could not connect to host=" + hostname, e);
163             throw new IOException(e.toString());
164         }
165     }
166
167
168     public String receiveUntil(String delimeters, int timeout, String cmdThatWasSent) throws IOException {
169         boolean match = false;
170         boolean cliPromptCmd = false;
171         StringBuilder sb = new StringBuilder();
172         StringBuilder sbReceive = new StringBuilder();
173         log.debug("delimeters='{0}' timeout={1} cmdThatWasSent='{2}'", delimeters, timeout, cmdThatWasSent);
174         int readCounts = 0;
175         aggregatedReceivedString = "";
176         FileWriter fileWriter = null;
177
178         long deadline = new Date().getTime() + timeout;
179         try {
180             session.setTimeout(timeout);  // This is the socket timeout value.
181             while (!match) {
182                 if (new Date().getTime() > deadline) {
183                     String formattedCmd = removeWhiteSpaceAndNewLineCharactersAroundString(cmdThatWasSent);
184                     log.error("Routine has timed out: routerName={0} CmdThatWasSent={1}", routerName, formattedCmd);
185                     throw new TimedOutException("Routine has timed out");
186                 }
187                 try {
188                     Thread.sleep(500);
189                 } catch (java.lang.InterruptedException ee) {
190                     Thread.currentThread().interrupt();
191                 }
192                 int len = reader.read(charBuffer, 0, BUFFER_SIZE);
193                 log.trace("After reader. Read command len={0}", len);
194                 if (len <= 0) {
195                     log.error("Reader failed to read any bytes. Suspected socket timeout, router={0}", routerName);
196                     throw new TimedOutException("Received a SocketTimeoutException router=" + routerName);
197                 }
198                 if (!cliPromptCmd) {
199                     if (cmdThatWasSent.indexOf("IOS_XR_uploadedSwConfigCmd") != -1) {
200                         if (out == null) {
201                             // This is a IOS XR sw config file. We will write it to the disk.
202                             timeout = timeout * 2;
203                             deadline = new Date().getTime() + timeout;
204                             log.debug("IOS XR upload for software config: timeout={0}", timeout);
205                             StringTokenizer st = new StringTokenizer(cmdThatWasSent);
206                             st.nextToken();
207                             routerFileName = st.nextToken();
208                             fileWriter = new FileWriter(routerFileName);
209                             out = new BufferedWriter(fileWriter);
210                             routerLogFileName = "/tmp/" + routerName;
211                             tmpFile = new File(routerLogFileName);
212                             log.debug("Prepared for writing swConfigFile to disk, routerFileName=" + routerFileName);
213                         }
214                         out.write(charBuffer, 0, len);
215                         out.flush();
216                         log.debug("{0} bytes has been written to the disk", len);
217                         if (tmpFile.exists()) {
218                             appendToRouterFile(routerLogFileName, len);
219                         }
220                         match = checkIfReceivedStringMatchesDelimeter(len, "\nXML>");
221                         if (match) {
222                             out.flush();
223                             out.close();
224                             out = null;
225                             return null;
226                         }
227                     } else {
228                         readCounts++;
229                         log.debug("Reader read {0} of data within {1} read iteration", len, readCounts);
230                         int c;
231                         sb.setLength(0);
232                         for (int i = 0; i < len; i++) {
233                             c = charBuffer[i];
234                             if ((c != 7) && (c != 13) && (c != 0) && (c != 27)) {
235                                 sbReceive.append(charBuffer[i]);
236                                 sb.append(charBuffer[i]);
237                             }
238                         }
239                         appendToRouterFile("/tmp/" + routerName, len);
240                         if (listener != null) {
241                             listener.receivedString(sb.toString());
242                         }
243                         match = checkIfReceivedStringMatchesDelimeter(delimeters, sb.toString(), cmdThatWasSent);
244                         if (match) {
245                             log.trace("Match was true, breaking the loop.");
246                             break;
247                         }
248                     }
249                 } else {
250                     log.trace("cliPromptCmd");
251                     sb.setLength(0);
252                     for (int i = 0; i < len; i++) {
253                         sbReceive.append(charBuffer[i]);
254                         sb.append(charBuffer[i]);
255                     }
256                     appendToRouterFile("/tmp/" + routerName, sb);
257                     if (listener != null) {
258                         listener.receivedString(sb.toString());
259                     }
260                     log.debug("sb2={0}  delimiters={1}", sb.toString(), delimeters);
261                     if (sb.toString().contains("\nariPrompt>")) {
262                         log.debug("Found ari prompt");
263                         break;
264                     }
265                 }
266             }
267         } catch (JSchException e) {
268             log.error("JSchException occurred", e);
269             throw new TimedOutException(e.getMessage());
270         } catch (IOException e) {
271             log.error("IOException occurred", e);
272             throw new TimedOutException(e.getMessage());
273         } finally {
274             try {
275                 if (fileWriter != null) {
276                     fileWriter.close();
277                 }
278             } catch (IOException ex) {
279                 log.warn("Failed to close fileWriter output stream", ex);
280             }
281         }
282         return stripOffCmdFromRouterResponse(sbReceive.toString());
283     }
284
285     public boolean checkIfReceivedStringMatchesDelimeter(String delimeters, String receivedString,
286         String cmdThatWasSent) {
287         // The delimeters are in a '|' seperated string. Return true on the first match.
288         log.debug("Entered checkIfReceivedStringMatchesDelimeter: delimeters={0} cmdThatWasSent={1} receivedString={2}",
289             delimeters, cmdThatWasSent, receivedString);
290         StringTokenizer st = new StringTokenizer(delimeters, "|");
291
292         if ((delimeters.contains("#$")) || ("CLI".equals(routerCmdType)))  // This would be an IOS XR, CLI command.
293         {
294             int x = receivedString.lastIndexOf('#');
295             int y = receivedString.length() - 1;
296             log.debug("IOS XR, CLI command");
297             if (log.isTraceEnabled()) {
298                 log.trace("cmdThatWasSent={0}, lastIndexOf hash delimiter={1}, maxIndexNum={2}", cmdThatWasSent, x, y);
299             }
300             return (x != -1) && (y == x);
301         }
302         if (cmdThatWasSent.contains("show config")) {
303             log.trace("In the block for 'show config'");
304             while (st.hasMoreTokens()) {
305                 String delimeter = st.nextToken();
306                 // Make sure we don't get faked out by a response of " #".
307                 // Proc #0
308                 //   # signaling-local-address ipv6 FD00:F4D5:EA06:1::110:136:254
309                 // LAAR2#
310                 int x = receivedString.lastIndexOf(delimeter);
311                 if ((receivedString.lastIndexOf(delimeter) != -1) && (receivedString.lastIndexOf(" #") != x - 1)) {
312                     log.debug("receivedString={0}", receivedString);
313                     log.trace("Found ending for 'show config' command, exiting.");
314                     return true;
315                 }
316             }
317         } else {
318             aggregatedReceivedString = aggregatedReceivedString + receivedString;
319             appendToFile("/tmp/aggregatedReceivedString.debug", aggregatedReceivedString);
320
321             log.debug("receivedString={0}", receivedString);
322             while (st.hasMoreTokens()) {
323                 String delimeter = st.nextToken();
324                 log.debug("Looking for an delimiter of:{0}", delimeter);
325                 if (aggregatedReceivedString.indexOf(delimeter) != -1) {
326                     log.debug("Found delimiter={0}, exiting", delimeter);
327                     aggregatedReceivedString = "";
328                     return true;
329                 }
330             }
331         }
332         return false;
333     }
334
335     public boolean checkIfReceivedStringMatchesDelimeter(int len, String delimeter) {
336         int x;
337         int c;
338         String str = StringUtils.EMPTY;
339
340         if (jcraftReadSwConfigFileFromDisk()) {
341             log.trace("jcraftReadSwConfigFileFromDisk block");
342             File fileName = new File(routerFileName);
343             log.debug("jcraftReadSwConfigFileFromDisk::: Will read the tail end of the file from the disk");
344             try {
345                 str = getLastFewLinesOfFile(fileName, 3);
346             } catch (IOException e) {
347                 log.warn("IOException occurred, while reading file=" + fileName, e);
348             }
349         } else {
350             // When looking at the end of the charBuffer, don't include any linefeeds or spaces. We only want to make the smallest string possible.
351             for (x = len - 1; x >= 0; x--) {
352                 c = charBuffer[x];
353                 if ((c != 10) && (c != 32)) // Not a line feed nor a space.
354                 {
355                     break;
356                 }
357             }
358             if ((x + 1 - 13) >= 0) {
359                 str = new String(charBuffer, x + 1 - 13, 13);
360                 log.debug("str:{0}", str);
361             } else {
362                 File fileName = new File(routerFileName);
363                 log.debug("Will read the tail end of the file from the disk, x={0} len={1} str={2} routerFileName={3}",
364                     x, len, str, routerFileName);
365                 try {
366                     str = getLastFewLinesOfFile(fileName, 3);
367                 } catch (IOException e) {
368                     log.warn("IOException occurred, while reading file=" + fileName, e);
369                 }
370             }
371         }
372
373         log.debug("Parsed string was str='{0}', searched delimiter was {1}");
374         return str.contains(delimeter);
375     }
376
377     public void closeConnection() {
378         log.info("Closing connection");
379         inputStream = null;
380         dis = null;
381         charBuffer = null;
382         session.disconnect();
383         session = null;
384     }
385
386     public void send(String cmd) throws IOException {
387         try (OutputStream os = channel.getOutputStream(); DataOutputStream dos = new DataOutputStream(os)) {
388             sendSshCommand(cmd, dos);
389         } catch (IOException e) {
390             log.error("IOException occurred while sending command=" + cmd, e);
391             throw e;
392         }
393     }
394
395     public void sendChar(int v) throws IOException {
396         try (OutputStream os = channel.getOutputStream(); DataOutputStream dos = new DataOutputStream(os)) {
397             if (log.isTraceEnabled()) {
398                 log.trace("Sending charCode: {0}", v);
399             }
400             dos.writeChar(v);
401             dos.flush();
402         } catch (IOException e) {
403             log.error("IOException occurred while writing char to channel output stream", e);
404             throw e;
405         }
406     }
407
408     public void send(byte[] b, int off, int len) throws IOException {
409         try (OutputStream os = channel.getOutputStream(); DataOutputStream dos = new DataOutputStream(os)) {
410             dos.write(b, off, len);
411             dos.flush();
412         } catch (IOException e) {
413             log.error("IOException occurred while writing bytes to channel output stream", e);
414             throw e;
415         }
416     }
417
418     public static class MyUserInfo implements UserInfo, UIKeyboardInteractive {
419
420         @Override
421         public String getPassword() {
422             return null;
423         }
424
425         @Override
426         public boolean promptYesNo(String str) {
427             return false;
428         }
429
430         @Override
431         public String getPassphrase() {
432             return null;
433         }
434
435         @Override
436         public boolean promptPassphrase(String message) {
437             return false;
438         }
439
440         @Override
441         public boolean promptPassword(String message) {
442             return false;
443         }
444
445         @Override
446         public void showMessage(String message) {
447             //stub
448         }
449
450         @Override
451         public String[] promptKeyboardInteractive(String destination,
452             String name,
453             String instruction,
454             String[] prompt,
455             boolean[] echo) {
456             return new String[0];
457         }
458     }
459
460     public void addListener(TelnetListener listener) {
461         this.listener = listener;
462     }
463
464     private void appendToFile(String fileName, String dataToWrite) {
465         File outputFile = new File(fileName);
466         if (outputFile.exists()) {
467             try (FileWriter fw = new FileWriter(fileName, true); BufferedWriter ow = new BufferedWriter(fw)) {
468                 ow.write(dataToWrite);
469                 ow.close();
470             } catch (IOException e) {
471                 log.error("IOException occurred while writing to file=" + fileName, e);
472             }
473         }
474     }
475
476     public String getTheDate() {
477         DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy H:mm:ss  ");
478         return dateFormat.format(Calendar.getInstance().getTime());
479     }
480
481
482     public void appendToRouterFile(String fileName, StringBuilder dataToWrite) {
483         appendToFile(fileName, dataToWrite.toString());
484     }
485
486     public void appendToRouterFile(String fileName, int len) {
487         File outputFile = new File(fileName);
488         if (outputFile.exists()) {
489             try (FileWriter fw = new FileWriter(fileName, true); BufferedWriter ow = new BufferedWriter(fw)) {
490                 ow.write(charBuffer, 0, len);
491                 ow.close();
492             } catch (IOException e) {
493                 log.error("IOException occurred while writing to router file=" + fileName, e);
494             }
495         }
496     }
497
498     public String removeWhiteSpaceAndNewLineCharactersAroundString(String str) {
499         if (str != null && !StringUtils.EMPTY.equals(str)) {
500             StringTokenizer strTok = new StringTokenizer(str, "\n");
501             StringBuilder sb = new StringBuilder();
502
503             while (strTok.hasMoreTokens()) {
504                 String line = strTok.nextToken();
505                 sb.append(line);
506             }
507             return sb.toString().trim();
508         }
509         return StringUtils.EMPTY;
510     }
511
512     public String stripOffCmdFromRouterResponse(String routerResponse) {
513         // The session of SSH will echo the command sent to the router, in the router's response.
514         // Since all our commands are terminated by a '\n', strip off the first line
515         // of the response from the router. This first line contains the orginal command.
516         StringTokenizer rr = new StringTokenizer(routerResponse, "\n");
517         StringBuilder sb = new StringBuilder();
518
519         int numTokens = rr.countTokens();
520         if (numTokens > 1) {
521             rr.nextToken(); //Skip the first line.
522             while (rr.hasMoreTokens()) {
523                 sb.append(rr.nextToken()).append("\n");
524             }
525         }
526         return sb.toString();
527     }
528
529     public void setRouterCommandType(String type) {
530         this.routerCmdType = type;
531         log.debug("Router command type is set to: {0}", type);
532     }
533
534     public String getLastFewLinesOfFile(File file, int linesToRead) throws IOException {
535         RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
536         int lines = 0;
537         StringBuilder builder = new StringBuilder();
538         String tail = "";
539         long length = file.length();
540         length--;
541         randomAccessFile.seek(length);
542         for (long seek = length; seek >= 0; --seek) {
543             randomAccessFile.seek(seek);
544             char c = (char) randomAccessFile.read();
545             builder.append(c);
546             if (c == '\n') {
547                 builder = builder.reverse();
548                 tail = builder.toString() + tail;
549                 lines++;
550                 builder.setLength(0);
551                 if (lines == linesToRead) {
552                     break;
553                 }
554             }
555         }
556         randomAccessFile.close();
557         if (log.isDebugEnabled()) {
558             log.debug("Content read from file={0} was tail={1}", file.getName(), tail);
559         }
560         return tail;
561     }
562
563     public boolean jcraftReadSwConfigFileFromDisk() {
564         return jcraftReadSwConfigFileFromDisk.exists();
565     }
566
567     public String getEquipNameCode() {
568         return equipNameCode;
569     }
570
571     public void setEquipNameCode(String equipNameCode) {
572         this.equipNameCode = equipNameCode;
573     }
574
575     public String getRouterName() {
576         return routerName;
577     }
578
579     // Routine does reads until it has read 'nchars' or times out.
580     public void receiveUntilBufferFlush(int ncharsSent, int timeout, String message) throws IOException {
581         log.debug("ncharsSent={0}, timeout={1}, message={2}", ncharsSent, timeout, message);
582         int ncharsTotalReceived = 0;
583         int ncharsRead = 0;
584
585         long deadline = new Date().getTime() + timeout;
586         logMemoryUsage();
587         try {
588             session.setTimeout(timeout);  // This is the socket timeout value.
589             while (true) {
590                 if (new Date().getTime() > deadline) {
591                     log.error("Routine has timed out: ncharsSent={0}, ncharsTotalReceived={1}", ncharsSent,
592                         ncharsTotalReceived);
593                     throw new TimedOutException("Routine has timed out");
594                 }
595                 ncharsRead = reader.read(charBuffer, 0, BUFFER_SIZE);
596                 if (listener != null) {
597                     listener.receivedString(String.copyValueOf(charBuffer, 0, ncharsRead));
598                 }
599                 appendToRouterFile("/tmp/" + routerName, ncharsRead);
600                 ncharsTotalReceived = ncharsTotalReceived + ncharsRead;
601                 if (ncharsTotalReceived >= ncharsSent) {
602                     log.debug("Received the correct number of characters, ncharsSent={0}, ncharsTotalReceived={1}",
603                         ncharsSent, ncharsTotalReceived);
604                     logMemoryUsage();
605                     return;
606                 }
607             }
608         } catch (JSchException e) {
609             log.error("JSchException occurred while command sending", e);
610             log.debug("ncharsSent={0}, ncharsTotalReceived={1}, ncharsRead={2} until error occurred",
611                 ncharsSent, ncharsTotalReceived, ncharsRead);
612             throw new TimedOutException(e.getMessage());
613         }
614     }
615
616     public String getHostName() {
617         return hostName;
618     }
619
620     public String getUserName() {
621         return userName;
622     }
623
624     public String getPassWord() {
625         return passWord;
626     }
627
628     public void sftpPutFile(String sourcePath, String destDirectory) throws IOException {
629         try {
630             Session sftpSession = jsch.getSession(userName, hostName, 22);
631             UserInfo ui = new MyUserInfo();
632             sftpSession.setPassword(passWord);
633             sftpSession.setUserInfo(ui);
634             sftpSession.connect(30 * 1000);
635             ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
636             sftp.connect();
637             log.debug("Sending via sftp from source: {0} to destination: {1}", sourcePath, destDirectory);
638             sftp.put(sourcePath, destDirectory, ChannelSftp.OVERWRITE);
639             sftpSession.disconnect();
640         } catch (JSchException ex) {
641             log.error("JSchException occurred while handling sftp session", ex);
642             throw new IOException(ex.getMessage());
643         } catch (SftpException ex) {
644             log.error("SftpException occurred during file transfer", ex);
645             throw new IOException(ex.getMessage());
646         }
647     }
648
649     public void sftpPutStringData(String stringOfData, String fullPathDest) throws IOException {
650         try {
651             Session sftpSession = jsch.getSession(userName, hostName, 22);
652             UserInfo ui = new MyUserInfo();
653             sftpSession.setPassword(passWord);
654             sftpSession.setUserInfo(ui);
655             sftpSession.connect(30 * 1000);
656             ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
657             sftp.connect();
658             InputStream is = new ByteArrayInputStream(stringOfData.getBytes());
659             log.debug("Sending via sftp stringOfData to destination: {0}", fullPathDest);
660             sftp.put(is, fullPathDest, ChannelSftp.OVERWRITE);
661             sftpSession.disconnect();
662         } catch (JSchException ex) {
663             log.error("JSchException occurred while handling sftp session", ex);
664             throw new IOException(ex.getMessage());
665         } catch (SftpException ex) {
666             log.error("SftpException occurred during data transfer", ex);
667             throw new IOException(ex.getMessage());
668         }
669     }
670
671     public String sftpGet(String fullFilePathName) throws IOException {
672         try {
673             Session sftpSession = jsch.getSession(userName, hostName, 22);
674             UserInfo ui = new MyUserInfo();
675             sftpSession.setPassword(passWord);
676             sftpSession.setUserInfo(ui);
677             sftpSession.connect(30 * 1000);
678             ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
679             sftp.connect();
680             InputStream in = sftp.get(fullFilePathName);
681             String sftpFileString = readInputStreamAsString(in);
682             log.debug("Received data via sftp connection sftpFileString={0} from fullFilePathName={1}",
683                 sftpFileString, fullFilePathName);
684             sftpSession.disconnect();
685             return sftpFileString;
686         } catch (JSchException ex) {
687             log.error("JSchException occurred while handling sftp session", ex);
688             throw new IOException(ex.getMessage());
689         } catch (SftpException ex) {
690             log.error("SftpException occurred during data transfer", ex);
691             throw new IOException(ex.getMessage());
692         }
693     }
694
695     public static String readInputStreamAsString(InputStream in) throws IOException {
696         BufferedInputStream bis = new BufferedInputStream(in);
697         ByteArrayOutputStream buf = new ByteArrayOutputStream();
698         int result = bis.read();
699         while (result != -1) {
700             byte b = (byte) result;
701             buf.write(b);
702             result = bis.read();
703         }
704         return buf.toString();
705     }
706
707
708     public void logMemoryUsage() {
709         int mb = 1024 * 1024;
710         long usedMemory;
711         long maxMemoryAvailable;
712         long memoryLeftOnHeap;
713         maxMemoryAvailable =runtime.maxMemory() / mb;
714         usedMemory = (runtime.totalMemory() / mb) - (runtime.freeMemory() / mb);
715         memoryLeftOnHeap = maxMemoryAvailable - usedMemory;
716         log.info("Memory usage: maxMemoryAvailable={0}, usedMemory={1}, memoryLeftOnHeap={2}",
717             maxMemoryAvailable, usedMemory, memoryLeftOnHeap);
718     }
719
720     // User specifies the port number, and the subsystem
721     public void connect(String hostname, String username, String password, String prompt, int timeOut, int portNum,
722         String subsystem) throws IOException {
723
724         if (log.isDebugEnabled()) {
725             log.debug(
726                 "Attempting to connect to {0} username={1} prompt='{2}' timeOut={3} portNum={4} subsystem={5}",
727                 hostname, username, prompt, timeOut, portNum, subsystem);
728         }
729         routerName = hostname;
730         jsch = new JSch();
731         try {
732             session = jsch.getSession(username, hostname, portNum);
733             UserInfo ui = new MyUserInfo();
734             session.setPassword(password);
735             session.setUserInfo(ui);
736             session.setConfig("StrictHostKeyChecking", "no");
737             session.connect(timeOut);
738             session.setServerAliveCountMax(
739                 0); // If this is not set to '0', then socket timeout on all reads will not work!!!!
740             channel = session.openChannel("subsystem");
741             ((ChannelSubsystem) channel).setSubsystem(subsystem);
742             ((ChannelSubsystem) channel).setPty(true); //expected ptyType vt102
743
744             inputStream = channel.getInputStream();
745             dis = new DataInputStream(inputStream);
746             reader = new BufferedReader(new InputStreamReader(dis), BUFFER_SIZE);
747             channel.connect(5000);
748         } catch (JSchException e) {
749             log.error("JschException occurred ", e);
750             throw new IOException(e.getMessage());
751         } catch (TimedOutException e) {
752             log.error("TimedOutException occurred", e);
753             throw new IOException(e.getMessage());
754         }
755     }
756
757     public void connect(String hostName, String username, String password, int portNumber) throws IOException {
758         jsch = new JSch();
759         log.debug("Attempting to connect to {0} username={1} portNumber={2}", hostName, username, portNumber);
760         routerName = hostName;
761         this.hostName = hostName;
762         userName = username;
763         passWord = password;
764         try {
765             java.util.Properties config = new java.util.Properties();
766             config.put("StrictHostKeyChecking", "no");
767             session = jsch.getSession(username, hostName, 22);
768             UserInfo ui = new MyUserInfo();
769             session.setConfig(config);
770             session.setPassword(password);
771             session.setUserInfo(ui);
772             session.connect(30000);
773             channel = session.openChannel("shell");
774             session.setServerAliveCountMax(
775                 0); // If this is not set to '0', then socket timeout on all reads will not work!!!!
776             ((ChannelShell) channel).setPtyType("vt102");
777             inputStream = channel.getInputStream();
778             dis = new DataInputStream(inputStream);
779             reader = new BufferedReader(new InputStreamReader(dis), BUFFER_SIZE);
780             channel.connect();
781             try {
782                 receiveUntil(":~#", 9000, "No cmd was sent, just waiting, but we can stop on a '~#'");
783             } catch (Exception e) {
784                 log.warn("Caught an Exception: Nothing to flush out.", e);
785             }
786
787         } catch (JSchException e) {
788             log.error("JschException occurred ", e);
789             throw new IOException(e.getMessage());
790         }
791     }
792
793
794     public void put(String sourcePath, String destDirectory) throws IOException {
795         try {
796             Session sftpSession = jsch.getSession(userName, hostName, 22);
797             UserInfo ui = new MyUserInfo();
798             sftpSession.setPassword(passWord);
799             sftpSession.setUserInfo(ui);
800             sftpSession.connect(30 * 1000);
801             ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
802             sftp.connect();
803             log.debug("Sending via sftp from source: {0} to destination: {1}", sourcePath, destDirectory);
804             sftp.put(sourcePath, destDirectory, ChannelSftp.OVERWRITE);
805             sftpSession.disconnect();
806         } catch (JSchException ex) {
807             log.error("JSchException occurred while handling sftp session", ex);
808             throw new IOException(ex.getMessage());
809         } catch (SftpException ex) {
810             log.error("SftpException occurred during file transfer", ex);
811             throw new IOException(ex.getMessage());
812         }
813     }
814
815     public void put(InputStream is, String fullPathDest, String hostName, String userName, String passWord)
816         throws IOException {
817         Session sftpSession = null;
818         try {
819             log.debug("Sftp put invoked, connection details: username={1} hostname={2}",
820                 userName, hostName);
821             jsch = new JSch();
822             java.util.Properties config = new java.util.Properties();
823             config.put("StrictHostKeyChecking", "no");
824             sftpSession = jsch.getSession(userName, hostName, 22);
825             UserInfo ui = new MyUserInfo();
826             sftpSession.setPassword(passWord);
827             sftpSession.setUserInfo(ui);
828             sftpSession.setConfig(config);
829             sftpSession.connect(30 * 1000);
830             ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
831             sftp.connect();
832             String oldFiles = fullPathDest + "*";
833             log.debug("Deleting old files: {0}", oldFiles);
834             try {
835                 sftp.rm(oldFiles);
836             } catch (SftpException ex) {
837                 String exp = "No such file";
838                 if (ex.getMessage() != null && ex.getMessage().contains(exp)) {
839                     log.warn("No files found, continue");
840                 } else {
841                     log.error("SftpException while invoking rm command over sftp", ex);
842                     throw ex;
843                 }
844             }
845             log.debug("Sending stringOfData to destination {0}", fullPathDest);
846             sftp.put(is, fullPathDest, ChannelSftp.OVERWRITE);
847         } catch (JSchException ex) {
848             log.error("JSchException occurred while handling sftp session", ex);
849             throw new IOException(ex.getMessage());
850         } catch (SftpException ex) {
851             log.error("SftpException occurred during file transfer", ex);
852             throw new IOException(ex.getMessage());
853         } finally {
854             if (sftpSession != null) {
855                 sftpSession.disconnect();
856             }
857         }
858     }
859
860
861     public String get(String fullFilePathName, String hostName, String userName, String passWord) throws IOException {
862         Session sftpSession = null;
863         try {
864             log.debug("Sftp get invoked, connection details: username={1} hostname={2}",
865                 userName, hostName);
866             jsch = new JSch();
867             sftpSession = jsch.getSession(userName, hostName, 22);
868             java.util.Properties config = new java.util.Properties();
869             config.put("StrictHostKeyChecking", "no");
870             UserInfo ui = new MyUserInfo();
871             sftpSession.setPassword(passWord);
872             sftpSession.setUserInfo(ui);
873             sftpSession.setConfig(config);
874             sftpSession.connect(30 * 1000);
875             ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
876             sftp.connect();
877             InputStream in = sftp.get(fullFilePathName);
878             return readInputStreamAsString(in);
879         } catch (JSchException ex) {
880             log.error("JSchException occurred while handling sftp session", ex);
881             throw new IOException(ex.getMessage());
882         } catch (SftpException ex) {
883             log.error("SftpException occurred during file transfer", ex);
884             throw new IOException(ex.getMessage());
885         } finally {
886             if (sftpSession != null) {
887                 sftpSession.disconnect();
888             }
889         }
890     }
891
892     public String send(String cmd, String delimiter) throws IOException {
893         try (OutputStream os = channel.getOutputStream(); DataOutputStream dos = new DataOutputStream(os)) {
894             sendSshCommand(cmd, dos);
895             return receiveUntil(delimiter, 300000, cmd);
896         } catch (IOException ex) {
897             log.error("IOException occurred", ex);
898             throw new IOException(ex.getMessage());
899         }
900     }
901
902     private void sendSshCommand(@Nonnull String originalCommand, @Nonnull DataOutputStream channelOutputStream)
903         throws IOException {
904         String command = enhanceCommandWithEOL(originalCommand);
905         int length = command.length(); // 2,937,706
906         int charsChunkSize = 300000;
907         int charsTotalSent = 0;
908
909         log.debug("Sending ssh command: length={0}, payload: {1}", length, command);
910         if (isCmdLengthEnoughToSendInChunks(length, charsChunkSize)) {
911             int timeout = 9000;
912             for (int i = 0; i < length; i += charsChunkSize) {
913                 String commandChunk = command.substring(i, Math.min(length, i + charsChunkSize));
914                 int numCharsSentInChunk = commandChunk.length();
915                 charsTotalSent = charsTotalSent + commandChunk.length();
916                 log.debug("Iteration nr:{0}, sending command chunk: {1}", i, numCharsSentInChunk);
917                 channelOutputStream.writeBytes(commandChunk);
918                 channelOutputStream.flush();
919                 try {
920                     if (numCharsSentInChunk < length) {
921                         receiveUntilBufferFlush(numCharsSentInChunk, timeout, "buffer flush  i=" + i);
922                     } else {
923                         log.trace("i={0}, flush immediately", i);
924                         channelOutputStream.flush();
925                     }
926                 } catch (IOException ex) {
927                     log.warn("IOException occurred: nothing to flush out", ex);
928                 }
929             }
930         } else {
931             channelOutputStream.writeBytes(command);
932         }
933         channelOutputStream.flush();
934     }
935
936     private boolean isCmdLengthEnoughToSendInChunks(int length, int chunkSize) {
937         return length > 2 * chunkSize;
938     }
939
940     private String enhanceCommandWithEOL(@Nonnull String originalCommand) {
941         char commandEnding = originalCommand.charAt(originalCommand.length() - 1);
942         if (commandEnding != '\n' && commandEnding != '\r') {
943             return originalCommand + "\n";
944         }
945         return originalCommand;
946     }
947 }