diff options
12 files changed, 423 insertions, 89 deletions
diff --git a/cds-ui/server/src/controllers/blueprint-rest.controller.ts b/cds-ui/server/src/controllers/blueprint-rest.controller.ts index 1a8de6b47..b2a11281f 100644 --- a/cds-ui/server/src/controllers/blueprint-rest.controller.ts +++ b/cds-ui/server/src/controllers/blueprint-rest.controller.ts @@ -50,6 +50,7 @@ import * as multiparty from 'multiparty'; import * as request_lib from 'request'; import { processorApiConfig, appConfig } from '../config/app-config'; import { bluePrintManagementServiceGrpcClient } from '../clients/blueprint-management-service-grpc-client'; +import {BlueprintDetail} from '../models/blueprint.detail.model'; export class BlueprintRestController { constructor( @@ -68,6 +69,18 @@ export class BlueprintRestController { async getall() { return await this.bpservice.getAllblueprints(); } + @get('/controllerblueprint/{id}', { + responses: { + '200': { + description: 'Blueprint model instance', + content: { 'application/json': { schema: { 'x-ts-type': BlueprintDetail } } }, + }, + }, + }) + async getOneBluePrint(@param.path.string('id') id: string) { + return await this.bpservice.getOneBluePrint(id); + } + @get('/controllerblueprint/paged', { responses: { diff --git a/cds-ui/server/src/controllers/data-dictionary.controller.ts b/cds-ui/server/src/controllers/data-dictionary.controller.ts index eab0bc50c..076d714c6 100644 --- a/cds-ui/server/src/controllers/data-dictionary.controller.ts +++ b/cds-ui/server/src/controllers/data-dictionary.controller.ts @@ -96,6 +96,9 @@ export class DataDictionaryController { return await this.rdservice.getModelType(source); } + /** + * @deprecated use getResourceDictionaryByType Instead. + */ @get('/resourcedictionary/model-type/by-definition/data_type', { responses: { '200': { @@ -104,6 +107,19 @@ export class DataDictionaryController { }, }) async getDataTypes() { + console.warn("Calling deprecated function!"); return await this.rdservice.getDataTypes(); } + + @get('/resourcedictionary/model-type/by-definition/{type}', { + responses: { + '200': { + content: {'application/json': {}}, + }, + }, + }) + async getResourceDictionaryByType(@param.path.string('type') type: string) { + return await this.rdservice.getResourceDictionaryByType(type); + } + } diff --git a/cds-ui/server/src/datasources/blueprint.datasource-template.ts b/cds-ui/server/src/datasources/blueprint.datasource-template.ts index a7cea1d4a..d51d1ba7b 100644 --- a/cds-ui/server/src/datasources/blueprint.datasource-template.ts +++ b/cds-ui/server/src/datasources/blueprint.datasource-template.ts @@ -21,11 +21,10 @@ export default { "getAllblueprints": [] } - }, - { + }, { "template": { "method": "GET", - "url": processorApiConfig.http.url + "/blueprint-model/search/{tags}", + "url": processorApiConfig.http.url + "/blueprint-model/{id}", "headers": { "accepts": "application/json", "content-type": "application/json", @@ -34,41 +33,58 @@ export default { "responsePath": "$.*" }, "functions": { - "getByTags": ["tags"] + "getOneBluePrint": ["id"] } }, - { - "template": { - "method": "GET", - "url": processorApiConfig.http.url + "/blueprint-model/meta-data/{keyword}", - "headers": { - "accepts": "application/json", - "content-type": "application/json", - "authorization": processorApiConfig.http.authToken + + { + "template": { + "method": "GET", + "url": processorApiConfig.http.url + "/blueprint-model/search/{tags}", + "headers": { + "accepts": "application/json", + "content-type": "application/json", + "authorization": processorApiConfig.http.authToken + }, + "responsePath": "$.*" }, - "responsePath": "$.*" + "functions": { + "getByTags": ["tags"] + + } }, - "functions": { - "getBlueprintsByKeyword": ["keyword"] + { + "template": { + "method": "GET", + "url": processorApiConfig.http.url + "/blueprint-model/meta-data/{keyword}", + "headers": { + "accepts": "application/json", + "content-type": "application/json", + "authorization": processorApiConfig.http.authToken + }, + "responsePath": "$.*" + }, + "functions": { + "getBlueprintsByKeyword": ["keyword"] - } - }, - { - "template": { - "method": "GET", - "url": processorApiConfig.http.url + "/blueprint-model/paged?limit={limit}&offset={offset}&sort={sort}", - "headers": { - "accepts": "application/json", - "content-type": "application/json", - "authorization": processorApiConfig.http.authToken + } + }, + { + "template": { + "method": "GET", + "url": processorApiConfig.http.url + "/blueprint-model/paged?limit={limit}&offset={offset}&sort={sort}", + "headers": { + "accepts": "application/json", + "content-type": "application/json", + "authorization": processorApiConfig.http.authToken + }, + "responsePath": "$", }, - "responsePath": "$", + "functions": { + "getPagedBueprints": ["limit", "offset", "sort"], + } }, - "functions": { - "getPagedBueprints": ["limit","offset", "sort"], - } - }, { "template": { "method": "GET", @@ -81,8 +97,9 @@ export default { "responsePath": "$", }, "functions": { - "getMetaDataPagedBlueprints": ["limit","offset", "sort","keyword"], + "getMetaDataPagedBlueprints": ["limit", "offset", "sort", "keyword"], } }, -] + ] + }; diff --git a/cds-ui/server/src/datasources/resource-dictionary.datasource-template.ts b/cds-ui/server/src/datasources/resource-dictionary.datasource-template.ts index f839bb225..7373d069e 100644 --- a/cds-ui/server/src/datasources/resource-dictionary.datasource-template.ts +++ b/cds-ui/server/src/datasources/resource-dictionary.datasource-template.ts @@ -119,6 +119,22 @@ export default { "getDataTypes": [] } + }, + { + "template": { + "method": "GET", + "url": processorApiConfig.http.url + "/model-type/by-definition/{type}", + "headers": { + "accepts": "application/json", + "content-type": "application/json", + "authorization": processorApiConfig.http.authToken + }, + "responsePath": "$.*" + }, + "functions": { + "getResourceDictionaryByType": ["type"] + + } } ] -};
\ No newline at end of file +}; diff --git a/cds-ui/server/src/models/blueprint.detail.model.ts b/cds-ui/server/src/models/blueprint.detail.model.ts new file mode 100644 index 000000000..1a6b9f2f8 --- /dev/null +++ b/cds-ui/server/src/models/blueprint.detail.model.ts @@ -0,0 +1,37 @@ +/* +============LICENSE_START========================================== +=================================================================== +Copyright (C) 2018-19 IBM Intellectual Property. All rights reserved. +=================================================================== + +Unless otherwise specified, all software contained herein is licensed +under the Apache License, Version 2.0 (the License); +you may not use this software except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +============LICENSE_END============================================ +*/ + + +import { model, property} from '@loopback/repository'; +import {Blueprint} from "./blueprint.model"; + +@model() +export class BlueprintDetail extends Blueprint { + + @property({ + type: 'object', + }) + blueprintModelContent?: object; + + constructor(data?: Partial<BlueprintDetail>) { + super(data); + } +} diff --git a/cds-ui/server/src/services/blueprint.service.ts b/cds-ui/server/src/services/blueprint.service.ts index b6474a3a2..875eb5e38 100644 --- a/cds-ui/server/src/services/blueprint.service.ts +++ b/cds-ui/server/src/services/blueprint.service.ts @@ -3,6 +3,7 @@ import {inject, Provider} from '@loopback/core'; import {BlueprintDataSource} from '../datasources'; export interface BlueprintService { + getOneBluePrint(id: string): any; getAllblueprints(): Promise<any>; getBlueprintsByKeyword(keyword: string): Promise<any>; getByTags(tags: string): Promise<JSON>; diff --git a/cds-ui/server/src/services/resource-dictionary.service.ts b/cds-ui/server/src/services/resource-dictionary.service.ts index 8df22085c..a36c7fe75 100644 --- a/cds-ui/server/src/services/resource-dictionary.service.ts +++ b/cds-ui/server/src/services/resource-dictionary.service.ts @@ -7,9 +7,10 @@ export interface ResourceDictionaryService { getSourceMapping(): Promise<JSON>; getByTags(tags: string): Promise<JSON>; save(resourceDictionary: JSON): Promise<JSON>; - searchbyNames(resourceDictionaryList: JSON): Promise<JSON>; + searchbyNames(resourceDictionaryList: JSON): Promise<JSON>; getModelType(source: string): Promise<JSON>; getDataTypes(): Promise<JSON>; + getResourceDictionaryByType(type: string): Promise<JSON>; } export class ResourceDictionaryServiceProvider implements Provider<ResourceDictionaryService> { diff --git a/ms/blueprintsprocessor/functions/cli-executor/src/main/kotlin/internal/scripts/InternalSimpleCli.kt b/ms/blueprintsprocessor/functions/cli-executor/src/main/kotlin/internal/scripts/InternalSimpleCli.kt index b3708a092..a0dfb8b0b 100644 --- a/ms/blueprintsprocessor/functions/cli-executor/src/main/kotlin/internal/scripts/InternalSimpleCli.kt +++ b/ms/blueprintsprocessor/functions/cli-executor/src/main/kotlin/internal/scripts/InternalSimpleCli.kt @@ -1,6 +1,8 @@ /* * Copyright © 2019 IBM. * + * Modifications Copyright © 2019 IBM, Bell Canada + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -23,7 +25,7 @@ import org.onap.ccsdk.cds.blueprintsprocessor.functions.cli.executor.cliDeviceIn import org.onap.ccsdk.cds.blueprintsprocessor.functions.cli.executor.getSshClientService import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractScriptComponentFunction import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.ComponentScriptExecutor -import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive +import org.onap.ccsdk.cds.controllerblueprints.core.asJsonType import org.slf4j.LoggerFactory open class TestCliScriptFunction : AbstractScriptComponentFunction() { @@ -70,7 +72,7 @@ open class Check : AbstractScriptComponentFunction() { sshClientService.closeSessionNB() // Set the Response Data - setAttribute(ComponentScriptExecutor.ATTRIBUTE_RESPONSE_DATA, responseLog.asJsonPrimitive()) + setAttribute(ComponentScriptExecutor.ATTRIBUTE_RESPONSE_DATA, responseLog.asJsonType()) log.info("Executing process") } diff --git a/ms/blueprintsprocessor/modules/commons/ssh-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ssh/service/BasicAuthSshClientService.kt b/ms/blueprintsprocessor/modules/commons/ssh-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ssh/service/BasicAuthSshClientService.kt index 61baaa1ef..2885d6528 100644 --- a/ms/blueprintsprocessor/modules/commons/ssh-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ssh/service/BasicAuthSshClientService.kt +++ b/ms/blueprintsprocessor/modules/commons/ssh-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ssh/service/BasicAuthSshClientService.kt @@ -1,6 +1,8 @@ /* * Copyright © 2019 IBM. * + * Modifications Copyright © 2018-2019 IBM, Bell Canada + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -16,9 +18,9 @@ package org.onap.ccsdk.cds.blueprintsprocessor.ssh.service +import org.apache.commons.io.output.TeeOutputStream import org.apache.sshd.client.SshClient -import org.apache.sshd.client.channel.ChannelExec -import org.apache.sshd.client.channel.ClientChannel +import org.apache.sshd.client.channel.ChannelShell import org.apache.sshd.client.channel.ClientChannelEvent import org.apache.sshd.client.keyverifier.AcceptAllServerKeyVerifier import org.apache.sshd.client.session.ClientSession @@ -26,75 +28,142 @@ import org.onap.ccsdk.cds.blueprintsprocessor.ssh.BasicAuthSshClientProperties import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException import org.slf4j.LoggerFactory import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.PipedInputStream +import java.io.PipedOutputStream import java.util.Collections import java.util.EnumSet +import java.util.Scanner +import java.util.ArrayList open class BasicAuthSshClientService(private val basicAuthSshClientProperties: BasicAuthSshClientProperties) : - BlueprintSshClientService { + BlueprintSshClientService { private val log = LoggerFactory.getLogger(BasicAuthSshClientService::class.java)!! + private val newLine = "\n".toByteArray() + private var channel: ChannelShell? = null + private var teeOutput: TeeOutputStream? = null private lateinit var sshClient: SshClient private lateinit var clientSession: ClientSession - var channel: ChannelExec? = null override suspend fun startSessionNB(): ClientSession { sshClient = SshClient.setUpDefaultClient() sshClient.serverKeyVerifier = AcceptAllServerKeyVerifier.INSTANCE sshClient.start() log.debug("SSH Client Service started successfully") + clientSession = sshClient.connect( - basicAuthSshClientProperties.username, basicAuthSshClientProperties.host, - basicAuthSshClientProperties.port - ) - .verify(basicAuthSshClientProperties.connectionTimeOut) - .session + basicAuthSshClientProperties.username, basicAuthSshClientProperties.host, + basicAuthSshClientProperties.port).verify(basicAuthSshClientProperties.connectionTimeOut).session clientSession.addPasswordIdentity(basicAuthSshClientProperties.password) clientSession.auth().verify(basicAuthSshClientProperties.connectionTimeOut) + startChannel() + log.info("SSH client session($clientSession) created") return clientSession } - override suspend fun executeCommandsNB(commands: List<String>, timeOut: Long): String { - val buffer = StringBuffer() + private fun startChannel() { try { - commands.forEach { command -> - buffer.append("\nCommand : $command") - buffer.append("\n" + executeCommandNB(command, timeOut)) + channel = clientSession.createShellChannel() + val pipedIn = PipedOutputStream() + channel!!.setIn(PipedInputStream(pipedIn)) + teeOutput = TeeOutputStream(ByteArrayOutputStream(), pipedIn) + channel!!.out = ByteArrayOutputStream() + channel!!.err = ByteArrayOutputStream() + channel!!.open() + } catch (e: Exception) { + throw BluePrintProcessorException("Failed to start Shell channel: ${e.message}") + } + } + + override suspend fun executeCommandsNB(commands: List <String>, timeOut: Long): List<CommandResult> { + val response = ArrayList<CommandResult>() + try { + var stopLoop = false + val commandsIterator = commands.iterator() + while (commandsIterator.hasNext() && !stopLoop) { + val command = commandsIterator.next() + log.debug("Executing host command($command) \n") + val result = executeCommand(command, timeOut) + response.add(result) + // Once a command in the template has failed break out of the loop to stop executing further commands + if (!result.successful) { + log.debug("Template execution will stop because command ({}) has failed.", command) + stopLoop = true + } } } catch (e: Exception) { - throw BluePrintProcessorException("Failed to execute commands, below the output : $buffer") + throw BluePrintProcessorException("Failed to execute commands, below the error message : ${e.message}") } - return buffer.toString() + return response } - override suspend fun executeCommandNB(command: String, timeOut: Long): String { - log.debug("Executing host($clientSession) command($command)") + override suspend fun executeCommandNB(command: String, timeOut: Long): CommandResult { + val deviceOutput: String + var isSuccessful = true + try { + teeOutput!!.write(command.toByteArray()) + teeOutput!!.write(newLine) + teeOutput!!.flush() + deviceOutput = waitForPrompt(timeOut) + } catch (e: IOException) { + throw BluePrintProcessorException("Exception during command execution: ${e.message}", e) + } + + if (detectFailure(deviceOutput)) { + isSuccessful = false + } - channel = clientSession.createExecChannel(command) - checkNotNull(channel) { "failed to create Channel for the command : $command" } + val commandResult = CommandResult(command, deviceOutput, isSuccessful) + log.info("Command Response: ({}) $newLine", commandResult) + return commandResult + } - // TODO("Convert to streaming ") - val outputStream = ByteArrayOutputStream() - channel!!.out = outputStream - channel!!.err = outputStream - channel!!.open().await() - val waitMask = channel!!.waitFor(Collections.unmodifiableSet(EnumSet.of(ClientChannelEvent.CLOSED)), timeOut) - if (waitMask.contains(ClientChannelEvent.TIMEOUT)) { - throw BluePrintProcessorException("Failed to retrieve command result in time: $command") + private fun waitForPrompt(timeOut: Long): String { + val waitMask = channel!!.waitFor( + Collections.unmodifiableSet(EnumSet.of(ClientChannelEvent.CLOSED)), timeOut) + if (channel!!.out.toString().indexOfAny(arrayListOf("$", ">", "#")) <= 0 && waitMask.contains(ClientChannelEvent.TIMEOUT)) { + throw BluePrintProcessorException("Timeout: Failed to retrieve commands result in $timeOut ms") } - val exitStatus = channel!!.exitStatus - ClientChannel.validateCommandExitStatusCode(command, exitStatus!!) - return outputStream.toString() + val outputResult = channel!!.out.toString() + channel!!.out.flush() + return outputResult } override suspend fun closeSessionNB() { - if (channel != null) + if (channel != null) { channel!!.close() + } + + if (clientSession.isOpen && !clientSession.isClosing) { + clientSession.close() + } + if (sshClient.isStarted) { sshClient.stop() } log.debug("SSH Client Service stopped successfully") } + + // TODO filter output to check error message + private fun detectFailure(output: String): Boolean { + if (output.isNotBlank()) { + // Output can be multiline, need to check if any of the line starts with % + Scanner(output).use { scanner -> + while (scanner.hasNextLine()) { + val temp = scanner.nextLine() + if (temp.isNotBlank() && (temp.trim { it <= ' ' }.startsWith("%") || + temp.trim { it <= ' ' }.startsWith("syntax error"))) { + return true + } + } + } + } + return false + } } + +data class CommandResult(val command: String, val deviceOutput: String, val successful: Boolean) diff --git a/ms/blueprintsprocessor/modules/commons/ssh-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ssh/service/BlueprintSshClientService.kt b/ms/blueprintsprocessor/modules/commons/ssh-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ssh/service/BlueprintSshClientService.kt index 724c4277d..27ebf50bc 100644 --- a/ms/blueprintsprocessor/modules/commons/ssh-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ssh/service/BlueprintSshClientService.kt +++ b/ms/blueprintsprocessor/modules/commons/ssh-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ssh/service/BlueprintSshClientService.kt @@ -1,6 +1,8 @@ /* * Copyright © 2019 IBM. * + * Modifications Copyright © 2018-2019 IBM, Bell Canada + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -25,11 +27,11 @@ interface BlueprintSshClientService { startSessionNB() } - fun executeCommands(commands: List<String>, timeOut: Long): String = runBlocking { + fun executeCommands(commands: List<String>, timeOut: Long): List<CommandResult> = runBlocking { executeCommandsNB(commands, timeOut) } - fun executeCommand(command: String, timeOut: Long): String = runBlocking { + fun executeCommand(command: String, timeOut: Long): CommandResult = runBlocking { executeCommandNB(command, timeOut) } @@ -39,9 +41,9 @@ interface BlueprintSshClientService { suspend fun startSessionNB(): ClientSession - suspend fun executeCommandsNB(commands: List<String>, timeOut: Long): String + suspend fun executeCommandsNB(commands: List<String>, timeOut: Long): List<CommandResult> - suspend fun executeCommandNB(command: String, timeOut: Long): String + suspend fun executeCommandNB(command: String, timeOut: Long): CommandResult suspend fun closeSessionNB() } diff --git a/ms/blueprintsprocessor/modules/commons/ssh-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ssh/service/BlueprintSshClientServiceTest.kt b/ms/blueprintsprocessor/modules/commons/ssh-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ssh/service/BlueprintSshClientServiceTest.kt index 683816f7f..3785a21b2 100644 --- a/ms/blueprintsprocessor/modules/commons/ssh-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ssh/service/BlueprintSshClientServiceTest.kt +++ b/ms/blueprintsprocessor/modules/commons/ssh-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ssh/service/BlueprintSshClientServiceTest.kt @@ -1,6 +1,8 @@ /* * Copyright © 2019 IBM. * + * Modifications Copyright © 2018-2019 IBM, Bell Canada + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -25,18 +27,22 @@ import org.apache.sshd.server.auth.pubkey.AcceptAllPublickeyAuthenticator import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider import org.apache.sshd.server.session.ServerSession import org.apache.sshd.server.shell.ProcessShellCommandFactory -import org.junit.runner.RunWith import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintPropertiesService import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintPropertyConfiguration import org.onap.ccsdk.cds.blueprintsprocessor.ssh.BluePrintSshLibConfiguration +import org.onap.ccsdk.cds.blueprintsprocessor.ssh.service.echoShell.EchoShellFactory import org.springframework.beans.factory.annotation.Autowired import org.springframework.test.context.ContextConfiguration import org.springframework.test.context.TestPropertySource import org.springframework.test.context.junit4.SpringRunner import java.nio.file.Paths +import org.junit.runner.RunWith +import kotlin.test.BeforeTest +import kotlin.test.AfterTest import kotlin.test.Test -import kotlin.test.assertEquals +import kotlin.test.assertTrue import kotlin.test.assertNotNull +import kotlin.test.assertEquals @RunWith(SpringRunner::class) @ContextConfiguration( @@ -57,29 +63,76 @@ class BlueprintSshClientServiceTest { @Autowired lateinit var bluePrintSshLibPropertyService: BluePrintSshLibPropertyService - @Test - fun testBasicAuthSshClientService() { + lateinit var bluePrintSshLibPropertyServiceMock: BluePrintSshLibPropertyService + + private lateinit var sshServer: SshServer + + @BeforeTest + fun startShellServer() { runBlocking { - val sshServer = setupTestServer("localhost", 52815, "root", "dummyps") + println("Start local Shell server") + sshServer = setupTestServer("localhost", 52815, "root", "dummyps") sshServer.start() println(sshServer) - val bluePrintSshLibPropertyService = bluePrintSshLibPropertyService.blueprintSshClientService("sample") - val sshSession = bluePrintSshLibPropertyService.startSession() - val response = bluePrintSshLibPropertyService.executeCommandsNB(arrayListOf("echo '1'", "echo '2'"), 2000) - assertNotNull(response, "failed to get command response") - bluePrintSshLibPropertyService.closeSession() - sshServer.stop(true) } } - private fun setupTestServer(host: String, port: Int, userName: String, password: String): SshServer { + @AfterTest + fun stopShellServer() { + println("End the Shell server") + sshServer.stop(true) + } + + @Test + fun testStartSessionNB() { + val clientSession = getSshClientService().startSession() + assertNotNull(clientSession, "Failed to start ssh session with server") + } + + @Test + fun testBasicAuthSshClientService() { + runBlocking { + val blueprintSshClientService = getSshClientService() + blueprintSshClientService.startSession() + // Preparing response + val commandResults = arrayListOf<CommandResult>() + commandResults.add(CommandResult("echo 1", "echo 1\n#", true)) + commandResults.add(CommandResult("echo 2", "echo 1\n#echo 2\n#", true)) + val response = blueprintSshClientService.executeCommands(arrayListOf("echo 1", "echo 2"), 2000) + blueprintSshClientService.closeSession() + + assertEquals(response, commandResults, "failed to get command responses") + } + } + + @Test + fun `testBasicAuthSshClientService single execution command`() { + runBlocking { + val blueprintSshClientService = getSshClientService() + blueprintSshClientService.startSession() + val response = blueprintSshClientService.executeCommand("echo 1", 2000) + blueprintSshClientService.closeSession() + + assertEquals(response, CommandResult("echo 1", "echo 1\n#", true), "failed to get command response") + } + } + + @Test + fun testCloseSessionNB() { + val bluePrintSshLibPropertyService = bluePrintSshLibPropertyService.blueprintSshClientService("sample") + val clientSession = bluePrintSshLibPropertyService.startSession() + bluePrintSshLibPropertyService.closeSession() + assertTrue(clientSession.isClosed, "Failed to close ssh session with server") + } + + private fun setupTestServer(host: String, port: Int, username: String, password: String): SshServer { val sshd = SshServer.setUpDefaultServer() sshd.port = port sshd.host = host sshd.keyPairProvider = createTestHostKeyProvider() - sshd.passwordAuthenticator = BogusPasswordAuthenticator(userName, password) + sshd.passwordAuthenticator = BogusPasswordAuthenticator(username, password) sshd.publickeyAuthenticator = AcceptAllPublickeyAuthenticator.INSTANCE - // sshd.shellFactory = EchoShellFactory() + sshd.shellFactory = EchoShellFactory.INSTANCE sshd.commandFactory = ProcessShellCommandFactory.INSTANCE return sshd } @@ -90,12 +143,17 @@ class BlueprintSshClientServiceTest { keyProvider.algorithm = RSA_ALGORITHM return keyProvider } + + private fun getSshClientService(): BlueprintSshClientService { + return bluePrintSshLibPropertyService.blueprintSshClientService("sample") + } } -class BogusPasswordAuthenticator(userName: String, password: String) : PasswordAuthenticator { +class BogusPasswordAuthenticator(private val usr: String, private val pwd: String) : PasswordAuthenticator { + override fun authenticate(username: String, password: String, serverSession: ServerSession): Boolean { - assertEquals(username, "root", "failed to match username") - assertEquals(password, "dummyps", "failed to match password") + assertEquals(username, usr, "failed to match username") + assertEquals(password, pwd, "failed to match password") return true } } diff --git a/ms/blueprintsprocessor/modules/commons/ssh-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ssh/service/echoShell/EchoShellFactory.kt b/ms/blueprintsprocessor/modules/commons/ssh-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ssh/service/echoShell/EchoShellFactory.kt new file mode 100644 index 000000000..9d308202f --- /dev/null +++ b/ms/blueprintsprocessor/modules/commons/ssh-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ssh/service/echoShell/EchoShellFactory.kt @@ -0,0 +1,102 @@ +/* + * Copyright © 2019 IBM. Bell Canada + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onap.ccsdk.cds.blueprintsprocessor.ssh.service.echoShell + +import org.apache.sshd.common.Factory +import org.apache.sshd.server.Environment +import org.apache.sshd.server.command.Command +import org.apache.sshd.server.ExitCallback +import java.io.InputStream +import java.io.OutputStream +import java.io.IOException +import java.io.BufferedReader +import java.io.InputStreamReader +import java.io.InterruptedIOException + +class EchoShellFactory : Factory<Command> { + + override fun create(): Command { + return EchoShell() + } + + companion object { + val INSTANCE = EchoShellFactory() + } +} + +class EchoShell : Command, Runnable { + + var `in`: InputStream? = null + private set + var out: OutputStream? = null + private set + var err: OutputStream? = null + private set + private var callback: ExitCallback? = null + var environment: Environment? = null + private set + private var thread: Thread? = null + + override fun setInputStream(`in`: InputStream) { + this.`in` = `in` + } + + override fun setOutputStream(out: OutputStream) { + this.out = out + } + + override fun setErrorStream(err: OutputStream) { + this.err = err + } + + override fun setExitCallback(callback: ExitCallback) { + this.callback = callback + } + + @Throws(IOException::class) + override fun start(env: Environment) { + environment = env + thread = Thread(this, "EchoShell") + thread!!.isDaemon = true + thread!!.start() + } + + override fun destroy() { + thread!!.interrupt() + } + + override fun run() { + val r = BufferedReader(InputStreamReader(`in`)) + try { + while (true) { + val s = r.readLine() ?: return + out!!.write((s + "\n").toByteArray()) + out!!.write("#".toByteArray()) + out!!.flush() + if ("exit" == s) { + return + } + } + } catch (e: InterruptedIOException) { + // Ignore + } catch (e: Exception) { + e.printStackTrace() + } finally { + callback!!.onExit(0) + } + } +} |