From: Amichai Hemli Date: Tue, 5 Nov 2019 06:53:37 +0000 (+0000) Subject: Merge "vfModule upgrade: don't fallback on mismatching newest model" X-Git-Tag: 6.0.1~206 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=1612c8702b6d48c4711bda86c4fa715ea2f1d880;hp=5e0e88d7c19479494215af17d23e07b26dd62dbc;p=vid.git Merge "vfModule upgrade: don't fallback on mismatching newest model" --- diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/InstanceGroupCommand.kt b/vid-app-common/src/main/java/org/onap/vid/job/command/InstanceGroupCommand.kt index 26fb9aa09..886251a0c 100644 --- a/vid-app-common/src/main/java/org/onap/vid/job/command/InstanceGroupCommand.kt +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/InstanceGroupCommand.kt @@ -67,14 +67,13 @@ class InstanceGroupCommand @Autowired constructor( } override fun planCreateMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String, testApi: String?): MsoRestCallPlan { - val serviceInstanceId = commandParentData.getInstanceId(CommandParentData.CommandDataKey.SERVICE_INSTANCE_ID) - val serviceModelInfo = commandParentData.getModelInfo(CommandParentData.CommandDataKey.SERVICE_MODEL_INFO) + val serviceInstanceId = serviceInstanceIdFromRequest() val instantiatePath = asyncInstantiationBL.getInstanceGroupInstantiationPath() val requestDetailsWrapper = msoRequestBuilder.generateInstanceGroupInstantiationRequest( request as InstanceGroup, - serviceModelInfo, serviceInstanceId, + serviceModelInfoFromRequest(), serviceInstanceId, userId, testApi ) diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/NetworkCommand.kt b/vid-app-common/src/main/java/org/onap/vid/job/command/NetworkCommand.kt index bc4de7ccd..6c9af14ea 100644 --- a/vid-app-common/src/main/java/org/onap/vid/job/command/NetworkCommand.kt +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/NetworkCommand.kt @@ -34,13 +34,11 @@ class NetworkCommand @Autowired constructor( } override fun planCreateMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String, testApi: String?): MsoRestCallPlan { - val serviceInstanceId = commandParentData.getInstanceId(CommandParentData.CommandDataKey.SERVICE_INSTANCE_ID) - val serviceModelInfo = commandParentData.getModelInfo(CommandParentData.CommandDataKey.SERVICE_MODEL_INFO) - + val serviceInstanceId = serviceInstanceIdFromRequest() val instantiatePath = asyncInstantiationBL.getNetworkInstantiationPath(serviceInstanceId) val requestDetailsWrapper = msoRequestBuilder.generateNetworkInstantiationRequest( request as Network, - serviceModelInfo, + serviceModelInfoFromRequest(), serviceInstanceId, userId, testApi @@ -52,7 +50,7 @@ class NetworkCommand @Autowired constructor( } override fun planDeleteMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String): MsoRestCallPlan { - val serviceInstanceId = commandParentData.getInstanceId(CommandParentData.CommandDataKey.SERVICE_INSTANCE_ID) + val serviceInstanceId = serviceInstanceIdFromRequest() val path = asyncInstantiationBL.getNetworkDeletePath(serviceInstanceId, getRequest().instanceId) val requestDetailsWrapper = msoRequestBuilder.generateDeleteNetworkRequest(getRequest() as Network, userId) return MsoRestCallPlan(HttpMethod.DELETE, path, Optional.of(requestDetailsWrapper), Optional.of(userId), diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/ResourceCommand.kt b/vid-app-common/src/main/java/org/onap/vid/job/command/ResourceCommand.kt index 60a579e30..a266dd002 100644 --- a/vid-app-common/src/main/java/org/onap/vid/job/command/ResourceCommand.kt +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/ResourceCommand.kt @@ -35,6 +35,7 @@ import org.onap.vid.model.Action import org.onap.vid.model.RequestReferencesContainer import org.onap.vid.model.serviceInstantiation.BaseResource import org.onap.vid.mso.RestMsoImplementation +import org.onap.vid.mso.model.ModelInfo import org.onap.vid.utils.JACKSON_OBJECT_MAPPER import org.onap.vid.utils.getEnumFromMapOfStrings import org.slf4j.MDC @@ -408,6 +409,9 @@ abstract class ResourceCommand( return commandParentData.parentData } + protected fun serviceModelInfoFromRequest(): ModelInfo = commandParentData.getModelInfo(CommandParentData.CommandDataKey.SERVICE_MODEL_INFO) + protected fun serviceInstanceIdFromRequest(): String = commandParentData.getInstanceId(CommandParentData.CommandDataKey.SERVICE_INSTANCE_ID) + protected open fun addMyselfToChildrenData(commandParentData: CommandParentData, request: BaseResource) { // Nothing by default } diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/VfmoduleCommand.kt b/vid-app-common/src/main/java/org/onap/vid/job/command/VfmoduleCommand.kt index 25373d4ac..bee42fbd4 100644 --- a/vid-app-common/src/main/java/org/onap/vid/job/command/VfmoduleCommand.kt +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/VfmoduleCommand.kt @@ -1,6 +1,7 @@ package org.onap.vid.job.command import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate +import org.onap.vid.exceptions.GenericUncheckedException import org.onap.vid.job.Job import org.onap.vid.job.JobAdapter import org.onap.vid.job.JobCommand @@ -42,8 +43,8 @@ class VfmoduleCommand @Autowired constructor( } override fun planCreateMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String, testApi: String?): MsoRestCallPlan { - val serviceInstanceId = commandParentData.getInstanceId(CommandParentData.CommandDataKey.SERVICE_INSTANCE_ID) - val serviceModelInfo = commandParentData.getModelInfo(CommandParentData.CommandDataKey.SERVICE_MODEL_INFO) + val serviceInstanceId = serviceInstanceIdFromRequest() + val serviceModelInfo = serviceModelInfoFromRequest() val vnfModelInfo = commandParentData.getModelInfo(CommandParentData.CommandDataKey.VNF_MODEL_INFO) val vnfInstanceId = commandParentData.getInstanceId(CommandParentData.CommandDataKey.VNF_INSTANCE_ID) val vgInstaceId = commandParentData.getInstanceId(CommandParentData.CommandDataKey.VG_INSTANCE_ID) @@ -61,7 +62,7 @@ class VfmoduleCommand @Autowired constructor( } override fun planDeleteMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String): MsoRestCallPlan { - val serviceInstanceId = commandParentData.getInstanceId(CommandParentData.CommandDataKey.SERVICE_INSTANCE_ID) + val serviceInstanceId = serviceInstanceIdFromRequest() val vnfInstanceId = commandParentData.getInstanceId(CommandParentData.CommandDataKey.VNF_INSTANCE_ID) val path = asyncInstantiationBL.getVfModuleDeletePath(serviceInstanceId, vnfInstanceId, getRequest().instanceId) @@ -82,7 +83,7 @@ class VfmoduleCommand @Autowired constructor( val newestModel = fetchNewestServiceModel() - val serviceInstanceId = commandParentData.getInstanceId(CommandParentData.CommandDataKey.SERVICE_INSTANCE_ID) + val serviceInstanceId = serviceInstanceIdFromRequest() val vnfInstanceId = commandParentData.getInstanceId(CommandParentData.CommandDataKey.VNF_INSTANCE_ID) val (serviceModelInfo, vnfModelInfo, vfmModelInfo) = newestSelector(newestModel, commandParentData); @@ -103,44 +104,52 @@ class VfmoduleCommand @Autowired constructor( data class ModelsInfoTriplet(val serviceModelInfo: ModelInfo, val vnfModelInfo: ModelInfo, val vfmModelInfo: ModelInfo) private fun newestSelector(newestModel: ServiceModel, commandParentData: CommandParentData): ModelsInfoTriplet { - val serviceModelInfo = commandParentData.getModelInfo(CommandParentData.CommandDataKey.SERVICE_MODEL_INFO) - val vfmModelInfo = getRequest().modelInfo - val vnfModelInfo = commandParentData.getModelInfo(CommandParentData.CommandDataKey.VNF_MODEL_INFO) - - val newestServiceModelInfo = newestServiceModelInfo(newestModel) - val newestVfmModelInfo = newestVfmModelInfo(newestModel) - val newestVnfModelInfo = newestVnfModelInfo(newestModel, commandParentData) - - return if (newestServiceModelInfo == null || newestVfmModelInfo == null || newestVnfModelInfo == null) { - ModelsInfoTriplet(serviceModelInfo, vnfModelInfo, vfmModelInfo) - } else { - ModelsInfoTriplet(newestServiceModelInfo, newestVnfModelInfo, newestVfmModelInfo) + try { + return ModelsInfoTriplet( + newestServiceModelInfo(newestModel), + newestVnfModelInfo(newestModel, commandParentData), + newestVfmModelInfo(newestModel) + ) + } catch (e: Exception) { + throw GenericUncheckedException("Cannot upgrade ${serviceModelInfoFromRequest()} to ${newestModel.service}", e) } } private fun newestServiceModelInfo(newestModel: ServiceModel) = toModelInfo(newestModel.service) - private fun newestVfmModelInfo(newestModel: ServiceModel): ModelInfo? { + private fun newestVfmModelInfo(newestModel: ServiceModel): ModelInfo { val vfmModelInfo = getRequest().modelInfo - val matchingVfms = selectVfms(newestModel, vfmModelInfo) - return toModelInfo(matchingVfms.getOrNull(0)) + val matchingVfm = selectVfm(newestModel, vfmModelInfo) + return toModelInfo(matchingVfm) } - private fun newestVnfModelInfo(newestModel: ServiceModel, commandParentData: CommandParentData): ModelInfo? { + private fun newestVnfModelInfo(newestModel: ServiceModel, commandParentData: CommandParentData): ModelInfo { val vnfModelInfo = commandParentData.getModelInfo(CommandParentData.CommandDataKey.VNF_MODEL_INFO) - val matchingVnfs = selectVnfs(newestModel, vnfModelInfo) - return toModelInfo(matchingVnfs.getOrNull(0)) + val matchingVnf = selectVnf(newestModel, vnfModelInfo) + return toModelInfo(matchingVnf) } - private fun selectVfms(newestModel: ServiceModel, modelInfo: ModelInfo) = - newestModel.vfModules.values.filter { it.modelCustomizationName == modelInfo.modelCustomizationName } - - private fun selectVnfs(newestModel: ServiceModel, modelInfo: ModelInfo) = - newestModel.vnfs.values.filter { it.modelCustomizationName == modelInfo.modelCustomizationName } + internal fun selectVfm(serviceModel: ServiceModel, modelInfo: ModelInfo): ToscaVfm = + exactlyOne("vfModule for modelCustomizationName \"${modelInfo.modelCustomizationName}\"") { + serviceModel.vfModules.values.single { it.modelCustomizationName == modelInfo.modelCustomizationName } + } + + internal fun selectVnf(serviceModel: ServiceModel, modelInfo: ModelInfo): VNF = + exactlyOne("VNF for modelCustomizationName \"${modelInfo.modelCustomizationName}\"") { + serviceModel.vnfs.values.single { it.modelCustomizationName == modelInfo.modelCustomizationName } + } + + private fun exactlyOne(predicateDescription: String, itemSupplier: () -> T): T { + return try { + itemSupplier.invoke() + } catch (cause: Exception) { + throw IllegalStateException("Cannot match ${predicateDescription}: ${cause.localizedMessage}", cause) + } + } - private fun toModelInfo(toBeConverted: VNF?): ModelInfo? = toBeConverted?.let { toModelInfo(it, "vnf") } + private fun toModelInfo(toBeConverted: VNF): ModelInfo = toModelInfo(toBeConverted, "vnf") - private fun toModelInfo(toBeConverted: ToscaVfm?): ModelInfo? = toBeConverted?.let { toModelInfo(it, "vfModule") } + private fun toModelInfo(toBeConverted: ToscaVfm): ModelInfo = toModelInfo(toBeConverted, "vfModule") private fun toModelInfo(toBeConverted: MinimalNode, modelType: String): ModelInfo { val targetModelInfo = ModelInfo() @@ -167,11 +176,7 @@ class VfmoduleCommand @Autowired constructor( return targetModelInfo } - private fun toModelInfo(toBeConverted: Service?): ModelInfo? { - - if (toBeConverted == null) - return null - + internal fun toModelInfo(toBeConverted: Service): ModelInfo { val targetModelInfo = ModelInfo() targetModelInfo.modelVersionId = toBeConverted.uuid @@ -197,10 +202,15 @@ class VfmoduleCommand @Autowired constructor( return getActionType() == Action.Upgrade } + @Throws(IllegalStateException::class) private fun fetchNewestServiceModel(): ServiceModel { - val serviceModelInfo = commandParentData.getModelInfo(CommandParentData.CommandDataKey.SERVICE_MODEL_INFO) - var modelNewestUuid = commandUtils.getNewestModelUuid(serviceModelInfo.modelInvariantId); - var serviceNewestModel = commandUtils.getServiceModel(modelNewestUuid); + val serviceModelInfo = serviceModelInfoFromRequest() + val modelNewestUuid = commandUtils.getNewestModelUuid(serviceModelInfo.modelInvariantId); + + check(!modelNewestUuid.equals(serviceModelInfo.modelVersionId, true)) { + "Model version id ${serviceModelInfo.modelVersionId} is already the latest version of model's invariant id ${serviceModelInfo.modelInvariantId}" } + + val serviceNewestModel = commandUtils.getServiceModel(modelNewestUuid); return serviceNewestModel; } diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/VnfCommand.kt b/vid-app-common/src/main/java/org/onap/vid/job/command/VnfCommand.kt index a89e196de..6f00f9ae6 100644 --- a/vid-app-common/src/main/java/org/onap/vid/job/command/VnfCommand.kt +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/VnfCommand.kt @@ -62,7 +62,8 @@ class VnfCommand @Autowired constructor( childJobs = pushChildrenJobsToBroker(vfModules.filter { filterModuleByNeedToCreateBase(it) }, dataForChild, JobType.VolumeGroupInstantiation) } catch (e: AsdcCatalogException) { LOGGER.error("Failed to retrieve service definitions from SDC, for VfModule is BaseModule.. Error: " + e.message , e) - return Job.JobStatus.FAILED + //return Job.JobStatus.FAILED + throw e; } } @@ -72,19 +73,18 @@ class VnfCommand @Autowired constructor( private fun filterModuleByNeedToCreateBase(it: VfModule):Boolean { return needToCreateBaseModule == commandUtils.isVfModuleBaseModule( - commandParentData.getModelInfo(CommandParentData.CommandDataKey.SERVICE_MODEL_INFO).getModelVersionId(), - it.modelInfo.modelVersionId) + serviceModelInfoFromRequest().modelVersionId, + it.modelInfo.modelVersionId) } override fun planCreateMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String, testApi: String?): MsoRestCallPlan { - val serviceInstanceId = commandParentData.getInstanceId(CommandParentData.CommandDataKey.SERVICE_INSTANCE_ID) - val serviceModelInfo = commandParentData.getModelInfo(CommandParentData.CommandDataKey.SERVICE_MODEL_INFO) + val serviceInstanceId = serviceInstanceIdFromRequest() val instantiatePath = asyncInstantiationBL.getVnfInstantiationPath(serviceInstanceId) val requestDetailsWrapper = msoRequestBuilder.generateVnfInstantiationRequest( request as Vnf, - serviceModelInfo, serviceInstanceId, + serviceModelInfoFromRequest(), serviceInstanceId, userId, testApi ) @@ -110,7 +110,7 @@ class VnfCommand @Autowired constructor( } override fun planDeleteMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String): MsoRestCallPlan { - val serviceInstanceId = commandParentData.getInstanceId(CommandParentData.CommandDataKey.SERVICE_INSTANCE_ID) + val serviceInstanceId = serviceInstanceIdFromRequest() val path = asyncInstantiationBL.getVnfDeletionPath(serviceInstanceId, getRequest().instanceId) val requestDetailsWrapper = msoRequestBuilder.generateDeleteVnfRequest(getRequest(), userId) return MsoRestCallPlan(HttpMethod.DELETE, path, Optional.of(requestDetailsWrapper), Optional.of(userId), diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/VolumeGroupCommand.kt b/vid-app-common/src/main/java/org/onap/vid/job/command/VolumeGroupCommand.kt index 4da1dad15..9794933ce 100644 --- a/vid-app-common/src/main/java/org/onap/vid/job/command/VolumeGroupCommand.kt +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/VolumeGroupCommand.kt @@ -43,9 +43,7 @@ class VolumeGroupCommand @Autowired constructor( } override fun planCreateMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String, testApi: String?): MsoRestCallPlan { - - val serviceInstanceId = commandParentData.getInstanceId(CommandParentData.CommandDataKey.SERVICE_INSTANCE_ID) - val serviceModelInfo = commandParentData.getModelInfo(CommandParentData.CommandDataKey.SERVICE_MODEL_INFO) + val serviceInstanceId = serviceInstanceIdFromRequest() val vnfInstanceId = commandParentData.getInstanceId(CommandParentData.CommandDataKey.VNF_INSTANCE_ID) val vnfModelInfo = commandParentData.getModelInfo(CommandParentData.CommandDataKey.VNF_MODEL_INFO) @@ -53,7 +51,7 @@ class VolumeGroupCommand @Autowired constructor( val requestDetailsWrapper = msoRequestBuilder.generateVolumeGroupInstantiationRequest( request as VfModule, - serviceModelInfo, serviceInstanceId, + serviceModelInfoFromRequest(), serviceInstanceId, vnfModelInfo,vnfInstanceId, userId, testApi diff --git a/vid-app-common/src/test/java/org/onap/vid/job/command/VfmoduleCommandTest.kt b/vid-app-common/src/test/java/org/onap/vid/job/command/VfmoduleCommandTest.kt new file mode 100644 index 000000000..ea67f2372 --- /dev/null +++ b/vid-app-common/src/test/java/org/onap/vid/job/command/VfmoduleCommandTest.kt @@ -0,0 +1,85 @@ +package org.onap.vid.job.command + +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs +import org.mockito.InjectMocks +import org.mockito.Mock +import org.onap.vid.job.JobAdapter +import org.onap.vid.job.JobsBrokerService +import org.onap.vid.model.ServiceModel +import org.onap.vid.model.VfModule +import org.onap.vid.mso.RestMsoImplementation +import org.onap.vid.mso.model.ModelInfo +import org.onap.vid.services.AsyncInstantiationBusinessLogic +import org.onap.vid.testUtils.TestUtils.initMockitoMocks +import org.testng.annotations.BeforeMethod +import org.testng.annotations.Test + +class VfmoduleCommandTest { + + @Mock lateinit var asyncInstantiationBL: AsyncInstantiationBusinessLogic; + @Mock lateinit var restMso: RestMsoImplementation; + @Mock lateinit var msoRequestBuilder: MsoRequestBuilder; + @Mock lateinit var msoResultHandlerService: MsoResultHandlerService; + @Mock lateinit var inProgressStatusService: InProgressStatusService; + @Mock lateinit var watchChildrenJobsBL: WatchChildrenJobsBL; + @Mock lateinit var jobsBrokerService: JobsBrokerService; + @Mock lateinit var jobAdapter: JobAdapter; + + @InjectMocks lateinit var vfModuleCommand: VfmoduleCommand; + + private val uniqueCustomizationName = "my unique customization name" + + @BeforeMethod + fun initMocks() { + initMockitoMocks(this) + } + + @Test + fun `given correlated modelCustomizationName, selectVfms returns the vfm`() { + val newestModel = ServiceModel() + newestModel.vfModules = someVfModules() + .plus("my vfm" to vfModuleWithCustomizationName()) + + val selectedVfm = vfModuleCommand.selectVfm(newestModel, modelInfoWithCustomizationName()) + + assertThat(selectedVfm, samePropertyValuesAs(vfModuleWithCustomizationName())) + } + + @Test( + expectedExceptions = [IllegalStateException::class], + expectedExceptionsMessageRegExp = + """Cannot match vfModule for modelCustomizationName "my unique customization name": Collection contains no element matching the predicate.""") + fun `given no matching modelCustomizationName, selectVfms throws`() { + val newestModel = ServiceModel() + newestModel.vfModules = someVfModules() + + vfModuleCommand.selectVfm(newestModel, modelInfoWithCustomizationName()) + } + + @Test( + expectedExceptions = [IllegalStateException::class], + expectedExceptionsMessageRegExp = + """Cannot match vfModule for modelCustomizationName "my unique customization name": Collection contains more than one matching element.""") + fun `given a few matching modelCustomizationName, selectVfms throws`() { + + val newestModel = ServiceModel() + newestModel.vfModules = someVfModules() + .plus("my vfm" to vfModuleWithCustomizationName()) + .plus("my vfm2" to vfModuleWithCustomizationName()) + + vfModuleCommand.selectVfm(newestModel, modelInfoWithCustomizationName()) + } + + private fun modelInfoWithCustomizationName(customizationName: String = uniqueCustomizationName) = + ModelInfo().also { it.modelCustomizationName = customizationName } + + private fun someVfModules(): Map = mapOf( + "any vfm 1" to vfModuleWithCustomizationName("any customization name 1"), + "any vfm 2" to vfModuleWithCustomizationName("any customization name 2") + ) + + private fun vfModuleWithCustomizationName(customizationName: String = uniqueCustomizationName) = + VfModule().also { it.modelCustomizationName = customizationName } + +} \ No newline at end of file