diff options
2 files changed, 267 insertions, 38 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 4d81b6c24..205924584 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 @@ -91,6 +91,8 @@ public class SshJcraftWrapper { private String passWord = null; private int readIntervalMs = 500; private int readBufferSizeBytes = 512_000; + private int charsChunkSize = 300_000; + private int sessionTimeoutMs = 9_000; private char[] charBuffer; private Runtime runtime = Runtime.getRuntime(); @@ -405,12 +407,19 @@ public class SshJcraftWrapper { } boolean isConnected() { - return (channel != null && session != null); + return channel != null && session != null; } public void send(String cmd) throws IOException { try (OutputStream os = channel.getOutputStream(); DataOutputStream dos = new DataOutputStream(os)) { - sendSshCommand(cmd, dos); + String command = enhanceCommandWithEOL(cmd); + int length = command.length(); + log.debug("Sending ssh command: length={0}, payload: {1}", command.length(), command); + if(isCmdLengthEnoughToSendInChunks(length, charsChunkSize)) { + sendSshCommandInChunks(command, dos); + } else { + sendSshCommand(command, dos); + } } catch (IOException e) { log.error(Msg.SSH_DATA_EXCEPTION, e.getMessage()); throw e; @@ -594,10 +603,11 @@ public class SshJcraftWrapper { } // Routine does reads until it has read 'nchars' or times out. - public void receiveUntilBufferFlush(int ncharsSent, int timeout, String command) throws IOException { + public String receiveUntilBufferFlush(int ncharsSent, int timeout, String command) throws IOException { log.debug("ncharsSent={0}, timeout={1}, message={2}", ncharsSent, timeout, command); int ncharsTotalReceived = 0; int ncharsRead = 0; + StringBuilder received = new StringBuilder(); long deadline = new Date().getTime() + timeout; logMemoryUsage(); @@ -609,6 +619,9 @@ public class SshJcraftWrapper { throw new TimedOutException("Routine has timed out"); } ncharsRead = reader.read(charBuffer, 0, readBufferSizeBytes); + if(ncharsRead >=0) { + received.append(charBuffer, 0, ncharsRead); + } if (listener != null) { listener.receivedString(String.copyValueOf(charBuffer, 0, ncharsRead)); } @@ -618,7 +631,7 @@ public class SshJcraftWrapper { log.debug("Received the correct number of characters, ncharsSent={0}, ncharsTotalReceived={1}", ncharsSent, ncharsTotalReceived); logMemoryUsage(); - return; + return received.toString(); } } } catch (JSchException e) { @@ -882,43 +895,55 @@ public class SshJcraftWrapper { public String send(String cmd, String delimiter) throws IOException { try (OutputStream os = channel.getOutputStream(); DataOutputStream dos = new DataOutputStream(os)) { - sendSshCommand(cmd, dos); - return receiveUntil(delimiter, 300000, cmd); + String command = enhanceCommandWithEOL(cmd); + int length = command.length(); + log.debug("Sending ssh command: length={0}, payload: {1}", command.length(), command); + if(isCmdLengthEnoughToSendInChunks(length, charsChunkSize)) { + return sendSshCommandInChunks(command, dos); + } else { + sendSshCommand(command, dos); + return receiveUntil(delimiter, 300000, cmd); + } } } - private void sendSshCommand(@Nonnull String originalCommand, @Nonnull DataOutputStream channelOutputStream) + private void sendSshCommand(@Nonnull String command, @Nonnull DataOutputStream channelOutputStream) throws IOException { - String command = enhanceCommandWithEOL(originalCommand); - int length = command.length(); // 2,937,706 - int charsChunkSize = 300000; - int charsTotalSent = 0; + channelOutputStream.writeBytes(command); + channelOutputStream.flush(); + } - log.debug("Sending ssh command: length={0}, payload: {1}", length, command); - if (isCmdLengthEnoughToSendInChunks(length, charsChunkSize)) { - int timeout = 9000; - for (int i = 0; i < length; i += charsChunkSize) { - String commandChunk = command.substring(i, Math.min(length, i + charsChunkSize)); - int numCharsSentInChunk = commandChunk.length(); - charsTotalSent = charsTotalSent + commandChunk.length(); - log.debug("Iteration nr:{0}, sending command chunk: {1}", i, numCharsSentInChunk); - channelOutputStream.writeBytes(commandChunk); - channelOutputStream.flush(); - try { - if (numCharsSentInChunk < length) { - receiveUntilBufferFlush(numCharsSentInChunk, timeout, originalCommand); - } else { - log.trace("i={0}, flush immediately", i); - channelOutputStream.flush(); - } - } catch (IOException ex) { - log.warn("IOException occurred: nothing to flush out", ex); + private String sendSshCommandInChunks(@Nonnull String command, @Nonnull DataOutputStream channelOutputStream) throws IOException { + StringBuilder received = new StringBuilder(); + int charsTotalSent = 0; + int length = command.length(); + for (int i = 0; i < length; i += charsChunkSize) { + String commandChunk = command.substring(i, Math.min(length, i + charsChunkSize)); + int numCharsSentInChunk = commandChunk.length(); + charsTotalSent = charsTotalSent + commandChunk.length(); + log.debug("Iteration nr:{0}, sending command chunk: {1}", i, numCharsSentInChunk); + channelOutputStream.writeBytes(commandChunk); + channelOutputStream.flush(); + try { + if (numCharsSentInChunk < length) { + received.append(receiveUntilBufferFlush(numCharsSentInChunk, sessionTimeoutMs, command)); + } else { + log.trace("i={0}, flush immediately", i); + channelOutputStream.flush(); } + } catch (IOException ex) { + log.warn("IOException occurred: nothing to flush out", ex); } - } else { - channelOutputStream.writeBytes(command); } - channelOutputStream.flush(); + return received.toString(); + } + + public void setSessionTimeoutMs(int sessionTimeoutMs) { + this.sessionTimeoutMs = sessionTimeoutMs; + } + + void setCharsChunkSize(int charsChunkSize) { + this.charsChunkSize = charsChunkSize; } private boolean isCmdLengthEnoughToSendInChunks(int length, int chunkSize) { 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 76b4c628f..4047e9326 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 @@ -33,11 +33,14 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; import com.jcraft.jsch.ChannelShell; import com.jcraft.jsch.ChannelSubsystem; @@ -49,8 +52,10 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.net.URL; +import org.apache.commons.lang.StringUtils; import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; @@ -87,6 +92,8 @@ public class SshJcraftWrapperTest { private ChannelSubsystem channelSubsystem; @Mock private InputStream channelIs; + @Mock + private OutputStream channelOs; @Before public void setUpTest() throws Exception { @@ -121,14 +128,14 @@ public class SshJcraftWrapperTest { public void testStripOffCmdFromRouterResponse(){ SshJcraftWrapper wrapper = new SshJcraftWrapper(); String result = wrapper.stripOffCmdFromRouterResponse("test\nsuccess"); - Assert.assertEquals("success\n", result); + Assert.assertEquals("success\n", result); } - + //@Test public void testGetLastFewLinesOfFile() throws FileNotFoundException, IOException{ SshJcraftWrapper wrapper = new SshJcraftWrapper(); URL path = SshJcraftWrapperTest.class.getResource("Test"); - File file = new File(path.getFile()); + File file = new File(path.getFile()); String value = wrapper.getLastFewLinesOfFile(file,1); Assert.assertEquals("\nTest data 3", value); } @@ -137,7 +144,7 @@ public class SshJcraftWrapperTest { @Test(expected=Exception.class) public void testSetRouterCommandType() throws IOException{ SshJcraftWrapper wrapper = new SshJcraftWrapper(); - wrapper.setRouterCommandType("test"); + wrapper.setRouterCommandType("test"); wrapper.receiveUntil("test", 2, "test"); } @@ -164,7 +171,7 @@ public class SshJcraftWrapperTest { StringBuilder sb = new StringBuilder(); sb.append("test"); wrapper.appendToRouterFile("Test.txt", sb); - wrapper.receiveUntilBufferFlush(3, 4, "test"); + wrapper.receiveUntilBufferFlush(3, 4, "test"); } @Ignore @@ -680,4 +687,201 @@ public class SshJcraftWrapperTest { assertTrue(file.isFile()); } + @Test + public void send_withReceive_shouldWriteCommandToChannelOutputStream_andReturnReceivedCommand() throws Exception { + //given + String command = "sdc"; + String delimiter = ":"; + InOrder inOrder = inOrder(channelOs); + provideConnectedSubsystemInstanceWithStreamContent(command+delimiter); + given(channelSubsystem.getOutputStream()).willReturn(channelOs); + + //when + String result = cut.send(command, delimiter); + + //then + verifySdcCommandSent(inOrder); + assertEquals(command+delimiter, result); + } + + @Test + public void send_shouldWriteCommandToChannelOutputStream() throws Exception { + //given + String command = "sdc"; + InOrder inOrder = inOrder(channelOs); + provideConnectedSubsystemInstance(); + given(channelSubsystem.getOutputStream()).willReturn(channelOs); + + //when + cut.send(command); + + //then + verifySdcCommandSent(inOrder); + } + + private void verifySdcCommandSent(InOrder inOrder) throws IOException { + inOrder.verify(channelOs).write('s'); + inOrder.verify(channelOs).write('d'); + inOrder.verify(channelOs).write('c'); + inOrder.verify(channelOs, atLeastOnce()).flush(); + inOrder.verify(channelOs, atLeastOnce()).close(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void send_withReceive_shouldWriteCommandInChunksToChannelOutputStream_andReturnReceivedCommand() throws Exception { + //given + cut = new SshJcraftWrapper(jSchMock, READ_INTERVAL_MS, 1); + cut.setCharsChunkSize(1); + String command = "sdc"; + String delimiter = ":"; + int timeout = 9000; + provideConnectedSubsystemInstanceWithStreamContent(command+delimiter+SshJcraftWrapper.EOL); + given(channelSubsystem.getOutputStream()).willReturn(channelOs); + InOrder inOrder = inOrder(channelOs, session); + + //when + String result = cut.send(command, delimiter); + + //then + verifySdcCommandSentInChunk(inOrder, timeout); + assertEquals(command+delimiter, result); + } + + @Test + public void send_shouldWriteCommandInChunksToChannelOutputStream() throws Exception { + //given + cut = new SshJcraftWrapper(jSchMock, READ_INTERVAL_MS, 1); + cut.setCharsChunkSize(1); + String command = "sdc"; + int timeout = 9000; + provideConnectedSubsystemInstanceWithStreamContent(command+SshJcraftWrapper.EOL); + given(channelSubsystem.getOutputStream()).willReturn(channelOs); + InOrder inOrder = inOrder(channelOs, session); + + //when + cut.send(command); + + //then + verifySdcCommandSentInChunk(inOrder, timeout); + } + + private void verifySdcCommandSentInChunk(InOrder inOrder, int timeout) throws Exception{ + inOrder.verify(channelOs).write('s'); + inOrder.verify(channelOs).flush(); + inOrder.verify(session).setTimeout(timeout); + inOrder.verify(channelOs).write('d'); + inOrder.verify(channelOs).flush(); + inOrder.verify(session).setTimeout(timeout); + inOrder.verify(channelOs).write('c'); + inOrder.verify(channelOs).flush(); + inOrder.verify(session).setTimeout(timeout); + inOrder.verify(channelOs, atLeastOnce()).flush(); + inOrder.verify(channelOs, atLeastOnce()).close(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void send_withReceive_shouldReturnActualResult_whenTimeoutReached() throws Exception { + //given + cut = new SshJcraftWrapper(jSchMock, READ_INTERVAL_MS, 1); + cut.setCharsChunkSize(1); + cut.setSessionTimeoutMs(-1); + String command = "sdc"; + String delimiter = ":"; + provideConnectedSubsystemInstanceWithStreamContent(command+SshJcraftWrapper.EOL); + given(channelSubsystem.getOutputStream()).willReturn(channelOs); + + //when + String result = cut.send(command, delimiter); + + //then + assertEquals(StringUtils.EMPTY, result); + } + + @Test + public void send_withReceive_shouldReturnActualResult_whenCouldNotSetSessionTimeout() throws Exception { + //given + cut = new SshJcraftWrapper(jSchMock, READ_INTERVAL_MS, 1); + cut.setCharsChunkSize(1); + String command = "sdc"; + String delimiter = ":"; + provideConnectedSubsystemInstanceWithStreamContent(command+SshJcraftWrapper.EOL); + given(channelSubsystem.getOutputStream()).willReturn(channelOs); + doThrow(new JSchException("failed to set session timeout")).when(session).setTimeout(anyInt()); + + //when + String result = cut.send(command, delimiter); + + //then + assertEquals(StringUtils.EMPTY, result); + } + + @Test + public void sendChar_shouldWriteCharacterToChannelOutputStream() throws Exception { + //given + int charNum = 100; + provideConnectedSubsystemInstance(); + given(channelSubsystem.getOutputStream()).willReturn(channelOs); + InOrder inOrder = inOrder(channelOs); + + //when + cut.sendChar(charNum); + + //then + inOrder.verify(channelOs).write(charNum); + inOrder.verify(channelOs, atLeastOnce()).flush(); + inOrder.verify(channelOs, atLeastOnce()).close(); + inOrder.verifyNoMoreInteractions(); + } + + @Test(expected = IOException.class) + public void sendChar_shouldRethrowIOException_whenOccurs() throws Exception { + //given + int charNum = 100; + provideConnectedSubsystemInstance(); + given(channelSubsystem.getOutputStream()).willReturn(channelOs); + doThrow(new IOException()).when(channelOs).write(charNum); + + //when + cut.sendChar(charNum); + + //then + fail("IOException should be thrown"); + } + + @Test + public void send_withByteBuffer_shouldWriteBufferToChannelOutputStream() throws Exception { + //given + byte[] buffer = "Command".getBytes(); + int offset = 5; + provideConnectedSubsystemInstance(); + given(channelSubsystem.getOutputStream()).willReturn(channelOs); + InOrder inOrder = inOrder(channelOs); + + //when + cut.send(buffer,offset,buffer.length); + + //then + inOrder.verify(channelOs).write(buffer, offset, buffer.length); + inOrder.verify(channelOs, atLeastOnce()).flush(); + inOrder.verify(channelOs, atLeastOnce()).close(); + inOrder.verifyNoMoreInteractions(); + } + + @Test(expected = IOException.class) + public void send_withByteBuffer_shouldRethrowIOException_whenOccurs() throws Exception { + //given + byte[] buffer = "Command".getBytes(); + int offset = 5; + provideConnectedSubsystemInstance(); + given(channelSubsystem.getOutputStream()).willReturn(channelOs); + doThrow(new IOException()).when(channelOs).write(buffer, offset, buffer.length); + + //when + cut.send(buffer,offset,buffer.length); + + //then + fail("IOException should be thrown"); + } } |