From 89c17ae2e3222b98450e7c8d17566cd76ecf113b Mon Sep 17 00:00:00 2001 From: Juhi Arora Date: Mon, 6 Jun 2022 13:30:03 -0400 Subject: 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 --- .../resolution/ResourceResolutionService.kt | 99 ++++++++++++++++++- .../resolution/db/ResourceResolutionDBService.kt | 29 ++++++ .../resolution/db/ResourceResolutionRepository.kt | 23 +++++ .../resolution/ResourceResolutionServiceTest.kt | 110 +++++++++++++++++++++ .../sample-resourceresolution-request.json | 5 +- .../sample-resourceresolution-request2.json | 4 +- 6 files changed, 267 insertions(+), 3 deletions(-) (limited to 'ms/blueprintsprocessor/functions/resource-resolution/src') 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 = + 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 + ) + // 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, + artifactPrefix: String, + resourceAssignmentFilteredMap: MutableMap + ) { + 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, + artifactPrefix: String + ): List { + + 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 @@ -169,6 +169,35 @@ class ResourceResolutionDBService(private val resourceResolutionRepository: Reso ).groupBy(ResourceResolution::occurrence).toSortedMap(reverseOrder()) } + /** + * 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> = 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. * 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 @@ -75,6 +75,29 @@ interface ResourceResolutionRepository : JpaRepository + @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 + @Query( value = """ SELECT * FROM RESOURCE_RESOLUTION WHERE resolution_key = :key 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 @@ -248,6 +248,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. */ 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" } } } -- cgit 1.2.3-korg