Merge "vfModule upgrade: don't fallback on mismatching newest model"
authorAmichai Hemli <amichai.hemli@intl.att.com>
Tue, 5 Nov 2019 06:53:37 +0000 (06:53 +0000)
committerGerrit Code Review <gerrit@onap.org>
Tue, 5 Nov 2019 06:53:37 +0000 (06:53 +0000)
vid-app-common/src/main/java/org/onap/vid/job/command/InstanceGroupCommand.kt
vid-app-common/src/main/java/org/onap/vid/job/command/NetworkCommand.kt
vid-app-common/src/main/java/org/onap/vid/job/command/ResourceCommand.kt
vid-app-common/src/main/java/org/onap/vid/job/command/VfmoduleCommand.kt
vid-app-common/src/main/java/org/onap/vid/job/command/VnfCommand.kt
vid-app-common/src/main/java/org/onap/vid/job/command/VolumeGroupCommand.kt
vid-app-common/src/test/java/org/onap/vid/job/command/VfmoduleCommandTest.kt [new file with mode: 0644]

index 26fb9aa..886251a 100644 (file)
@@ -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
         )
index bc4de7c..6c9af14 100644 (file)
@@ -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),
index 60a579e..a266dd0 100644 (file)
@@ -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
     }
index 25373d4..bee42fb 100644 (file)
@@ -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 <T: Any> 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;
     }
index a89e196..6f00f9a 100644 (file)
@@ -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),
index 4da1dad..9794933 100644 (file)
@@ -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 (file)
index 0000000..ea67f23
--- /dev/null
@@ -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<String, VfModule> = 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