diff options
Diffstat (limited to 'ms/blueprintsprocessor/functions/netconf-executor')
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> + * <rpc> + * <load-configuration rollback="1"/> + * </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> |