1 package org.onap.vid.job.command
3 import com.google.common.collect.ImmutableList
4 import org.apache.commons.lang3.ObjectUtils.defaultIfNull
5 import org.apache.commons.lang3.StringUtils
6 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate
7 import org.onap.vid.aai.AaiClientInterface
8 import org.onap.vid.aai.ExceptionWithRequestInfo
9 import org.onap.vid.aai.model.ResourceType
10 import org.onap.vid.changeManagement.RequestDetailsWrapper
11 import org.onap.vid.model.serviceInstantiation.*
12 import org.onap.vid.mso.model.*
13 import org.onap.vid.mso.model.BaseResourceInstantiationRequestDetails.*
14 import org.onap.vid.mso.model.VfModuleInstantiationRequestDetails.UserParamMap
15 import org.onap.vid.mso.rest.SubscriberInfo
16 import org.onap.vid.services.AsyncInstantiationBusinessLogic
17 import org.onap.vid.services.CloudOwnerService
18 import org.onap.vid.utils.JACKSON_OBJECT_MAPPER
19 import org.springframework.beans.factory.annotation.Autowired
20 import org.springframework.stereotype.Service
21 import org.togglz.core.manager.FeatureManager
23 import java.util.Collections.emptyList
24 import java.util.stream.Collectors
27 class MsoRequestBuilder
28 @Autowired constructor(private val asyncInstantiationBL: AsyncInstantiationBusinessLogic,
29 private val cloudOwnerService: CloudOwnerService,
30 private val aaiClient: AaiClientInterface,
31 private val featureManager: FeatureManager) {
34 private val LOGGER = EELFLoggerDelegate.getLogger(MsoRequestBuilder::class.java)
35 private const val VID_SOURCE = "VID"
38 fun generateALaCarteServiceInstantiationRequest(payload: ServiceInstantiation, optimisticUniqueServiceInstanceName: String, userId: String): RequestDetailsWrapper<ServiceInstantiationRequestDetails> {
39 val userParams = generateUserParamList()
41 val requestParameters = ServiceInstantiationRequestDetails.RequestParameters(payload.subscriptionServiceType, true, userParams, payload.testApi)
43 val requestDetails = generateServiceInstantiationRequestDetails(payload, requestParameters, optimisticUniqueServiceInstanceName, userId)
45 return RequestDetailsWrapper(requestDetails)
48 fun generateServiceDeletionRequest(payload: ServiceInstantiation, userId: String): RequestDetailsWrapper<ServiceDeletionRequestDetails> {
50 val requestParameters = ServiceDeletionRequestDetails.RequestParameters(payload.isALaCarte, payload.testApi)
52 val requestInfo = ServiceDeletionRequestDetails.RequestInfo(
56 val requestDetails = ServiceDeletionRequestDetails(payload.modelInfo, requestInfo, requestParameters)
58 return RequestDetailsWrapper(requestDetails)
61 fun generateMacroServiceInstantiationRequest(jobId: UUID?, payload: ServiceInstantiation, optimisticUniqueServiceInstanceName: String, userId: String): RequestDetailsWrapper<ServiceInstantiationRequestDetails> {
62 val serviceInstanceName = generateServiceName(jobId, payload, optimisticUniqueServiceInstanceName)
64 val serviceInstantiationServiceList = generateServiceInstantiationServicesList(payload, serviceInstanceName, createServiceInstantiationVnfList(jobId, payload))
66 val requestParameters = ServiceInstantiationRequestDetails.RequestParameters(payload.subscriptionServiceType, false, serviceInstantiationServiceList)
68 val requestDetails = generateServiceInstantiationRequestDetails(payload, requestParameters, serviceInstanceName, userId)
70 return RequestDetailsWrapper(requestDetails)
73 fun generateNetworkInstantiationRequest(networkDetails: Network, serviceModelInfo: ModelInfo, serviceInstanceId: String, userId: String, testApi: String?): RequestDetailsWrapper<NetworkInstantiationRequestDetails> {
74 val requestInfo = generateRequestInfo(networkDetails.instanceName, ResourceType.L3_NETWORK, networkDetails.isRollbackOnFailure, networkDetails.productFamilyId, userId)
75 val cloudConfiguration = generateCloudConfiguration(networkDetails.lcpCloudRegionId, networkDetails.tenantId)
76 val platform = Platform(networkDetails.platformName)
77 val lineOfBusiness = LineOfBusiness.of(networkDetails.lineOfBusiness)
78 val requestParameters = BaseResourceInstantiationRequestDetails.RequestParameters(generateUserParamList(), testApi)
79 val relatedInstanceList = generateRelatedInstances(mapOf(serviceInstanceId to serviceModelInfo))
80 return RequestDetailsWrapper(NetworkInstantiationRequestDetails(networkDetails.modelInfo, cloudConfiguration, requestInfo, platform, lineOfBusiness, relatedInstanceList, requestParameters))
83 fun generateVnfInstantiationRequest(vnfDetails: Vnf, serviceModelInfo: ModelInfo, serviceInstanceId: String, userId: String, testApi: String?): RequestDetailsWrapper<VnfInstantiationRequestDetails> {
84 val requestInfo = generateRequestInfo(vnfDetails.instanceName, ResourceType.GENERIC_VNF, vnfDetails.isRollbackOnFailure, vnfDetails.productFamilyId, userId)
85 val cloudConfiguration = generateCloudConfiguration(vnfDetails.lcpCloudRegionId, vnfDetails.tenantId)
86 val platform = Platform(vnfDetails.platformName)
87 val lineOfBusiness = LineOfBusiness.of(vnfDetails.lineOfBusiness)
88 val requestParameters = BaseResourceInstantiationRequestDetails.RequestParameters(generateUserParamList(), testApi)
89 val relatedInstanceList = generateRelatedInstances(mapOf(serviceInstanceId to serviceModelInfo))
90 return RequestDetailsWrapper(VnfInstantiationRequestDetails(vnfDetails.modelInfo, cloudConfiguration, requestInfo, platform, lineOfBusiness, relatedInstanceList, requestParameters))
93 fun generateDeleteVnfRequest(vnfDetails: Vnf, userId: String): RequestDetailsWrapper<VnfInstantiationRequestDetails> {
94 val requestInfo = generateRequestInfo(null, null, null, null, userId)
95 val cloudConfiguration = generateCloudConfiguration(vnfDetails.lcpCloudRegionId, vnfDetails.tenantId)
96 return RequestDetailsWrapper(VnfInstantiationRequestDetails(vnfDetails.modelInfo, cloudConfiguration, requestInfo, null, null, null, null))
99 fun generateVfModuleInstantiationRequest(vfModuleDetails: VfModule, serviceModelInfo: ModelInfo, serviceInstanceId: String, vnfModelInfo: ModelInfo, vnfInstanceId: String, vgInstanceId: String?, userId: String, testApi: String?): RequestDetailsWrapper<VfModuleInstantiationRequestDetails> {
100 val requestInfo = generateRequestInfo(vfModuleDetails.instanceName, ResourceType.VF_MODULE, vfModuleDetails.isRollbackOnFailure, null, userId)
102 //cloud configuration
103 val cloudConfiguration = generateCloudConfiguration(vfModuleDetails.lcpCloudRegionId, vfModuleDetails.tenantId)
106 val userParams = aggregateAllInstanceParams(extractActualInstanceParams(vfModuleDetails.instanceParams), vfModuleDetails.supplementaryParams)
107 val requestParameters = VfModuleInstantiationRequestDetails.RequestParametersVfModule(userParams, vfModuleDetails.isUsePreload, testApi)
109 //related instance list
110 val relatedInstanceList = generateRelatedInstances(mapOf(serviceInstanceId to serviceModelInfo, vnfInstanceId to vnfModelInfo))
111 if (StringUtils.isNotEmpty(vgInstanceId)) {
112 val volumeGroupModel = ModelInfo()
113 volumeGroupModel.modelType = "volumeGroup"
114 relatedInstanceList.add(RelatedInstance(volumeGroupModel, vgInstanceId, vfModuleDetails.volumeGroupInstanceName))
116 return RequestDetailsWrapper(VfModuleInstantiationRequestDetails(vfModuleDetails.modelInfo, cloudConfiguration, requestInfo, relatedInstanceList, requestParameters))
119 fun generateVolumeGroupInstantiationRequest(vfModuleDetails: VfModule, serviceModelInfo: ModelInfo, serviceInstanceId: String, vnfModelInfo: ModelInfo, vnfInstanceId: String, userId: String, testApi: String?): RequestDetailsWrapper<VolumeGroupRequestDetails> {
120 val requestInfo = generateRequestInfo(vfModuleDetails.volumeGroupInstanceName, ResourceType.VOLUME_GROUP, vfModuleDetails.isRollbackOnFailure, null, userId)
121 val cloudConfiguration = generateCloudConfiguration(vfModuleDetails.lcpCloudRegionId, vfModuleDetails.tenantId)
122 val userParams = aggregateAllInstanceParams(extractActualInstanceParams(vfModuleDetails.instanceParams), vfModuleDetails.supplementaryParams)
123 val requestParameters = VfModuleInstantiationRequestDetails.RequestParametersVfModule(userParams, vfModuleDetails.isUsePreload, testApi)
124 val relatedInstances = generateRelatedInstances(mapOf(serviceInstanceId to serviceModelInfo, vnfInstanceId to vnfModelInfo))
126 vfModuleDetails.modelInfo.modelType = "volumeGroup"
127 return RequestDetailsWrapper(VolumeGroupRequestDetails(vfModuleDetails.modelInfo, cloudConfiguration, requestInfo, relatedInstances, requestParameters))
130 fun generateInstanceGroupInstantiationRequest(instanceGroupDetails: InstanceGroup, serviceModelInfo: ModelInfo, serviceInstanceId: String, userId: String, testApi: String?): RequestDetailsWrapper<InstanceGroupInstantiationRequestDetails> {
131 val requestInfo = generateRequestInfo(instanceGroupDetails.instanceName, ResourceType.INSTANCE_GROUP, instanceGroupDetails.isRollbackOnFailure, null, userId)
132 val requestParameters = BaseResourceInstantiationRequestDetails.RequestParameters(generateUserParamList(), testApi)
133 val relatedInstanceList = generateRelatedInstances(mapOf(serviceInstanceId to serviceModelInfo))
134 return RequestDetailsWrapper(InstanceGroupInstantiationRequestDetails(instanceGroupDetails.modelInfo, requestInfo, relatedInstanceList, requestParameters))
137 fun generateInstanceGroupMemberRequest(instanceGroupMemberId: String, userId: String): RequestDetailsWrapper<AddOrRemoveInstanceGroupMemberRequestDetails> {
138 val requestInfo = generateRequestInfo(null, null, null, null, userId)
139 val modelInfo = ModelInfo()
140 modelInfo.modelType = "vnf"
141 val relatedInstanceList = generateRelatedInstances(mapOf(instanceGroupMemberId to modelInfo))
142 return RequestDetailsWrapper(AddOrRemoveInstanceGroupMemberRequestDetails(requestInfo, relatedInstanceList))
145 fun generateDeleteNetworkRequest(networkDetails: Network, userId: String): RequestDetailsWrapper<NetworkInstantiationRequestDetails> {
146 val requestInfo = generateRequestInfo(null, null, null, null, userId)
147 val cloudConfiguration = generateCloudConfiguration(networkDetails.lcpCloudRegionId, networkDetails.tenantId)
148 return RequestDetailsWrapper(NetworkInstantiationRequestDetails(networkDetails.modelInfo, cloudConfiguration, requestInfo, null, null, null, null))
151 fun generateDeleteVfModuleRequest(vfModuleDetails: VfModule, userId: String): RequestDetailsWrapper<VfModuleInstantiationRequestDetails> {
152 val requestInfo = generateRequestInfo(null, null, null, null, userId)
153 val cloudConfiguration = generateCloudConfiguration(vfModuleDetails.lcpCloudRegionId, vfModuleDetails.tenantId)
154 return RequestDetailsWrapper(VfModuleInstantiationRequestDetails(vfModuleDetails.modelInfo, cloudConfiguration, requestInfo, null, null))
157 private fun generateServiceName(jobId: UUID?, payload: ServiceInstantiation, optimisticUniqueServiceInstanceName: String): String? {
158 var serviceInstanceName: String? = null
159 if (StringUtils.isNotEmpty(optimisticUniqueServiceInstanceName)) {
160 serviceInstanceName = peekServiceName(jobId, payload, optimisticUniqueServiceInstanceName)
162 return serviceInstanceName
165 private fun peekServiceName(jobId: UUID?, payload: ServiceInstantiation, optimisticUniqueServiceInstanceName: String): String {
166 val serviceInstanceName: String
167 // unique name already exist in service info. If it's free in AAI we use it
168 if (isNameFreeInAai(optimisticUniqueServiceInstanceName, ResourceType.SERVICE_INSTANCE)) {
169 serviceInstanceName = optimisticUniqueServiceInstanceName
171 serviceInstanceName = asyncInstantiationBL.getUniqueName(payload.instanceName, ResourceType.SERVICE_INSTANCE)
172 }//otherwise we used the original service instance name (from payload) to get a new unique name from DB and AAI
174 //update serviceInfo with new name if needed
176 asyncInstantiationBL.updateServiceInfo(jobId) { x -> x.serviceInstanceName = serviceInstanceName }
177 } catch (e: Exception) {
178 LOGGER.error("Failed updating service name {} in serviceInfo", serviceInstanceName, e)
181 return serviceInstanceName
184 @Throws(ExceptionWithRequestInfo::class)
185 private fun isNameFreeInAai(name: String, resourceType: ResourceType): Boolean {
186 return !aaiClient.isNodeTypeExistsByName(name, resourceType)
189 private fun generateServiceInstantiationServicesList(payload: ServiceInstantiation, serviceInstanceName: String?, vnfList: ServiceInstantiationRequestDetails.ServiceInstantiationVnfList): List<ServiceInstantiationRequestDetails.ServiceInstantiationService> {
190 val serviceInstantiationServiceList = LinkedList<ServiceInstantiationRequestDetails.ServiceInstantiationService>()
191 val unFilteredInstanceParams = defaultIfNull<List<MutableMap<String, String>>>(payload.instanceParams, emptyList())
192 val filteredInstanceParams = removeUnNeededParams(unFilteredInstanceParams)
193 val serviceInstantiationService = ServiceInstantiationRequestDetails.ServiceInstantiationService(
196 filteredInstanceParams,
199 serviceInstantiationServiceList.add(serviceInstantiationService)
200 return serviceInstantiationServiceList
203 private fun removeUnNeededParams(instanceParams: List<MutableMap<String, String>>?): List<MutableMap<String, String>> {
204 val keysToRemove = mutableListOf<String>()
205 if (instanceParams.isNullOrEmpty()) {
209 for (key in instanceParams[0].keys) {
210 for (paramToIgnore in AsyncInstantiationBusinessLogic.PARAMS_TO_IGNORE)
211 if (key.equals(paramToIgnore, ignoreCase = true)) {
212 keysToRemove.add(key)
216 val result : MutableMap<String, String> = instanceParams[0].entries.stream()
217 .filter { entry -> !keysToRemove.contains(entry.key) }
218 .collect(Collectors.toMap({it.key}, {it.value}))
220 return if (result.isEmpty()) emptyList() else listOf(result)
223 private fun createServiceInstantiationVnfList(jobId: UUID?, payload: ServiceInstantiation): ServiceInstantiationRequestDetails.ServiceInstantiationVnfList {
224 val cloudConfiguration = generateCloudConfiguration(payload.lcpCloudRegionId, payload.tenantId)
225 val isBulk = asyncInstantiationBL.isPartOfBulk(jobId)
227 val vnfs = payload.vnfs
228 val vnfList = mutableListOf<ServiceInstantiationRequestDetails.ServiceInstantiationVnf>()
229 for (vnf in vnfs.values) {
230 val vfModules = vnf.vfModules
231 val convertedUnFilteredVfModules = convertVfModuleMapToList(vfModules)
232 val filteredVfModules = filterInstanceParamsFromVfModuleAndUniqueNames(convertedUnFilteredVfModules, isBulk)
233 val serviceInstantiationVnf = ServiceInstantiationRequestDetails.ServiceInstantiationVnf(
238 payload.productFamilyId,
239 buildVnfInstanceParams(vnf.instanceParams, filteredVfModules),
241 getUniqueNameIfNeeded(vnf.instanceName, ResourceType.GENERIC_VNF, isBulk)
243 vnfList.add(serviceInstantiationVnf)
246 return ServiceInstantiationRequestDetails.ServiceInstantiationVnfList(vnfList)
249 private fun convertVfModuleMapToList(vfModules: Map<String, Map<String, VfModule>>): List<VfModuleMacro> {
250 return vfModules.values.stream().flatMap { vfModule ->
251 vfModule.values.stream().map { item ->
252 val aggregatedParams = aggregateAllInstanceParams(extractActualInstanceParams(item.instanceParams), item.supplementaryParams)
253 val aggregatedParamsConverted = JACKSON_OBJECT_MAPPER.convertValue(aggregatedParams, List::class.java)
258 item.volumeGroupInstanceName,
259 aggregatedParamsConverted as List<Map<String, String>>)
261 }.collect(Collectors.toList<VfModuleMacro>())
264 fun aggregateAllInstanceParams(instanceParams: Map<String, String>?, supplementaryParams: Map<String, String>?): List<VfModuleInstantiationRequestDetails.UserParamMap<String, String>> {
265 var instanceParamsFinal: Map<String, String> = instanceParams ?: emptyMap()
266 val supplementaryParamsFinal: Map<String, String> = supplementaryParams ?: emptyMap()
268 if (!(instanceParamsFinal.isEmpty() && supplementaryParamsFinal.isEmpty())) {
269 //remove duplicate keys from instanceParams if exist in supplementaryParams
270 instanceParamsFinal = instanceParamsFinal.entries.stream()
271 .filter { m -> !supplementaryParamsFinal.containsKey(m.key) }
272 .collect(Collectors.toMap({ it.key }, { it.value }))
274 //aggregate the 2 collections and format them as UserParamMap
275 val aggregatedParams = UserParamMap<String, String>()
276 aggregatedParams.putAll(instanceParamsFinal)
277 aggregatedParams.putAll(supplementaryParamsFinal)
279 return mutableListOf(aggregatedParams)
285 //Make sure we always get a one Map from InstanceParams
286 private fun extractActualInstanceParams(originalInstanceParams: List<MutableMap<String, String>>?): MutableMap<String, String> {
287 return if (originalInstanceParams.isNullOrEmpty() || originalInstanceParams[0].isNullOrEmpty()) {
289 } else originalInstanceParams[0]
292 private fun filterInstanceParamsFromVfModuleAndUniqueNames(unFilteredVfModules: List<VfModuleMacro>, isBulk: Boolean): List<VfModuleMacro> {
293 return unFilteredVfModules.stream().map { vfModule ->
296 getUniqueNameIfNeeded(vfModule.instanceName, ResourceType.VF_MODULE, isBulk),
297 getUniqueNameIfNeeded(vfModule.volumeGroupInstanceName, ResourceType.VOLUME_GROUP, isBulk),
298 removeUnNeededParams(vfModule.instanceParams))
300 .collect(Collectors.toList<VfModuleMacro>())
303 fun buildVnfInstanceParams(currentVnfInstanceParams: List<MutableMap<String, String>>, vfModules: List<VfModuleMacro>): List<Map<String, String>> {
304 val filteredVnfInstanceParams = removeUnNeededParams(currentVnfInstanceParams)
306 val vnfInstanceParams = extractActualInstanceParams(filteredVnfInstanceParams)
308 .map { x -> extractActualInstanceParams(x.instanceParams) }
309 .forEach { vnfInstanceParams.putAll(it) }
310 return if (vnfInstanceParams.isEmpty()) emptyList() else ImmutableList.of(vnfInstanceParams)
313 private fun generateServiceInstantiationRequestDetails(payload: ServiceInstantiation, requestParameters: ServiceInstantiationRequestDetails.RequestParameters, serviceInstanceName: String?, userId: String): ServiceInstantiationRequestDetails {
314 val requestInfo = ServiceInstantiationRequestDetails.RequestInfo(serviceInstanceName,
315 payload.productFamilyId,
317 payload.isRollbackOnFailure,
319 val owningEntity = ServiceInstantiationRequestDetails.ServiceInstantiationOwningEntity(payload.owningEntityId, payload.owningEntityName)
320 val subscriberInfo = generateSubscriberInfo(payload)
321 val project = if (payload.projectName != null) ServiceInstantiationRequestDetails.Project(payload.projectName) else null
322 return ServiceInstantiationRequestDetails(payload.modelInfo, owningEntity, subscriberInfo, project, requestInfo, requestParameters)
325 private fun generateSubscriberInfo(payload: ServiceInstantiation): SubscriberInfo {
326 val subscriberInfo = SubscriberInfo()
327 subscriberInfo.globalSubscriberId = payload.globalSubscriberId
328 return subscriberInfo
331 private fun generateCloudConfiguration(lcpCloudRegionId: String?, tenantId: String?): CloudConfiguration {
332 val cloudConfiguration = CloudConfiguration(lcpCloudRegionId, tenantId)
333 if(lcpCloudRegionId != null){
334 cloudOwnerService.enrichCloudConfigurationWithCloudOwner(cloudConfiguration, lcpCloudRegionId)
336 return cloudConfiguration
339 private fun generateRelatedInstances(relatedInstances: Map<String, ModelInfo>): MutableList<RelatedInstance> {
340 return relatedInstances.entries.stream()
341 .map { RelatedInstance(it.value, it.key) }
342 .collect(Collectors.toList())
345 private fun generateRequestInfo(instanceName: String?, resourceType: ResourceType?, rollbackOnFailure: Boolean?, productFamilyId: String?, userId: String) : BaseResourceInstantiationRequestDetails.RequestInfo {
346 return BaseResourceInstantiationRequestDetails.RequestInfo(
347 if (resourceType == null) null else getUniqueNameIfNeeded(instanceName, resourceType, false),
355 private fun getUniqueNameIfNeeded(name: String?, resourceType: ResourceType, isBulk: Boolean): String? {
356 return if (StringUtils.isNotEmpty(name)) {
357 if (isBulk) asyncInstantiationBL.getUniqueName(name, resourceType) else name
363 private fun generateUserParamList(): List<ServiceInstantiationRequestDetails.UserParamNameAndValue> {
367 fun generateMacroServicePre1806InstantiationRequest(payload: ServiceInstantiation, userId: String): RequestDetailsWrapper<ServiceInstantiationRequestDetails> {
368 val requestInfo = ServiceInstantiationRequestDetails.RequestInfo(payload.instanceName, payload.productFamilyId, VID_SOURCE, payload.isRollbackOnFailure, userId)
369 val userParams = generateUserParamsNameAndValue(payload.instanceParams)
370 val requestParameters = ServiceInstantiationRequestDetails.RequestParameters(payload.subscriptionServiceType, false, userParams)
371 val subscriberInfo = generateSubscriberInfoPre1806(payload)
372 val project = if (payload.projectName != null) ServiceInstantiationRequestDetails.Project(payload.projectName) else null
373 val owningEntity = ServiceInstantiationRequestDetails.ServiceInstantiationOwningEntity(payload.owningEntityId, payload.owningEntityName)
374 val cloudConfiguration = generateCloudConfiguration(payload.lcpCloudRegionId, payload.tenantId)
375 val relatedInstanceList = generateRelatedInstanceListForVrfEntry(payload.vrfs)
377 return RequestDetailsWrapper(ServiceInstantiationPre1806RequestDetails(
385 relatedInstanceList))
388 private fun generateUserParamsNameAndValue(instanceParams: List<Map<String, String>>): List<ServiceInstantiationRequestDetails.UserParamNameAndValue> {
389 if (instanceParams == null){
392 return instanceParams.getOrElse(0, {emptyMap()}).map{x-> ServiceInstantiationRequestDetails.UserParamNameAndValue(x.key, x.value)}
395 private fun generateSubscriberInfoPre1806(payload: ServiceInstantiation): SubscriberInfo {
396 val subscriberInfo = SubscriberInfo()
397 subscriberInfo.globalSubscriberId = payload.globalSubscriberId
398 subscriberInfo.subscriberName = payload.subscriberName
399 return subscriberInfo
402 private fun generateRelatedInstanceListForVrfEntry(vrfEntries: MutableMap<String, VrfEntry>): List<RelatedInstance> {
403 //fe send map of vrfs, with maps of networks and vpns, but actually we expect to only one vpn and one network
404 return if (vrfEntries.isEmpty() || vrfEntries.values.first().vpns.isEmpty() || vrfEntries.values.first().networks.isEmpty()) emptyList()
406 val vpn = vrfEntries.values.first().vpns.values.first()
407 val network = vrfEntries.values.first().networks.values.first()
408 listOf(vpn, network).map { RelatedInstance(it.modelInfo, it.instanceId, it.instanceName) }