summaryrefslogtreecommitdiffstats
path: root/ms/blueprintsprocessor/functions/netconf-executor
diff options
context:
space:
mode:
authorJessica Wagantall <jwagantall@linuxfoundation.org>2019-03-22 11:03:50 -0700
committerJessica Wagantall <jwagantall@linuxfoundation.org>2019-03-22 11:04:20 -0700
commitc3fe53367983309c659ac19b01b9b638ed2d011d (patch)
treede96787f8e661c0b7a109823517d0a7582ed87c8 /ms/blueprintsprocessor/functions/netconf-executor
parent995a36660fc855447d69a41a9a25b6b27fa57bf0 (diff)
parent669f7a860035fd944699a85ee8726c831efe299c (diff)
Migrate "ms/blueprintsprocess" from ccsdk/apps
Migrate code from ccsdk/apps ms/blueprintsprocess subfolder into ccsdk/cds Issue-ID: CIMAN-245 Signed-off-by: Jessica Wagantall <jwagantall@linuxfoundation.org>
Diffstat (limited to 'ms/blueprintsprocessor/functions/netconf-executor')
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/pom.xml54
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/ComponentNetconfExecutor.kt75
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/JsonParserService.kt24
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/NetconfComponentFunction.kt70
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/NetconfDevice.kt31
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/DeviceInfo.kt43
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfException.kt24
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfMessage.kt74
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfRpcService.kt142
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfSession.kt86
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfSessionListener.kt21
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/core/NetconfDeviceCommunicator.kt243
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/core/NetconfRpcServiceImpl.kt220
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/core/NetconfSessionImpl.kt287
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/utils/NetconfConstant.kt90
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/utils/NetconfMessageUtils.kt404
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/ComponentNetconfExecutorTest.kt79
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/NetconfSessionImplTest.kt111
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/core/NetconfRpcServiceImplTest.kt124
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/mocks/NetconfDeviceSimulator.kt62
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/mocks/NetconfSubsystemFactory.kt125
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/utils/RpcMessageUtilsTest.kt306
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/application-test.properties32
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/logback-test.xml35
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/payload/requests/sample-activate-request.json31
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/requests/running-config-input.json15
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/requests/sample-activate-request.json28
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/requests/sample-resourceresolution-request.json28
-rw-r--r--ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/response/get-config-123456.xml10
29 files changed, 2874 insertions, 0 deletions
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/pom.xml b/ms/blueprintsprocessor/functions/netconf-executor/pom.xml
new file mode 100644
index 000000000..2e6d77edc
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/pom.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright © 2017-2018 AT&T Intellectual Property.
+ ~
+ ~ 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.onap.ccsdk.apps.blueprintsprocessor</groupId>
+ <artifactId>functions</artifactId>
+ <version>0.4.2-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.onap.ccsdk.apps.blueprintsprocessor.functions</groupId>
+ <artifactId>netconf-executor</artifactId>
+ <name>Blueprints Processor Function - Netconf Executor</name>
+ <description>Blueprints Processor Function - Netconf Executor</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onap.ccsdk.apps.blueprintsprocessor.functions</groupId>
+ <artifactId>resource-resolution</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jettison</groupId>
+ <artifactId>jettison</artifactId>
+ <version>${jettison.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.jcraft</groupId>
+ <artifactId>jsch</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/ComponentNetconfExecutor.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/ComponentNetconfExecutor.kt
new file mode 100644
index 000000000..c2ccdafd1
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/ComponentNetconfExecutor.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright © 2017-2018 AT&T Intellectual Property.
+ *
+ * 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
+ *
+ * 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.apps.blueprintsprocessor.functions.netconf.executor
+
+import com.fasterxml.jackson.databind.node.ArrayNode
+import org.onap.ccsdk.apps.blueprintsprocessor.core.api.data.ExecutionServiceInput
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants
+import org.onap.ccsdk.apps.blueprintsprocessor.services.execution.AbstractComponentFunction
+import org.onap.ccsdk.apps.blueprintsprocessor.services.execution.ComponentFunctionScriptingService
+import org.onap.ccsdk.apps.controllerblueprints.core.getAsString
+import org.slf4j.LoggerFactory
+import org.springframework.beans.factory.config.ConfigurableBeanFactory
+import org.springframework.context.annotation.Scope
+import org.springframework.stereotype.Component
+
+@Component("component-netconf-executor")
+@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
+open class ComponentNetconfExecutor(private var componentFunctionScriptingService: ComponentFunctionScriptingService)
+ : AbstractComponentFunction() {
+
+ private val log = LoggerFactory.getLogger(ComponentNetconfExecutor::class.java)
+
+ companion object {
+ const val SCRIPT_TYPE = "script-type"
+ const val SCRIPT_CLASS_REFERENCE = "script-class-reference"
+ const val INSTANCE_DEPENDENCIES = "instance-dependencies"
+ }
+
+
+ lateinit var scriptComponent: NetconfComponentFunction
+
+ override fun process(executionRequest: ExecutionServiceInput) {
+
+ val scriptType = operationInputs.getAsString(SCRIPT_TYPE)
+ val scriptClassReference = operationInputs.getAsString(SCRIPT_CLASS_REFERENCE)
+ val instanceDependenciesNode = operationInputs.get(INSTANCE_DEPENDENCIES) as? ArrayNode
+
+ val scriptDependencies: MutableList<String> = arrayListOf()
+ scriptDependencies.add(ResourceResolutionConstants.SERVICE_RESOURCE_RESOLUTION)
+
+ instanceDependenciesNode?.forEach { instanceName ->
+ scriptDependencies.add(instanceName.textValue())
+ }
+
+ scriptComponent = componentFunctionScriptingService.scriptInstance<NetconfComponentFunction>(this, scriptType,
+ scriptClassReference, scriptDependencies)
+
+
+ checkNotNull(scriptComponent) { "failed to get netconf script component" }
+
+ scriptComponent.process(executionServiceInput)
+ }
+
+ override fun recover(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
+ scriptComponent.recover(runtimeException, executionRequest)
+ }
+
+
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/JsonParserService.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/JsonParserService.kt
new file mode 100644
index 000000000..111736633
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/JsonParserService.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2017-2018 AT&T Intellectual Property.
+ *
+ * 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.apps.blueprintsprocessor.functions.netconf.executor
+
+import org.springframework.stereotype.Service
+
+@Service("json-parser-service")
+class JsonParserService {
+
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/NetconfComponentFunction.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/NetconfComponentFunction.kt
new file mode 100644
index 000000000..fe8ba54dd
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/NetconfComponentFunction.kt
@@ -0,0 +1,70 @@
+/*
+ * 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
+ *
+ * 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.apps.blueprintsprocessor.functions.netconf.executor
+
+import com.fasterxml.jackson.databind.JsonNode
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.DeviceInfo
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.resource.resolution.ResourceResolutionService
+import org.onap.ccsdk.apps.blueprintsprocessor.services.execution.AbstractScriptComponentFunction
+import org.onap.ccsdk.apps.controllerblueprints.core.utils.JacksonUtils
+
+abstract class NetconfComponentFunction : AbstractScriptComponentFunction() {
+
+ open fun resourceResolutionService(): ResourceResolutionService =
+ functionDependencyInstanceAsType(ResourceResolutionConstants.SERVICE_RESOURCE_RESOLUTION)
+
+ // Called from python script
+ fun initializeNetconfConnection(requirementName: String): NetconfDevice {
+ val deviceInfo = deviceProperties(requirementName)
+ return NetconfDevice(deviceInfo)
+ }
+
+ fun generateMessage(artifactName: String): String {
+ return bluePrintRuntimeService.resolveNodeTemplateArtifact(nodeTemplateName, artifactName)
+ }
+
+ fun resolveFromDatabase(resolutionKey: String, artifactName: String): String {
+ return resourceResolutionService().resolveFromDatabase(bluePrintRuntimeService, artifactName, resolutionKey)
+ }
+
+ fun resolveAndGenerateMessage(artifactMapping: String, artifactTemplate: String): String {
+ return resourceResolutionService().resolveResources(bluePrintRuntimeService, nodeTemplateName,
+ artifactMapping, artifactTemplate)
+ }
+
+ fun resolveAndGenerateMessage(artifactPrefix: String): String {
+ return resourceResolutionService().resolveResources(bluePrintRuntimeService, nodeTemplateName,
+ artifactPrefix, mapOf())
+ }
+
+ private fun deviceProperties(requirementName: String): DeviceInfo {
+
+ val blueprintContext = bluePrintRuntimeService.bluePrintContext()
+
+ val requirement = blueprintContext.nodeTemplateRequirement(nodeTemplateName, requirementName)
+
+ val capabilityProperties = bluePrintRuntimeService.resolveNodeTemplateCapabilityProperties(requirement
+ .node!!, requirement.capability!!)
+
+ return deviceProperties(capabilityProperties)
+ }
+
+ private fun deviceProperties(capabilityProperty: MutableMap<String, JsonNode>): DeviceInfo {
+ return JacksonUtils.getInstanceFromMap(capabilityProperty, DeviceInfo::class.java)
+ }
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/NetconfDevice.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/NetconfDevice.kt
new file mode 100644
index 000000000..1c07c5e6c
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/NetconfDevice.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 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.apps.blueprintsprocessor.functions.netconf.executor
+
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.DeviceInfo
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.NetconfSession
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.core.NetconfRpcServiceImpl
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.core.NetconfSessionImpl
+
+data class NetconfDevice(val deviceInfo: DeviceInfo) {
+ val netconfRpcService = NetconfRpcServiceImpl(deviceInfo)
+ val netconfSession: NetconfSession
+
+ init {
+ netconfSession = NetconfSessionImpl(deviceInfo, netconfRpcService)
+ netconfRpcService.setNetconfSession(netconfSession)
+ }
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/DeviceInfo.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/DeviceInfo.kt
new file mode 100644
index 000000000..466e6b5ed
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/DeviceInfo.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2017-2018 AT&T Intellectual Property.
+ *
+ * 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.apps.blueprintsprocessor.functions.netconf.executor.api
+
+import com.fasterxml.jackson.annotation.JsonIgnore
+import com.fasterxml.jackson.annotation.JsonProperty
+
+class DeviceInfo {
+ @get:JsonProperty("login-account")
+ var username: String? = null
+ @get:JsonProperty("login-key")
+ var password: String? = null
+ @get:JsonProperty("target-ip-address")
+ var ipAddress: String? = null
+ @get:JsonProperty("port-number")
+ var port: Int = 0
+ @get:JsonProperty("connection-time-out")
+ var connectTimeout: Long = 5
+ @get:JsonIgnore
+ var source: String? = null
+ @get:JsonIgnore
+ var replyTimeout: Int = 5
+ @get:JsonIgnore
+ var idleTimeout: Int = 99999
+
+ override fun toString(): String {
+ return "$ipAddress:$port"
+ }
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfException.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfException.kt
new file mode 100644
index 000000000..d7642e75f
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfException.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2017-2019 AT&T, 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.apps.blueprintsprocessor.functions.netconf.executor.api
+
+class NetconfException : Exception {
+
+ constructor(cause: Throwable) : super(cause)
+ constructor(message: String) : super(message)
+ constructor(message: String, cause: Throwable) : super(message, cause)
+
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfMessage.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfMessage.kt
new file mode 100644
index 000000000..da7466143
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfMessage.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright © 2017-2019 AT&T, 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.apps.blueprintsprocessor.functions.netconf.executor.api
+
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.utils.RpcStatus
+
+class DeviceResponse {
+ var status: String? = null
+ var errorMessage: String? = null
+ var responseMessage: String? = null
+ var requestMessage: String? = null
+ private var subDeviceResponse: MutableMap<Any, Any>? = null
+
+ fun addSubDeviceResponse(key: String, subDeviceResponse: DeviceResponse) {
+ if (this.subDeviceResponse == null) {
+ this.subDeviceResponse = hashMapOf()
+ }
+ this.subDeviceResponse!![key] = subDeviceResponse
+ }
+
+ fun isSuccess(): Boolean {
+ if (this.status != RpcStatus.SUCCESS && !this.errorMessage.isNullOrEmpty()) {
+ return false
+ }
+ return true
+ }
+}
+
+
+/**
+ * Creates an event of a given type and for the specified subject and the current time.
+ *
+ * @param type event type
+ * @param payload message from the device
+ * @param messageId id of the message related to the event
+ * @param deviceInfo device of event
+ */
+class NetconfReceivedEvent
+ (private var type: Type, private var payload: String = "", private var messageId: String = "",
+ private var deviceInfo: DeviceInfo) {
+
+ enum class Type {
+ DEVICE_REPLY,
+ DEVICE_UNREGISTERED,
+ DEVICE_ERROR,
+ SESSION_CLOSED
+ }
+
+ fun getType(): Type {
+ return type
+ }
+
+ fun getMessagePayload(): String {
+ return payload
+ }
+
+ fun getMessageID(): String {
+ return messageId
+ }
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfRpcService.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfRpcService.kt
new file mode 100644
index 000000000..02c0a3426
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfRpcService.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright © 2017-2018 AT&T Intellectual Property.
+ *
+ * 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.apps.blueprintsprocessor.functions.netconf.executor.api
+
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.utils.ModifyAction
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.utils.NetconfDatastore
+
+interface NetconfRpcService {
+
+ /**
+ * Lock
+ *
+ * @param configTarget running or candidate, default candidate
+ * @return Device response
+ */
+ fun lock(configTarget: String = NetconfDatastore.CANDIDATE.datastore): DeviceResponse
+
+ /**
+ * Get-config
+ *
+ * @param filter filter content, default empty
+ * @param configTarget running or candidate, default running
+ * @return Device response
+ */
+ fun getConfig(filter: String = "", configTarget: String = NetconfDatastore.RUNNING.datastore): DeviceResponse
+
+ /**
+ * Delete config
+ *
+ * @param configTarget running or candidate, default candidate
+ * @return Device response
+ */
+ fun deleteConfig(configTarget: String = NetconfDatastore.CANDIDATE.datastore): DeviceResponse
+
+ /**
+ * Edit-config
+ *
+ * @param messageContent edit config content.
+ * @param configTarget running or candidate, default candidate
+ * @param editDefaultOperation, default set to none. Valid values: merge, replace, create, delete, none
+ * @return Device response
+ */
+ fun editConfig(messageContent: String, configTarget: String = NetconfDatastore.CANDIDATE.datastore,
+ editDefaultOperation: String = ModifyAction.NONE.action): DeviceResponse
+
+ /**
+ * Invoke custom RPC as provided as input.
+ *
+ * Some use cases might required one to directly invoke a device
+ * specific RPC. The RPC must be correctly formatted.
+ *
+ * Ex: in order to rollback last submitted configuration
+ * for JUNOS devices, such RPC can be use:
+ * <code>
+ * &lt;rpc>
+ * &lt;load-configuration rollback="1"/>
+ * &lt;/rpc>
+ * </code>
+ *
+ * @param rpc the rpc content.
+ */
+ fun invokeRpc(rpc: String): DeviceResponse
+
+ /**
+ * Validate
+ *
+ * @param configTarget running or candidate, default candidate
+ * @return Device response
+ */
+ fun validate(configTarget: String = NetconfDatastore.CANDIDATE.datastore): DeviceResponse
+
+ /**
+ * Commit
+ *
+ * @param confirmed Perform a confirmed <commit> operation. If flag set to true,
+ * then it is expected to have a follow-up <commit> operation to confirm the request
+ * @param confirmTimeout Timeout period for confirmed commit, in seconds.
+ * @param persist Make the confirmed commit survive a session termination, and
+ * set a token on the ongoing confirmed commit.
+ * @param persistId Used to issue a follow-up confirmed commit or a confirming
+ * commit from any session, with the token from the previous <commit> operation.
+ * If unspecified, the confirm timeout defaults to 600 seconds.
+ * @return Device response
+ */
+ fun commit(confirmed: Boolean = false, confirmTimeout: Int = 60, persist: String = "",
+ persistId: String = ""): DeviceResponse
+
+ /**
+ * Cancels an ongoing confirmed commit. If the <persist-id> parameter is not given,
+ * the <cancel-commit> operation MUST be issued on the same session that issued
+ * the confirmed commit.
+ *
+ * @param persistId Cancels a persistent confirmed commit. The value MUST be equal
+ * to the value given in the <persist> parameter to the <commit> operation.
+ * If the value does not match, the operation fails with an "invalid-value" error.
+ */
+ fun cancelCommit(persistId: String = ""): DeviceResponse
+
+ /**
+ * Unlock
+ *
+ * @param configTarget running or candidate, default candidate
+ * @return Device response
+ */
+ fun unLock(configTarget: String = NetconfDatastore.CANDIDATE.datastore): DeviceResponse
+
+ /**
+ * Discard config
+ *
+ * @return Device response
+ */
+ fun discardConfig(): DeviceResponse
+
+ /**
+ * Close session
+ *
+ * @param force force closeSession
+ * @return Device response
+ */
+ fun closeSession(force: Boolean): DeviceResponse
+
+ /**
+ * Executes an RPC request to the netconf server.
+ *
+ * @param request the XML containing the RPC request for the server.
+ * @return Device response
+ */
+ fun asyncRpc(request: String, messageId: String): DeviceResponse
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfSession.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfSession.kt
new file mode 100644
index 000000000..6a655d91f
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfSession.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright © 2017-2018 AT&T Intellectual Property.
+ *
+ * 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.apps.blueprintsprocessor.functions.netconf.executor.api
+
+import java.util.concurrent.CompletableFuture
+
+interface NetconfSession {
+
+ /**
+ * Establish netconf session
+ */
+ fun connect()
+
+
+ /**
+ * Disconnect netconf session
+ */
+ fun disconnect()
+
+ /**
+ * Reconnect netconf session
+ */
+ fun reconnect()
+
+ /**
+ * Executes an synchronous RPC request.
+ *
+ * @param request the XML request
+ * @param messageId message id of the request.
+ * @return Response
+ */
+ @Throws(NetconfException::class)
+ fun syncRpc(request: String, messageId: String): String
+
+ /**
+ * Executes an asynchronous RPC request.
+ *
+ * @param request the XML request
+ * @param messageId message id of the request.
+ * @return Response
+ */
+ @Throws(NetconfException::class)
+ fun asyncRpc(request: String, messageId: String): CompletableFuture<String>
+
+ /**
+ * Checks the state of the underlying SSH session and connection and if necessary it reestablishes
+ * it.
+ */
+ @Throws(NetconfException::class)
+ fun checkAndReestablish()
+
+ /**
+ * Get the device information for initialised session.
+ *
+ * @return DeviceInfo as device information
+ */
+ fun getDeviceInfo(): DeviceInfo
+
+ /**
+ * Gets the session ID of the Netconf session.
+ *
+ * @return Session ID as a string.
+ */
+ fun getSessionId(): String
+
+ /**
+ * Gets the capabilities of the remote Netconf device associated to this session.
+ *
+ * @return Network capabilities as strings in a Set.
+ */
+ fun getDeviceCapabilitiesSet(): Set<String>
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfSessionListener.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfSessionListener.kt
new file mode 100644
index 000000000..8854894fa
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/api/NetconfSessionListener.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright © 2017-2019 AT&T, 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.apps.blueprintsprocessor.functions.netconf.executor.api
+
+interface NetconfSessionListener {
+
+ fun notify(event: NetconfReceivedEvent)
+}
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/core/NetconfDeviceCommunicator.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/core/NetconfDeviceCommunicator.kt
new file mode 100644
index 000000000..694756195
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/core/NetconfDeviceCommunicator.kt
@@ -0,0 +1,243 @@
+/*
+ * Copyright © 2017-2019 AT&T, 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.apps.blueprintsprocessor.functions.netconf.executor.core
+
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.DeviceInfo
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.NetconfReceivedEvent
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.NetconfSessionListener
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.utils.NetconfMessageUtils
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.utils.RpcMessageUtils
+import org.slf4j.LoggerFactory
+import java.io.BufferedReader
+import java.io.IOException
+import java.io.InputStream
+import java.io.InputStreamReader
+import java.io.OutputStream
+import java.io.OutputStreamWriter
+import java.nio.charset.StandardCharsets
+import java.util.concurrent.CompletableFuture
+
+class NetconfDeviceCommunicator(private var inputStream: InputStream,
+ private var out: OutputStream,
+ private val deviceInfo: DeviceInfo,
+ private val sessionListener: NetconfSessionListener,
+ private var replies: MutableMap<String, CompletableFuture<String>>) : Thread() {
+
+ private val log = LoggerFactory.getLogger(NetconfDeviceCommunicator::class.java)
+ private var state = NetconfMessageState.NO_MATCHING_PATTERN
+
+ init {
+ start()
+ }
+
+ override fun run() {
+ var bufferReader: BufferedReader? = null
+ while (bufferReader == null) {
+ bufferReader = BufferedReader(InputStreamReader(inputStream, StandardCharsets.UTF_8))
+ }
+
+ try {
+ var socketClosed = false
+ val deviceReplyBuilder = StringBuilder()
+ while (!socketClosed) {
+ val cInt = bufferReader.read()
+ if (cInt == -1) {
+ log.error("$deviceInfo: Received cInt = -1")
+// bufferReader.close()
+ socketClosed = true
+// sessionListener.notify(NetconfReceivedEvent(
+// NetconfReceivedEvent.Type.SESSION_CLOSED,
+// deviceInfo = deviceInfo))
+ }
+ val c = cInt.toChar()
+ state = state.evaluateChar(c)
+ deviceReplyBuilder.append(c)
+ if (state === NetconfMessageState.END_PATTERN) {
+ var deviceReply = deviceReplyBuilder.toString()
+ if (deviceReply == RpcMessageUtils.END_PATTERN) {
+ socketClosed = true
+ bufferReader.close()
+ sessionListener.notify(NetconfReceivedEvent(
+ NetconfReceivedEvent.Type.DEVICE_UNREGISTERED,
+ deviceInfo = deviceInfo))
+ } else {
+ deviceReply = deviceReply.replace(RpcMessageUtils.END_PATTERN, "")
+ receivedMessage(deviceReply)
+ deviceReplyBuilder.setLength(0)
+ }
+ } else if (state === NetconfMessageState.END_CHUNKED_PATTERN) {
+ var deviceReply = deviceReplyBuilder.toString()
+ if (!NetconfMessageUtils.validateChunkedFraming(deviceReply)) {
+ log.debug("$deviceInfo: Received badly framed message $deviceReply")
+ socketClosed = true
+ sessionListener.notify(NetconfReceivedEvent(
+ NetconfReceivedEvent.Type.DEVICE_ERROR,
+ deviceInfo = deviceInfo))
+ } else {
+ deviceReply = deviceReply.replace(RpcMessageUtils.MSGLEN_REGEX_PATTERN.toRegex(), "")
+ deviceReply = deviceReply.replace(NetconfMessageUtils.CHUNKED_END_REGEX_PATTERN.toRegex(), "")
+ receivedMessage(deviceReply)
+ deviceReplyBuilder.setLength(0)
+ }
+ }
+ }
+
+ } catch (e: IOException) {
+ log.warn("$deviceInfo: Fail while reading from channel", e)
+ sessionListener.notify(NetconfReceivedEvent(
+ NetconfReceivedEvent.Type.DEVICE_ERROR,
+ deviceInfo = deviceInfo))
+ }
+
+ }
+
+ private enum class NetconfMessageState {
+ NO_MATCHING_PATTERN {
+ override fun evaluateChar(c: Char): NetconfMessageState {
+ return if (c == ']') {
+ FIRST_BRACKET
+ } else if (c == '\n') {
+ FIRST_LF
+ } else {
+ this
+ }
+ }
+ },
+ FIRST_BRACKET {
+ override fun evaluateChar(c: Char): NetconfMessageState {
+ return if (c == ']') {
+ SECOND_BRACKET
+ } else {
+ NO_MATCHING_PATTERN
+ }
+ }
+ },
+ SECOND_BRACKET {
+ override fun evaluateChar(c: Char): NetconfMessageState {
+ return if (c == '>') {
+ FIRST_BIGGER
+ } else {
+ NO_MATCHING_PATTERN
+ }
+ }
+ },
+ FIRST_BIGGER {
+ override fun evaluateChar(c: Char): NetconfMessageState {
+ return if (c == ']') {
+ THIRD_BRACKET
+ } else {
+ NO_MATCHING_PATTERN
+ }
+ }
+ },
+ THIRD_BRACKET {
+ override fun evaluateChar(c: Char): NetconfMessageState {
+ return if (c == ']') {
+ ENDING_BIGGER
+ } else {
+ NO_MATCHING_PATTERN
+ }
+ }
+ },
+ ENDING_BIGGER {
+ override fun evaluateChar(c: Char): NetconfMessageState {
+ return if (c == '>') {
+ END_PATTERN
+ } else {
+ NO_MATCHING_PATTERN
+ }
+ }
+ },
+ FIRST_LF {
+ override fun evaluateChar(c: Char): NetconfMessageState {
+ return if (c == '#') {
+ FIRST_HASH
+ } else if (c == ']') {
+ FIRST_BRACKET
+ } else if (c == '\n') {
+ this
+ } else {
+ NO_MATCHING_PATTERN
+ }
+ }
+ },
+ FIRST_HASH {
+ override fun evaluateChar(c: Char): NetconfMessageState {
+ return if (c == '#') {
+ SECOND_HASH
+ } else {
+ NO_MATCHING_PATTERN
+ }
+ }
+ },
+ SECOND_HASH {
+ override fun evaluateChar(c: Char): NetconfMessageState {
+ return if (c == '\n') {
+ END_CHUNKED_PATTERN
+ } else {
+ NO_MATCHING_PATTERN
+ }
+ }
+ },
+ END_CHUNKED_PATTERN {
+ override fun evaluateChar(c: Char): NetconfMessageState {
+ return NO_MATCHING_PATTERN
+ }
+ },
+ END_PATTERN {
+ override fun evaluateChar(c: Char): NetconfMessageState {
+ return NO_MATCHING_PATTERN
+ }
+ };
+
+ internal abstract fun evaluateChar(c: Char): NetconfMessageState
+ }
+
+ fun sendMessage(request: String, messageId: String): CompletableFuture<String> {
+ log.info("$deviceInfo: Sending message: \n $request")
+ val future = CompletableFuture<String>()
+ replies.put(messageId, future)
+ val outputStream = OutputStreamWriter(out, StandardCharsets.UTF_8)
+ synchronized(this) {
+ try {
+ outputStream.write(request)
+ outputStream.flush()
+ } catch (e: IOException) {
+ log.error("$deviceInfo: Failed to send message : \n $request", e)
+ future.completeExceptionally(e)
+ }
+
+ }
+ return future
+ }
+
+ private fun receivedMessage(deviceReply: String) {
+ if (deviceReply.contains(RpcMessageUtils.RPC_REPLY) || deviceReply.contains(RpcMessageUtils.RPC_ERROR)
+ || deviceReply.contains(RpcMessageUtils.HELLO)) {
+ log.info("$deviceInfo: Received message with messageId: {} \n $deviceReply",
+ NetconfMessageUtils.getMsgId(deviceReply))
+
+ } else {
+ log.error("$deviceInfo: Invalid message received: \n $deviceReply")
+ }
+ sessionListener.notify(NetconfReceivedEvent(
+ NetconfReceivedEvent.Type.DEVICE_REPLY,
+ deviceReply,
+ NetconfMessageUtils.getMsgId(deviceReply),
+ deviceInfo))
+ }
+}
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/core/NetconfRpcServiceImpl.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/core/NetconfRpcServiceImpl.kt
new file mode 100644
index 000000000..15fb3122c
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/core/NetconfRpcServiceImpl.kt
@@ -0,0 +1,220 @@
+/*
+ * Copyright © 2017-2019 AT&T, 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.apps.blueprintsprocessor.functions.netconf.executor.core
+
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.DeviceInfo
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.DeviceResponse
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.NetconfException
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.NetconfRpcService
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.NetconfSession
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.utils.NetconfMessageUtils
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.utils.RpcStatus
+import org.slf4j.LoggerFactory
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicInteger
+
+class NetconfRpcServiceImpl(private var deviceInfo: DeviceInfo) : NetconfRpcService {
+
+ private val log = LoggerFactory.getLogger(NetconfRpcService::class.java)
+
+ private var responseTimeout: Int = deviceInfo.replyTimeout
+
+ private lateinit var netconfSession: NetconfSession
+
+ private val messageIdInteger = AtomicInteger(1)
+
+ fun setNetconfSession(netconfSession: NetconfSession) {
+ this.netconfSession = netconfSession
+ }
+
+ override fun invokeRpc(rpc: String): DeviceResponse {
+ var output = DeviceResponse()
+ val messageId = messageIdInteger.getAndIncrement().toString()
+ log.info("$deviceInfo: invokeRpc: messageId($messageId)")
+ try {
+ output = asyncRpc(rpc, messageId)
+ } catch (e: Exception) {
+ output.status = RpcStatus.FAILURE
+ output.errorMessage = "$deviceInfo: failed in invokeRpc command $e.message"
+ }
+ return output
+ }
+
+ override fun getConfig(filter: String, configTarget: String): DeviceResponse {
+ var output = DeviceResponse()
+ val messageId = messageIdInteger.getAndIncrement().toString()
+ log.info("$deviceInfo: getConfig: messageId($messageId)")
+ try {
+ val message = NetconfMessageUtils.getConfig(messageId, configTarget, filter)
+ output = asyncRpc(message, messageId)
+ } catch (e: Exception) {
+ output.status = RpcStatus.FAILURE
+ output.errorMessage = "$deviceInfo: failed in get-config command $e.message"
+ }
+ return output
+ }
+
+ override fun deleteConfig(configTarget: String): DeviceResponse {
+ var output = DeviceResponse()
+ val messageId = messageIdInteger.getAndIncrement().toString()
+ log.info("$deviceInfo: deleteConfig: messageId($messageId)")
+ try {
+ val deleteConfigMessage = NetconfMessageUtils.deleteConfig(messageId, configTarget)
+ output.requestMessage = deleteConfigMessage
+ output = asyncRpc(deleteConfigMessage, messageId)
+ } catch (e: Exception) {
+ output.status = RpcStatus.FAILURE
+ output.errorMessage = "$deviceInfo: failed in delete config command $e.message"
+ }
+ return output
+ }
+
+ override fun lock(configTarget: String): DeviceResponse {
+ var output = DeviceResponse()
+ val messageId = messageIdInteger.getAndIncrement().toString()
+ log.info("$deviceInfo: lock: messageId($messageId)")
+ try {
+ val lockMessage = NetconfMessageUtils.lock(messageId, configTarget)
+ output.requestMessage = lockMessage
+ output = asyncRpc(lockMessage, messageId)
+ } catch (e: Exception) {
+ output.status = RpcStatus.FAILURE
+ output.errorMessage = "$deviceInfo: failed in lock command $e.message"
+ }
+
+ return output
+ }
+
+ override fun unLock(configTarget: String): DeviceResponse {
+ var output = DeviceResponse()
+ val messageId = messageIdInteger.getAndIncrement().toString()
+ log.info("$deviceInfo: unLock: messageId($messageId)")
+ try {
+ val unlockMessage = NetconfMessageUtils.unlock(messageId, configTarget)
+ output.requestMessage = unlockMessage
+ output = asyncRpc(unlockMessage, messageId)
+ } catch (e: Exception) {
+ output.status = RpcStatus.FAILURE
+ output.errorMessage = "$deviceInfo: failed in lock command $e.message"
+ }
+ return output
+ }
+
+ override fun commit(confirmed: Boolean, confirmTimeout: Int, persist: String, persistId: String): DeviceResponse {
+ var output = DeviceResponse()
+ val messageId = messageIdInteger.getAndIncrement().toString()
+ log.info("$deviceInfo: commit: messageId($messageId)")
+ try {
+ val messageContent = NetconfMessageUtils.commit(messageId, confirmed, confirmTimeout, persist, persistId)
+ output = asyncRpc(messageContent, messageId)
+ } catch (e: Exception) {
+ output.status = RpcStatus.FAILURE
+ output.errorMessage = "$deviceInfo: failed in commit command $e.message"
+ }
+ return output
+ }
+
+ override fun cancelCommit(persistId: String): DeviceResponse {
+ var output = DeviceResponse()
+ val messageId = messageIdInteger.getAndIncrement().toString()
+ log.info("$deviceInfo: cancelCommit: messageId($messageId)")
+ try {
+ val messageContent = NetconfMessageUtils.cancelCommit(messageId, persistId)
+ output = asyncRpc(messageContent, messageId)
+ } catch (e: Exception) {
+ output.status = RpcStatus.FAILURE
+ output.errorMessage = "$deviceInfo: failed in cancelCommit command $e.message"
+ }
+ return output
+ }
+
+ override fun discardConfig(): DeviceResponse {
+ var output = DeviceResponse()
+ val messageId = messageIdInteger.getAndIncrement().toString()
+ log.info("$deviceInfo: discard: messageId($messageId)")
+ try {
+ val discardChangesMessage = NetconfMessageUtils.discardChanges(messageId)
+ output.requestMessage = discardChangesMessage
+ output = asyncRpc(discardChangesMessage, messageId)
+ } catch (e: Exception) {
+ output.status = RpcStatus.FAILURE
+ output.errorMessage = "$deviceInfo: failed in discard changes command " + e.message
+ }
+ return output
+ }
+
+ override fun editConfig(messageContent: String, configTarget: String,
+ editDefaultOperation: String): DeviceResponse {
+ var response = DeviceResponse()
+ val messageId = messageIdInteger.getAndIncrement().toString()
+ log.info("$deviceInfo: editConfig: messageId($messageId)")
+ try {
+ val editMessage =
+ NetconfMessageUtils.editConfig(messageId, configTarget, editDefaultOperation, messageContent)
+ response.requestMessage = editMessage
+ response = asyncRpc(editMessage, messageId)
+ } catch (e: Exception) {
+ response.status = RpcStatus.FAILURE
+ response.errorMessage = e.message
+ }
+ return response
+ }
+
+ override fun validate(configTarget: String): DeviceResponse {
+ var output = DeviceResponse()
+ val messageId = messageIdInteger.getAndIncrement().toString()
+ try {
+ val validateMessage = NetconfMessageUtils.validate(messageId, configTarget)
+ output.requestMessage = validateMessage
+ output = asyncRpc(validateMessage, messageId)
+ } catch (e: Exception) {
+ output.status = RpcStatus.FAILURE
+ output.errorMessage = "$deviceInfo: failed in validate command " + e.message
+ }
+ return output
+ }
+
+ override fun closeSession(force: Boolean): DeviceResponse {
+ var output = DeviceResponse()
+ val messageId = messageIdInteger.getAndIncrement().toString()
+ log.info("$deviceInfo: closeSession: messageId($messageId)")
+ try {
+ val messageContent = NetconfMessageUtils.closeSession(messageId, force)
+ output = asyncRpc(messageContent, messageId)
+ } catch (e: Exception) {
+ output.status = RpcStatus.FAILURE
+ output.errorMessage = "$deviceInfo: failed in closeSession command " + e.message
+ }
+ return output
+ }
+
+ @Throws(NetconfException::class)
+ override fun asyncRpc(request: String, messageId: String): DeviceResponse {
+ val response = DeviceResponse()
+ log.info("$deviceInfo: send asyncRpc with messageId($messageId)")
+ response.requestMessage = request
+
+ val rpcResponse = netconfSession.asyncRpc(request, messageId).get(responseTimeout.toLong(), TimeUnit.SECONDS)
+ if (!NetconfMessageUtils.checkReply(rpcResponse)) {
+ throw NetconfException(rpcResponse)
+ }
+ response.responseMessage = rpcResponse
+ response.status = RpcStatus.SUCCESS
+ response.errorMessage = null
+ return response
+ }
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/core/NetconfSessionImpl.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/core/NetconfSessionImpl.kt
new file mode 100644
index 000000000..2dd73ef18
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/core/NetconfSessionImpl.kt
@@ -0,0 +1,287 @@
+/*
+ * Copyright © 2017-2019 AT&T, 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.apps.blueprintsprocessor.functions.netconf.executor.core
+
+import com.google.common.collect.ImmutableList
+import com.google.common.collect.ImmutableSet
+import org.apache.sshd.client.SshClient
+import org.apache.sshd.client.channel.ChannelSubsystem
+import org.apache.sshd.client.channel.ClientChannel
+import org.apache.sshd.client.session.ClientSession
+import org.apache.sshd.client.session.ClientSessionImpl
+import org.apache.sshd.common.FactoryManager
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.DeviceInfo
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.NetconfException
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.NetconfReceivedEvent
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.NetconfRpcService
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.NetconfSession
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.NetconfSessionListener
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.utils.NetconfMessageUtils
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.utils.RpcMessageUtils
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.utils.RpcStatus
+import org.slf4j.LoggerFactory
+import java.io.IOException
+import java.util.*
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.ExecutionException
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
+import java.util.concurrent.atomic.AtomicReference
+
+class NetconfSessionImpl(private val deviceInfo: DeviceInfo, private val rpcService: NetconfRpcService) :
+ NetconfSession {
+
+ private val log = LoggerFactory.getLogger(NetconfSessionImpl::class.java)
+
+ private val errorReplies: MutableList<String> = Collections.synchronizedList(listOf())
+ private val replies: MutableMap<String, CompletableFuture<String>> = ConcurrentHashMap()
+ private val deviceCapabilities = setOf<String>()
+
+ private var connectionTimeout: Long = 0
+ private var replyTimeout: Int = 0
+ private var idleTimeout: Int = 0
+ private var sessionId: String? = null
+
+ private lateinit var session: ClientSession
+ private lateinit var client: SshClient
+ private lateinit var channel: ClientChannel
+ private lateinit var streamHandler: NetconfDeviceCommunicator
+
+ private var capabilities =
+ ImmutableList.of(RpcMessageUtils.NETCONF_10_CAPABILITY, RpcMessageUtils.NETCONF_11_CAPABILITY)
+
+ override fun connect() {
+ try {
+ log.info("$deviceInfo: Connecting to Netconf Device with timeouts C:${deviceInfo.connectTimeout}, " +
+ "R:${deviceInfo.replyTimeout}, I:${deviceInfo.idleTimeout}")
+ startConnection()
+ log.info("$deviceInfo: Connected to Netconf Device")
+ } catch (e: NetconfException) {
+ log.error("$deviceInfo: Netconf Device Connection Failed. ${e.message}")
+ throw NetconfException(e)
+ }
+ }
+
+ override fun disconnect() {
+ if (rpcService.closeSession(false).status.equals(
+ RpcStatus.FAILURE, true)) {
+ rpcService.closeSession(true)
+ }
+
+ session.close()
+ // Closes the socket which should interrupt the streamHandler
+ channel.close()
+ client.close()
+ }
+
+ override fun reconnect() {
+ disconnect()
+ connect()
+ }
+
+ override fun syncRpc(request: String, messageId: String): String {
+ val formattedRequest = NetconfMessageUtils.formatRPCRequest(request, messageId, deviceCapabilities)
+
+ checkAndReestablish()
+
+ try {
+ return streamHandler.sendMessage(formattedRequest, messageId).get(replyTimeout.toLong(), TimeUnit.SECONDS)
+// replies.remove(messageId)
+ } catch (e: InterruptedException) {
+ Thread.currentThread().interrupt()
+ throw NetconfException("$deviceInfo: Interrupted while waiting for reply for request: $formattedRequest", e)
+ } catch (e: TimeoutException) {
+ throw NetconfException("$deviceInfo: Timed out while waiting for reply for request $formattedRequest after $replyTimeout sec.",
+ e)
+ } catch (e: ExecutionException) {
+ log.warn("$deviceInfo: Closing session($sessionId) due to unexpected Error", e)
+ try {
+ session.close()
+ // Closes the socket which should interrupt the streamHandler
+ channel.close()
+ client.close()
+ } catch (ioe: IOException) {
+ log.warn("$deviceInfo: Error closing session($sessionId) for host($deviceInfo)", ioe)
+ }
+
+// NetconfReceivedEvent(NetconfReceivedEvent.Type.SESSION_CLOSED, "",
+// "Closed due to unexpected error " + e.cause, "-1", deviceInfo)
+ errorReplies.clear() // move to cleanUp()?
+ replies.clear()
+
+ throw NetconfException("$deviceInfo: Closing session $sessionId for request $formattedRequest", e)
+ }
+ }
+
+ override fun asyncRpc(request: String, messageId: String): CompletableFuture<String> {
+ val formattedRequest = NetconfMessageUtils.formatRPCRequest(request, messageId, deviceCapabilities)
+
+ checkAndReestablish()
+
+ return streamHandler.sendMessage(formattedRequest, messageId).handleAsync { reply, t ->
+ if (t != null) {
+ throw NetconfException(messageId, t)
+ }
+ reply
+ }
+ }
+
+ override fun checkAndReestablish() {
+ try {
+ if (client.isClosed) {
+ log.info("Trying to restart the whole SSH connection with {}", deviceInfo)
+ replies.clear()
+ startConnection()
+ } else if (session.isClosed) {
+ log.info("Trying to restart the session with {}", deviceInfo)
+ replies.clear()
+ startSession()
+ } else if (channel.isClosed) {
+ log.info("Trying to reopen the channel with {}", deviceInfo)
+ replies.clear()
+ openChannel()
+ } else {
+ return
+ }
+ } catch (e: IOException) {
+ log.error("Can't reopen connection for device {}", e.message)
+ throw NetconfException(String.format("Cannot re-open the connection with device (%s)", deviceInfo), e)
+ } catch (e: IllegalStateException) {
+ log.error("Can't reopen connection for device {}", e.message)
+ throw NetconfException(String.format("Cannot re-open the connection with device (%s)", deviceInfo), e)
+ }
+
+ }
+
+ override fun getDeviceInfo(): DeviceInfo {
+ return deviceInfo
+ }
+
+ override fun getSessionId(): String {
+ return this.sessionId!!
+ }
+
+ override fun getDeviceCapabilitiesSet(): Set<String> {
+ return Collections.unmodifiableSet(deviceCapabilities)
+ }
+
+ private fun startConnection() {
+ connectionTimeout = deviceInfo.connectTimeout
+ replyTimeout = deviceInfo.replyTimeout
+ idleTimeout = deviceInfo.idleTimeout
+ try {
+ startClient()
+ } catch (e: Exception) {
+ throw NetconfException("$deviceInfo: Failed to establish SSH session", e)
+ }
+
+ }
+
+ private fun startClient() {
+ client = SshClient.setUpDefaultClient()
+ client.properties.putIfAbsent(FactoryManager.IDLE_TIMEOUT, TimeUnit.SECONDS.toMillis(idleTimeout.toLong()))
+ client.properties.putIfAbsent(FactoryManager.NIO2_READ_TIMEOUT, TimeUnit.SECONDS.toMillis(idleTimeout + 15L))
+ client.keyPairProvider = SimpleGeneratorHostKeyProvider()
+ client.start()
+
+ startSession()
+ }
+
+ private fun startSession() {
+ log.info("$deviceInfo: Starting SSH session")
+ val connectFuture = client.connect(deviceInfo.username, deviceInfo.ipAddress, deviceInfo.port)
+ .verify(connectionTimeout, TimeUnit.SECONDS)
+ session = connectFuture.session
+ log.info("$deviceInfo: SSH session created")
+
+ authSession()
+ }
+
+ private fun authSession() {
+ session.addPasswordIdentity(deviceInfo.password)
+ session.auth().verify(connectionTimeout, TimeUnit.SECONDS)
+ val event = session.waitFor(ImmutableSet.of(ClientSession.ClientSessionEvent.WAIT_AUTH,
+ ClientSession.ClientSessionEvent.CLOSED, ClientSession.ClientSessionEvent.AUTHED), 0)
+ if (!event.contains(ClientSession.ClientSessionEvent.AUTHED)) {
+ throw NetconfException("$deviceInfo: Failed to authenticate session.")
+ }
+ log.info("$deviceInfo: SSH session authenticated")
+
+ openChannel()
+ }
+
+ private fun openChannel() {
+ channel = session.createSubsystemChannel("netconf")
+ val channelFuture = channel.open()
+ if (channelFuture.await(connectionTimeout, TimeUnit.SECONDS) && channelFuture.isOpened) {
+ log.info("$deviceInfo: SSH NETCONF subsystem channel opened")
+ setupHandler()
+ } else {
+ throw NetconfException("$deviceInfo: Failed to open SSH subsystem channel")
+ }
+ }
+
+ private fun setupHandler() {
+ val sessionListener: NetconfSessionListener = NetconfSessionListenerImpl()
+ streamHandler = NetconfDeviceCommunicator(channel.invertedOut, channel.invertedIn, deviceInfo,
+ sessionListener, replies)
+
+ exchangeHelloMessage()
+ }
+
+ private fun exchangeHelloMessage() {
+ sessionId = "-1"
+ val messageId = "-1"
+
+ val serverHelloResponse = syncRpc(NetconfMessageUtils.createHelloString(capabilities), messageId)
+ val sessionIDMatcher = NetconfMessageUtils.SESSION_ID_REGEX_PATTERN.matcher(serverHelloResponse)
+
+ if (sessionIDMatcher.find()) {
+ sessionId = sessionIDMatcher.group(1)
+ } else {
+ throw NetconfException("$deviceInfo: Missing sessionId in server hello message: $serverHelloResponse")
+ }
+
+ val capabilityMatcher = NetconfMessageUtils.CAPABILITY_REGEX_PATTERN.matcher(serverHelloResponse)
+ while (capabilityMatcher.find()) {
+ deviceCapabilities.plus(capabilityMatcher.group(1))
+ }
+ }
+
+ inner class NetconfSessionListenerImpl : NetconfSessionListener {
+ override fun notify(event: NetconfReceivedEvent) {
+ val messageId = event.getMessageID()
+
+ when (event.getType()) {
+ NetconfReceivedEvent.Type.DEVICE_UNREGISTERED -> disconnect()
+ NetconfReceivedEvent.Type.DEVICE_ERROR -> errorReplies.add(event.getMessagePayload())
+ NetconfReceivedEvent.Type.DEVICE_REPLY -> replies[messageId]?.complete(event.getMessagePayload())
+ NetconfReceivedEvent.Type.SESSION_CLOSED -> disconnect()
+ }
+ }
+ }
+
+ fun sessionstatus(state:String): Boolean{
+ return when (state){
+ "Close" -> channel.isClosed
+ "Open" -> channel.isOpen
+ else -> false
+ }
+ }
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/utils/NetconfConstant.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/utils/NetconfConstant.kt
new file mode 100644
index 000000000..3b3b0c025
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/utils/NetconfConstant.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright © 2017-2019 AT&T, 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.apps.blueprintsprocessor.functions.netconf.executor.utils
+
+enum class NetconfDatastore(val datastore: String) {
+ RUNNING("running"),
+ CANDIDATE("candidate");
+}
+
+enum class ModifyAction(val action: String) {
+ MERGE("merge"),
+ REPLACE("replace"),
+ NONE("none")
+}
+
+object RpcStatus {
+ const val SUCCESS = "success"
+ const val FAILURE = "failure"
+}
+
+object RpcMessageUtils {
+ const val OPEN = "<"
+ const val CLOSE = ">"
+ const val EQUAL = "="
+
+ const val HASH = "#"
+ const val HASH_CHAR = '#'
+
+ const val LF_CHAR = '\n'
+ const val NEW_LINE = "\n"
+
+ const val QUOTE = "\""
+ const val QUOTE_SPACE = "\" "
+
+ const val TAG_CLOSE = "/>"
+ const val END_OF_RPC_OPEN_TAG = "\">"
+ const val END_PATTERN = "]]>]]>"
+
+ const val HELLO = "hello"
+ const val RPC_REPLY = "rpc-reply"
+ const val RPC_ERROR = "rpc-error"
+
+ const val RPC_OPEN = "<rpc "
+ const val RPC_CLOSE = "</rpc>"
+ const val WITH_DEFAULT_OPEN = "<with-defaults "
+ const val WITH_DEFAULT_CLOSE = "</with-defaults>"
+ const val DEFAULT_OPERATION_OPEN = "<default-operation>"
+ const val DEFAULT_OPERATION_CLOSE = "</default-operation>"
+ const val SUBTREE_FILTER_OPEN = "<filter type=\"subtree\">"
+ const val SUBTREE_FILTER_CLOSE = "</filter>"
+ const val TARGET_OPEN = "<target>"
+ const val TARGET_CLOSE = "</target>"
+ const val SOURCE_OPEN = "<source>"
+ const val SOURCE_CLOSE = "</source>"
+ const val CONFIG_OPEN = "<config xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ const val CONFIG_CLOSE = "</config>"
+ const val MSGLEN_REGEX_PATTERN = "\n#\\d+\n"
+
+ const val NUMBER_BETWEEN_QUOTES_MATCHER = "\"+([0-9]+)+\""
+
+ const val XML_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ const val NETCONF_BASE_NAMESPACE = "xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\""
+ const val NETCONF_WITH_DEFAULTS_NAMESPACE = "xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\""
+ const val SUBSCRIPTION_SUBTREE_FILTER_OPEN =
+ "<filter xmlns:base10=\"urn:ietf:params:xml:ns:netconf:base:1.0\" base10:type=\"subtree\">"
+
+ const val INTERLEAVE_CAPABILITY_STRING = "urn:ietf:params:netconf:capability:interleave:1.0"
+
+ const val CAPABILITY_REGEX = "capability>\\s*(.*?)\\s*capability>"
+
+ const val SESSION_ID_REGEX = "session-id>\\s*(.*?)\\s*session-id>"
+
+ const val MESSAGE_ID_STRING = "message-id"
+
+ const val NETCONF_10_CAPABILITY = "urn:ietf:params:netconf:base:1.0"
+ const val NETCONF_11_CAPABILITY = "urn:ietf:params:netconf:base:1.1"
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/utils/NetconfMessageUtils.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/utils/NetconfMessageUtils.kt
new file mode 100644
index 000000000..4de3860c5
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/utils/NetconfMessageUtils.kt
@@ -0,0 +1,404 @@
+/*
+ * Copyright © 2017-2019 AT&T, 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.apps.blueprintsprocessor.functions.netconf.executor.utils
+
+import org.apache.commons.lang3.StringUtils
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.NetconfException
+import org.slf4j.LoggerFactory
+import org.xml.sax.InputSource
+import java.io.StringReader
+import java.nio.charset.StandardCharsets
+import java.util.regex.MatchResult
+import java.util.regex.Pattern
+import javax.xml.XMLConstants
+import javax.xml.parsers.DocumentBuilderFactory
+import kotlin.text.Charsets.UTF_8
+
+
+class NetconfMessageUtils {
+
+ companion object {
+ val log = LoggerFactory.getLogger(NetconfMessageUtils::class.java)
+
+ const val NEW_LINE = "\n"
+ const val CHUNKED_END_REGEX_PATTERN = "\n##\n"
+
+ val CAPABILITY_REGEX_PATTERN: Pattern = Pattern.compile(RpcMessageUtils.CAPABILITY_REGEX)
+ val SESSION_ID_REGEX_PATTERN: Pattern = Pattern.compile(RpcMessageUtils.SESSION_ID_REGEX)
+
+ private val CHUNKED_FRAMING_PATTERN: Pattern =
+ Pattern.compile("(\\n#([1-9][0-9]*)\\n(.+))+\\n##\\n", Pattern.DOTALL)
+ private val CHUNKED_SIZE_PATTERN: Pattern = Pattern.compile("\\n#([1-9][0-9]*)\\n")
+ private val MSG_ID_STRING_PATTERN = Pattern.compile("${RpcMessageUtils.MESSAGE_ID_STRING}=\"(.*?)\"")
+
+ fun getConfig(messageId: String, configType: String, filterContent: String?): String {
+ val request = StringBuilder()
+
+ request.append("<get-config>").append(NEW_LINE)
+ request.append(RpcMessageUtils.SOURCE_OPEN).append(NEW_LINE)
+ request.append(RpcMessageUtils.OPEN).append(configType).append(RpcMessageUtils.TAG_CLOSE)
+ .append(NEW_LINE)
+ request.append(RpcMessageUtils.SOURCE_CLOSE).append(NEW_LINE)
+
+ if (!filterContent.isNullOrEmpty()) {
+ request.append(RpcMessageUtils.SUBTREE_FILTER_OPEN).append(NEW_LINE)
+ request.append(filterContent).append(NEW_LINE)
+ request.append(RpcMessageUtils.SUBTREE_FILTER_CLOSE).append(NEW_LINE)
+ }
+ request.append("</get-config>").append(NEW_LINE)
+
+ return doWrappedRpc(messageId, request.toString())
+ }
+
+ fun doWrappedRpc(messageId: String, request: String): String {
+ val rpc = StringBuilder(RpcMessageUtils.XML_HEADER).append(NEW_LINE)
+ rpc.append(RpcMessageUtils.RPC_OPEN)
+ rpc.append(RpcMessageUtils.MESSAGE_ID_STRING).append(RpcMessageUtils.EQUAL)
+ rpc.append(RpcMessageUtils.QUOTE).append(messageId).append(RpcMessageUtils.QUOTE_SPACE)
+ rpc.append(RpcMessageUtils.NETCONF_BASE_NAMESPACE).append(RpcMessageUtils.CLOSE)
+ .append(NEW_LINE)
+ rpc.append(request)
+ rpc.append(RpcMessageUtils.RPC_CLOSE)
+ // rpc.append(NEW_LINE).append(END_PATTERN);
+
+ return rpc.toString()
+ }
+
+ fun editConfig(messageId: String, configType: String, defaultOperation: String?,
+ newConfiguration: String): String {
+
+ val request = StringBuilder()
+
+ request.append("<edit-config>").append(NEW_LINE)
+ request.append(RpcMessageUtils.TARGET_OPEN).append(NEW_LINE)
+ request.append(RpcMessageUtils.OPEN).append(configType).append(RpcMessageUtils.TAG_CLOSE)
+ .append(NEW_LINE)
+ request.append(RpcMessageUtils.TARGET_CLOSE).append(NEW_LINE)
+
+ if (defaultOperation != null) {
+ request.append(RpcMessageUtils.DEFAULT_OPERATION_OPEN).append(defaultOperation)
+ .append(RpcMessageUtils.DEFAULT_OPERATION_CLOSE)
+ request.append(NEW_LINE)
+ }
+
+ request.append(RpcMessageUtils.CONFIG_OPEN).append(NEW_LINE)
+ request.append(newConfiguration.trim { it <= ' ' }).append(NEW_LINE)
+ request.append(RpcMessageUtils.CONFIG_CLOSE).append(NEW_LINE)
+ request.append("</edit-config>").append(NEW_LINE)
+
+ return doWrappedRpc(messageId, request.toString())
+ }
+
+ fun validate(messageId: String, configType: String): String {
+ val request = StringBuilder()
+
+ request.append("<validate>").append(NEW_LINE)
+ request.append(RpcMessageUtils.SOURCE_OPEN).append(NEW_LINE)
+ request.append(RpcMessageUtils.OPEN).append(configType).append(RpcMessageUtils.TAG_CLOSE)
+ .append(NEW_LINE)
+ request.append(RpcMessageUtils.SOURCE_CLOSE).append(NEW_LINE)
+ request.append("</validate>").append(NEW_LINE)
+
+ return doWrappedRpc(messageId, request.toString())
+ }
+
+ fun commit(messageId: String, confirmed: Boolean, confirmTimeout: Int, persist: String,
+ persistId: String): String {
+
+ if (!persist.isEmpty() && !persistId.isEmpty()) {
+ throw NetconfException("Can't proceed <commit> with both persist($persist) and " +
+ "persistId($persistId) specified. Only one should be specified.")
+ }
+ if (confirmed && !persistId.isEmpty()) {
+ throw NetconfException("Can't proceed <commit> with both confirmed flag and " +
+ "persistId($persistId) specified. Only one should be specified.")
+ }
+
+ val request = StringBuilder()
+ request.append("<commit>").append(NEW_LINE)
+ if (confirmed) {
+ request.append("<confirmed/>")
+ request.append("<confirm-timeout>$confirmTimeout</confirm-timeout>")
+ if (!persist.isEmpty()) {
+ request.append("<persist>$persist</persist>")
+ }
+ }
+ if (!persistId.isEmpty()) {
+ request.append("<persist-id>$persistId</persist-id>")
+ }
+ request.append("</commit>").append(NEW_LINE)
+
+ return doWrappedRpc(messageId, request.toString())
+ }
+
+ fun cancelCommit(messageId: String, persistId: String): String {
+ val request = StringBuilder()
+ request.append("<cancel-commit>").append(NEW_LINE)
+ if (!persistId.isEmpty()) {
+ request.append("<persist-id>$persistId</persist-id>")
+ }
+ request.append("</cancel-commit>").append(NEW_LINE)
+
+ return doWrappedRpc(messageId, request.toString())
+ }
+
+ fun unlock(messageId: String, configType: String): String {
+ val request = StringBuilder()
+
+ request.append("<unlock>").append(NEW_LINE)
+ request.append(RpcMessageUtils.TARGET_OPEN).append(NEW_LINE)
+ request.append(RpcMessageUtils.OPEN).append(configType).append(RpcMessageUtils.TAG_CLOSE)
+ .append(NEW_LINE)
+ request.append(RpcMessageUtils.TARGET_CLOSE).append(NEW_LINE)
+ request.append("</unlock>").append(NEW_LINE)
+
+ return doWrappedRpc(messageId, request.toString())
+ }
+
+ @Throws(NetconfException::class)
+ fun deleteConfig(messageId: String, configType: String): String {
+ if (configType == NetconfDatastore.RUNNING.datastore) {
+ log.warn("Target configuration for delete operation can't be \"running\" {}", configType)
+ throw NetconfException("Target configuration for delete operation can't be running")
+ }
+
+ val request = StringBuilder()
+
+ request.append("<delete-config>").append(NEW_LINE)
+ request.append(RpcMessageUtils.TARGET_OPEN).append(NEW_LINE)
+ request.append(RpcMessageUtils.OPEN).append(configType)
+ .append(RpcMessageUtils.TAG_CLOSE)
+ .append(NEW_LINE)
+ request.append(RpcMessageUtils.TARGET_CLOSE).append(NEW_LINE)
+ request.append("</delete-config>").append(NEW_LINE)
+
+ return doWrappedRpc(messageId, request.toString())
+ }
+
+ fun discardChanges(messageId: String): String {
+ val request = StringBuilder()
+ request.append("<discard-changes/>").append(NEW_LINE)
+ return doWrappedRpc(messageId, request.toString())
+ }
+
+ fun lock(messageId: String, configType: String): String {
+ val request = StringBuilder()
+
+ request.append("<lock>").append(NEW_LINE)
+ request.append(RpcMessageUtils.TARGET_OPEN).append(NEW_LINE)
+ request.append(RpcMessageUtils.OPEN).append(configType).append(RpcMessageUtils.TAG_CLOSE)
+ .append(NEW_LINE)
+ request.append(RpcMessageUtils.TARGET_CLOSE).append(NEW_LINE)
+ request.append("</lock>").append(NEW_LINE)
+
+ return doWrappedRpc(messageId, request.toString())
+ }
+
+ fun closeSession(messageId: String, force: Boolean): String {
+ val request = StringBuilder()
+
+ if (force) {
+ request.append("<kill-session/>").append(NEW_LINE)
+ } else {
+ request.append("<close-session/>").append(NEW_LINE)
+ }
+
+ return doWrappedRpc(messageId, request.toString())
+ }
+
+ fun validateRPCXML(rpcRequest: String): Boolean {
+ try {
+ if (StringUtils.isBlank(rpcRequest)) {
+ return false
+ }
+ val dbf = DocumentBuilderFactory.newInstance()
+ dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
+ dbf.setFeature("http://xml.org/sax/features/external-general-entities", false)
+ dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false)
+ dbf.newDocumentBuilder()
+ .parse(InputSource(StringReader(rpcRequest.replace(RpcMessageUtils.END_PATTERN, ""))))
+ return true
+ } catch (e: Exception) {
+ return false
+ }
+
+ }
+
+ fun getMsgId(message: String): String {
+ val matcher = MSG_ID_STRING_PATTERN.matcher(message)
+ if (matcher.find()) {
+ return matcher.group(1)
+ }
+ return if (message.contains(RpcMessageUtils.HELLO)) {
+ (-1).toString()
+ } else ""
+ }
+
+ fun validateChunkedFraming(reply: String): Boolean {
+ val matcher = CHUNKED_FRAMING_PATTERN.matcher(reply)
+ if (!matcher.matches()) {
+ log.debug("Error Reply: {}", reply)
+ return false
+ }
+ val chunkM = CHUNKED_SIZE_PATTERN.matcher(reply)
+ val chunks = ArrayList<MatchResult>()
+ var chunkdataStr = ""
+ while (chunkM.find()) {
+ chunks.add(chunkM.toMatchResult())
+ // extract chunk-data (and later) in bytes
+ val bytes = Integer.parseInt(chunkM.group(1))
+ val chunkdata = reply.substring(chunkM.end()).toByteArray(StandardCharsets.UTF_8)
+ if (bytes > chunkdata.size) {
+ log.debug("Error Reply - wrong chunk size {}", reply)
+ return false
+ }
+ // convert (only) chunk-data part into String
+ chunkdataStr = String(chunkdata, 0, bytes, StandardCharsets.UTF_8)
+ // skip chunk-data part from next match
+ chunkM.region(chunkM.end() + chunkdataStr.length, reply.length)
+ }
+ if (!CHUNKED_END_REGEX_PATTERN.equals(reply.substring(chunks[chunks.size - 1].end() + chunkdataStr.length))) {
+ log.debug("Error Reply: {}", reply)
+ return false
+ }
+ return true
+ }
+
+ fun createHelloString(capabilities: List<String>): String {
+ val helloMessage = StringBuilder()
+ helloMessage.append(RpcMessageUtils.XML_HEADER).append(NEW_LINE)
+ helloMessage.append("<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">").append(NEW_LINE)
+ helloMessage.append(" <capabilities>").append(NEW_LINE)
+ if (capabilities.isNotEmpty()) {
+ capabilities.forEach { cap ->
+ helloMessage.append(" <capability>").append(cap).append("</capability>").append(NEW_LINE)
+ }
+ }
+ helloMessage.append(" </capabilities>").append(NEW_LINE)
+ helloMessage.append("</hello>").append(NEW_LINE)
+ helloMessage.append(RpcMessageUtils.END_PATTERN)
+ return helloMessage.toString()
+ }
+
+ fun formatRPCRequest(request: String, messageId: String, deviceCapabilities: Set<String>): String {
+ var request = request
+ request = NetconfMessageUtils.formatNetconfMessage(deviceCapabilities, request)
+ request = NetconfMessageUtils.formatXmlHeader(request)
+ request = NetconfMessageUtils.formatRequestMessageId(request, messageId)
+
+ return request
+ }
+
+ /**
+ * Validate and format netconf message. - NC1.0 if no EOM sequence present on `message`,
+ * append. - NC1.1 chunk-encode given message unless it already is chunk encoded
+ *
+ * @param deviceCapabilities Set containing Device Capabilities
+ * @param message to format
+ * @return formated message
+ */
+ fun formatNetconfMessage(deviceCapabilities: Set<String>, message: String): String {
+ var message = message
+ if (deviceCapabilities.contains(RpcMessageUtils.NETCONF_11_CAPABILITY)) {
+ message = formatChunkedMessage(message)
+ } else if (!message.endsWith(RpcMessageUtils.END_PATTERN)) {
+ message = message + NEW_LINE + RpcMessageUtils.END_PATTERN
+ }
+ return message
+ }
+
+ /**
+ * Validate and format message according to chunked framing mechanism.
+ *
+ * @param message to format
+ * @return formated message
+ */
+ fun formatChunkedMessage(message: String): String {
+ var message = message
+ if (message.endsWith(RpcMessageUtils.END_PATTERN)) {
+ // message given had Netconf 1.0 EOM pattern -> remove
+ message = message.substring(0, message.length - RpcMessageUtils.END_PATTERN.length)
+ }
+ if (!message.startsWith(RpcMessageUtils.NEW_LINE + RpcMessageUtils.HASH)) {
+ // chunk encode message
+ message =
+ (RpcMessageUtils.NEW_LINE + RpcMessageUtils.HASH + message.toByteArray(UTF_8).size + RpcMessageUtils.NEW_LINE + message + RpcMessageUtils.NEW_LINE + RpcMessageUtils.HASH + RpcMessageUtils.HASH
+ + RpcMessageUtils.NEW_LINE)
+ }
+ return message
+ }
+
+ /**
+ * Ensures xml start directive/declaration appears in the `request`.
+ *
+ * @param request RPC request message
+ * @return XML RPC message
+ */
+ fun formatXmlHeader(request: String): String {
+ var request = request
+ if (!request.contains(RpcMessageUtils.XML_HEADER)) {
+ if (request.startsWith(RpcMessageUtils.NEW_LINE + RpcMessageUtils.HASH)) {
+ request =
+ request.split("<".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0] + RpcMessageUtils.XML_HEADER + request.substring(
+ request.split("<".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0].length)
+ } else {
+ request = RpcMessageUtils.XML_HEADER + "\n" + request
+ }
+ }
+ return request
+ }
+
+ fun formatRequestMessageId(request: String, messageId: String): String {
+ var request = request
+ if (request.contains(RpcMessageUtils.MESSAGE_ID_STRING)) {
+ request =
+ request.replaceFirst((RpcMessageUtils.MESSAGE_ID_STRING + RpcMessageUtils.EQUAL + RpcMessageUtils.NUMBER_BETWEEN_QUOTES_MATCHER).toRegex(),
+ RpcMessageUtils.MESSAGE_ID_STRING + RpcMessageUtils.EQUAL + RpcMessageUtils.QUOTE + messageId + RpcMessageUtils.QUOTE)
+ } else if (!request.contains(RpcMessageUtils.MESSAGE_ID_STRING) && !request.contains(
+ RpcMessageUtils.HELLO)) {
+ request = request.replaceFirst(RpcMessageUtils.END_OF_RPC_OPEN_TAG.toRegex(),
+ RpcMessageUtils.QUOTE_SPACE + RpcMessageUtils.MESSAGE_ID_STRING + RpcMessageUtils.EQUAL + RpcMessageUtils.QUOTE + messageId + RpcMessageUtils.QUOTE + ">")
+ }
+ return updateRequestLength(request)
+ }
+
+ fun updateRequestLength(request: String): String {
+ if (request.contains(NEW_LINE + RpcMessageUtils.HASH + RpcMessageUtils.HASH + NEW_LINE)) {
+ val oldLen =
+ Integer.parseInt(request.split(RpcMessageUtils.HASH.toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray()[1].split(
+ NEW_LINE.toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray()[0])
+ val rpcWithEnding = request.substring(request.indexOf('<'))
+ val firstBlock =
+ request.split(RpcMessageUtils.MSGLEN_REGEX_PATTERN.toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray()[1].split(
+ (NEW_LINE + RpcMessageUtils.HASH + RpcMessageUtils.HASH + NEW_LINE).toRegex()).dropLastWhile(
+ { it.isEmpty() }).toTypedArray()[0]
+ var newLen = 0
+ newLen = firstBlock.toByteArray(UTF_8).size
+ if (oldLen != newLen) {
+ return NEW_LINE + RpcMessageUtils.HASH + newLen + NEW_LINE + rpcWithEnding
+ }
+ }
+ return request
+ }
+
+ fun checkReply(reply: String?): Boolean {
+ return if (reply != null) {
+ !reply.contains("rpc-error>") || reply.contains("warning") || reply.contains("ok/>")
+ } else false
+ }
+ }
+
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/ComponentNetconfExecutorTest.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/ComponentNetconfExecutorTest.kt
new file mode 100644
index 000000000..e2b901998
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/ComponentNetconfExecutorTest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2017-2018 AT&T Intellectual Property.
+ *
+ * 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
+ *
+ * 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.apps.blueprintsprocessor.functions.netconf.executor
+
+import com.fasterxml.jackson.databind.JsonNode
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.onap.ccsdk.apps.blueprintsprocessor.core.api.data.ExecutionServiceInput
+import org.onap.ccsdk.apps.controllerblueprints.core.BluePrintConstants
+import org.onap.ccsdk.apps.controllerblueprints.core.asJsonNode
+import org.onap.ccsdk.apps.controllerblueprints.core.putJsonElement
+import org.onap.ccsdk.apps.controllerblueprints.core.utils.BluePrintMetadataUtils
+import org.onap.ccsdk.apps.controllerblueprints.core.utils.JacksonUtils
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration
+import org.springframework.context.annotation.ComponentScan
+import org.springframework.test.annotation.DirtiesContext
+import org.springframework.test.context.TestPropertySource
+import org.springframework.test.context.junit4.SpringRunner
+
+@RunWith(SpringRunner::class)
+@EnableAutoConfiguration
+@ComponentScan(basePackages = ["org.onap.ccsdk.apps.blueprintsprocessor", "org.onap.ccsdk.apps.controllerblueprints"])
+@DirtiesContext
+@TestPropertySource(properties =
+["blueprints.processor.functions.python.executor.modulePaths=./../../../../components/scripts/python/ccsdk_netconf,./../../../../components/scripts/python/ccsdk_blueprints",
+ "blueprints.processor.functions.python.executor.executionPath=./../../../../components/scripts/python/ccsdk_netconf"],
+ locations = ["classpath:application-test.properties"])
+class ComponentNetconfExecutorTest {
+
+ @Autowired
+ lateinit var componentNetconfExecutor: ComponentNetconfExecutor
+
+
+ @Test
+ fun testComponentNetconfExecutor() {
+
+ val executionServiceInput = JacksonUtils.readValueFromClassPathFile("requests/sample-activate-request.json",
+ ExecutionServiceInput::class.java)!!
+
+ val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime("1234",
+ "./../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration")
+
+ val executionContext = bluePrintRuntimeService.getExecutionContext()
+
+
+ componentNetconfExecutor.bluePrintRuntimeService = bluePrintRuntimeService
+
+ //TODO("Set Attribute properties")
+ val stepMetaData: MutableMap<String, JsonNode> = hashMapOf()
+ stepMetaData.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_NODE_TEMPLATE, "activate-netconf")
+ stepMetaData.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_INTERFACE, "ComponentNetconfExecutor")
+ stepMetaData.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_OPERATION, "process")
+ // Set Step Inputs in Blueprint Runtime Service
+ bluePrintRuntimeService.put("activate-netconf-step-inputs", stepMetaData.asJsonNode())
+
+ componentNetconfExecutor.bluePrintRuntimeService = bluePrintRuntimeService
+ componentNetconfExecutor.stepName = "activate-netconf"
+ componentNetconfExecutor.apply(executionServiceInput)
+
+ }
+}
+
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/NetconfSessionImplTest.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/NetconfSessionImplTest.kt
new file mode 100644
index 000000000..e7a514347
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/NetconfSessionImplTest.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright © 2017-2018 AT&T Intellectual Property.
+ *
+ * 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.apps.blueprintsprocessor.functions.netconf.executor
+
+import org.apache.sshd.client.channel.ChannelSubsystem
+import org.apache.sshd.client.session.ClientSessionImpl
+import org.junit.After
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.DeviceInfo
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.core.NetconfRpcServiceImpl
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.core.NetconfSessionImpl
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.mocks.NetconfDeviceSimulator
+import java.util.concurrent.atomic.AtomicReference
+import kotlin.script.experimental.api.asSuccess
+
+class NetconfSessionImplTest {
+
+ private var device: NetconfDeviceSimulator? = null
+ private var deviceInfo: DeviceInfo? = null
+
+ @Before
+ fun before() {
+ deviceInfo = DeviceInfo().apply {
+ username = "username"
+ password = "password"
+ ipAddress = "localhost"
+ port = 2224
+ connectTimeout = 10
+ }
+
+ device = NetconfDeviceSimulator(deviceInfo!!.port)
+ device!!.start()
+ }
+
+ @After
+ fun after() {
+ device!!.stop()
+ }
+
+ @Throws(Exception::class)
+ fun testNetconfSession() {
+ val netconfSession = NetconfSessionImpl(deviceInfo!!, NetconfRpcServiceImpl(DeviceInfo()))
+
+ Assert.assertNotNull(netconfSession.getSessionId())
+ Assert.assertEquals("localhost:2224", netconfSession.getDeviceInfo().toString())
+
+ netconfSession.checkAndReestablish()
+
+ Assert.assertNotNull(netconfSession.getSessionId())
+ Assert.assertEquals("localhost:2224", netconfSession.getDeviceInfo().toString())
+
+ Assert.assertTrue(!netconfSession.getDeviceCapabilitiesSet().isEmpty())
+ }
+
+ @Test
+ fun testNetconfSessionconnect() {
+ val netconfSession = NetconfSessionImpl(deviceInfo!!, NetconfRpcServiceImpl(deviceInfo!!))
+ netconfSession.connect()
+ Assert.assertTrue(netconfSession.sessionstatus("Open"))
+ }
+
+ @Test
+ fun testNetconfSessionreconnect() {
+ val netconfSession = NetconfSessionImpl(deviceInfo!!, NetconfRpcServiceImpl(deviceInfo!!))
+ netconfSession.connect()
+ netconfSession.reconnect()
+ Assert.assertTrue(netconfSession.sessionstatus("Open"))
+
+ }
+ @Test
+ fun testNetconfSessiondisconnect() {
+ val netconfSession = NetconfSessionImpl(deviceInfo!!, NetconfRpcServiceImpl(deviceInfo!!))
+ netconfSession.connect()
+ netconfSession.disconnect()
+ Assert.assertTrue(netconfSession.sessionstatus("Close"))
+
+ }
+ @Test
+ fun testNetconfSessioncheckAndReestablish() {
+ val netconfSession = NetconfSessionImpl(deviceInfo!!, NetconfRpcServiceImpl(deviceInfo!!))
+ netconfSession.connect()
+ netconfSession.checkAndReestablish()
+ Assert.assertTrue(netconfSession.sessionstatus("Open"))
+
+
+ }
+ @Test
+ fun testNetconfSessionconnecgetDeviceInfo() {
+ val netconfSession = NetconfSessionImpl(deviceInfo!!, NetconfRpcServiceImpl(deviceInfo!!))
+ netconfSession.connect()
+ Assert.assertNotNull(netconfSession.getDeviceInfo())
+ Assert.assertFalse(!netconfSession.getDeviceCapabilitiesSet().isEmpty())
+ }
+
+
+}
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/core/NetconfRpcServiceImplTest.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/core/NetconfRpcServiceImplTest.kt
new file mode 100644
index 000000000..8f1f71501
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/core/NetconfRpcServiceImplTest.kt
@@ -0,0 +1,124 @@
+package org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.core
+
+import org.junit.After
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+
+import org.junit.Assert.*
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.DeviceInfo
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.mocks.NetconfDeviceSimulator
+
+class NetconfRpcServiceImplTest {
+
+ private var device: NetconfDeviceSimulator? = null
+ private var deviceInfo: DeviceInfo? = null
+
+ @Before
+ fun before() {
+ deviceInfo = DeviceInfo().apply {
+ username = "username"
+ password = "password"
+ ipAddress = "localhost"
+ port = 2224
+ connectTimeout = 10
+ }
+
+ device = NetconfDeviceSimulator(deviceInfo!!.port)
+ device!!.start()
+ }
+
+ @After
+ fun after() {
+ device!!.stop()
+ }
+
+ @Test
+ fun setNetconfSession() {
+
+ }
+
+ @Test
+ fun getConfig() {
+
+ val netconfRpcServiceImpl = NetconfRpcServiceImpl(deviceInfo!!)
+ val netconfSession = NetconfSessionImpl(deviceInfo!!, netconfRpcServiceImpl)
+ netconfRpcServiceImpl.setNetconfSession(netconfSession)
+ netconfSession.connect()
+ Assert.assertTrue(netconfRpcServiceImpl.getConfig("filter","target").status.equals("failure"))
+ }
+
+
+ @Test
+ fun deleteConfig() {
+
+ val netconfRpcServiceImpl = NetconfRpcServiceImpl(deviceInfo!!)
+ val netconfSession = NetconfSessionImpl(deviceInfo!!, netconfRpcServiceImpl)
+ netconfRpcServiceImpl.setNetconfSession(netconfSession)
+ netconfSession.connect()
+ Assert.assertTrue(netconfRpcServiceImpl.deleteConfig("target").status.equals("failure"))
+ }
+
+ @Test
+ fun lock() {
+ val netconfRpcServiceImpl = NetconfRpcServiceImpl(deviceInfo!!)
+ val netconfSession = NetconfSessionImpl(deviceInfo!!, netconfRpcServiceImpl)
+ netconfRpcServiceImpl.setNetconfSession(netconfSession)
+ netconfSession.connect()
+ Assert.assertTrue(netconfRpcServiceImpl.lock("target").status.equals("failure"))
+ }
+
+ @Test
+ fun unLock() {
+ val netconfRpcServiceImpl = NetconfRpcServiceImpl(deviceInfo!!)
+ val netconfSession = NetconfSessionImpl(deviceInfo!!, netconfRpcServiceImpl)
+ netconfRpcServiceImpl.setNetconfSession(netconfSession)
+ netconfSession.connect()
+ Assert.assertTrue(netconfRpcServiceImpl.unLock("target").status.equals("failure"))
+ }
+
+ @Test
+ fun commit() {
+ val netconfRpcServiceImpl = NetconfRpcServiceImpl(deviceInfo!!)
+ val netconfSession = NetconfSessionImpl(deviceInfo!!, netconfRpcServiceImpl)
+ netconfRpcServiceImpl.setNetconfSession(netconfSession)
+ netconfSession.connect()
+ Assert.assertTrue(netconfRpcServiceImpl.commit(true,60,"persist","1").status.equals("failure"))
+
+ }
+
+ @Test
+ fun cancelCommit() {
+ val netconfSession = NetconfSessionImpl(deviceInfo!!, NetconfRpcServiceImpl(DeviceInfo()))
+ val netconfRpcServiceImpl = NetconfRpcServiceImpl(DeviceInfo())
+ netconfRpcServiceImpl.setNetconfSession(netconfSession)
+ netconfSession.connect()
+
+ Assert.assertNotNull(netconfRpcServiceImpl.cancelCommit("1"))
+ }
+
+ @Test
+ fun discardConfig() {
+ val netconfRpcServiceImpl = NetconfRpcServiceImpl(deviceInfo!!)
+ val netconfSession = NetconfSessionImpl(deviceInfo!!, netconfRpcServiceImpl)
+ netconfRpcServiceImpl.setNetconfSession(netconfSession)
+ netconfSession.connect()
+ Assert.assertTrue(netconfRpcServiceImpl.discardConfig().status.equals("failure"))
+
+ }
+
+ @Test
+ fun editConfig() {
+ }
+
+ @Test
+ fun validate() {
+ val netconfRpcServiceImpl = NetconfRpcServiceImpl(deviceInfo!!)
+ val netconfSession = NetconfSessionImpl(deviceInfo!!, netconfRpcServiceImpl)
+ netconfRpcServiceImpl.setNetconfSession(netconfSession)
+ netconfSession.connect()
+ Assert.assertTrue(netconfRpcServiceImpl.validate("target").status.equals("failure"))
+
+ }
+
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/mocks/NetconfDeviceSimulator.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/mocks/NetconfDeviceSimulator.kt
new file mode 100644
index 000000000..a2a3946db
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/mocks/NetconfDeviceSimulator.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2017-2018 AT&T Intellectual Property.
+ *
+ * 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.apps.blueprintsprocessor.functions.netconf.executor.mocks
+
+
+import org.apache.sshd.common.NamedFactory
+import org.apache.sshd.server.Command
+import org.apache.sshd.server.SshServer
+import org.apache.sshd.server.auth.UserAuth
+import org.apache.sshd.server.auth.UserAuthNoneFactory
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.utils.NetconfSubsystemFactory
+import java.util.*
+
+
+class NetconfDeviceSimulator(private val port: Int) {
+
+ private var sshd: SshServer? = null
+
+ fun start() {
+ sshd = SshServer.setUpDefaultServer()
+ sshd!!.port = port
+ sshd!!.keyPairProvider = SimpleGeneratorHostKeyProvider()
+
+ val userAuthFactories = ArrayList<NamedFactory<UserAuth>>()
+ userAuthFactories.add(UserAuthNoneFactory())
+ sshd!!.userAuthFactories = userAuthFactories
+
+ val namedFactoryList = ArrayList<NamedFactory<Command>>()
+ namedFactoryList.add(NetconfSubsystemFactory())
+ sshd!!.subsystemFactories = namedFactoryList
+
+ try {
+ sshd!!.start()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+
+ }
+
+ fun stop() {
+ try {
+ sshd!!.stop(true)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+
+ }
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/mocks/NetconfSubsystemFactory.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/mocks/NetconfSubsystemFactory.kt
new file mode 100644
index 000000000..7eaef030b
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/mocks/NetconfSubsystemFactory.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright © 2017-2018 AT&T Intellectual Property.
+ *
+ * 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.apps.blueprintsprocessor.functions.netconf.executor.utils
+
+
+import java.io.IOException
+import java.io.InputStream
+import java.io.OutputStream
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.ExitCallback;
+
+
+class NetconfSubsystemFactory : NamedFactory<Command> {
+
+ private val END_CHAR_SEQUENCE = "]]>]]>"
+
+ override fun create(): Command {
+ return NetconfSubsystem()
+ }
+
+ override fun getName(): String {
+ return "netconf"
+ }
+
+ /**
+ * Simple implementation of netconf reading 1 request, sending a 'hello' response and quitting
+ */
+ inner class NetconfSubsystem : Command {
+ private var input: InputStream? = null
+ private var out: OutputStream? = null
+ private var clientThread: Thread? = null
+ private var r: Int = 0
+
+ @Throws(IOException::class)
+ override fun start(env: Environment) {
+ clientThread = Thread(object : Runnable {
+
+ override fun run() {
+ try {
+ val message = StringBuilder()
+ while (true) {
+ process(createHelloString())
+ r = input!!.read()
+ if (r == -1) {
+ break
+ } else {
+ val c = r.toChar()
+ message.append(c)
+ val messageString = message.toString()
+ if (messageString.endsWith(END_CHAR_SEQUENCE)) {
+ println("Detected end message:\n$messageString")
+ process(createHelloString())
+ message.setLength(0)
+ break
+ }
+ }
+ }
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+
+ }
+
+ @Throws(IOException::class)
+ private fun process(xmlMessage: String) {
+ println("Sending message:\n$xmlMessage")
+ out!!.write(xmlMessage.toByteArray(charset("UTF-8")))
+ out!!.write((END_CHAR_SEQUENCE + "\n").toByteArray(charset("UTF-8")))
+ out!!.flush()
+ }
+
+ private fun createHelloString(): String {
+ val sessionId = "" + (Math.random() * Integer.MAX_VALUE).toInt()
+ return ("<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ + "<capabilities>\n<capability>urn:ietf:params:netconf:base:1.0</capability>\n"
+ + "<capability>urn:ietf:params:netconf:base:1.1</capability>\n</capabilities>\n"
+ + "<session-id>" + sessionId + "</session-id>\n</hello>")
+ }
+ })
+
+ clientThread!!.start()
+ }
+
+ @Throws(Exception::class)
+ override fun destroy() {
+ try {
+ clientThread!!.join(2000)
+ } catch (e: InterruptedException) {
+ // log.warn("Error joining Client thread" + e.getMessage());
+ }
+
+ clientThread!!.interrupt()
+ }
+
+ override fun setInputStream(input: InputStream) {
+ this.input = input
+ }
+
+ override fun setOutputStream(out: OutputStream) {
+ this.out = out
+ }
+
+ override fun setErrorStream(err: OutputStream) {}
+
+ override fun setExitCallback(callback: ExitCallback) {}
+
+
+
+ }
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/utils/RpcMessageUtilsTest.kt b/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/utils/RpcMessageUtilsTest.kt
new file mode 100644
index 000000000..8a60d8097
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/netconf/executor/utils/RpcMessageUtilsTest.kt
@@ -0,0 +1,306 @@
+/*
+ * Copyright © 2017-2018 AT&T Intellectual Property.
+ *
+ * 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.apps.blueprintsprocessor.functions.netconf.executor.utils
+
+import org.junit.Assert
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.onap.ccsdk.apps.blueprintsprocessor.functions.netconf.executor.api.NetconfException
+import kotlin.test.fail
+
+class RpcMessageUtilsTest {
+
+ @Test
+ fun getConfig() {
+ val checkString = ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<rpc message-id=\"Test-Message-ID\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ + "<get-config><source><candidate/></source><filter type=\"subtree\">Test-Filter-Content</filter>"
+ + "</get-config></rpc>")
+
+ val messageId = "Test-Message-ID"
+ val configType = NetconfDatastore.CANDIDATE.datastore
+ val filterContent = "Test-Filter-Content"
+
+ val result =
+ NetconfMessageUtils.getConfig(messageId, configType, filterContent).replace("[\n\r\t]".toRegex(), "")
+
+ assertTrue(NetconfMessageUtils.validateRPCXML(result))
+ Assert.assertEquals(checkString, result)
+ }
+
+
+ @Test
+ fun editConfig() {
+ val checkString = ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<rpc message-id=\"Test-Message-ID\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ + "<edit-config><target><candidate/></target><default-operation>Test-Default-Operation</default-operation>"
+ + "<config xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">Test-Filter-Content</config></edit-config></rpc>")
+
+ val messageId = "Test-Message-ID"
+ val configType = NetconfDatastore.CANDIDATE.datastore
+ val filterContent = "Test-Filter-Content"
+ val defaultOperation = "Test-Default-Operation"
+
+ val result =
+ NetconfMessageUtils.editConfig(messageId, configType, defaultOperation, filterContent).replace("[\n\r\t]".toRegex(), "")
+
+ assertTrue(NetconfMessageUtils.validateRPCXML(result))
+ Assert.assertEquals(checkString, result)
+ }
+
+ @Test
+ fun validate() {
+ val checkString = ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<rpc message-id=\"Test-Message-ID\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ + "<validate><source><candidate/></source></validate></rpc>")
+
+ val messageId = "Test-Message-ID"
+ val configType = NetconfDatastore.CANDIDATE.datastore
+
+ val result = NetconfMessageUtils.validate(messageId, configType).replace("[\n\r\t]".toRegex(), "")
+
+ assertTrue(NetconfMessageUtils.validateRPCXML(result))
+ Assert.assertEquals(checkString, result)
+ }
+
+ @Test
+ fun cancelCommit() {
+ val checkString =
+ ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
+ "<rpc message-id=\"Test-Message-ID\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +
+ "<cancel-commit>" +
+ "<persist-id>1234</persist-id>" +
+ "</cancel-commit></rpc>")
+
+ val messageId = "Test-Message-ID"
+
+ val cancelCommitPersistId =
+ NetconfMessageUtils.cancelCommit(messageId, "1234").replace("[\n\r\t]".toRegex(), "")
+
+ assertTrue(NetconfMessageUtils.validateRPCXML(cancelCommitPersistId))
+ Assert.assertEquals(checkString, cancelCommitPersistId)
+ }
+
+ @Test
+ fun cancelCommitNoPersistId() {
+ val checkString =
+ ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
+ "<rpc message-id=\"Test-Message-ID\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +
+ "<cancel-commit>" +
+ "</cancel-commit></rpc>")
+
+ val messageId = "Test-Message-ID"
+
+ val cancelCommitNoPersistId = NetconfMessageUtils.cancelCommit(messageId, "").replace("[\n\r\t]".toRegex(), "")
+
+ assertTrue(NetconfMessageUtils.validateRPCXML(cancelCommitNoPersistId))
+ Assert.assertEquals(checkString, cancelCommitNoPersistId)
+ }
+
+ @Test
+ fun commit() {
+ val checkString = ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<rpc message-id=\"Test-Message-ID\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ + "<commit></commit></rpc>")
+
+ val messageId = "Test-Message-ID"
+
+ val commit = NetconfMessageUtils.commit(messageId, false, 0, "", "").replace("[\n\r\t]".toRegex(), "")
+
+ val commitWithPersistButNotConfirmed =
+ NetconfMessageUtils.commit(messageId, false, 0, "1234", "").replace("[\n\r\t]".toRegex(), "")
+
+ assertTrue(NetconfMessageUtils.validateRPCXML(commit))
+ Assert.assertEquals(checkString, commit)
+ Assert.assertEquals(checkString, commitWithPersistButNotConfirmed)
+
+ }
+
+ @Test
+ fun commitPersistId() {
+ val checkString =
+ ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
+ "<rpc message-id=\"Test-Message-ID\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +
+ "<commit>" +
+ "<persist-id>1234</persist-id>" +
+ "</commit></rpc>")
+
+ val messageId = "Test-Message-ID"
+
+ val result = NetconfMessageUtils.commit(messageId, false, 30, "", "1234").replace("[\n\r\t]".toRegex(), "")
+ assertTrue(NetconfMessageUtils.validateRPCXML(result))
+ Assert.assertEquals(checkString, result)
+
+ try {
+ NetconfMessageUtils.commit(messageId, true, 30, "", "1234").replace("[\n\r\t]".toRegex(), "")
+ } catch (e: NetconfException) {
+ Assert.assertEquals("Can't proceed <commit> with both confirmed flag and persistId(1234) specified. Only one should be specified.",
+ e.message)
+ return
+ }
+
+ fail()
+ }
+
+ @Test
+ fun commitPersist() {
+ val checkString =
+ ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
+ "<rpc message-id=\"Test-Message-ID\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +
+ "<commit>" +
+ "<confirmed/>" +
+ "<confirm-timeout>30</confirm-timeout>" +
+ "<persist>1234</persist>" +
+ "</commit></rpc>")
+
+ val messageId = "Test-Message-ID"
+
+ val result = NetconfMessageUtils.commit(messageId, true, 30, "1234", "").replace("[\n\r\t]".toRegex(), "")
+
+ assertTrue(NetconfMessageUtils.validateRPCXML(result))
+ Assert.assertEquals(checkString, result)
+
+ try {
+ NetconfMessageUtils.commit(messageId, false, 30, "1234", "1234").replace("[\n\r\t]".toRegex(), "")
+ } catch (e: NetconfException) {
+ Assert.assertEquals("Can't proceed <commit> with both persist(1234) and persistId(1234) specified. Only one should be specified.",
+ e.message)
+ return
+ }
+ fail()
+ }
+
+ @Test
+ fun unlock() {
+ val checkString = ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<rpc message-id=\"Test-Message-ID\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ + "<unlock><target><candidate/></target></unlock></rpc>")
+
+ val messageId = "Test-Message-ID"
+ val configType = NetconfDatastore.CANDIDATE.datastore
+
+ val result = NetconfMessageUtils.unlock(messageId, configType).replace("[\n\r\t]".toRegex(), "")
+
+ assertTrue(NetconfMessageUtils.validateRPCXML(result))
+ Assert.assertEquals(checkString, result)
+ }
+
+ @Test
+ fun deleteConfig() {
+ val checkString = ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<rpc message-id=\"Test-Message-ID\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ + "<delete-config><target><candidate/></target></delete-config></rpc>")
+
+ val messageId = "Test-Message-ID"
+ val netconfTargetConfig = NetconfDatastore.CANDIDATE.datastore
+
+ val result = NetconfMessageUtils.deleteConfig(messageId, netconfTargetConfig).replace("[\n\r\t]".toRegex(), "")
+
+ assertTrue(NetconfMessageUtils.validateRPCXML(result))
+ Assert.assertEquals(checkString, result)
+ }
+
+ @Test
+ fun discardChanges() {
+ val checkString = ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<rpc message-id=\"Test-Message-ID\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ + "<discard-changes/></rpc>")
+
+ val messageId = "Test-Message-ID"
+
+ val result = NetconfMessageUtils.discardChanges(messageId).replace("[\n\r\t]".toRegex(), "")
+
+ assertTrue(NetconfMessageUtils.validateRPCXML(result))
+ Assert.assertEquals(checkString, result)
+ }
+
+ @Test
+ fun lock() {
+ val checkString = ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<rpc message-id=\"Test-Message-ID\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ + "<lock><target><candidate/></target></lock></rpc>")
+
+ val messageId = "Test-Message-ID"
+ val configType = NetconfDatastore.CANDIDATE.datastore
+ val result = NetconfMessageUtils.lock(messageId, configType).replace("[\n\r\t]".toRegex(), "")
+
+ assertTrue(NetconfMessageUtils.validateRPCXML(result))
+ Assert.assertEquals(checkString, result)
+ }
+
+ @Test
+ fun getMsgId() {
+ val checkString = ("testmessage")
+
+ var messageId = "message-id=\"testmessage\""
+ var result = NetconfMessageUtils.getMsgId(messageId).replace("[\n\r\t]".toRegex(), "")
+ Assert.assertEquals(checkString, result)
+
+ messageId = "message-id=\"hello\""
+ result = NetconfMessageUtils.getMsgId(messageId).replace("[\n\r\t]".toRegex(), "")
+ Assert.assertEquals("hello", result)
+
+ messageId = "message-id"
+ result = NetconfMessageUtils.getMsgId(messageId).replace("[\n\r\t]".toRegex(), "")
+ Assert.assertEquals("", result)
+ }
+
+ @Test
+ fun createHelloString() {
+ val checkString = ("<?xml version=\"1.0\" encoding=\"UTF-8\"?><hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> "
+ +"<capabilities> <capability>hi</capability> <capability>hello</capability> </capabilities></hello>]]>]]>")
+
+ val capability = listOf<String>("hi", "hello")
+
+ val result = NetconfMessageUtils.createHelloString(capability).replace("[\n\r\t]".toRegex(), "")
+ Assert.assertEquals(checkString, result)
+ }
+
+ @Test
+ fun validateChunkedFraming() {
+ val reply = ("hello")
+ val result = NetconfMessageUtils.validateChunkedFraming(reply)
+ Assert.assertFalse(result)
+ }
+
+ @Test
+ fun checkReply(){
+ assertTrue(NetconfMessageUtils.checkReply("ok"))
+ }
+
+ @Test
+ fun formatRPCRequest(){
+ val checkString = ("#199" +
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> <capabilities> <capability>hi</capability> <capability>hello</capability> </capabilities></hello>" +
+ "##")
+
+ val request = ("<?xml version=\"1.0\" encoding=\"UTF-8\"?><hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> "
+ +"<capabilities> <capability>hi</capability> <capability>hello</capability> </capabilities></hello>]]>]]>")
+
+ val messageId = "Test-Message-ID"
+
+ val capabilities = setOf<String>("hi", "hello","urn:ietf:params:netconf:base:1.1")
+
+ val result = NetconfMessageUtils.formatRPCRequest(request,messageId,capabilities).replace("[\n\r\t]".toRegex(), "")
+ Assert.assertEquals(checkString, result)
+
+
+ }
+
+
+
+
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/application-test.properties b/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/application-test.properties
new file mode 100644
index 000000000..6d8b62ff9
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/application-test.properties
@@ -0,0 +1,32 @@
+#
+# Copyright © 2017-2018 AT&T Intellectual Property.
+#
+# 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
+#
+# 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.
+#
+blueprintsprocessor.db.primary.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
+blueprintsprocessor.db.primary.username=sa
+blueprintsprocessor.db.primary.password=
+blueprintsprocessor.db.primary.driverClassName=org.h2.Driver
+blueprintsprocessor.db.primary.hibernateHbm2ddlAuto=create-drop
+blueprintsprocessor.db.primary.hibernateDDLAuto=update
+blueprintsprocessor.db.primary.hibernateNamingStrategy=org.hibernate.cfg.ImprovedNamingStrategy
+blueprintsprocessor.db.primary.hibernateDialect=org.hibernate.dialect.H2Dialect
+# Controller Blueprints Core Configuration
+blueprintsprocessor.blueprintDeployPath=./target/blueprints/deploy
+blueprintsprocessor.blueprintArchivePath=./target/blueprints/archive
+
+# Python executor
+blueprints.processor.functions.python.executor.executionPath=./../../../../components/scripts/python/ccsdk_blueprints
+blueprints.processor.functions.python.executor.modulePaths=./../../../../components/scripts/python/ccsdk_blueprints
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/logback-test.xml b/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/logback-test.xml
new file mode 100644
index 000000000..f9ec9fe57
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/logback-test.xml
@@ -0,0 +1,35 @@
+<!--
+ ~ Copyright © 2017-2018 AT&T Intellectual Property.
+ ~
+ ~ 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.
+ -->
+
+<configuration>
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <!-- encoders are assigned the type
+ ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
+ <encoder>
+ <pattern>%d{HH:mm:ss.SSS} %-5level %logger{100} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <logger name="org.springframework.test" level="warn"/>
+ <logger name="org.springframework" level="warn"/>
+ <logger name="org.hibernate" level="info"/>
+ <logger name="org.onap.ccsdk.apps.blueprintsprocessor" level="info"/>
+
+ <root level="warn">
+ <appender-ref ref="STDOUT"/>
+ </root>
+
+</configuration>
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/payload/requests/sample-activate-request.json b/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/payload/requests/sample-activate-request.json
new file mode 100644
index 000000000..694589de1
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/payload/requests/sample-activate-request.json
@@ -0,0 +1,31 @@
+{
+ "actionIdentifiers": {
+ "actionName": "activate",
+ "blueprintName": "baseconfiguration",
+ "blueprintVersion": "1.0.0",
+ "mode": "sync"
+ },
+ "commonHeader": {
+ "flags": {
+ "force": true,
+ "ttl": 3600
+ },
+ "originatorId": "sdnc",
+ "requestId": "123456-1000",
+ "subRequestId": "sub-123456-1000",
+ "timestamp": "2012-04-23T18:25:43.511Z"
+ },
+ "payload": {
+ "resource-assignment-request": {
+ "resource-assignment-properties": {
+ "request-id": "1234",
+ "service-instance-id": "siid_1234",
+ "vnf-id": "vnf_1234",
+ "action-name": "assign-activate",
+ "scope-type": "vnf-type",
+ "hostname": "localhost",
+ "vnf_name": "temp_vnf"
+ }
+ }
+ }
+}
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/requests/running-config-input.json b/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/requests/running-config-input.json
new file mode 100644
index 000000000..381cc16cd
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/requests/running-config-input.json
@@ -0,0 +1,15 @@
+{
+ "api-ver": "2.00",
+ "originator-id": "MSO",
+ "request-id": "123456",
+ "service-instance-id": "ibcx0001vm001",
+ "service-type": "AVPN",
+ "vnf-type": "vUSP - vDBE-IPX HUB",
+ "vnf-id": "123456",
+ "service-template-name": "VRR-baseconfiguration",
+ "service-template-version": "1.0.0",
+ "action-name": "running-config-action",
+ "hostname": "localhost",
+ "host-port": "22",
+ "reservation-id": "hostname"
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/requests/sample-activate-request.json b/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/requests/sample-activate-request.json
new file mode 100644
index 000000000..d0b4a0c1e
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/requests/sample-activate-request.json
@@ -0,0 +1,28 @@
+{
+ "actionIdentifiers": {
+ "actionName": "activate",
+ "blueprintName": "baseconfiguration",
+ "blueprintVersion": "1.0.0",
+ "mode": "sync"
+ },
+ "commonHeader": {
+ "flags": {
+ "force": true,
+ "ttl": 3600
+ },
+ "originatorId": "sdnc",
+ "requestId": "123456-1000",
+ "subRequestId": "sub-123456-1000",
+ "timestamp": "2012-04-23T18:25:43.511Z"
+ },
+ "payload": {
+ "resource-assignment-request": {
+ "resource-assignment-properties": {
+ "request-id": "1234",
+ "action-name": "assign-activate",
+ "scope-type": "vnf-type",
+ "hostname": "localhost"
+ }
+ }
+ }
+}
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/requests/sample-resourceresolution-request.json b/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/requests/sample-resourceresolution-request.json
new file mode 100644
index 000000000..c37e88912
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/requests/sample-resourceresolution-request.json
@@ -0,0 +1,28 @@
+{
+ "actionIdentifiers": {
+ "actionName": "sample-action",
+ "blueprintName": "sample-blurprint",
+ "blueprintVersion": "1.0.0",
+ "mode": "sync"
+ },
+ "commonHeader": {
+ "flags": {
+ "force": true,
+ "ttl": 3600
+ },
+ "originatorId": "sdnc",
+ "requestId": "123456-1000",
+ "subRequestId": "sub-123456-1000",
+ "timestamp": "2012-04-23T18:25:43.511Z"
+ },
+ "payload": {
+ "resource-assignment-request": {
+ "resource-assignment-properties": {
+ "request-id": "1234",
+ "action-name": "assign-activate",
+ "scope-type": "vnf-type",
+ "hostname": "localhost"
+ }
+ }
+ }
+}
diff --git a/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/response/get-config-123456.xml b/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/response/get-config-123456.xml
new file mode 100644
index 000000000..85cbeeac4
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/netconf-executor/src/test/resources/response/get-config-123456.xml
@@ -0,0 +1,10 @@
+<rpc-reply message-id="runningconfig-template-123456"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
+ xmlns:junos="http://xml.juniper.net/junos/17.4R1/junos">
+ <interface-information
+ xmlns="http://xml.juniper.net/junos/17.4R1/junos-interface">
+ <physical-interface>
+ <name>ge-2/3/0</name>
+ </physical-interface>
+ </interface-information>
+</rpc-reply>