CDS offers scripting for resource resolution and for Component execution. In both cases, the CDS user needs to make use of existing library in the scripting class or component. Scripting in CDS could be :
User provides a python script artifact to execute in two different ways Jython or in a remote python environment defined in CDS as a Component Node Type: Component-remote-executor and Component-netconf-executor.
- Kotlin based: That allows CDS user to prepare a customized Kotlin script to execute in CDS runtime environment to resolve a resources.
To implement a such type of script, CDS provides wrapper function for each type of component or function. This documentation describes all the needed library available to the CDS user to design a script artifact.
1) Python Library :
This library is available under cds/components/scripts/python
When defining a script to perform resource resolution or NETCONF, RESTCONF and SSH commands, the user has many functions that he can use do a specific action in CDS such as connect to a Netconf device, get resolved artifacts, push a config is a device etc.
The following list all functions available during each type of execution.
a) Library for Resource Resolution
- blueprint_constants: contains all constants related to Python Library
Add to your artifact (python script):
from blueprint_constants import *
PROPERTY_BLUEPRINT_PROCESS_ID= "blueprint-process-id"
PROPERTY_BLUEPRINT_BASE_PATH= "blueprint-basePath"
PROPERTY_BLUEPRINT_RUNTIME= "blueprint-runtime"
PROPERTY_BLUEPRINT_INPUTS_DATA= "blueprint-inputs-data"
PROPERTY_BLUEPRINT_CONTEXT= "blueprint-context"
PROPERTY_BLUEPRINT_NAME= "template_name"
PROPERTY_BLUEPRINT_VERSION= "template_version"
PROPERTY_BLUEPRINT_USER_SYSTEM= "System"
PROPERTY_BLUEPRINT_STATUS_SUCCESS= "success"
PROPERTY_BLUEPRINT_STATUS_FAILURE= "failure"
METADATA_USER_GROUPS = "user-groups"
METADATA_TEMPLATE_NAME = "template_name"
METADATA_TEMPLATE_VERSION = "template_version"
METADATA_TEMPLATE_AUTHOR = "template_author"
METADATA_TEMPLATE_TAGS = "template_tags"
METADATA_WORKFLOW_NAME = "workflow_name"
PAYLOAD_DATA = "payload-data"
PROPERTY_CURRENT_STEP = "current-step"
PROPERTY_CURRENT_NODE_TEMPLATE = "current-node-template"
PROPERTY_CURRENT_INTERFACE = "current-interface"
PROPERTY_CURRENT_OPERATION = "current-operation"
PROPERTY_CURRENT_IMPLEMENTATION = "current-implementation"
PROPERTY_EXECUTION_REQUEST = "execution-request"
- abstract_ra_processor: This class provides all Runtime ResourceAssignmentProcessor. The current class helps the user to execute a resource resolution
Add to your artifact:
from abstract_ra_processor import AbstractRAProcessor
from org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.processor import \
ResourceAssignmentProcessor
from org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.utils import \
ResourceAssignmentUtils
from org.onap.ccsdk.cds.controllerblueprints.core import \
BluePrintProcessorException
class AbstractRAProcessor(ResourceAssignmentProcessor):
def process(self, resource_assignment):
print "Processing.."
return None
def recover(self, runtime_exception, resource_assignment):
print "Recovering.."
return None
def set_resource_data_value(self, resource_assignment, value):
try:
if value is not None:
ResourceAssignmentUtils.Companion.setResourceDataValue(
resource_assignment, self.raRuntimeService, value)
else:
ResourceAssignmentUtils.Companion.setFailedResourceDataValue(
resource_assignment, "Fail to resolve value")
except BluePrintProcessorException, err:
raise BluePrintProcessorException(
"Error on resource assignment. Message = " + err.message)
abstract_ra_processor provides an interface with functions to implement in your python script:
- process(self, resource_assignment): This function is the entry point of the python class. Implement all your business logic here in this class.
- recover(self, runtime_exception, resource_assignment): This function is called when the business logic process fails. It could be helpful to properly terminate resources or contexts.
- set_resource_data_value(self, resource_assignment, value): use to set value of your resource resolution variable.
- blueprint_runtime_service: This class provides all CDS Run-time environment. Given this class, your python script can call some run-time execution functions.
Add to your artifact:
from blueprint_runtime_service import BluePrintRuntimeService
class BluePrintRuntimeService:
def __init__(self, bps):
self.bps = bps
def resolveNodeTemplateArtifact(self, node_template_name, artifact_name):
return self.bps.resolveNodeTemplateArtifact(node_template_name, artifact_name)
def setNodeTemplateAttributeValue(self, nodeTemplateName, attributeName, value):
return self.bps.setNodeTemplateAttributeValue(nodeTemplateName, attributeName, value)
def setNodeTemplatePropertyValue(self, nodeTemplateName, propertyName, value):
return self.bps.setNodeTemplatePropertyValue(nodeTemplateName, propertyName, value)
def get_node_template_attribute_value(self, node_template_name, attribute_name, value):
return self.bps.getNodeTemplateAttributeValue(node_template_name, attribute_name, value)
def get_node_template_property_value(self, node_template_name, property_name, value):
return self.bps.getNodeTemplatePropertyValue(node_template_name, property_name, value)
############## the following functions are not available not. Should be added ####################
def put_resolution_store(self, ra_name, value):
self.bps.putResolutionStore(ra_name, value)
return None
def put_dictionary_store(self, ra_dictionary_name, value):
self.bps.putResolutionStore(ra_dictionary_name, value)
return None
def get_json_node_from_resolution_store(self, key):
return self.bps.getJsonNodeFromResolutionStore(key)
def get_string_from_resolution_store(self, key):
return self.bps.getStringFromResolutionStore(key)
def get_boolean_from_resolution_store(self, key):
return self.bps.getBooleanFromResolutionStore(key)
def get_int_from_resolution_store(self, key):
return self.bps.getIntFromResolutionStore(key)
def get_double_from_resolution_store(self, key):
return self.bps.getDoubleFromResolutionStore(key)
def get_json_node_from_dictionary_store(self, key):
return self.bps.getJsonNodeFromDictionaryStore(key)
def get_string_from_dictionary_store(self, key):
return self.bps.getStringFromDictionaryStore(key)
def get_boolean_from_dictionary_store(self, key):
return self.bps.getBooleanFromDictionaryStore(key)
def get_int_from_dictionary_store(self, key):
return self.bps.getIntFromDictionaryStore(key)
def get_double_from_dictionary_store(self, key):
return self.bps.getDoubleFromDictionaryStore(key)
def check_resolution_store(self, key):
return self.bps.checkResolutionStore(key)
def check_dictionary_store(self, key):
return self.bps.checkDictionaryStore(key)
This section shows an example of python script Class that can be added as artifact in CDS.
# Copyright (c) 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.
from java.lang import Exception as JavaException
from abstract_ra_processor import AbstractRAProcessor
from blueprint_constants import *
from blueprint_runtime_service import BluePrintRuntimeService
class ResolvProperties(AbstractRAProcessor):
def process(self, resource_assignment):
result = ""
ATTRIBUTE_RESPONSE_DATA = "response-data"
try:
blueprint_runtime = BluePrintRuntimeService(self.raRuntimeService)
#get response of another node_template in the same workflow
result = blueprint_runtime.get_node_template_attribute_value("my_node_template_name", ATTRIBUTE_RESPONSE_DATA)
print(result) # result is a JsonNode
if resource_assignment.name == "minlink":
attribute = "minlink"
if resource_assignment.name == "configlink":
attribute = "config_link"
# get a resource resolution value from the current workflow
resolution_key = blueprint_runtime.get_string_from_resolution_store("resolution-key")
vnf_id = blueprint_runtime.get_string_from_resolution_store("vnf-id")
port = blueprint_runtime.get_int_from_resolution_store("port")
# set value for resource getting currently resolved
self.set_resource_data_value(resource_assignment, result.get(attribute))
except JavaException, err:
log.error("Java Exception in the script {}", err)
except Exception, err:
log.error("Python Exception in the script {}", err)
return None
def recover(self, runtime_exception, resource_assignment):
log.error("Exception in the script {}", runtime_exception)
print self.addError(runtime_exception.cause.message)
return None
b) Library for Component executions
To design an artifact as a component executor, we should define a Class that inherit AbstractScriptComponentFunction and override function to run actions.
from org.onap.ccsdk.cds.blueprintsprocessor.services.execution import AbstractScriptComponentFunction
class someArtifact(AbstractScriptComponentFunction):
import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractScriptComponentFunction
open class SampleScriptArtifact : AbstractScriptComponentFunction() {
}
An artifact need to implement at least 2 functions in order to be valid:
def process(self, resource_assignment):
try:
######################### Component action here ########################
#Do action here
########################### End ##########################
except JavaException, err:
log.error("Java Exception in the script {}", err)
except Exception, err:
log.error("Python Exception in the script {}", err)
return None
def recover(self, runtime_exception, resource_assignment):
log.error("Exception in the script {}", runtime_exception)
print self.addError(runtime_exception.cause.message)
return None
- Functions available in Artifact script
The main library available while running a Component in CDS is the BlueprintRuntimeService. This provides to the user the Blueprint running context and run-time execution functions.The following shows functions available for all the different Components execution from a python script:
BlueprintRuntimeService library in Component functions
- self.bluePrintRuntimeService.setInputValue(propertyName: String, propertyDefinition: PropertyDefinition, value: JsonNode): Set the value of the input in the current node template.
- self.bluePrintRuntimeService.setWorkflowInputValue(workflowName: String, propertyName: String, propertyDefinition: PropertyDefinition, value: JsonNode): Set the value of the input in the a specific workflow.
- self.bluePrintRuntimeService.getWorkflowInputValue(workflowName: String, propertyName: String): get a value of the input in the a specific workflow..
- self.bluePrintRuntimeService.getInputValue(propertyName: String): get an input value for the current node template
- NETCONF Component
- netconf_constant: contains all constants related to Python Netconf Library
Add to your artifact (python script):
from netconf_constant import *
SERVICE_LOG = "log"
SERVICE_NETCONF = "netconfService"
SERVICE_MESSAGE = "messageService"
PARAM_REQUEST_ID = "requestId"
PARAM_ACTION = "action"
STATUS_SUCCESS = "success"
STATUS_FAILURE = "failure"
ATTRIBUTE_RESPONSE_DATA = "response-data"
CONFIG_TARGET_RUNNING = "running"
CONFIG_TARGET_CANDIDATE = "candidate"
CONFIG_DEFAULT_OPERATION_MERGE = "merge"
CONFIG_DEFAULT_OPERATION_REPLACE = "replace"
CONFIG_DEFAULT_OPERATION_NONE = "none"
- common: This class provides all common function in Run-time execution.
Add to your artifact:
from common import ResolutionHelper
Common function in Netconf component execution
- resolve_and_generate_message_from_template_prefix(artifact_prefix) To get the resolved template using the template artifact prefix. Use get a template content from the current workflow.
- retrieve_resolved_template_from_database(key, artifact_template) To get the resolved template using the template artifact prefix and resolution-key. Use retrieve a template content that was previously resolved.
- set_execution_attribute_response_data(response_data) where response_data is a JsonNode. Set the attribute value of the current node_template.
- get_node_template_attribute(node_template_name, attribute_key) Get the value into the node template attribute attribute_key.
- get_input_value(key) Get input value associated to the key of the current node template. (Not available now)
- netconfclient: This class provides all netconf execution functions
Add the following to your artifact to have netconfclient library available:
from netconfclient import NetconfClient
Netconf Client function in Netconf component execution
- NetconfClient(log, netconfComonent, netconf_connection) The class constructor that provide Netconf service. netconf_connection is a String that identify a device in a node template.
- connect() To establish the session with the device
- disconnect() End session connection with the device
- lock(config_target=CONFIG_TARGET_CANDIDATE) Lock Netconf device
- get_config(filter_content="", config_target=CONFIG_TARGET_RUNNING) Get running configs. filter_content is an XML filter config.
- edit_config(message_content, config_target=CONFIG_TARGET_CANDIDATE, edit_default_peration=CONFIG_DEFAULT_OPERATION_REPLACE) Push a running config into the device
- commit(confirmed=False, confirm_timeout=60, persist="", persist_id="")
- invoke_rpc(rpc_content)
- cancel_commit(persist_id="")
- unlock(config_target=CONFIG_TARGET_CANDIDATE) Unlock Netconf device
- validate(config_target=CONFIG_TARGET_CANDIDATE)
- discard_change()
- get(filter_content) Get operational commands.
# Copyright (c) 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.
########################################################################
# Add customs libraries needed for the script
from java.lang import Exception as JavaException
# CDS runtime scripts library
from blueprint_constants import *
from common import ResolutionHelper
from netconfclient import NetconfClient
#### Your artifact could use one or many other component executors. Import the corresponding extensions class ####
# resource resolution extension provides to your artifact what you need from resource resolution in CDS. e.g.: get a resolved template
from org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution import ResourceResolutionExtensionsKt
# Netconf executor extension provides to your artifact what you need from Netconf component in CDS. e.g.: Connect to Netconf device
from org.onap.ccsdk.cds.blueprintsprocessor.functions.netconf.executor import NetconfExecutorExtensionsKt
from org.onap.ccsdk.cds.blueprintsprocessor.services.execution import AbstractScriptComponentFunction
class ShowCommand(AbstractScriptComponentFunction):
def process(self, resource_assignment):
try:
######################### Netconf Sample ########################
log = globals()[netconf_constant.SERVICE_LOG]
netconf_device_info = NetconfExecutorExtensionsKt.netconfDevice("netconf-connection")
netconf_client = NetconfClient(log, self, "netconf-connection")
resource_resolution = ResolutionHelper(self)
payload = resource_resolution .resolve_and_generate_message_from_template_prefix("hostname")
netconf_client.connect()
response = netconf_client.lock()
if not response.isSuccess():
log.error(response.errorMessage)
netconf_client.edit_config(message_content=payload, edit_default_peration="none")
netconf_client.validate()
netconf_client.commit()
netconf_client.unlock()
netconf_client.disconnect()
########################### End ##########################
except JavaException, err:
log.error("Java Exception in the script {}", err)
except Exception, err:
log.error("Python Exception in the script {}", err)
return None
def recover(self, runtime_exception, resource_assignment):
log.error("Exception in the script {}", runtime_exception)
print self.addError(runtime_exception.cause.message)
return None
- RESTCONF Component
- restconf_constant: contains all constants related to Python Restconf Library
Add to your artifact (python script):
from restconf_constant import *
ATTRIBUTE_RESPONSE_DATA = "response-data"
- common: This class provides all common function in Run-time execution. (Not available now)
Add to your artifact:
from common import ResolutionHelper
Common function in Restconf component execution
- resolve_and_generate_message_from_template_prefix(artifact_prefix) To get the resolved template using the template artifact prefix. Use get a template content from the current workflow.
- retrieve_resolved_template_from_database(key, artifact_template) To get the resolved template using the template artifact prefix and resolution-key. Use retrieve a template content that was previously resolved.
- set_execution_attribute_response_data(response_data) where response_data is a JsonNode. Set the attribute value of the current node_template.
- get_node_template_attribute(node_template_name, attribute_key) Get the value into the node template attribute attribute_key.
- get_input_value(key) Get input value associated to the key of the current node template.
- restconf_client: This class provides all Restconf execution functions
Add the following to your artifact to have restconfclient library available:
from restconf_client import RestconfClient
Netconf Client function in Netconf component execution
- RestconfClient(log, restconf_component) The class constructor that provide Restconf service.
- web_client_service(identifier) To establish a Restconf session
- mount_device(web_client_service, nf_id, mount_payload, content_type="application/xml") Sending mount request to the device
- configure_device_json_patch(web_client_service, nf_id, configlet_resource_path, configlet_to_apply) Apply a Json patch to configure a device.
- configure_device_xml_patch(web_client_service, nf_id, configlet_resource_path, configlet_to_apply) Apply a XML patch to configure a device
- retrieve_device_configuration_subtree(web_client_service, nf_id, configlet_resource_path)
- unmount_device(web_client_service, nf_id)
- set_execution_attribute_response_data(response_data) Set an attribute value in the current node template
# Copyright (c) 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.
########################################################################
# Add customs libraries needed for the script
from java.lang import Exception as JavaException
# CDS runtime scripts library
from blueprint_constants import *
from common import ResolutionHelper
from restconfclient import RestconfClient
#### Your artifact could use one or many other component executors. Import the corresponding extensions class ####
# resource resolution extension provides to your artifact what you need from resource resolution in CDS. e.g.: get a resolved template
from org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution import ResourceResolutionExtensionsKt
# Provides to your artifact what you need from Restconf component in CDS. e.g.: Apply config in Restconf device
from org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor import RestconfExecutorExtensionsKt
from org.onap.ccsdk.cds.blueprintsprocessor.services.execution import AbstractScriptComponentFunction
class RestconfExecutor(AbstractScriptComponentFunction):
def process(self, resource_assignment):
try:
######################### Restconf Sample ########################
log = globals()[netconf_constant.SERVICE_LOG]
resource_resolution = ResolutionHelper(self)
try:
restconf_client = RestconfClient(log, self, "restconf-selector")
restconf_client_service = RestconfExecutorExtensionsKt.restconfClientService("restconf-selector")
resolution_key = self.getDynamicProperties("resolution-key").asText()
template_to_apply = self.getDynamicProperties("template-name").asText()
payload = resource_resolution.retrieve_resolved_template_from_database(resolution_key, template_to_apply)
device_id = self.getDynamicProperties("device-id").asText()
RestconfExecutorExtensionsKt.restconfMountDevice(restconf_client_service, device_id, payload)
# Place the response in the current node template attribute
resource_resolution.set_execution_attribute_response_data(ATTRIBUTE_RESPONSE_DATA, response)
except Exception, err:
log.error("Python Exception in the script {}", err)
########################### End ##########################
except JavaException, err:
log.error("Java Exception in the script {}", err)
except Exception, err:
log.error("Python Exception in the script {}", err)
return None
def recover(self, runtime_exception, resource_assignment):
log.error("Exception in the script {}", runtime_exception)
print self.addError(runtime_exception.cause.message)
return None
- CLI Component
# Copyright (c) 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.
########################################################################
# Add customs libraries needed for the script
from java.lang import Exception as JavaException
# CDS runtime scripts library
from blueprint_constants import *
from common import ResolutionHelper
#### Your artifact could use one or many other component executors. Import the corresponding extensions class ####
# resource resolution extension provides to your artifact what you need from resource resolution in CDS. e.g.: get a resolved template
from org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution import ResourceResolutionExtensionsKt
# Cli executor extension provides to your artifact what you need from CLI component in CDS. e.g.: SSH to a device
from org.onap.ccsdk.cds.blueprintsprocessor.functions.cli.executor import CliExecutorExtensionsKt
from org.onap.ccsdk.cds.blueprintsprocessor.services.execution import AbstractScriptComponentFunction
class CliExecutor(AbstractScriptComponentFunction):
def process(self, resource_assignment):
try:
######################### CLI Sample ########################
# Get a cli device info from DSL definition (See an example of DSL definition bellow)
val device_information = CliExecutorExtensionsKt.cliDeviceInfo("device-properties")
# Get the Client Service
sshClientService = CliExecutorExtensionsKt.getSshClientService(device_information )
CliExecutorExtensionsKt.sshClientService.startSessionNB()
# Read Commands
commands = CliExecutorExtensionsKt.readLinesFromArtifact("command-template")
# Execute multiple Commands
responseLog = CliExecutorExtensionsKt.sshClientService.executeCommandsNB(commands, 5000)
# Close Session
CliExecutorExtensionsKt.sshClientService.closeSessionNB()
########################### End ##########################
except JavaException, err:
log.error("Java Exception in the script {}", err)
except Exception, err:
log.error("Python Exception in the script {}", err)
return None
def recover(self, runtime_exception, resource_assignment):
log.error("Exception in the script {}", runtime_exception)
print self.addError(runtime_exception.cause.message)
return None
2) Scripting functions Library
Developer can decide to go with Kotlin scripting instead of Python. In this case, it will create a Kotlin script class file in the CBA and the class should inherit AbstractScriptComponentFunction. See the example below:
/*
* Copyright © 2019 IBM.
*
* 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.
*/
import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
import org.onap.ccsdk.cds.blueprintsprocessor.functions.cli.executor.cliDeviceInfo
import org.onap.ccsdk.cds.blueprintsprocessor.functions.cli.executor.getSshClientService
import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractScriptComponentFunction
import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.ComponentScriptExecutor
import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive
import org.slf4j.LoggerFactory
open class SampleScriptArtifact : AbstractScriptComponentFunction() {
private val log = LoggerFactory.getLogger(TestCliScriptFunction::class.java)!!
override fun getName(): String {
return "TestCliScriptFunction"
}
override suspend fun processNB(executionRequest: ExecutionServiceInput) {
log.info("Executing process ...")
}
override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
log.info("Executing Recovery")
}
}