diff options
author | Juhi Arora <juhi.arora1@bell.ca> | 2022-06-06 13:30:03 -0400 |
---|---|---|
committer | kuldipr <kuldip.rai@amdocs.com> | 2022-09-01 08:48:15 -0400 |
commit | 89c17ae2e3222b98450e7c8d17566cd76ecf113b (patch) | |
tree | d8ca903df5dd3c189d87aef3dac6065f22e1b303 | |
parent | e8e0087c6aea0bf9b2d3d17207574e8db84ba0f3 (diff) |
CDS max-occurrence feature
As part of occurrence feature, one or more version of the resource
resolution can be resolved. However, user did not have granular
control in case the user wants to resolve a specific value once and
never again.
Max-Occurrence feature implements the granular control to be give the
user an option to specify the max number of times a resource to be
resolved. It is specified as part of mapping in a cba. Max-occurrence
value of 0 or not specifying it explicitly denotes the current default
behaviour of unlimited resoltions. If a user specify a particular
max-occurrence value then the resource is resolved that many times in
subsquent requests and never again once we reached the max-occurrence
limit of resource resolutions.
Issue-ID: CCSDK-3736
Change-Id: Ie18764a313530e36be14531d8c7b93bf54f0b651
Signed-off-by: kuldipr <kuldip.rai@amdocs.com>
11 files changed, 308 insertions, 3 deletions
diff --git a/components/model-catalog/blueprint-model/test-blueprint/baseconfiguration/Definitions/activation-blueprint.json b/components/model-catalog/blueprint-model/test-blueprint/baseconfiguration/Definitions/activation-blueprint.json index 85a056c5e..c506ad05e 100644 --- a/components/model-catalog/blueprint-model/test-blueprint/baseconfiguration/Definitions/activation-blueprint.json +++ b/components/model-catalog/blueprint-model/test-blueprint/baseconfiguration/Definitions/activation-blueprint.json @@ -180,6 +180,14 @@ "type": "artifact-mapping-resource", "file": "Definitions/another-mapping.json" }, + "maxoccurrence-template": { + "type": "artifact-template-velocity", + "file": "Templates/maxoccurrence-template.vtl" + }, + "maxoccurrence-mapping": { + "type": "artifact-mapping-resource", + "file": "Definitions/maxoccurrence-mapping.json" + }, "notemplate-mapping": { "type": "artifact-mapping-resource", "file": "Definitions/notemplate-mapping.json" diff --git a/components/model-catalog/blueprint-model/test-blueprint/baseconfiguration/Definitions/data_types.json b/components/model-catalog/blueprint-model/test-blueprint/baseconfiguration/Definitions/data_types.json index 6d771cd68..94c587af7 100644 --- a/components/model-catalog/blueprint-model/test-blueprint/baseconfiguration/Definitions/data_types.json +++ b/components/model-catalog/blueprint-model/test-blueprint/baseconfiguration/Definitions/data_types.json @@ -51,6 +51,12 @@ "vnf_name": { "required": true, "type": "string" + }, + "firmware-version": { + "type" : "string" + }, + "ip-address": { + "type" : "string" } }, "derived_from": "tosca.datatypes.Dynamic" diff --git a/components/model-catalog/blueprint-model/test-blueprint/baseconfiguration/Definitions/maxoccurrence-mapping.json b/components/model-catalog/blueprint-model/test-blueprint/baseconfiguration/Definitions/maxoccurrence-mapping.json new file mode 100644 index 000000000..729488520 --- /dev/null +++ b/components/model-catalog/blueprint-model/test-blueprint/baseconfiguration/Definitions/maxoccurrence-mapping.json @@ -0,0 +1,23 @@ +[ + { + "name": "firmware-version", + "input-param": true, + "property": { + "type": "string" + }, + "dictionary-name": "input-source", + "dictionary-source": "input", + "dependencies": [] + }, + { + "name": "ip-address", + "input-param": true, + "property": { + "type": "string" + }, + "max-occurrence": 1, + "dictionary-name": "input-source", + "dictionary-source": "input", + "dependencies": [] + } +]
\ No newline at end of file diff --git a/components/model-catalog/blueprint-model/test-blueprint/baseconfiguration/Templates/maxoccurrence-template.vtl b/components/model-catalog/blueprint-model/test-blueprint/baseconfiguration/Templates/maxoccurrence-template.vtl new file mode 100644 index 000000000..9a9fc27eb --- /dev/null +++ b/components/model-catalog/blueprint-model/test-blueprint/baseconfiguration/Templates/maxoccurrence-template.vtl @@ -0,0 +1 @@ +This is maxoccurrence Velocity Template
\ No newline at end of file diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionService.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionService.kt index a3c137807..427d48f3c 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionService.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionService.kt @@ -218,7 +218,18 @@ open class ResourceResolutionServiceImpl( val occurrence = findNextOccurrence(bluePrintRuntimeService, properties, artifactPrefix) log.info("Always perform new resolutions - next occurrence: $occurrence") propertiesMutableMap[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] = occurrence - // Since we are performing new resolution, simply pass empty list. + // If resolution has already been performed previously then may need to update some assignments + val resourceAssignmentFilteredMap: Map<String, ResourceAssignment> = + resourceAssignments.filter { it.maxOccurrence != null && it.maxOccurrence!! in 1 until occurrence } + .associateBy { it.name } + if (occurrence > 1 && resourceAssignmentFilteredMap.isNotEmpty()) + updateResourceAssignmentByOccurrence( + bluePrintRuntimeService as ResourceAssignmentRuntimeService, + propertiesMutableMap, + artifactPrefix, + resourceAssignmentFilteredMap as MutableMap<String, ResourceAssignment> + ) + // we may be performing new resolution for some resources, simply pass an empty list. emptyList() } else { isNewResolution(bluePrintRuntimeService, properties, artifactPrefix) @@ -500,6 +511,92 @@ open class ResourceResolutionServiceImpl( } } + /** + * Utility to update the resourceAssignments with existing resolutions if maxOccurrence is defined on it. + */ + private suspend fun updateResourceAssignmentByOccurrence( + raRuntimeService: ResourceAssignmentRuntimeService, + properties: Map<String, Any>, + artifactPrefix: String, + resourceAssignmentFilteredMap: MutableMap<String, ResourceAssignment> + ) { + val resourceResolutionMap = + getResourceResolutionByLastOccurrence(raRuntimeService, properties, artifactPrefix).associateBy { it.name } + + resourceAssignmentFilteredMap + .filter { + resourceResolutionMap.containsKey(it.key) && compareOne( + resourceResolutionMap[it.key]!!, + it.value + ) + } + .forEach { raMapEntry -> + val resourceResolution = resourceResolutionMap[raMapEntry.key] + val resourceAssignment = raMapEntry.value + + resourceResolution?.value?.let { + val value = it.asJsonType(resourceAssignment.property!!.type) + resourceAssignment.property!!.value = value + ResourceAssignmentUtils.setResourceDataValue( + resourceAssignment, + raRuntimeService, + value + ) + } + resourceAssignment.status = resourceResolution?.status + resourceResolutionDBService.write( + properties, + raRuntimeService, + artifactPrefix, + resourceAssignment + ) + } + } + + /** + * Utility to find resource resolution based on occurrence. + */ + private suspend fun getResourceResolutionByLastOccurrence( + bluePrintRuntimeService: BluePrintRuntimeService<*>, + properties: Map<String, Any>, + artifactPrefix: String + ): List<ResourceResolution> { + + val resolutionKey = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] as String + val resourceId = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] as String + val resourceType = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] as String + + // This should not happen since the request has already been validated but worth to check it here as well. + if (resourceType.isEmpty() && resourceId.isEmpty() && resolutionKey.isEmpty()) { + throw BluePrintProcessorException( + "Can't proceed to get last occurrence: " + + "Either provide a resolution-key OR combination of resource-id and resource-type" + ) + } + val metadata = bluePrintRuntimeService.bluePrintContext().metadata!! + val blueprintVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION]!! + val blueprintName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME]!! + + return if (resolutionKey.isNotEmpty()) { + resourceResolutionDBService.findLastNOccurrences( + blueprintName, + blueprintVersion, + artifactPrefix, + resolutionKey, + 1 + ).takeIf { it.isNotEmpty() }?.values?.first() ?: emptyList() + } else { + resourceResolutionDBService.findLastNOccurrences( + blueprintName, + blueprintVersion, + artifactPrefix, + resourceId, + resourceType, + 1 + ).takeIf { it.isNotEmpty() }?.values?.first() ?: emptyList() + } + } + // Comparison between what we have in the database vs what we have to assign. private fun compareOne(resourceResolution: ResourceResolution, resourceAssignment: ResourceAssignment): Boolean { return ( diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionDBService.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionDBService.kt index aa7b61f6a..bd8f9d27d 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionDBService.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionDBService.kt @@ -170,6 +170,35 @@ class ResourceResolutionDBService(private val resourceResolutionRepository: Reso } /** + * This returns the resolutions of last N 'occurrences'. + * + * @param blueprintName + * @param blueprintVersion + * @param artifactPrefix + * @param resourceId + * @param resourceType + * @param lastN + */ + suspend fun findLastNOccurrences( + blueprintName: String, + blueprintVersion: String, + artifactPrefix: String, + resourceId: String, + resourceType: String, + lastN: Int + ): Map<Int, List<ResourceResolution>> = withContext(Dispatchers.IO) { + + resourceResolutionRepository.findLastNOccurrences( + resourceId, + resourceType, + blueprintName, + blueprintVersion, + artifactPrefix, + lastN + ).groupBy(ResourceResolution::occurrence).toSortedMap(reverseOrder()) + } + + /** * This returns the resolutions with 'occurrence' value between begin and end. * * @param blueprintName diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionRepository.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionRepository.kt index 9317a71cd..5861cf8cb 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionRepository.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionRepository.kt @@ -77,6 +77,29 @@ interface ResourceResolutionRepository : JpaRepository<ResourceResolution, Strin @Query( value = """ + SELECT * FROM RESOURCE_RESOLUTION WHERE resource_id = :resourceId + AND resource_type =:resourceType AND blueprint_name = :blueprintName + AND blueprint_version = :blueprintVersion AND artifact_name = :artifactName + AND occurrence > ( + select max(occurrence) - :lastN from RESOURCE_RESOLUTION + WHERE resource_id = :resourceId + AND resource_type =:resourceType AND blueprint_name = :blueprintName + AND blueprint_version = :blueprintVersion AND artifact_name = :artifactName) + ORDER BY occurrence DESC, creation_date DESC + """, + nativeQuery = true + ) + fun findLastNOccurrences( + @Param("resourceId")resourceId: String, + @Param("resourceType")resourceType: String, + @Param("blueprintName")blueprintName: String, + @Param("blueprintVersion")blueprintVersion: String, + @Param("artifactName")artifactName: String, + @Param("lastN")begin: Int + ): List<ResourceResolution> + + @Query( + value = """ SELECT * FROM RESOURCE_RESOLUTION WHERE resolution_key = :key AND blueprint_name = :blueprintName AND blueprint_version = :blueprintVersion AND artifact_name = :artifactName AND occurrence BETWEEN :begin AND :end diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionServiceTest.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionServiceTest.kt index a801a7eb9..b39e709d0 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionServiceTest.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionServiceTest.kt @@ -249,6 +249,116 @@ class ResourceResolutionServiceTest { } /** + * Always perform new resolution even if resolution exists in the database. + */ + @Test + @Throws(Exception::class) + fun testResolveResourcesForMaxOccurrence() { + runBlocking { + // Occurrence <= 0 indicates to perform new resolution even if resolution exists in the database. + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] = -1 + Assert.assertNotNull("failed to create ResourceResolutionService", resourceResolutionService) + + // Run time for Request#1 + val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime( + "1234", + "./../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration" + ) + + // Request#1 + val executionServiceInput = + JacksonUtils.readValueFromClassPathFile( + "payload/requests/sample-resourceresolution-request.json", + ExecutionServiceInput::class.java + )!! + + // Prepare inputs from Request#1 + PayloadUtils.prepareInputsFromWorkflowPayload( + bluePrintRuntimeService, + executionServiceInput.payload, + "resource-assignment" + ) + + val resourceAssignmentRuntimeService = + ResourceAssignmentUtils.transformToRARuntimeService( + bluePrintRuntimeService, + "testResolveResource" + ) + + // Resolve resources as per Request#1 + resourceResolutionService.resolveResources( + resourceAssignmentRuntimeService, + "resource-assignment", + "maxoccurrence", + props + ) + + // Run time for Request#2 + val bluePrintRuntimeService2 = BluePrintMetadataUtils.getBluePrintRuntime( + "1234", + "./../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration" + ) + + // Request#2 + val executionServiceInput2 = + JacksonUtils.readValueFromClassPathFile( + "payload/requests/sample-resourceresolution-request2.json", + ExecutionServiceInput::class.java + )!! + + val resourceAssignmentRuntimeService2 = + ResourceAssignmentUtils.transformToRARuntimeService( + bluePrintRuntimeService2, + "testResolveResource" + ) + + // Prepare inputs from Request#2 + PayloadUtils.prepareInputsFromWorkflowPayload( + bluePrintRuntimeService2, + executionServiceInput2.payload, + "resource-assignment" + ) + + // Resolve resources as per Request#2 + resourceResolutionService.resolveResources( + resourceAssignmentRuntimeService2, + "resource-assignment", + "maxoccurrence", + props + ) + }.let { (template, assignmentList) -> + assertEquals("This is maxoccurrence Velocity Template", template) + + val assignmentListForRequest1 = mutableListOf( + "firmware-version" to "firmware-version-0", + "ip-address" to "192.0.0.1" + ) + val assignmentListForRequest2 = mutableListOf( + "firmware-version" to "firmware-version-1", + "ip-address" to "192.0.0.1" + ) + assertEquals(assignmentListForRequest1.size, assignmentList.size) + assertEquals(assignmentListForRequest2.size, assignmentList.size) + + // firmware-version has max-occurrence = 0 means perform new resolution all the time. + // ip-address has max-occurrence = 1 so its resolution should only be done once. + // + // AlwaysPerformNewResolution + max-occurrence feature use case - resolution request #2 should returns + // the resolution as per assignmentListForRequest2 since new resolution is only performed for + // firmware-version and not for ip-address. + var areEqual = assignmentListForRequest1.zip(assignmentList).all { (it1, it2) -> + it1.first == it2.name && it1.second == it2.property?.value?.asText() ?: null + } + assertEquals(false, areEqual) + + areEqual = assignmentListForRequest2.zip(assignmentList).all { (it1, it2) -> + it1.first == it2.name && it1.second == it2.property?.value?.asText() ?: null + } + assertEquals(true, areEqual) + } + } + + /** * Don't perform new resolution in case resolution already exists in the database. */ @Test diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/test/resources/payload/requests/sample-resourceresolution-request.json b/ms/blueprintsprocessor/functions/resource-resolution/src/test/resources/payload/requests/sample-resourceresolution-request.json index 3ca754f87..e247ea248 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/test/resources/payload/requests/sample-resourceresolution-request.json +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/test/resources/payload/requests/sample-resourceresolution-request.json @@ -25,7 +25,10 @@ "action-name": "assign-activate", "scope-type": "vnf-type", "hostname": "localhost", - "vnf_name": "temp_vnf" + "vnf_name": "temp_vnf", + "firmware-version": "firmware-version-0", + "ip-address": "192.0.0.1" + } } } diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/test/resources/payload/requests/sample-resourceresolution-request2.json b/ms/blueprintsprocessor/functions/resource-resolution/src/test/resources/payload/requests/sample-resourceresolution-request2.json index 726477515..17f5aad52 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/test/resources/payload/requests/sample-resourceresolution-request2.json +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/test/resources/payload/requests/sample-resourceresolution-request2.json @@ -24,7 +24,9 @@ "action-name": "assign-activate", "scope-type": "vnf-type", "hostname": "localhost", - "vnf_name": "temp_vnf_new_resolution" + "vnf_name": "temp_vnf_new_resolution", + "firmware-version": "firmware-version-1", + "ip-address": "192.0.0.2" } } } diff --git a/ms/blueprintsprocessor/modules/blueprints/resource-dict/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/resource/dict/ResourceDefinition.kt b/ms/blueprintsprocessor/modules/blueprints/resource-dict/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/resource/dict/ResourceDefinition.kt index 2e7e18258..f92548612 100644 --- a/ms/blueprintsprocessor/modules/blueprints/resource-dict/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/resource/dict/ResourceDefinition.kt +++ b/ms/blueprintsprocessor/modules/blueprints/resource-dict/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/resource/dict/ResourceDefinition.kt @@ -62,6 +62,9 @@ open class ResourceAssignment { @JsonProperty(value = "property") var property: PropertyDefinition? = null + @JsonProperty(value = "max-occurrence") + var maxOccurrence: Int? = null + @JsonProperty("input-param") var inputParameter: Boolean = false |