aboutsummaryrefslogtreecommitdiffstats
path: root/ms/blueprintsprocessor/functions
diff options
context:
space:
mode:
authorBrinda Santh Muthuramalingam <brindasanth@in.ibm.com>2019-08-13 15:52:55 +0000
committerGerrit Code Review <gerrit@onap.org>2019-08-13 15:52:55 +0000
commit82bc5bf7beb713c14757d73de595b419366fc9c4 (patch)
tree5e7c1968b22d60bd79f0f5eed67f00aaff2ea8a9 /ms/blueprintsprocessor/functions
parent13904e48fc907f4c5013593a60ad6efbfcc42822 (diff)
parent34c424689a52614fb414d65899282497fe25b164 (diff)
Merge "Resource Configuration Snapshots Executor and API"
Diffstat (limited to 'ms/blueprintsprocessor/functions')
-rw-r--r--ms/blueprintsprocessor/functions/config-snapshots/pom.xml58
-rw-r--r--ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/ComponentConfigSnapshotsExecutor.kt262
-rw-r--r--ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshot.kt85
-rw-r--r--ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotRepository.kt39
-rw-r--r--ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotService.kt77
-rw-r--r--ms/blueprintsprocessor/functions/config-snapshots/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/ComponentConfigSnapshotsExecutorTest.kt365
-rw-r--r--ms/blueprintsprocessor/functions/config-snapshots/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotServiceTest.kt94
-rw-r--r--ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/application-test.properties32
-rw-r--r--ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/keystore.p12bin0 -> 2588 bytes
-rw-r--r--ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/logback-test.xml35
-rw-r--r--ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/payload/requests/config-payload-candidate.json39
-rw-r--r--ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/payload/requests/config-payload-running.json35
-rw-r--r--ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/payload/requests/interface-candidate.xml34
-rw-r--r--ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/payload/requests/interface-running.xml32
-rwxr-xr-xms/blueprintsprocessor/functions/pom.xml1
15 files changed, 1188 insertions, 0 deletions
diff --git a/ms/blueprintsprocessor/functions/config-snapshots/pom.xml b/ms/blueprintsprocessor/functions/config-snapshots/pom.xml
new file mode 100644
index 000000000..7963aa339
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/config-snapshots/pom.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright © 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.
+ -->
+<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>
+ <artifactId>functions</artifactId>
+ <groupId>org.onap.ccsdk.cds.blueprintsprocessor</groupId>
+ <version>0.5.2-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.onap.ccsdk.cds.blueprintsprocessor.functions</groupId>
+ <artifactId>config-snapshots</artifactId>
+ <name>Blueprints Processor Function - Config Snapshots</name>
+ <description>Blueprints Processor Function - Config Snapshots</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.python</groupId>
+ <artifactId>jython-standalone</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.mariadb.jdbc</groupId>
+ <artifactId>mariadb-java-client</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.github.fge</groupId>
+ <artifactId>json-patch</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.xmlunit</groupId>
+ <artifactId>xmlunit-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-testing</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/ComponentConfigSnapshotsExecutor.kt b/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/ComponentConfigSnapshotsExecutor.kt
new file mode 100644
index 000000000..eafcaf44b
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/ComponentConfigSnapshotsExecutor.kt
@@ -0,0 +1,262 @@
+/*
+ * Copyright © 2019 Bell Canada.
+ * Modifications Copyright © 2018-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.
+ */
+
+package org.onap.ccsdk.cds.blueprintsprocessor.functions.ansible.executor
+
+import com.github.fge.jsonpatch.diff.JsonDiff
+import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.db.ResourceConfigSnapshot
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.db.ResourceConfigSnapshot.Status.RUNNING
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.db.ResourceConfigSnapshot.Status.CANDIDATE
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.db.ResourceConfigSnapshotService
+import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractComponentFunction
+import org.onap.ccsdk.cds.controllerblueprints.core.*
+import org.slf4j.LoggerFactory
+import org.springframework.beans.factory.config.ConfigurableBeanFactory
+import org.springframework.context.annotation.Scope
+import org.springframework.stereotype.Component
+import org.w3c.dom.Node
+import org.xmlunit.builder.DiffBuilder
+import org.xmlunit.builder.Input
+import org.xmlunit.diff.*
+
+
+/**
+ * ComponentConfigSnapshotsExecutor
+ *
+ * Component that retrieves the saved configuration snapshot as identified by the input parameters,
+ * named resource-id and resource-type.
+ *
+ * It reports the content of the requested snapshot via properties, config-snapshot-status
+ * and config-snapshot-value. In case of error, details can be found in the config-snapshot-status
+ * and config-snapshot-message properties
+ *
+ * @author Serge Simard
+ */
+@Component("component-config-snapshots-executor")
+@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
+open class ComponentConfigSnapshotsExecutor(private val cfgSnapshotService: ResourceConfigSnapshotService) :
+ AbstractComponentFunction() {
+
+ companion object {
+ private val log = LoggerFactory.getLogger(ComponentConfigSnapshotsExecutor::class.java)
+
+ // input fields names accepted by this executor
+ const val INPUT_OPERATION = "operation"
+ const val INPUT_RESOURCE_ID = "resource-id"
+ const val INPUT_RESOURCE_TYPE = "resource-type"
+ const val INPUT_RESOURCE_STATUS = "resource-status"
+ const val INPUT_RESOURCE_SNAPSHOT = "resource-snapshot"
+ const val INPUT_DIFF_CONTENT_TYPE = "diff-content-type"
+
+ const val OPERATION_FETCH = "fetch"
+ const val OPERATION_STORE = "store"
+ const val OPERATION_DIFF = "diff"
+
+ const val DIFF_JSON = "JSON"
+ const val DIFF_XML = "XML"
+
+ // output fields names (and values) populated by this executor.
+ const val OUTPUT_STATUS = "config-snapshot-status"
+ const val OUTPUT_MESSAGE = "config-snapshot-message"
+ const val OUTPUT_SNAPSHOT = "config-snapshot-value"
+
+ const val OUTPUT_STATUS_SUCCESS = "success"
+ const val OUTPUT_STATUS_ERROR = "error"
+ }
+
+ /**
+ * Main entry point for ComponentConfigSnapshotsExecutor
+ * Supports 3 operations : fetch, store or diff
+ */
+ override suspend fun processNB(executionRequest: ExecutionServiceInput) {
+
+ val operation = getOptionalOperationInput(INPUT_OPERATION)?.returnNullIfMissing()?.textValue() ?: ""
+ val contentType = getOptionalOperationInput(INPUT_DIFF_CONTENT_TYPE)?.returnNullIfMissing()?.textValue() ?: ""
+ val resourceId = getOptionalOperationInput(INPUT_RESOURCE_ID)?.returnNullIfMissing()?.textValue() ?: ""
+ val resourceType = getOptionalOperationInput(INPUT_RESOURCE_TYPE)?.returnNullIfMissing()?.textValue() ?: ""
+ val resourceStatus = getOptionalOperationInput(INPUT_RESOURCE_STATUS)?.returnNullIfMissing()?.textValue() ?: RUNNING.name
+ val snapshot = getOptionalOperationInput(INPUT_RESOURCE_SNAPSHOT)?.returnNullIfMissing()?.textValue() ?: ""
+ val status = ResourceConfigSnapshot.Status.valueOf(resourceStatus)
+
+ when (operation) {
+ OPERATION_FETCH -> fetchConfigurationSnapshot(resourceId, resourceType, status)
+ OPERATION_STORE -> storeConfigurationSnapshot(snapshot, resourceId, resourceType, status)
+ OPERATION_DIFF -> compareConfigurationSnapshot(resourceId, resourceType, contentType)
+
+ else -> setNodeOutputErrors(OUTPUT_STATUS_ERROR,
+ "Operation parameter must be fetch, store or diff")
+ }
+ }
+
+ /**
+ * General error handling for the executor.
+ */
+ override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
+ setNodeOutputErrors(OUTPUT_STATUS_ERROR, "Error : ${runtimeException.message}")
+ }
+
+ /**
+ * Fetch a configuration snapshot, for resource identified by ID/type, of type status (RUNNING by default)
+ */
+ private suspend fun fetchConfigurationSnapshot(resourceId: String, resourceType: String,
+ status : ResourceConfigSnapshot.Status = RUNNING) {
+ try {
+ val cfgSnapshotValue = cfgSnapshotService.findByResourceIdAndResourceTypeAndStatus(resourceId, resourceType, status)
+ setNodeOutputProperties(OUTPUT_STATUS_SUCCESS, cfgSnapshotValue)
+ } catch (er : NoSuchElementException) {
+ val message = "No Resource config snapshot identified by resourceId={$resourceId}, " +
+ "resourceType={$resourceType} does not exists"
+ setNodeOutputErrors(OUTPUT_STATUS_ERROR, message)
+ }
+ }
+
+ /**
+ * Store a configuration snapshot, for resource identified by ID/type, of type status (RUNNING by default)
+ */
+ private suspend fun storeConfigurationSnapshot(cfgSnapshotValue : String, resourceId: String, resourceType: String,
+ status : ResourceConfigSnapshot.Status = RUNNING) {
+ if (cfgSnapshotValue.isNotEmpty()) {
+ val cfgSnapshotSaved = cfgSnapshotService.write(cfgSnapshotValue, resourceId, resourceType, status)
+ setNodeOutputProperties(OUTPUT_STATUS_SUCCESS, cfgSnapshotSaved.config_snapshot ?: "" )
+ } else {
+ val message = "Could not store config snapshot identified by resourceId={$resourceId},resourceType={$resourceType} does not exists"
+ setNodeOutputErrors(OUTPUT_STATUS_ERROR, message)
+ }
+ }
+
+ /**
+ * Compare two configs (RUNNING vs CANDIDATE) for resource identified by ID/type, using the specified contentType
+ */
+ private suspend fun compareConfigurationSnapshot(resourceId: String, resourceType: String, contentType : String) {
+
+ when (contentType.toUpperCase()) {
+ DIFF_JSON -> {
+ val cfgRunning = cfgSnapshotService.findByResourceIdAndResourceTypeAndStatus(resourceId, resourceType, RUNNING)
+ val cfgCandidate = cfgSnapshotService.findByResourceIdAndResourceTypeAndStatus(resourceId, resourceType, CANDIDATE)
+
+ val patchNode = JsonDiff.asJson(cfgRunning.jsonAsJsonType(), cfgCandidate.jsonAsJsonType())
+ setNodeOutputProperties(OUTPUT_STATUS_SUCCESS, patchNode.toString())
+ }
+ DIFF_XML -> {
+ val cfgRunning = cfgSnapshotService.findByResourceIdAndResourceTypeAndStatus(resourceId, resourceType, RUNNING)
+ val cfgCandidate = cfgSnapshotService.findByResourceIdAndResourceTypeAndStatus(resourceId, resourceType, CANDIDATE)
+
+ val myDiff = DiffBuilder
+ .compare(Input.fromString(cfgRunning))
+ .withTest(Input.fromString(cfgCandidate))
+ .checkForSimilar()
+ .ignoreComments()
+ .ignoreWhitespace()
+ .normalizeWhitespace()
+ .build()
+
+ setNodeOutputProperties(OUTPUT_STATUS_SUCCESS, formatXmlDifferences(myDiff))
+ }
+ else -> {
+ val message = "Could not compare config snapshots for type $contentType"
+ setNodeOutputErrors(OUTPUT_STATUS_ERROR, message)
+ }
+ }
+ }
+
+ /**
+ * Utility function to set the output properties of the executor node
+ */
+ private fun setNodeOutputProperties(status: String, snapshot: String) {
+ setAttribute(OUTPUT_STATUS, status.asJsonPrimitive())
+ setAttribute(OUTPUT_SNAPSHOT, snapshot.asJsonPrimitive())
+ log.info("Setting output $OUTPUT_STATUS=$status, $OUTPUT_SNAPSHOT=$snapshot ")
+ }
+
+ /**
+ * Utility function to set the output properties and errors of the executor node, in case of errors
+ */
+ private fun setNodeOutputErrors(status: String, message: String) {
+ setAttribute(OUTPUT_STATUS, status.asJsonPrimitive())
+ setAttribute(OUTPUT_MESSAGE, message.asJsonPrimitive())
+ setAttribute(OUTPUT_SNAPSHOT, "".asJsonPrimitive())
+
+ log.info("Setting error and output $OUTPUT_STATUS=$status, $OUTPUT_MESSAGE=$message ")
+
+ addError(status, OUTPUT_MESSAGE, message)
+ }
+
+ /**
+ * Formats XmlUnit differences into xml-patch like response (RFC5261)
+ */
+ private fun formatXmlDifferences(differences : Diff) : String {
+ val output = StringBuilder()
+ output.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
+ "<diff>")
+ val diffIterator = differences.getDifferences().iterator()
+ while (diffIterator.hasNext()) {
+
+ val aDiff = diffIterator.next().comparison
+ when (aDiff.type) {
+ ComparisonType.ATTR_VALUE -> {
+ output.append("<replace sel=\"").append(aDiff.testDetails.xPath).append("\">")
+ .append(aDiff.testDetails.value)
+ .append("</replace>")
+ }
+ ComparisonType.TEXT_VALUE -> {
+ output.append("<replace sel=\"").append(aDiff.testDetails.xPath).append("\">")
+ .append(aDiff.testDetails.value)
+ .append("</replace>")
+ }
+ ComparisonType.CHILD_LOOKUP -> {
+ output.append("<add sel=\"").append(aDiff.testDetails.parentXPath).append("\">")
+ .append(formatNode(aDiff.testDetails.target))
+ .append("</add>")
+ }
+ ComparisonType.CHILD_NODELIST_LENGTH -> {
+ // Ignored; will be processed in the CHILD_LOOKUP case
+ }
+ else -> {
+ log.warn("Unsupported XML difference found: $aDiff")
+ }
+ }
+ }
+ output.append("</diff>")
+
+ return output.toString()
+ }
+
+ /**
+ * Formats a node value obtained from an XmlUnit differences node
+ */
+ private fun formatNode(node: Node): String {
+ val output = StringBuilder()
+
+ val parentName = node.localName
+ output.append("<$parentName>")
+ if (node.hasChildNodes()) {
+ val nodes = node.childNodes
+ for (index in 1..nodes.length) {
+ val child = nodes.item(index-1)
+ if (child.nodeType == Node.TEXT_NODE || child.nodeType == Node.COMMENT_NODE) {
+ output.append(child.nodeValue)
+ } else {
+ output.append(formatNode(child))
+ }
+ }
+ }
+ output.append("</$parentName>")
+
+ return output.toString()
+ }
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshot.kt b/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshot.kt
new file mode 100644
index 000000000..36a547032
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshot.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 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.cds.blueprintsprocessor.functions.config.snapshots.db
+
+import com.fasterxml.jackson.annotation.JsonFormat
+import io.swagger.annotations.ApiModelProperty
+import org.hibernate.annotations.Proxy
+import org.springframework.data.annotation.LastModifiedDate
+import org.springframework.data.jpa.domain.support.AuditingEntityListener
+import java.io.Serializable
+import java.util.*
+import javax.persistence.Column
+import javax.persistence.Entity
+import javax.persistence.EntityListeners
+import javax.persistence.Id
+import javax.persistence.Lob
+import javax.persistence.Table
+import javax.persistence.Temporal
+import javax.persistence.TemporalType
+
+/**
+ * ResourceConfigSnapshot model
+ * Stores RUNNING or CANDIDATE resource configuration snapshots, captured during the execution
+ * of blueprints. A resource is identified by an identifier and a type.
+ *
+ * @author Serge Simard
+ * @version 1.0
+ */
+@EntityListeners(AuditingEntityListener::class)
+@Entity
+@Table(name = "RESOURCE_CONFIG_SNAPSHOT")
+@Proxy(lazy = false)
+class ResourceConfigSnapshot : Serializable {
+
+ @get:ApiModelProperty(value = "Resource type.", required = true, example = "ServiceInstance, VfModule, VNF, PNF")
+ @Column(name = "resource_type", nullable = false)
+ var resourceType: String? = null
+
+ @get:ApiModelProperty(value = "ID associated with the resource type in the inventory system.", required = true)
+ @Column(name = "resource_id", nullable = false)
+ var resourceId: String? = null
+
+ @get:ApiModelProperty(value = "Status of the snapshot, either running or candidate.", required = true)
+ @Column(name = "status", nullable = false)
+ var status: Status? = null
+
+ @get:ApiModelProperty(value = "Snapshot of the resource as retrieved from resource.", required = true)
+ @Lob
+ @Column(name = "config_snapshot", nullable = false)
+ var config_snapshot: String? = null
+
+ @Id
+ @Column(name = "resource_config_snapshot_id")
+ var id: String? = null
+
+ @get:ApiModelProperty(value = "Creation date of the snapshot.", required = true)
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
+ @LastModifiedDate
+ @Temporal(TemporalType.TIMESTAMP)
+ @Column(name = "creation_date")
+ var createdDate = Date()
+
+ companion object {
+ private const val serialVersionUID = 1L
+ }
+
+ enum class Status(val state: String) {
+ RUNNING("RUNNING"),
+ CANDIDATE("CANDIDATE")
+ }
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotRepository.kt b/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotRepository.kt
new file mode 100644
index 000000000..4ab7d7f0e
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotRepository.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.cds.blueprintsprocessor.functions.config.snapshots.db
+
+import org.springframework.data.jpa.repository.JpaRepository
+import javax.transaction.Transactional
+
+/**
+ * JPA repository managing the underlying ResourceConfigSnapshot table.
+ *
+ * @author Serge Simard
+ * @version 1.0
+ */
+interface ResourceConfigSnapshotRepository : JpaRepository<ResourceConfigSnapshot, String> {
+
+ fun findByResourceIdAndResourceTypeAndStatus(
+ resourceId: String,
+ resourceType: String,
+ status : ResourceConfigSnapshot.Status): ResourceConfigSnapshot?
+
+ @Transactional
+ fun deleteByResourceIdAndResourceTypeAndStatus(
+ resourceId: String,
+ resourceType: String,
+ status : ResourceConfigSnapshot.Status)
+}
diff --git a/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotService.kt b/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotService.kt
new file mode 100644
index 000000000..50c90f332
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotService.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.cds.blueprintsprocessor.functions.config.snapshots.db
+
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.db.ResourceConfigSnapshot.Status.RUNNING
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException
+import org.slf4j.LoggerFactory
+import org.springframework.dao.DataIntegrityViolationException
+import org.springframework.stereotype.Service
+import java.util.*
+import kotlin.NoSuchElementException
+
+/**
+ * ResourceConfigSnapshot managing service.
+ *
+ * @author Serge Simard
+ * @version 1.0
+ */
+@Service
+class ResourceConfigSnapshotService(private val repository: ResourceConfigSnapshotRepository) {
+
+ private val log = LoggerFactory.getLogger(ResourceConfigSnapshotService::class.toString())
+
+ suspend fun findByResourceIdAndResourceTypeAndStatus(resourceId: String, resourceType: String,
+ status : ResourceConfigSnapshot.Status = RUNNING): String =
+ withContext(Dispatchers.IO) {
+ repository.findByResourceIdAndResourceTypeAndStatus(resourceId, resourceType, status)
+ ?.config_snapshot ?: throw NoSuchElementException()
+ }
+
+ suspend fun write(snapshot: String, resId: String, resType: String,
+ status: ResourceConfigSnapshot.Status = RUNNING) : ResourceConfigSnapshot =
+ withContext(Dispatchers.IO) {
+
+ val resourceConfigSnapshotEntry = ResourceConfigSnapshot()
+ resourceConfigSnapshotEntry.id = UUID.randomUUID().toString()
+ resourceConfigSnapshotEntry.resourceId = resId
+ resourceConfigSnapshotEntry.resourceType = resType
+ resourceConfigSnapshotEntry.status = status
+ resourceConfigSnapshotEntry.config_snapshot = snapshot
+
+ // Overwrite configuration snapshot entry of resId/resType
+ if (resId.isNotEmpty() && resType.isNotEmpty()) {
+ repository.findByResourceIdAndResourceTypeAndStatus(resId, resType, status)?.
+ let {
+ log.info("Overwriting configuration snapshot entry for resourceId=($resId), " +
+ "resourceType=($resType), status=($status)")
+ repository.deleteByResourceIdAndResourceTypeAndStatus(resId, resType, status)
+ }
+ }
+ var storedSnapshot: ResourceConfigSnapshot
+ try {
+ storedSnapshot = repository.saveAndFlush(resourceConfigSnapshotEntry)
+ log.info("Stored configuration snapshot for resourceId=($resId), " +
+ "resourceType=($resType), status=($status), " +
+ "dated=(${storedSnapshot.createdDate})")
+ } catch (ex: DataIntegrityViolationException) {
+ throw BluePrintException("Failed to store configuration snapshot entry.", ex)
+ }
+ storedSnapshot
+ }
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/config-snapshots/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/ComponentConfigSnapshotsExecutorTest.kt b/ms/blueprintsprocessor/functions/config-snapshots/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/ComponentConfigSnapshotsExecutorTest.kt
new file mode 100644
index 000000000..79dd93037
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/config-snapshots/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/ComponentConfigSnapshotsExecutorTest.kt
@@ -0,0 +1,365 @@
+/*
+ * Copyright © 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.cds.blueprintsprocessor.functions.ansible.executor
+
+import com.fasterxml.jackson.databind.JsonNode
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintProperties
+import org.onap.ccsdk.cds.blueprintsprocessor.core.BlueprintPropertyConfiguration
+import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
+import org.onap.ccsdk.cds.blueprintsprocessor.db.BluePrintDBLibConfiguration
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.ansible.executor.ComponentConfigSnapshotsExecutor.Companion.DIFF_JSON
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.ansible.executor.ComponentConfigSnapshotsExecutor.Companion.DIFF_XML
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.ansible.executor.ComponentConfigSnapshotsExecutor.Companion.OPERATION_DIFF
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.ansible.executor.ComponentConfigSnapshotsExecutor.Companion.OPERATION_FETCH
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.ansible.executor.ComponentConfigSnapshotsExecutor.Companion.OPERATION_STORE
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.db.ResourceConfigSnapshot
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.db.ResourceConfigSnapshotService
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
+import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive
+import org.onap.ccsdk.cds.controllerblueprints.core.config.BluePrintLoadConfiguration
+import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils
+import org.onap.ccsdk.cds.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.context.ContextConfiguration
+import org.springframework.test.context.TestPropertySource
+import org.springframework.test.context.junit4.SpringRunner
+
+@RunWith(SpringRunner::class)
+@ContextConfiguration(classes = [ResourceConfigSnapshotService::class,
+ BlueprintPropertyConfiguration::class, BluePrintProperties::class,
+ BluePrintDBLibConfiguration::class, BluePrintLoadConfiguration::class])
+@TestPropertySource(locations = ["classpath:application-test.properties"])
+@ComponentScan(basePackages = ["org.onap.ccsdk.cds.blueprintsprocessor", "org.onap.ccsdk.cds.controllerblueprints"])
+@EnableAutoConfiguration
+@Suppress("SameParameterValue")
+class ComponentConfigSnapshotsExecutorTest {
+
+ @Autowired
+ lateinit var cfgSnapshotService : ResourceConfigSnapshotService
+ lateinit var cfgSnapshotComponent : ComponentConfigSnapshotsExecutor
+ private var bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime("123456-1000",
+ "./../../../../components/model-catalog/blueprint-model/test-blueprint/remote_scripts")
+
+ private val resourceId = "1"
+ private val resourceType = "ServiceInstance"
+ private val props = mutableMapOf<String, JsonNode>()
+ private val nodeTemplateName = "nodeTemplateName"
+
+ private val executionRequest = ExecutionServiceInput()
+
+ @Before
+ fun setup() {
+ cfgSnapshotComponent = ComponentConfigSnapshotsExecutor(cfgSnapshotService)
+ props[ComponentConfigSnapshotsExecutor.INPUT_RESOURCE_ID] = resourceId.asJsonPrimitive()
+ props[ComponentConfigSnapshotsExecutor.INPUT_RESOURCE_TYPE] = resourceType.asJsonPrimitive()
+
+
+ cfgSnapshotComponent.operationInputs = props
+ cfgSnapshotComponent.bluePrintRuntimeService = bluePrintRuntimeService
+ cfgSnapshotComponent.nodeTemplateName = nodeTemplateName
+
+ cfgSnapshotComponent.executionServiceInput = executionRequest
+ cfgSnapshotComponent.processId = "12"
+ cfgSnapshotComponent.workflowName = "workflow"
+ cfgSnapshotComponent.stepName = "step"
+ cfgSnapshotComponent.interfaceName = "interfaceName"
+ cfgSnapshotComponent.operationName = "operationName"
+ }
+
+ @Test
+ fun processNBFetchWithResourceIdAndResourceTypeSingleFind() {
+ val snapshot = ResourceConfigSnapshot()
+ val snapshotConfig = "TEST1"
+ snapshot.config_snapshot = snapshotConfig
+
+ runBlocking {
+ try {
+ val resId = "121111"
+ val resType = "PNF"
+ cfgSnapshotService.write(snapshotConfig, resId, resType)
+ prepareRequestProperties(OPERATION_FETCH, resId, resType, ResourceConfigSnapshot.Status.RUNNING.name)
+
+ cfgSnapshotComponent.processNB(executionRequest)
+ } catch (e: BluePrintProcessorException) {
+ kotlin.test.assertEquals("Can't proceed with the cfg snapshot lookup: provide resource-id and resource-type.",
+ e.message)
+ return@runBlocking
+ }
+ // then; we should get success and the TEST1 payload in our output properties
+ assertEquals(ComponentConfigSnapshotsExecutor.OUTPUT_STATUS_SUCCESS.asJsonPrimitive(),
+ bluePrintRuntimeService.getNodeTemplateAttributeValue(nodeTemplateName,
+ ComponentConfigSnapshotsExecutor.OUTPUT_STATUS))
+ assertEquals(snapshotConfig.asJsonPrimitive(),
+ bluePrintRuntimeService.getNodeTemplateAttributeValue(nodeTemplateName,
+ ComponentConfigSnapshotsExecutor.OUTPUT_SNAPSHOT))
+ }
+ }
+
+ @Test
+ fun processNBFetchCandidateWithResourceIdAndResourceTypeSingleFind() {
+ val snapshot = ResourceConfigSnapshot()
+ val snapshotConfig = "TEST"
+ snapshot.config_snapshot = snapshotConfig
+
+ runBlocking {
+ try {
+ val resId = "121111"
+ val resType = "PNF"
+ cfgSnapshotService.write(snapshotConfig, resId, resType, ResourceConfigSnapshot.Status.CANDIDATE)
+ prepareRequestProperties(OPERATION_FETCH, resId, resType, ResourceConfigSnapshot.Status.CANDIDATE.name)
+
+ cfgSnapshotComponent.processNB(executionRequest)
+ } catch (e: BluePrintProcessorException) {
+ kotlin.test.assertEquals("Can't proceed with the cfg snapshot lookup: provide resource-id and resource-type.",
+ e.message)
+ return@runBlocking
+ }
+ // then; we should get success and the TEST payload in our output properties
+ assertEquals(ComponentConfigSnapshotsExecutor.OUTPUT_STATUS_SUCCESS.asJsonPrimitive(),
+ bluePrintRuntimeService.getNodeTemplateAttributeValue(nodeTemplateName,
+ ComponentConfigSnapshotsExecutor.OUTPUT_STATUS))
+ assertEquals(snapshotConfig.asJsonPrimitive(),
+ bluePrintRuntimeService.getNodeTemplateAttributeValue(nodeTemplateName,
+ ComponentConfigSnapshotsExecutor.OUTPUT_SNAPSHOT))
+ }
+ }
+
+ @Test
+ fun processNBStoreWithResourceIdAndResourceType() {
+ val snapshot = ResourceConfigSnapshot()
+ val snapshotConfig = "PAYLOAD"
+ snapshot.config_snapshot = snapshotConfig
+
+ runBlocking {
+ try {
+ val resId = "121111"
+ val resType = "PNF"
+ prepareRequestProperties(OPERATION_STORE, resId, resType, snapshotConfig)
+
+ cfgSnapshotComponent.processNB(executionRequest)
+
+ } catch (e: BluePrintProcessorException) {
+ kotlin.test.assertEquals("Can't proceed with the cfg snapshot lookup: provide resource-id and resource-type.",
+ e.message)
+ return@runBlocking
+ }
+
+ // then; we should get success and the PAYLOAD payload in our output properties
+ assertEquals(ComponentConfigSnapshotsExecutor.OUTPUT_STATUS_SUCCESS.asJsonPrimitive(),
+ bluePrintRuntimeService.getNodeTemplateAttributeValue(nodeTemplateName,
+ ComponentConfigSnapshotsExecutor.OUTPUT_STATUS))
+ assertEquals(snapshotConfig.asJsonPrimitive(),
+ bluePrintRuntimeService.getNodeTemplateAttributeValue(nodeTemplateName,
+ ComponentConfigSnapshotsExecutor.OUTPUT_SNAPSHOT))
+ }
+ }
+
+ @Test
+ fun processNBFetchNoneFound() {
+
+ runBlocking {
+ // when; asking for unknown resource Id/ resource Type combo; should get an error response
+ try {
+ prepareRequestProperties(OPERATION_FETCH, "asdasd", "PNF", ResourceConfigSnapshot.Status.RUNNING.name)
+
+ cfgSnapshotComponent.processNB(executionRequest)
+
+ } catch (e: BluePrintProcessorException) {
+ kotlin.test.assertEquals("Can't proceed with the cfg snapshot lookup: provide resource-id and resource-type.",
+ e.message)
+ return@runBlocking
+ }
+
+ // then; we should get error and the PAYLOAD payload in our output properties
+ assertTrue( bluePrintRuntimeService.getBluePrintError().errors.size > 0 )
+ assertEquals(ComponentConfigSnapshotsExecutor.OUTPUT_STATUS_ERROR.asJsonPrimitive(),
+ bluePrintRuntimeService.getNodeTemplateAttributeValue(nodeTemplateName,
+ ComponentConfigSnapshotsExecutor.OUTPUT_STATUS))
+ }
+ }
+
+ @Test
+ fun processNBErrorOperationUnknown() {
+
+ runBlocking {
+ // when; asking for unknown operation update; should get an error response
+ try {
+ prepareRequestProperties("update", "asdasd", "PNF", ResourceConfigSnapshot.Status.RUNNING.name)
+
+ cfgSnapshotComponent.processNB(executionRequest)
+
+ } catch (e: BluePrintProcessorException) {
+ kotlin.test.assertEquals("Can't proceed with the cfg snapshot lookup: provide resource-id and resource-type.",
+ e.message)
+ return@runBlocking
+ }
+
+ // then; we should get error in our output properties
+ assertTrue( bluePrintRuntimeService.getBluePrintError().errors.size == 1 )
+ assertEquals(ComponentConfigSnapshotsExecutor.OUTPUT_STATUS_ERROR.asJsonPrimitive(),
+ bluePrintRuntimeService.getNodeTemplateAttributeValue(nodeTemplateName,
+ ComponentConfigSnapshotsExecutor.OUTPUT_STATUS))
+ val msg = "Operation parameter must be fetch, store or diff"
+ assertEquals(msg.asJsonPrimitive(),
+ bluePrintRuntimeService.getNodeTemplateAttributeValue(nodeTemplateName,
+ ComponentConfigSnapshotsExecutor.OUTPUT_MESSAGE))
+ }
+ }
+
+ @Test
+ fun processNBErrorDiffContentTypeUnknown() {
+
+ runBlocking {
+ // when; asking for unknown content type diff operation; should get an error response
+ try {
+ prepareRequestProperties(OPERATION_DIFF, "asdasd", "PNF", "YANG")
+
+ cfgSnapshotComponent.processNB(executionRequest)
+
+ } catch (e: BluePrintProcessorException) {
+ kotlin.test.assertEquals("Can't proceed with the cfg snapshot lookup: provide resource-id and resource-type.",
+ e.message)
+ return@runBlocking
+ }
+
+ // then; we should get error in our output properties
+ assertTrue( bluePrintRuntimeService.getBluePrintError().errors.size == 1 )
+ assertEquals(ComponentConfigSnapshotsExecutor.OUTPUT_STATUS_ERROR.asJsonPrimitive(),
+ bluePrintRuntimeService.getNodeTemplateAttributeValue(nodeTemplateName,
+ ComponentConfigSnapshotsExecutor.OUTPUT_STATUS))
+ val message = "Could not compare config snapshots for type YANG"
+ assertEquals(message.asJsonPrimitive(),
+ bluePrintRuntimeService.getNodeTemplateAttributeValue(nodeTemplateName,
+ ComponentConfigSnapshotsExecutor.OUTPUT_MESSAGE))
+ }
+ }
+
+ @Test
+ fun processNBCompareTwoJsonConfigSnapshots() {
+
+ runBlocking {
+
+ // when; comparing RUNNING vs CANDIDATE json configs; should get an success response; with differences
+ try {
+ val resId = "131313"
+ val resType = "PNF"
+ preparePayload("config-payload-running.json", resId, resType, ResourceConfigSnapshot.Status.RUNNING)
+ preparePayload("config-payload-candidate.json", resId, resType, ResourceConfigSnapshot.Status.CANDIDATE)
+
+ prepareRequestProperties(OPERATION_DIFF, resId, resType, DIFF_JSON)
+ cfgSnapshotComponent.processNB(executionRequest)
+
+ } catch (e: BluePrintProcessorException) {
+ kotlin.test.assertEquals("Can't proceed with the cfg snapshot diff: provide resource-id and resource-type.",
+ e.message)
+ return@runBlocking
+ }
+
+ // then; we should get success
+ assertTrue( bluePrintRuntimeService.getBluePrintError().errors.size == 0 )
+ assertEquals(ComponentConfigSnapshotsExecutor.OUTPUT_STATUS_SUCCESS.asJsonPrimitive(),
+ bluePrintRuntimeService.getNodeTemplateAttributeValue(nodeTemplateName,
+ ComponentConfigSnapshotsExecutor.OUTPUT_STATUS))
+
+ // then; we should get JSON-patches differences in our response property
+ val diffJson = "[{\"op\":\"add\",\"path\":\"/system-uptime-information/last-configured-time/new-child-object\",\"value\":{\"property\":\"value\"}}," +
+ "{\"op\":\"replace\",\"path\":\"/system-uptime-information/system-booted-time/time-length\",\"value\":\"14:52:54\"}," +
+ "{\"op\":\"replace\",\"path\":\"/system-uptime-information/time-source\",\"value\":\" DNS CLOCK \"}," +
+ "{\"op\":\"add\",\"path\":\"/system-uptime-information/uptime-information/load-average-10\",\"value\":\"0.05\"}]"
+ assertEquals(diffJson.asJsonPrimitive(),
+ bluePrintRuntimeService.getNodeTemplateAttributeValue(nodeTemplateName,
+ ComponentConfigSnapshotsExecutor.OUTPUT_SNAPSHOT))
+ }
+ }
+
+ @Test
+ fun processNBCompareTwoXmlConfigSnapshots() {
+
+ runBlocking {
+
+ // when; comparing RUNNING vs CANDIDATE xml configs; should get an success response; with differences
+ try {
+ val resId = "141414"
+ val resType = "VNF"
+ preparePayload("interface-running.xml", resId, resType, ResourceConfigSnapshot.Status.RUNNING)
+ preparePayload("interface-candidate.xml", resId, resType, ResourceConfigSnapshot.Status.CANDIDATE)
+
+ prepareRequestProperties(OPERATION_DIFF, resId, resType, DIFF_XML)
+
+ cfgSnapshotComponent.processNB(executionRequest)
+
+ } catch (e: BluePrintProcessorException) {
+ kotlin.test.assertEquals("Can't proceed with the cfg snapshot diff: provide resource-id and resource-type.",
+ e.message)
+ return@runBlocking
+ }
+
+ // then; we should get success
+ assertTrue( bluePrintRuntimeService.getBluePrintError().errors.size == 0 )
+ assertEquals(ComponentConfigSnapshotsExecutor.OUTPUT_STATUS_SUCCESS.asJsonPrimitive(),
+ bluePrintRuntimeService.getNodeTemplateAttributeValue(nodeTemplateName,
+ ComponentConfigSnapshotsExecutor.OUTPUT_STATUS))
+
+ // then; we should get XML-patches differences in our response property
+ val diffXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
+ "<diff>" +
+ "<replace sel=\"/output[1]/interface-information[1]/interface-flapped[1]/@seconds\">2343</replace>" +
+ "<replace sel=\"/output[1]/interface-information[1]/interface-flapped[1]/text()[1]\">34</replace>" +
+ "<replace sel=\"/output[1]/interface-information[1]/traffic-statistics[1]/input-packets[1]/text()[1]\">09098789</replace>" +
+ "<replace sel=\"/output[1]/interface-information[1]/traffic-statistics[1]/output-packets[1]/text()[1]\">2828828</replace>" +
+ "<add sel=\"/output[1]/interface-information[1]/physical-interface[1]\"><interface-name>TEGig400-int01</interface-name></add>" +
+ "</diff>"
+ assertEquals(diffXml.asJsonPrimitive(),
+ bluePrintRuntimeService.getNodeTemplateAttributeValue(nodeTemplateName,
+ ComponentConfigSnapshotsExecutor.OUTPUT_SNAPSHOT)) }
+ }
+
+ private fun preparePayload(filename : String, resId : String, resType : String, status: ResourceConfigSnapshot.Status) {
+ runBlocking {
+ cfgSnapshotService.write(JacksonUtils.getClassPathFileContent("payload/requests/$filename"), resId, resType, status)
+ }
+ }
+
+ private fun prepareRequestProperties (oper : String, resId : String, resType : String, optional: String = "") {
+ cfgSnapshotComponent.operationInputs[ComponentConfigSnapshotsExecutor.INPUT_OPERATION] = oper.asJsonPrimitive()
+ cfgSnapshotComponent.operationInputs[ComponentConfigSnapshotsExecutor.INPUT_RESOURCE_ID] = resId.asJsonPrimitive()
+ cfgSnapshotComponent.operationInputs[ComponentConfigSnapshotsExecutor.INPUT_RESOURCE_TYPE] = resType.asJsonPrimitive()
+
+ // Optional inputs
+ cfgSnapshotComponent.operationInputs[ComponentConfigSnapshotsExecutor.INPUT_DIFF_CONTENT_TYPE] = "".asJsonPrimitive()
+ cfgSnapshotComponent.operationInputs[ComponentConfigSnapshotsExecutor.INPUT_RESOURCE_SNAPSHOT] = "".asJsonPrimitive()
+ cfgSnapshotComponent.operationInputs[ComponentConfigSnapshotsExecutor.INPUT_RESOURCE_STATUS] =
+ ResourceConfigSnapshot.Status.RUNNING.name.asJsonPrimitive()
+
+ when (oper) {
+ OPERATION_DIFF ->
+ cfgSnapshotComponent.operationInputs[ComponentConfigSnapshotsExecutor.INPUT_DIFF_CONTENT_TYPE] = optional.asJsonPrimitive()
+ OPERATION_STORE ->
+ cfgSnapshotComponent.operationInputs[ComponentConfigSnapshotsExecutor.INPUT_RESOURCE_SNAPSHOT] = optional.asJsonPrimitive()
+ OPERATION_FETCH ->
+ cfgSnapshotComponent.operationInputs[ComponentConfigSnapshotsExecutor.INPUT_RESOURCE_STATUS] = optional.asJsonPrimitive()
+ }
+ }
+}
diff --git a/ms/blueprintsprocessor/functions/config-snapshots/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotServiceTest.kt b/ms/blueprintsprocessor/functions/config-snapshots/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotServiceTest.kt
new file mode 100644
index 000000000..2830cb547
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/config-snapshots/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotServiceTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright © 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.cds.blueprintsprocessor.functions.config.snapshots.db
+
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.verify
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+class ResourceConfigSnapshotServiceTest {
+
+ private val cfgRepository = mockk<ResourceConfigSnapshotRepository>()
+
+ private val cfgService = ResourceConfigSnapshotService(cfgRepository)
+
+ private val resourceId = "1"
+ private val resourceType = "PNF"
+ private val configSnapshot = "config_snapshot"
+ private val resourceStatus = ResourceConfigSnapshot.Status.RUNNING
+
+ @Test
+ fun findByResourceIdAndResourceTypeTest() {
+ val tr = ResourceConfigSnapshot()
+ tr.config_snapshot = "res"
+ runBlocking {
+ every {
+ cfgRepository.findByResourceIdAndResourceTypeAndStatus(any(), any(), any())
+ } returns tr
+ val res = cfgService.findByResourceIdAndResourceTypeAndStatus(resourceId, resourceType)
+ assertEquals(tr.config_snapshot, res)
+ }
+ }
+
+ @Test(expected = NoSuchElementException::class)
+ fun notFoundEntryReturnsExceptionTest() {
+ val tr = ResourceConfigSnapshot()
+ runBlocking {
+ every {
+ cfgRepository.findByResourceIdAndResourceTypeAndStatus(any(), any(), any())
+ } returns tr
+ val snap = cfgService.findByResourceIdAndResourceTypeAndStatus("MISSING_ID", "UNKNOWN_TYPE")
+ assertTrue ( snap.isBlank(), "Not found but returned a non empty string" )
+ }
+ }
+
+ @Test
+ fun createNewResourceConfigSnapshotTest() {
+ val tr = ResourceConfigSnapshot()
+ runBlocking {
+ every { cfgRepository.saveAndFlush(any<ResourceConfigSnapshot>()) } returns tr
+ every {
+ cfgRepository.findByResourceIdAndResourceTypeAndStatus(any(), any(), any())
+ } returns null
+ val res = cfgService.write( configSnapshot, resourceId, resourceType, resourceStatus)
+ assertEquals(tr, res)
+ }
+ }
+
+ @Test
+ fun updateExistingResourceConfigSnapshotTest() {
+ val tr = ResourceConfigSnapshot()
+ runBlocking {
+ every { cfgRepository.saveAndFlush(any<ResourceConfigSnapshot>()) } returns tr
+ every {
+ cfgRepository.findByResourceIdAndResourceTypeAndStatus(any(), any(), any())
+ } returns tr
+ every {
+ cfgRepository.deleteByResourceIdAndResourceTypeAndStatus(any(), any(), any())
+ } returns Unit
+ val res = cfgService.write( configSnapshot, resourceId, resourceType)
+ verify {
+ cfgRepository.deleteByResourceIdAndResourceTypeAndStatus(eq(resourceId), eq(resourceType), eq(resourceStatus))
+ }
+ assertEquals(tr, res)
+ }
+ }
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/application-test.properties b/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/application-test.properties
new file mode 100644
index 000000000..ce5b4e39f
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/application-test.properties
@@ -0,0 +1,32 @@
+# Copyright © 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.
+
+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
+
+# Executor Options
+blueprintprocessor.netconfExecutor.enabled=true \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/keystore.p12 b/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/keystore.p12
new file mode 100644
index 000000000..96b0d3ac3
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/keystore.p12
Binary files differ
diff --git a/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/logback-test.xml b/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/logback-test.xml
new file mode 100644
index 000000000..f33adcdb8
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/logback-test.xml
@@ -0,0 +1,35 @@
+<!--
+ ~ Copyright © 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.
+ -->
+
+<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{55} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+
+ <logger name="org.springframework" level="warn"/>
+ <logger name="org.hibernate" level="info"/>
+ <logger name="org.onap.ccsdk.cds.blueprintsprocessor" level="info"/>
+
+ <root level="warn">
+ <appender-ref ref="STDOUT"/>
+ </root>
+
+</configuration>
diff --git a/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/payload/requests/config-payload-candidate.json b/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/payload/requests/config-payload-candidate.json
new file mode 100644
index 000000000..ad012878d
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/payload/requests/config-payload-candidate.json
@@ -0,0 +1,39 @@
+{
+ "system-uptime-information" :
+ {
+ "current-time" :
+ {
+ "date-time" : "2018-05-15 13:49:56 PDT"
+ },
+ "time-source" : " DNS CLOCK ",
+ "system-booted-time" :
+ {
+ "date-time" : "2018-05-15 10:57:02 PDT",
+ "time-length" : "14:52:54"
+ },
+ "protocols-started-time" :
+ {
+ "date-time" : "2018-05-15 10:59:33 PDT",
+ "time-length" : "02:50:23"
+ },
+ "last-configured-time" :
+ {
+ "date-time" : "2018-05-15 13:49:40 PDT",
+ "time-length" : "00:00:16",
+ "user" : "admin",
+ "new-child-object" : {
+ "property" : "value"
+ }
+ },
+ "uptime-information" :
+ {
+ "date-time" : "1:49PM",
+ "up-time" : "2:53",
+ "active-user-count" : "1",
+ "load-average-1" : "0.00",
+ "load-average-5" : "0.06",
+ "load-average-10" : "0.05",
+ "load-average-15" : "0.06"
+ }
+ }
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/payload/requests/config-payload-running.json b/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/payload/requests/config-payload-running.json
new file mode 100644
index 000000000..9857eb978
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/payload/requests/config-payload-running.json
@@ -0,0 +1,35 @@
+{
+ "system-uptime-information" :
+ {
+ "current-time" :
+ {
+ "date-time" : "2018-05-15 13:49:56 PDT"
+ },
+ "time-source" : " NTP CLOCK ",
+ "system-booted-time" :
+ {
+ "date-time" : "2018-05-15 10:57:02 PDT",
+ "time-length" : "02:52:54"
+ },
+ "protocols-started-time" :
+ {
+ "date-time" : "2018-05-15 10:59:33 PDT",
+ "time-length" : "02:50:23"
+ },
+ "last-configured-time" :
+ {
+ "date-time" : "2018-05-15 13:49:40 PDT",
+ "time-length" : "00:00:16",
+ "user" : "admin"
+ },
+ "uptime-information" :
+ {
+ "date-time" : "1:49PM",
+ "up-time" : "2:53",
+ "active-user-count" : "1",
+ "load-average-1" : "0.00",
+ "load-average-5" : "0.06",
+ "load-average-15" : "0.06"
+ }
+ }
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/payload/requests/interface-candidate.xml b/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/payload/requests/interface-candidate.xml
new file mode 100644
index 000000000..d0673aa4e
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/payload/requests/interface-candidate.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright © 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.
+ -->
+<output xmlns="http://yang.juniper.net/junos-qfx/rpc/interfaces">
+ <interface-information xmlns:junos="http://xml.juniper.net/junos/17.4R1/junos" junos:style="normal">
+ <ifd-specific-config-flags />
+ <if-config-flags />
+ <link-type>Full-Duplex</link-type>
+ <if-media-flags>
+ <ifmf-none />
+ </if-media-flags>
+ <interface-flapped junos:seconds="2343">34</interface-flapped>
+ <traffic-statistics junos:style="brief">
+ <input-packets>09098789</input-packets>
+ <output-packets>2828828</output-packets>
+ </traffic-statistics>
+ <physical-interface>
+ <interface-name>TEGig400-int01</interface-name>
+ </physical-interface>
+ </interface-information>
+</output> \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/payload/requests/interface-running.xml b/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/payload/requests/interface-running.xml
new file mode 100644
index 000000000..3d875c8fb
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/config-snapshots/src/test/resources/payload/requests/interface-running.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright © 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.
+ -->
+<output xmlns="http://yang.juniper.net/junos-qfx/rpc/interfaces">
+ <interface-information xmlns:junos="http://xml.juniper.net/junos/17.4R1/junos" junos:style="normal">
+ <ifd-specific-config-flags />
+ <if-config-flags />
+ <link-type>Full-Duplex</link-type>
+ <if-media-flags>
+ <ifmf-none />
+ </if-media-flags>
+ <interface-flapped junos:seconds="0">Never</interface-flapped>
+ <traffic-statistics junos:style="brief">
+ <input-packets>0</input-packets>
+ <output-packets>0</output-packets>
+ </traffic-statistics>
+ <physical-interface/>
+ </interface-information>
+</output> \ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/pom.xml b/ms/blueprintsprocessor/functions/pom.xml
index 819373ffd..a5790b985 100755
--- a/ms/blueprintsprocessor/functions/pom.xml
+++ b/ms/blueprintsprocessor/functions/pom.xml
@@ -34,6 +34,7 @@
<module>netconf-executor</module>
<module>restconf-executor</module>
<module>cli-executor</module>
+ <module>config-snapshots</module>
</modules>
<dependencies>