summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--appc-config/appc-config-adaptor/provider/src/main/java/org/onap/appc/ccadaptor/SshJcraftWrapper.java75
-rw-r--r--appc-config/appc-config-adaptor/provider/src/test/java/org/onap/appc/ccadaptor/SshJcraftWrapperTest.java231
2 files changed, 275 insertions, 31 deletions
diff --git a/appc-config/appc-config-adaptor/provider/src/main/java/org/onap/appc/ccadaptor/SshJcraftWrapper.java b/appc-config/appc-config-adaptor/provider/src/main/java/org/onap/appc/ccadaptor/SshJcraftWrapper.java
index acdb87b90..07eb431f4 100644
--- a/appc-config/appc-config-adaptor/provider/src/main/java/org/onap/appc/ccadaptor/SshJcraftWrapper.java
+++ b/appc-config/appc-config-adaptor/provider/src/main/java/org/onap/appc/ccadaptor/SshJcraftWrapper.java
@@ -55,24 +55,24 @@ import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.StringTokenizer;
+import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import org.apache.commons.lang.StringUtils;
public class SshJcraftWrapper {
private static final EELFLogger log = EELFManager.getInstance().getLogger(SshJcraftWrapper.class);
-
- private static final int BUFFER_SIZE = 512000;
static final int DEFAULT_PORT = 22;
+ static final String EOL = "\n";
static final String CHANNEL_SHELL_TYPE = "shell";
static final String CHANNEL_SUBSYSTEM_TYPE = "subsystem";
private static final String TERMINAL_BASIC_MODE = "vt102";
static final String STRICT_HOST_CHECK_KEY = "StrictHostKeyChecking";
static final String STRICT_HOST_CHECK_VALUE = "no";
+ static final String DELIMITERS_SEPARATOR = "|";
+
private TelnetListener listener = null;
private String routerLogFileName = null;
- private String routerName = null;
- private char[] charBuffer = new char[BUFFER_SIZE];
private BufferedReader reader = null;
private BufferedWriter out = null;
private File tmpFile = null;
@@ -84,18 +84,25 @@ public class SshJcraftWrapper {
private String routerFileName = null;
private File jcraftReadSwConfigFileFromDisk = new File("/tmp/jcraftReadSwConfigFileFromDisk");
private String equipNameCode = null;
+ private String routerName = null;
private String hostName = null;
private String userName = null;
private String passWord = null;
+ private int readIntervalMs = 500;
+ private int readBufferSizeBytes = 512_000;
+ private char[] charBuffer;
private Runtime runtime = Runtime.getRuntime();
-
public SshJcraftWrapper() {
this.jsch = new JSch();
+ this.charBuffer = new char[readBufferSizeBytes];
}
- SshJcraftWrapper(JSch jsch) {
+ SshJcraftWrapper(JSch jsch, int readIntervalMs, int readBufferSizeBytes) {
+ this.readIntervalMs = readIntervalMs;
this.jsch = jsch;
+ this.readBufferSizeBytes = readBufferSizeBytes;
+ this.charBuffer = new char[readBufferSizeBytes];
}
public void connect(String hostname, String username, String password, String prompt, int timeOut)
@@ -109,7 +116,7 @@ public class SshJcraftWrapper {
try {
channel = provideSessionChannel(CHANNEL_SHELL_TYPE, DEFAULT_PORT, timeOut);
((ChannelShell) channel).setPtyType(TERMINAL_BASIC_MODE);
- reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), BUFFER_SIZE);
+ reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), readBufferSizeBytes);
channel.connect();
log.info("Successfully connected. Flushing input buffer.");
try {
@@ -135,7 +142,7 @@ public class SshJcraftWrapper {
try {
channel = provideSessionChannel(CHANNEL_SHELL_TYPE, portNum, timeOut);
((ChannelShell) channel).setPtyType(TERMINAL_BASIC_MODE);
- reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), BUFFER_SIZE);
+ reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), readBufferSizeBytes);
channel.connect();
log.info("Successfully connected. Flushing input buffer.");
try {
@@ -155,6 +162,7 @@ public class SshJcraftWrapper {
public String receiveUntil(String delimeters, int timeout, String cmdThatWasSent) throws IOException {
+ checkConnection();
boolean match = false;
boolean cliPromptCmd = false;
StringBuilder sb = new StringBuilder();
@@ -173,12 +181,8 @@ public class SshJcraftWrapper {
log.error("Routine has timed out: routerName={0} CmdThatWasSent={1}", routerName, formattedCmd);
throw new TimedOutException("Routine has timed out");
}
- try {
- Thread.sleep(500);
- } catch (java.lang.InterruptedException ee) {
- Thread.currentThread().interrupt();
- }
- int len = reader.read(charBuffer, 0, BUFFER_SIZE);
+ sleep(readIntervalMs);
+ int len = reader.read(charBuffer, 0, readBufferSizeBytes);
log.trace("After reader. Read command len={0}", len);
if (len <= 0) {
log.error("Reader failed to read any bytes. Suspected socket timeout, router={0}", routerName);
@@ -271,12 +275,30 @@ public class SshJcraftWrapper {
return stripOffCmdFromRouterResponse(sbReceive.toString());
}
+ private void sleep(long timeoutMs) {
+ try {
+ TimeUnit.MILLISECONDS.sleep(timeoutMs);
+ } catch (java.lang.InterruptedException ee) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ private void checkConnection() {
+ try {
+ if (!isConnected() || !reader.ready()) {
+ throw new IllegalStateException("Connection not established. Cannot perform action.");
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException("Reader stream is closed. Cannot perform action.", e);
+ }
+ }
+
public boolean checkIfReceivedStringMatchesDelimeter(String delimeters, String receivedString,
String cmdThatWasSent) {
// The delimeters are in a '|' seperated string. Return true on the first match.
log.debug("Entered checkIfReceivedStringMatchesDelimeter: delimeters={0} cmdThatWasSent={1} receivedString={2}",
delimeters, cmdThatWasSent, receivedString);
- StringTokenizer st = new StringTokenizer(delimeters, "|");
+ StringTokenizer st = new StringTokenizer(delimeters, DELIMITERS_SEPARATOR);
if ((delimeters.contains("#$")) || ("CLI".equals(routerCmdType))) // This would be an IOS XR, CLI command.
{
@@ -500,7 +522,7 @@ public class SshJcraftWrapper {
public String removeWhiteSpaceAndNewLineCharactersAroundString(String str) {
if (str != null && !StringUtils.EMPTY.equals(str)) {
- StringTokenizer strTok = new StringTokenizer(str, "\n");
+ StringTokenizer strTok = new StringTokenizer(str, EOL);
StringBuilder sb = new StringBuilder();
while (strTok.hasMoreTokens()) {
@@ -516,17 +538,9 @@ public class SshJcraftWrapper {
// The session of SSH will echo the command sent to the router, in the router's response.
// Since all our commands are terminated by a '\n', strip off the first line
// of the response from the router. This first line contains the orginal command.
- StringTokenizer rr = new StringTokenizer(routerResponse, "\n");
- StringBuilder sb = new StringBuilder();
- int numTokens = rr.countTokens();
- if (numTokens > 1) {
- rr.nextToken(); //Skip the first line.
- while (rr.hasMoreTokens()) {
- sb.append(rr.nextToken()).append("\n");
- }
- }
- return sb.toString();
+ String[] responseTokens = routerResponse.split(EOL, 2);
+ return responseTokens[responseTokens.length-1];
}
public void setRouterCommandType(String type) {
@@ -595,7 +609,7 @@ public class SshJcraftWrapper {
ncharsTotalReceived);
throw new TimedOutException("Routine has timed out");
}
- ncharsRead = reader.read(charBuffer, 0, BUFFER_SIZE);
+ ncharsRead = reader.read(charBuffer, 0, readBufferSizeBytes);
if (listener != null) {
listener.receivedString(String.copyValueOf(charBuffer, 0, ncharsRead));
}
@@ -736,7 +750,7 @@ public class SshJcraftWrapper {
channel = provideSessionChannel(CHANNEL_SUBSYSTEM_TYPE, portNum, timeOut);
((ChannelSubsystem) channel).setSubsystem(subsystem);
((ChannelSubsystem) channel).setPty(true); //expected ptyType vt102
- reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), BUFFER_SIZE);
+ reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), readBufferSizeBytes);
channel.connect(5000);
} catch (JSchException e) {
log.error("JschException occurred ", e);
@@ -753,7 +767,7 @@ public class SshJcraftWrapper {
try {
channel = provideSessionChannel(CHANNEL_SHELL_TYPE, DEFAULT_PORT, 30000);
((ChannelShell) channel).setPtyType(TERMINAL_BASIC_MODE);
- reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), BUFFER_SIZE);
+ reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), readBufferSizeBytes);
channel.connect();
try {
receiveUntil(":~#", 9000, "No cmd was sent, just waiting, but we can stop on a '~#'");
@@ -916,7 +930,7 @@ public class SshJcraftWrapper {
private String enhanceCommandWithEOL(@Nonnull String originalCommand) {
char commandEnding = originalCommand.charAt(originalCommand.length() - 1);
if (commandEnding != '\n' && commandEnding != '\r') {
- return originalCommand + "\n";
+ return originalCommand + EOL;
}
return originalCommand;
}
@@ -931,4 +945,5 @@ public class SshJcraftWrapper {
0); // If this is not set to '0', then socket timeout on all reads will not work!!!!
return session.openChannel(channelType);
}
+
}
diff --git a/appc-config/appc-config-adaptor/provider/src/test/java/org/onap/appc/ccadaptor/SshJcraftWrapperTest.java b/appc-config/appc-config-adaptor/provider/src/test/java/org/onap/appc/ccadaptor/SshJcraftWrapperTest.java
index 2495c64d0..76b4c628f 100644
--- a/appc-config/appc-config-adaptor/provider/src/test/java/org/onap/appc/ccadaptor/SshJcraftWrapperTest.java
+++ b/appc-config/appc-config-adaptor/provider/src/test/java/org/onap/appc/ccadaptor/SshJcraftWrapperTest.java
@@ -25,6 +25,7 @@
package org.onap.appc.ccadaptor;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
@@ -68,8 +69,12 @@ public class SshJcraftWrapperTest {
private static final String HOST = "hostname";
private static final String SUBSYSTEM = "netconf";
private static final String PROMPT = "]]>]]>";
+ private static final String SEARCH_STR = "</rpc-reply>";
+ private static final int READ_TIMEOUT = 180_000;
private static final int PORT_NUM = 23;
private static final int SESSION_TIMEOUT = 30_000;
+ private static final int READ_INTERVAL_MS = 1;
+ private static final int READ_BUFFER_SIZE = 10;
private SshJcraftWrapper cut;
@Mock
@@ -91,7 +96,7 @@ public class SshJcraftWrapperTest {
given(session.openChannel(SshJcraftWrapper.CHANNEL_SHELL_TYPE)).willReturn(channelShell);
given(session.openChannel(SshJcraftWrapper.CHANNEL_SUBSYSTEM_TYPE)).willReturn(channelSubsystem);
given(jSchMock.getSession(anyString(), anyString(), anyInt())).willReturn(session);
- cut = new SshJcraftWrapper(jSchMock);
+ cut = new SshJcraftWrapper(jSchMock, READ_INTERVAL_MS, READ_BUFFER_SIZE);
}
@Ignore
@@ -451,4 +456,228 @@ public class SshJcraftWrapperTest {
assertTrue(cut.isConnected());
}
+ //receiveUntil tests begin
+ @Test(expected = IllegalStateException.class)
+ public void receiveUntil_shouldThrowIllegalStateException_whenInstanceIsNotConnected() throws Exception {
+ //given
+ assertFalse(cut.isConnected());
+
+ //when
+ cut.receiveUntil(SEARCH_STR, READ_TIMEOUT, "");
+
+ //then
+ fail("IllegalStateException should be thrown");
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void receiveUntil_shouldThrowIllegalStateException_whenJschReaderStreamIsNotAvailable() throws Exception {
+ //given
+ provideConnectedSubsystemInstance();
+ given(channelIs.available()).willReturn(0);
+
+ //when
+ cut.receiveUntil(SEARCH_STR, READ_TIMEOUT, "");
+
+ //then
+ fail("IllegalStateException should be thrown");
+ }
+
+ @Test(expected = TimedOutException.class)
+ public void receiveUntil_shouldThrowTimedOutException_whenSessionFails() throws Exception {
+ //given
+ given(channelSubsystem.getInputStream()).willReturn(IOUtils.toInputStream("test input stream:~#", "UTF-8"));
+ cut.connect(HOST, USER, PASS, SESSION_TIMEOUT, PORT_NUM, SUBSYSTEM);
+ assertTrue(cut.isConnected());
+ doThrow(new JSchException("Session is not available")).when(session).setTimeout(anyInt());
+
+ //when
+ cut.receiveUntil(SEARCH_STR, READ_TIMEOUT, "");
+
+ //then
+ fail("TimedOutException should be thrown");
+ }
+
+ @Test(expected = TimedOutException.class)
+ public void receiveUntil_shouldThrowTimedOutException_whenReadFails() throws Exception {
+ //given
+ provideConnectedSubsystemInstance();
+ given(channelIs.available()).willReturn(1);
+ given(channelIs.read(any(), anyInt(), anyInt())).willThrow(new IOException("Could not read stream"));
+
+ //when
+ cut.receiveUntil(SEARCH_STR, READ_TIMEOUT, "");
+
+ //then
+ fail("TimedOutException should be thrown");
+ }
+
+ @Test(expected = TimedOutException.class)
+ public void receiveUntil_shouldThrowException_whenTimeoutIsReached() throws Exception {
+ //given
+ String streamContent = "test input stream:~#";
+ provideConnectedSubsystemInstanceWithStreamContent(streamContent);
+
+ //when
+ cut.receiveUntil(SEARCH_STR, -1000, " Some fake command\n");
+
+ //then
+ fail("TimedOutException should be thrown");
+ }
+
+ @Test(expected = TimedOutException.class)
+ public void receiveUntil_shouldThrowException_whenReachedEndOfStream_andCouldNotReadMoreBytes() throws Exception {
+ //given
+ provideConnectedSubsystemInstance();
+ given(channelIs.available()).willReturn(1);
+ given(channelIs.read(any(), anyInt(), anyInt())).willReturn(-1);
+
+ //when
+ cut.receiveUntil(SEARCH_STR, READ_TIMEOUT, "");
+
+ //then
+ fail("TimedOutException should be thrown");
+ }
+
+ @Test
+ public void receiveUntil_shouldReadUnderlyingStream_andStripOffFirstLine() throws Exception {
+ //given
+ String command = "Command"+SshJcraftWrapper.EOL;
+ String reply = "Reply"+SshJcraftWrapper.EOL;
+ String streamContent = command+reply+PROMPT;
+ provideConnectedSubsystemInstanceWithStreamContent(streamContent);
+
+ //when
+ String result = cut.receiveUntil(PROMPT, SESSION_TIMEOUT, command);
+
+ //then
+ assertEquals(reply+PROMPT, result);
+ }
+
+ @Test
+ public void receiveUntil_shouldReadUnderlyingStream_andReturnWholeReadString() throws Exception {
+ //given
+ String streamContent = "Command and Reply in just one line"+PROMPT;
+ provideConnectedSubsystemInstanceWithStreamContent(streamContent);
+
+ //when
+ String result = cut.receiveUntil(PROMPT, SESSION_TIMEOUT, streamContent);
+
+ //then
+ assertEquals(streamContent, result);
+ }
+
+ @Test
+ public void receiveUntil_shouldCutOffSpecialCharactersFromStream() throws Exception {
+ //given
+ char special1 = Character.UNASSIGNED;
+ char special2 = Character.ENCLOSING_MARK;
+ char special3 = Character.LINE_SEPARATOR;
+ char special4 = Character.MODIFIER_SYMBOL;
+ StringBuilder sb = new StringBuilder("Command");
+ sb.append(special1).append("With").append(special2).append("Special")
+ .append(special3).append("Characters").append(special4).append("Set").append(PROMPT);
+
+ provideConnectedSubsystemInstanceWithStreamContent(sb.toString());
+
+ //when
+ String result = cut.receiveUntil(PROMPT, SESSION_TIMEOUT, "");
+
+ //then
+ assertEquals("CommandWithSpecialCharactersSet"+PROMPT, result);
+ }
+
+ @Test
+ public void receiveUntil_shouldReadUnderlyingStream_untilCLIDelimiterFound_whenProperDelimiterSet() throws Exception {
+ //given
+ String cliDelimiter = "#$";
+ String delimiters = PROMPT+SshJcraftWrapper.DELIMITERS_SEPARATOR+cliDelimiter;
+ String streamContent = "Command for CLI invocation #";
+ provideConnectedSubsystemInstanceWithStreamContent(streamContent);
+
+ //when
+ String result = cut.receiveUntil(delimiters, SESSION_TIMEOUT, streamContent);
+
+ //then
+ assertEquals(streamContent, result);
+ }
+
+ @Test
+ public void receiveUntil_shouldReadUnderlyingStream_untilCLIDelimiterFound_whenCLICommandSet() throws Exception {
+ //given
+ String streamContent = "Command for CLI invocation #";
+ provideConnectedSubsystemInstanceWithStreamContent(streamContent);
+ cut.setRouterCommandType("CLI");
+
+ //when
+ String result = cut.receiveUntil("", SESSION_TIMEOUT, streamContent);
+
+ //then
+ assertEquals(streamContent, result);
+ }
+
+ @Test
+ public void receiveUntil_shouldReadUnderlyingStream_untilCLIDelimiterFound_forShowConfigCommand() throws Exception {
+ //given
+ String streamContent = "show config\nconfig content#";
+ provideConnectedSubsystemInstanceWithStreamContent(streamContent);
+
+ //when
+ String result = cut.receiveUntil("#", SESSION_TIMEOUT, streamContent);
+
+ //then
+ assertEquals("config content#", result);
+ }
+
+ @Test
+ public void receiveUntil_shouldWriteOutputToRouterFile_whenReadingIOSXRswConfigFile_confirmFromFile() throws Exception {
+ receiveUntil_shouldWriteOutputToRouterFile_whenReadingIOSXRswConfigFile();
+ }
+
+ @Test
+ public void receiveUntil_shouldWriteOutputToRouterFile_whenReadingIOSXRswConfigFile_confirmFromBuffer() throws Exception {
+ //given
+ int biggerBufferSize = 32;
+ cut = new SshJcraftWrapper(jSchMock, READ_INTERVAL_MS, biggerBufferSize);
+
+ receiveUntil_shouldWriteOutputToRouterFile_whenReadingIOSXRswConfigFile();
+ }
+
+ private void receiveUntil_shouldWriteOutputToRouterFile_whenReadingIOSXRswConfigFile() throws Exception {
+ //given
+ String routerName = "router";
+ String command = "RP/0/RP0/CPU0: "+routerName+" #IOS_XR_uploadedSwConfigCmd";
+ String configFileEnding = "\nXML>";
+ String streamContent = "Config file\ncontent"+configFileEnding;
+ provideConnectedSubsystemInstanceWithStreamContent(streamContent);
+
+ //when
+ String result = cut.receiveUntil("", SESSION_TIMEOUT, command);
+
+ //then
+ assertNull(result); //TO-DO: it would be better to return empty string in this situation
+ assertFileExist(routerName);
+
+ //after
+ teardownFile(routerName);
+ }
+
+ private void provideConnectedSubsystemInstanceWithStreamContent( String streamContent) throws Exception {
+ given(channelSubsystem.getInputStream()).willReturn(IOUtils.toInputStream(streamContent, "UTF-8"));
+ cut.connect(HOST, USER, PASS, SESSION_TIMEOUT, PORT_NUM, SUBSYSTEM);
+ assertTrue(cut.isConnected());
+ }
+
+ private void teardownFile(String routerName) {
+ File file = new File(routerName);
+ if(file.exists() && file.isFile()) {
+ file.delete();
+ }
+ }
+
+ private void assertFileExist(String fileName) {
+ File file = new File(fileName);
+ assertTrue(file.exists());
+ assertTrue(file.isFile());
+ }
+
}