From 88e37209870c269747d9dff49ab1169cb6399654 Mon Sep 17 00:00:00 2001 From: Michael Lando Date: Sun, 12 Nov 2017 02:07:27 +0200 Subject: [PATCH] fix capabilities Change-Id: Iff0448a083627b881affcacbf12b5fb5baebe294 Issue-Id: SDC-533 Signed-off-by: Michael Lando --- .../be/components/impl/ArtifactsBusinessLogic.java | 7 +- .../be/components/impl/ComponentBusinessLogic.java | 5 + .../impl/ComponentInstanceBusinessLogic.java | 18 +- .../be/components/impl/ResourceBusinessLogic.java | 519 ++++++++++++--------- .../sdc/be/info/ArtifactTemplateInfo.java | 10 +- .../be/tosca/CapabiltyRequirementConvertor.java | 435 ++++++++++++----- .../java/org/openecomp/sdc/be/tosca/CsarUtils.java | 7 +- .../openecomp/sdc/be/tosca/ToscaExportHandler.java | 352 ++++++++++---- .../org/openecomp/sdc/be/tosca/ToscaUtils.java | 56 ++- .../jsontitan/operations/NodeTypeOperation.java | 149 +++++- .../jsontitan/operations/ToscaOperationFacade.java | 62 ++- .../be/model/jsontitan/utils/ModelConverter.java | 36 +- .../be/datatypes/tosca/ToscaDataDefinition.java | 10 + sdc-os-chef/pom.xml | 1 + sdc-os-chef/scripts/docker_sanity_run.sh | 122 +++++ sdc-os-chef/scripts/sanity_run.sh | 117 +++++ ..._vPCRF_aligned_fixed.csar => Huawei_vPCRF.csar} | Bin .../files/default/Files/VNFs/Huawei_vSPGW.csar | Bin 0 -> 3907 bytes .../default/Files/VNFs/Huawei_vSPGW_fixed.csar | Bin 3887 -> 0 bytes ...-ZteEpcMmeVf-csar_fix.csar => ZteEpcMmeVf.csar} | Bin .../{ZteEpcSpgwVf-csar.csar => ZteEpcSpgwVf.csar} | Bin .../VNFs/{cscf_si_fixed.csar => cscf_si.csar} | Bin 50982 -> 50982 bytes .../sdc-sanity/files/default/Files/VNFs/ntas.csar | Bin 61134 -> 0 bytes .../Files/VNFs/{vCSCF_aligned.csar => vCSCF.csar} | Bin .../files/default/Files/VNFs/vCSCF_v3.0.csar | Bin 44851 -> 44977 bytes .../Files/VNFs/{vSBC_aligned.csar => vSBC.csar} | Bin .../default/Files/VNFs/{base_vfw.zip => vfw.zip} | Bin .../default/Files/VNFs/{base_vvg.zip => vvg.zip} | Bin .../sdc/ci/tests/api/ComponentBaseTest.java | 2 +- .../sdc/ci/tests/utils/rest/ResponseParser.java | 2 +- 30 files changed, 1425 insertions(+), 485 deletions(-) create mode 100644 sdc-os-chef/scripts/docker_sanity_run.sh create mode 100644 sdc-os-chef/scripts/sanity_run.sh rename sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/{Huawei_vPCRF_aligned_fixed.csar => Huawei_vPCRF.csar} (100%) create mode 100644 sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/Huawei_vSPGW.csar delete mode 100644 sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/Huawei_vSPGW_fixed.csar rename sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/{resource-ZteEpcMmeVf-csar_fix.csar => ZteEpcMmeVf.csar} (100%) rename sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/{ZteEpcSpgwVf-csar.csar => ZteEpcSpgwVf.csar} (100%) rename sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/{cscf_si_fixed.csar => cscf_si.csar} (96%) delete mode 100644 sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/ntas.csar rename sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/{vCSCF_aligned.csar => vCSCF.csar} (100%) rename sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/{vSBC_aligned.csar => vSBC.csar} (100%) rename sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/{base_vfw.zip => vfw.zip} (100%) rename sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/{base_vvg.zip => vvg.zip} (100%) diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ArtifactsBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ArtifactsBusinessLogic.java index 1ee3bc61c8..b7344e9911 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ArtifactsBusinessLogic.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ArtifactsBusinessLogic.java @@ -191,7 +191,12 @@ public class ArtifactsBusinessLogic extends BaseBusinessLogic { } public static enum ArtifactOperationEnum { - Create(), Update(), Delete(), Download(); + Create(), Update(), Delete(), Download(), Link(); + + public static boolean isCreateOrLink(ArtifactOperationEnum operation) { + return (operation.equals(Create) || operation.equals(Link)); + + } } public class ArtifactOperationInfo { diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentBusinessLogic.java index a34bf00a7a..108e03c274 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentBusinessLogic.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentBusinessLogic.java @@ -1178,6 +1178,11 @@ public abstract class ComponentBusinessLogic extends BaseBusinessLogic { return text; } + public Either shouldUpgradeToLatestDerived(Component clonedComponent) { + //general implementation. Must be error for service, VF . In ResourceBuisnessLogic exist override + return Either.right(ActionStatus.GENERAL_ERROR); + } + } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java index 6dc83bfc2b..66d8668fea 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java @@ -32,7 +32,6 @@ import java.util.UUID; import java.util.stream.Collectors; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.openecomp.sdc.be.config.BeEcompErrorManager; @@ -42,15 +41,12 @@ import org.openecomp.sdc.be.dao.jsongraph.types.JsonParseFlagEnum; import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary; import org.openecomp.sdc.be.dao.titan.TitanOperationStatus; import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition; -import org.openecomp.sdc.be.datatypes.elements.ComponentInstanceDataDefinition; import org.openecomp.sdc.be.datatypes.elements.GroupDataDefinition; import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition; import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition; import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum; -import org.openecomp.sdc.be.datatypes.enums.JsonPresentationFields; import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum; import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum; -import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum; import org.openecomp.sdc.be.info.CreateAndAssotiateInfo; import org.openecomp.sdc.be.model.ArtifactDefinition; import org.openecomp.sdc.be.model.Component; @@ -60,26 +56,20 @@ import org.openecomp.sdc.be.model.ComponentInstanceProperty; import org.openecomp.sdc.be.model.ComponentParametersView; import org.openecomp.sdc.be.model.DataTypeDefinition; import org.openecomp.sdc.be.model.GroupDefinition; -import org.openecomp.sdc.be.model.GroupInstance; import org.openecomp.sdc.be.model.InputDefinition; import org.openecomp.sdc.be.model.LifecycleStateEnum; import org.openecomp.sdc.be.model.PropertyDefinition.PropertyNames; -import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache; import org.openecomp.sdc.be.model.RequirementCapabilityRelDef; -import org.openecomp.sdc.be.model.Resource; import org.openecomp.sdc.be.model.User; -import org.openecomp.sdc.be.model.jsontitan.datamodel.ToscaElement; +import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache; import org.openecomp.sdc.be.model.jsontitan.operations.ToscaOperationFacade; +import org.openecomp.sdc.be.model.jsontitan.utils.ModelConverter; import org.openecomp.sdc.be.model.operations.api.IComponentInstanceOperation; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter; -import org.openecomp.sdc.be.model.operations.impl.PropertyOperation; -import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder; import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils; import org.openecomp.sdc.be.model.tosca.ToscaPropertyType; import org.openecomp.sdc.be.resources.data.ComponentInstanceData; -import org.openecomp.sdc.be.resources.data.PropertyValueData; -import org.openecomp.sdc.be.tosca.ToscaUtils; import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum; import org.openecomp.sdc.common.api.ArtifactTypeEnum; import org.openecomp.sdc.common.api.Constants; @@ -150,7 +140,7 @@ public abstract class ComponentInstanceBusinessLogic extends BaseBusinessLogic { containerComponent = validateComponentExists.left().value(); } - if (ToscaUtils.isAtomicType(containerComponent)) { + if (ModelConverter.isAtomicComponent(containerComponent)) { log.debug("Cannot attach resource instances to container resource of type {}", containerComponent.assetType()); return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_CANNOT_CONTAIN_RESOURCE_INSTANCES, containerComponent.assetType())); } @@ -209,7 +199,7 @@ public abstract class ComponentInstanceBusinessLogic extends BaseBusinessLogic { } org.openecomp.sdc.be.model.Component containerComponent = validateComponentExists.left().value(); - if (ToscaUtils.isAtomicType(containerComponent)) { + if (ModelConverter.isAtomicComponent(containerComponent)) { log.debug("Cannot attach resource instances to container resource of type {}", containerComponent.assetType()); return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_CANNOT_CONTAIN_RESOURCE_INSTANCES, containerComponent.assetType())); } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogic.java index 08d377c7db..b664efebcb 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogic.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogic.java @@ -111,6 +111,7 @@ import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache; import org.openecomp.sdc.be.model.category.CategoryDefinition; import org.openecomp.sdc.be.model.category.SubCategoryDefinition; import org.openecomp.sdc.be.model.heat.HeatParameterType; +import org.openecomp.sdc.be.model.jsontitan.utils.ModelConverter; import org.openecomp.sdc.be.model.operations.api.ICacheMangerOperation; import org.openecomp.sdc.be.model.operations.api.ICapabilityTypeOperation; import org.openecomp.sdc.be.model.operations.api.IElementOperation; @@ -595,6 +596,14 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { log.trace("YAML topology file found in CSAR, file name: {}, contents: {}", yamlFileName, yamlFileContent); + Either genericResourceEither = handleResourceGenericType(preparedResource); + if (genericResourceEither.isRight()) { + log.debug("failed to get resource generic type. status is {}", genericResourceEither.right().value()); + ResponseFormat responseFormat = genericResourceEither.right().value(); + componentsUtils.auditResource(genericResourceEither.right().value(), csarInfo.getModifier(), preparedResource, "", "", updateResource, null); + return Either.right(responseFormat); + } + parseNodeTypeInfoYamlEither = this.handleNodeTypes(yamlFileName, preparedResource, yamlFileContent, shouldLock, nodeTypesArtifactsToHandle, createdArtifacts, nodeTypesInfo, csarInfo, nodeName); if (parseNodeTypeInfoYamlEither.isRight()) { ResponseFormat responseFormat = parseNodeTypeInfoYamlEither.right().value(); @@ -687,6 +696,17 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { return result; } + private Either handleResourceGenericType(Resource resource) { + Either genericResourceEither = fetchAndSetDerivedFromGenericType(resource); + if (genericResourceEither.isRight()) { + return genericResourceEither; + } + if (resource.shouldGenerateInputs()) { + generateInputsFromGenericTypeProperties(resource, genericResourceEither.left().value()); + } + return genericResourceEither; + } + private Either>>, ResponseFormat> findNodeTypesArtifactsToHandle(Map nodeTypesInfo, CsarInfo csarInfo, Resource oldResource) { Map> extractedVfcsArtifacts = CsarUtils.extractVfcsArtifactsFromCsar(csarInfo.getCsar()); @@ -888,7 +908,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { handleNodeTypeArtifactsRes = Either.right(handleNodeTypeArtifactsRequestRes.right().value()); break; } - if (curOperation == ArtifactOperationEnum.Create) { + if (ArtifactOperationEnum.isCreateOrLink(curOperation)) { createdArtifacts.addAll(handleNodeTypeArtifactsRequestRes.left().value()); } handledNodeTypeArtifacts.addAll(handleNodeTypeArtifactsRequestRes.left().value()); @@ -1110,7 +1130,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { return Either.right(validateRes.right().value()); } // VF / PNF "derivedFrom" should be null (or ignored) - if (ToscaUtils.isAtomicType(resource)) { + if (ModelConverter.isAtomicComponent(resource)) { Either validateDerivedFromNotEmpty = validateDerivedFromNotEmpty(user, resource, AuditingActionEnum.CREATE_RESOURCE); if (validateDerivedFromNotEmpty.isRight()) { return Either.right(validateDerivedFromNotEmpty.right().value()); @@ -1236,7 +1256,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { } if(result == null){ newComplexVfc = buildCvfcRes.left().value(); - Either oldComplexVfcRes = toscaOperationFacade.getLatestByToscaResourceName(newComplexVfc.getToscaResourceName()); + Either oldComplexVfcRes = toscaOperationFacade.getFullLatestComponentByToscaResourceName(newComplexVfc.getToscaResourceName()); if(oldComplexVfcRes.isRight() && oldComplexVfcRes.right().value() != StorageOperationStatus.NOT_FOUND){ log.debug("Failed to fetch previous complex VFC by tosca resource name {}. Status is {}. ", newComplexVfc.getToscaResourceName(), oldComplexVfcRes.right().value()); result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR)); @@ -1873,25 +1893,27 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { log.debug("************* Going to create all nodes {}", yamlName); Either, ResponseFormat> createdResourcesFromdNodeTypeMap = this.handleNodeTypes(yamlName, resource, topologyTemplateYaml, false, nodeTypesArtifactsToCreate, nodeTypesNewCreatedArtifacts, nodeTypesInfo, csarInfo, nodeName); + log.debug("************* Finished to create all nodes {}", yamlName); if (createdResourcesFromdNodeTypeMap.isRight()) { log.debug("failed to resources from node types status is {}", createdResourcesFromdNodeTypeMap.right().value()); return Either.right(createdResourcesFromdNodeTypeMap.right().value()); } - log.debug("************* Finished to create all nodes {}", yamlName); log.debug("************* Going to create all resource instances {}", yamlName); createResourcesInstancesEither = createResourceInstances(csarInfo.getModifier(), yamlName, resource, uploadComponentInstanceInfoMap, true, false, csarInfo.getCreatedNodes()); + log.debug("************* Finished to create all resource instances {}", yamlName); if (createResourcesInstancesEither.isRight()) { log.debug("failed to create resource instances status is {}", createResourcesInstancesEither.right().value()); result = createResourcesInstancesEither; return createResourcesInstancesEither; } - log.debug("************* Finished to create all resource instances for {}", yamlName); resource = createResourcesInstancesEither.left().value(); log.debug("************* Going to create all relations {}", yamlName); createResourcesInstancesEither = createResourceInstancesRelations(csarInfo.getModifier(), yamlName, resource, uploadComponentInstanceInfoMap); + log.debug("************* Finished to create all relations {}", yamlName); + if (createResourcesInstancesEither.isRight()) { log.debug("failed to create relation between resource instances status is {}", createResourcesInstancesEither.right().value()); result = createResourcesInstancesEither; @@ -1899,7 +1921,6 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { } else { resource = createResourcesInstancesEither.left().value(); } - log.debug("************* Finished to create all relations {}", yamlName); log.debug("************* Going to create positions {}", yamlName); Either, ResponseFormat> eitherSetPosition = compositionBusinessLogic.setPositionsForComponentInstances(resource, csarInfo.getModifier().getUserId()); @@ -1991,7 +2012,14 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { if (eitherCreateResult.isRight()) { return Either.right(eitherCreateResult.right().value()); } - resource = eitherCreateResult.left().value(); + Either eitherGerResource = toscaOperationFacade.getToscaElement(resource.getUniqueId()); + if (eitherGerResource.isRight()) { + ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(eitherGerResource.right().value()), resource); + + return Either.right(responseFormat); + + } + resource = eitherGerResource.left().value(); Either, ResponseFormat> artifacsMetaCsarStatus = CsarValidationUtils.getArtifactsMeta(csarInfo.getCsar(), csarInfo.getCsarUUID(), componentsUtils); if (artifacsMetaCsarStatus.isLeft()) { @@ -1999,7 +2027,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { String artifactsFileName = artifacsMetaCsarStatus.left().value().getKey(); String artifactsContents = artifacsMetaCsarStatus.left().value().getValue(); Either createArtifactsFromCsar = Either.left(resource); - if (artifactOperation.getArtifactOperationEnum() == ArtifactOperationEnum.Create) + if (ArtifactOperationEnum.isCreateOrLink(artifactOperation.getArtifactOperationEnum())) createArtifactsFromCsar = createResourceArtifactsFromCsar(csarInfo, resource, artifactsContents, artifactsFileName, createdArtifacts, shouldLock, inTransaction); else createArtifactsFromCsar = updateResourceArtifactsFromCsar(csarInfo, resource, artifactsContents, artifactsFileName, createdArtifacts, shouldLock, inTransaction); @@ -2079,7 +2107,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { } private void addNonMetaCreatedArtifactsToSupportRollback(ArtifactOperationInfo operation, List createdArtifacts, Either, ResponseFormat> eitherNonMetaArtifacts) { - if (operation.getArtifactOperationEnum() == ArtifactOperationEnum.Create && createdArtifacts != null && eitherNonMetaArtifacts.isLeft()) { + if (ArtifactOperationEnum.isCreateOrLink(operation.getArtifactOperationEnum()) && createdArtifacts != null && eitherNonMetaArtifacts.isLeft()) { Either eitherResult = eitherNonMetaArtifacts.left().value(); if (eitherResult.isLeft()) { createdArtifacts.add(eitherResult.left().value()); @@ -2145,16 +2173,27 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { Map> parsedGroup = new HashMap>(); for (List parsedGroupTemplateList : parsedArifactsCollection) { + for (ArtifactTemplateInfo parsedGroupTemplate : parsedGroupTemplateList) { - parsedGroupTemplate.setGroupName(""); - Set parsedArtifactsNames = new HashSet(); - parsedArtifactsNames.add(parsedGroupTemplate); - List relatedGroupTemplateList = parsedGroupTemplate.getRelatedArtifactsInfo(); - if (relatedGroupTemplateList != null && !relatedGroupTemplateList.isEmpty()) { - createArtifactsGroupSet(parsedGroupTemplateList, parsedArtifactsNames); + if(parsedGroupTemplate.getGroupName() != null){ + parsedGroupTemplate.setGroupName(""); + Set parsedArtifactsNames = new HashSet(); + parsedArtifactsNames.add(parsedGroupTemplate); + List relatedGroupTemplateList = parsedGroupTemplate.getRelatedArtifactsInfo(); + if (relatedGroupTemplateList != null && !relatedGroupTemplateList.isEmpty()) { + createArtifactsGroupSet(parsedGroupTemplateList, parsedArtifactsNames); + } + parsedGroup.put(parsedGroupTemplate, parsedArtifactsNames); + }else{ + List arrtifacts = new ArrayList(); + arrtifacts.add(parsedGroupTemplate); + Either resStatus = createGroupDeploymentArtifactsFromCsar(csarInfo, resource, arrtifacts, createdNewArtifacts, createdDeplymentArtifactsAfterDelete, labelCounter, shouldLock, inTransaction); + if (resStatus.isRight()) + return resStatus; + } - parsedGroup.put(parsedGroupTemplate, parsedArtifactsNames); } + } ///////////////////////////////// find artifacts to @@ -2675,42 +2714,40 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { resStatus = createDeploymentArtifactsFromCsar(csarInfo, resource, artifactsGroup, artifactsUUIDGroup, groupTemplateInfo, createdNewArtifacts, artifactsFromResource, labelCounter, shouldLock, inTransaction); if (resStatus.isRight()) return resStatus; - - Map members = new HashMap(); - associateMembersToArtifacts(createdNewArtifacts, artifactsFromResource, heatGroups, artifactsGroup, members); - - List artifactsList = new ArrayList(artifactsGroup); - List artifactsUUIDList = new ArrayList(artifactsUUIDGroup); - - GroupDefinition groupDefinition = new GroupDefinition(); - groupDefinition.setName(groupName); - groupDefinition.setType(Constants.DEFAULT_GROUP_VF_MODULE); - groupDefinition.setArtifacts(artifactsList); - groupDefinition.setArtifactsUuid(artifactsUUIDList); - - if (!members.isEmpty()) - groupDefinition.setMembers(members); - - List properties = new ArrayList(); - GroupProperty prop = new GroupProperty(); - prop.setName(Constants.IS_BASE); - prop.setValue(Boolean.toString(groupTemplateInfo.isBase())); - properties.add(prop); - - List createdArtifacts = new ArrayList<>(); - createdArtifacts.addAll(createdNewArtifacts); - createdArtifacts.addAll(artifactsFromResource); - Either getLatestGroupTypeRes = groupTypeOperation.getLatestGroupTypeByType(Constants.DEFAULT_GROUP_VF_MODULE, true); - if (getLatestGroupTypeRes.isRight()) { - return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(getLatestGroupTypeRes.right().value()))); + if(groupName != null && !groupName.isEmpty()){ + Map members = new HashMap(); + associateMembersToArtifacts(createdNewArtifacts, artifactsFromResource, heatGroups, artifactsGroup, members); + + List artifactsList = new ArrayList(artifactsGroup); + List artifactsUUIDList = new ArrayList(artifactsUUIDGroup); + + GroupDefinition groupDefinition = new GroupDefinition(); + groupDefinition.setName(groupName); + groupDefinition.setType(Constants.DEFAULT_GROUP_VF_MODULE); + groupDefinition.setArtifacts(artifactsList); + groupDefinition.setArtifactsUuid(artifactsUUIDList); + + if (!members.isEmpty()) + groupDefinition.setMembers(members); + + List properties = new ArrayList(); + GroupProperty prop = new GroupProperty(); + prop.setName(Constants.IS_BASE); + prop.setValue(Boolean.toString(groupTemplateInfo.isBase())); + properties.add(prop); + + List createdArtifacts = new ArrayList<>(); + createdArtifacts.addAll(createdNewArtifacts); + createdArtifacts.addAll(artifactsFromResource); + Either getLatestGroupTypeRes = groupTypeOperation.getLatestGroupTypeByType(Constants.DEFAULT_GROUP_VF_MODULE, true); + if (getLatestGroupTypeRes.isRight()) { + return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(getLatestGroupTypeRes.right().value()))); + } + properties = createVfModuleAdditionalProperties(groupTemplateInfo.isBase(), groupName, properties, createdArtifacts, artifactsList, getLatestGroupTypeRes.left().value()); + groupDefinition.convertFromGroupProperties(properties); + + needToAdd.add(groupDefinition); } - properties = createVfModuleAdditionalProperties(groupTemplateInfo.isBase(), groupName, properties, createdArtifacts, artifactsList, getLatestGroupTypeRes.left().value()); - groupDefinition.convertFromGroupProperties(properties); - - // Either createGroup = groupBusinessLogic.createGroup(resource.getUniqueId(), user.getUserId(), ComponentTypeEnum.RESOURCE, groupDefinition, inTransaction); - // if (createGroup.isRight()) - // return Either.right(createGroup.right().value()); - needToAdd.add(groupDefinition); } ComponentParametersView componentParametersView = new ComponentParametersView(); componentParametersView.disableAll(); @@ -2863,7 +2900,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { EnumMap> vfCsarArtifactsToHandle = null; - if (artifactOperation.getArtifactOperationEnum() == ArtifactOperationEnum.Create) { + if (ArtifactOperationEnum.isCreateOrLink(artifactOperation.getArtifactOperationEnum())) { vfCsarArtifactsToHandle = new EnumMap<>(ArtifactOperationEnum.class); vfCsarArtifactsToHandle.put(artifactOperation.getArtifactOperationEnum(), artifactPathAndNameList); } else { @@ -2900,9 +2937,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { } } if (resStatus == null) { - Either toscaElement = toscaOperationFacade.getToscaElement(resource.getUniqueId()); - resStatus = toscaElement.bimap(resourceResponse -> resourceResponse, - storageResponse -> componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(storageResponse), resource)); + resStatus = Either.left(resource); } } catch (Exception e) { resStatus = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR)); @@ -2996,42 +3031,36 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { log.debug("createDeploymentArtifactsFromCsar end"); if (resStatus.isRight()) return resStatus; - - Map members = new HashMap(); - associateMembersToArtifacts(createdArtifacts, null, heatGroups, artifactsGroup, members); - - List artifactsList = new ArrayList(artifactsGroup); - List artifactsUUIDList = new ArrayList(artifactsUUIDGroup); - - GroupDefinition groupDefinition = new GroupDefinition(); - groupDefinition.setName(groupName); - groupDefinition.setType(Constants.DEFAULT_GROUP_VF_MODULE); - groupDefinition.setArtifacts(artifactsList); - groupDefinition.setArtifactsUuid(artifactsUUIDList); - - if (!members.isEmpty()) - groupDefinition.setMembers(members); - List properties = new ArrayList(); - GroupProperty prop = new GroupProperty(); - prop.setName(Constants.IS_BASE); - prop.setValue(Boolean.toString(groupTemplateInfo.isBase())); - properties.add(prop); - Either getLatestGroupTypeRes = groupTypeOperation.getLatestGroupTypeByType(Constants.DEFAULT_GROUP_VF_MODULE, true); - if (getLatestGroupTypeRes.isRight()) { - return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(getLatestGroupTypeRes.right().value()))); + if(groupName != null && !groupName.isEmpty()){ + Map members = new HashMap(); + associateMembersToArtifacts(createdArtifacts, null, heatGroups, artifactsGroup, members); + + List artifactsList = new ArrayList(artifactsGroup); + List artifactsUUIDList = new ArrayList(artifactsUUIDGroup); + + GroupDefinition groupDefinition = new GroupDefinition(); + groupDefinition.setName(groupName); + groupDefinition.setType(Constants.DEFAULT_GROUP_VF_MODULE); + groupDefinition.setArtifacts(artifactsList); + groupDefinition.setArtifactsUuid(artifactsUUIDList); + + if (!members.isEmpty()) + groupDefinition.setMembers(members); + List properties = new ArrayList(); + GroupProperty prop = new GroupProperty(); + prop.setName(Constants.IS_BASE); + prop.setValue(Boolean.toString(groupTemplateInfo.isBase())); + properties.add(prop); + Either getLatestGroupTypeRes = groupTypeOperation.getLatestGroupTypeByType(Constants.DEFAULT_GROUP_VF_MODULE, true); + if (getLatestGroupTypeRes.isRight()) { + return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(getLatestGroupTypeRes.right().value()))); + } + properties = createVfModuleAdditionalProperties(groupTemplateInfo.isBase(), groupName, properties, createdArtifacts, artifactsList, getLatestGroupTypeRes.left().value()); + groupDefinition.convertFromGroupProperties(properties); + log.debug("createGroup start"); + + needToCreate.add(groupDefinition); } - properties = createVfModuleAdditionalProperties(groupTemplateInfo.isBase(), groupName, properties, createdArtifacts, artifactsList, getLatestGroupTypeRes.left().value()); - groupDefinition.convertFromGroupProperties(properties); - log.debug("createGroup start"); - - // Since in these groups we handle only artifacts, then no need to - // fetch component instances - - // Either createGroup = groupBusinessLogic.createGroup(comp, user, ComponentTypeEnum.RESOURCE, groupDefinition, inTransaction); - // log.debug("createGroup end"); - // if (createGroup.isRight()) - // return Either.right(createGroup.right().value()); - needToCreate.add(groupDefinition); } ComponentParametersView componentParametersView = new ComponentParametersView(); @@ -3503,7 +3532,8 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { return Either.right(componentsUtils.getResponseFormat(ActionStatus.CSAR_INVALID_FORMAT, artifactFileName)); } - allGroups.addAll(artifactTemplateInfoList); + if(!artifactsTypeKey.equalsIgnoreCase(ArtifactTemplateInfo.CSAR_ARTIFACT)) + allGroups.addAll(artifactTemplateInfoList); artifactsMap.put(artifactsTypeKey, artifactTemplateInfoList); } int counter = groupBusinessLogic.getNextVfModuleNameCounter(resource.getGroups()); @@ -3609,10 +3639,40 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { } else { originResource = originCompMap.get(currentCompInstance.getComponentUid()); } - if (originResource.getCapabilities() != null && !originResource.getCapabilities().isEmpty()) - instCapabilties.put(currentCompInstance, originResource.getCapabilities()); if (originResource.getRequirements() != null && !originResource.getRequirements().isEmpty()) instRequirements.put(currentCompInstance, originResource.getRequirements()); + if (MapUtils.isNotEmpty(originResource.getCapabilities())) { + Map> originCapabilities ; + if (MapUtils.isNotEmpty(uploadComponentInstanceInfo.getCapabilities())) { + originCapabilities = new HashMap<>(); + originResource.getCapabilities().entrySet().stream().forEach(e ->{ + List list = e.getValue().stream().map(l -> new CapabilityDefinition(l)).collect(Collectors.toList()); + originCapabilities.put(e.getKey(), list); + }); + Map> newPropertiesMap = new HashMap<>(); + for(List capabilities : uploadComponentInstanceInfo.getCapabilities().values()){ + for(UploadCapInfo capability :capabilities){ + if(CollectionUtils.isNotEmpty(capability.getProperties())){ + newPropertiesMap.put(capability.getName(), capability.getProperties().stream().collect(Collectors.toMap(p->p.getName(), p->p))); + } + } + } + for (List capabilities : originCapabilities.values()) { + List filteredCapabilities = capabilities.stream().filter(c -> newPropertiesMap.containsKey(c.getName())).collect(Collectors.toList()); + for(CapabilityDefinition cap : filteredCapabilities){ + Either updateRes = updatePropertyValues(cap.getProperties(),newPropertiesMap.get(cap.getName()), allDataTypes.left().value()); + if(updateRes.isRight()){ + log.debug("Failed to update capability properties of capability {} . Status is {}. ", cap.getName(), updateRes.right().value()); + return Either.right(updateRes.right().value()); + } + } + } + } + else{ + originCapabilities = originResource.getCapabilities(); + } + instCapabilties.put(currentCompInstance, originCapabilities); + } if (originResource.getDeploymentArtifacts() != null && !originResource.getDeploymentArtifacts().isEmpty()) instDeploymentArtifacts.put(resourceInstanceId, originResource.getDeploymentArtifacts()); if (originResource.getArtifacts() != null && !originResource.getArtifacts().isEmpty()) @@ -3625,14 +3685,6 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { return Either.right(addPropertiesValueToRiRes); } } else { - Either genericResourceEither = fetchAndSetDerivedFromGenericType(originResource); - if (genericResourceEither.isRight()) { - return genericResourceEither; - } - log.trace("************* Going to add inputs from from original resource {} to resource instance. ", originResource.getName()); - if (originResource.shouldGenerateInputs()) - generateInputsFromGenericTypeProperties(originResource, genericResourceEither.left().value()); - ResponseFormat addInputValueToRiRes = addInputsValuesToRi(uploadComponentInstanceInfo, resource, originResource, currentCompInstance, yamlName, instInputs, allDataTypes.left().value()); if (addInputValueToRiRes.getStatus() != 200) { return Either.right(addInputValueToRiRes); @@ -3731,25 +3783,54 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { return Either.right(responseFormat); } + if(resource.getResourceType() == ResourceTypeEnum.CVFC){ + eitherGetResource = toscaOperationFacade.getToscaFullElement(resource.getUniqueId()); + if (eitherGetResource.isRight()) { + ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(eitherGetResource.right().value()), resource); + return Either.right(responseFormat); + } + eitherGetResource = updateCalculatedCapReqWithSubstitutionMappings(eitherGetResource.left().value(), uploadResInstancesMap); + if (eitherGetResource.isRight()) { + ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(eitherGetResource.right().value()), resource); + return Either.right(responseFormat); + } + } + log.debug("************* in create relations, getResource start"); - eitherGetResource = toscaOperationFacade.getToscaElement(resource.getUniqueId()); log.debug("************* in create relations, getResource end"); if (eitherGetResource.isRight()) { ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(eitherGetResource.right().value()), resource); - return Either.right(responseFormat); - } - resource = eitherGetResource.left().value(); - if(resource.getResourceType() == ResourceTypeEnum.CVFC){ - eitherGetResource = updateCalculatedCapReqWithSubstitutionMappings(resource, uploadResInstancesMap); - if (eitherGetResource.isRight()) { - ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(eitherGetResource.right().value()), resource); - return Either.right(responseFormat); + return Either.left(eitherGetResource.left().value()); + } + + private Either updatePropertyValues(List properties, Map newProperties, Map allDataTypes) { + for(ComponentInstanceProperty property : properties){ + Either updateRes = updatePropertyValue(property ,newProperties.get(property.getName()), allDataTypes); + if(updateRes.isRight()){ + log.debug("Failed to update capability property {} . Status is {}. ", property.getName(), updateRes.right().value()); + return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(updateRes.right().value()))); } } - return Either.left(eitherGetResource.left().value()); + return Either.left(true); + } + + private Either updatePropertyValue(ComponentInstanceProperty property, UploadPropInfo propertyInfo, Map allDataTypes) { + String value = null; + List getInputs = null; + boolean isValidate = true; + if (propertyInfo.getValue() != null) { + getInputs = propertyInfo.getGet_input(); + isValidate = getInputs == null || getInputs.isEmpty(); + if (isValidate) { + value = ImportUtils.getPropertyJsonStringValue(propertyInfo.getValue(), property.getType()); + } else + value = ImportUtils.getPropertyJsonStringValue(propertyInfo.getValue(), ToscaTagNamesEnum.GET_INPUT.getElementName()); + } + property.setValue(value); + return validatePropValueBeforeCreate(property, value, isValidate, null, allDataTypes); } private Either updateCalculatedCapReqWithSubstitutionMappings(Resource resource, Map uploadResInstancesMap) { @@ -3770,7 +3851,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { } } if(updateRes == null){ - updateRes = toscaOperationFacade.getToscaElement( resource.getUniqueId()); + updateRes = Either.left(resource); } return updateRes; } @@ -4017,25 +4098,28 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { } private ResponseFormat addPropertyValuesToRi(UploadComponentInstanceInfo uploadComponentInstanceInfo, Resource resource, Resource originResource, ComponentInstance currentCompInstance, String yamlName, - Map> instProperties, Map allDataTypes) { + Map> instProperties, Map allDataTypes) { Map> propMap = uploadComponentInstanceInfo.getProperties(); - if (propMap != null && propMap.size() > 0) { - Map currPropertiesMap = new HashMap(); + Map currPropertiesMap = new HashMap(); - List listFromMap = originResource.getProperties(); - if (listFromMap == null || listFromMap.isEmpty()) { - log.debug("failed to find properties "); - ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND); - return responseFormat; - } - for (PropertyDefinition prop : listFromMap) { - String propName = prop.getName(); - if (!currPropertiesMap.containsKey(propName)) { - currPropertiesMap.put(propName, prop); - } + List listFromMap = originResource.getProperties(); + if ((propMap != null && !propMap.isEmpty()) && (listFromMap == null || listFromMap.isEmpty())) { + log.debug("failed to find properties "); + ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND); + return responseFormat; + } + if(listFromMap == null || listFromMap.isEmpty()){ + return componentsUtils.getResponseFormat(ActionStatus.OK); + } + for (PropertyDefinition prop : listFromMap) { + String propName = prop.getName(); + if (!currPropertiesMap.containsKey(propName)) { + currPropertiesMap.put(propName, prop); } - List instPropList = new ArrayList<>(); + } + List instPropList = new ArrayList<>(); + if (propMap != null && propMap.size() > 0) { for (List propertyList : propMap.values()) { UploadPropInfo propertyInfo = propertyList.get(0); @@ -4111,14 +4195,14 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { // delete overriden property currPropertiesMap.remove(property.getName()); } - // add rest of properties - if (!currPropertiesMap.isEmpty()) { - for (PropertyDefinition value : currPropertiesMap.values()) { - instPropList.add(new ComponentInstanceProperty(value)); - } + } + // add rest of properties + if (!currPropertiesMap.isEmpty()) { + for (PropertyDefinition value : currPropertiesMap.values()) { + instPropList.add(new ComponentInstanceProperty(value)); } - instProperties.put(currentCompInstance.getUniqueId(), instPropList); } + instProperties.put(currentCompInstance.getUniqueId(), instPropList); return componentsUtils.getResponseFormat(ActionStatus.OK); } @@ -4271,6 +4355,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { return Either.left(validRegDef); } + @SuppressWarnings("unchecked") public Either parseResourceInfoFromYaml(String yamlFileName, Resource resource, String resourceYml, Map createdNodesToscaResourceNames, Map nodeTypesInfo, String nodeName) { Map mappedToscaTemplate; @@ -4324,7 +4409,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { Map nodeNamespaceMap) { Either eitherResource = null; - log.debug("{} - going to create resource instanse from CSAR", yamlName); + log.debug("createResourceInstances is {} - going to create resource instanse from CSAR", yamlName); if (uploadResInstancesMap == null || uploadResInstancesMap.isEmpty()) { ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.NOT_TOPOLOGY_TOSCA_TEMPLATE); @@ -4339,13 +4424,13 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { Iterator> nodesInfoValue = uploadResInstancesMap.entrySet().iterator(); Map resourcesInstancesMap = new HashMap<>(); while (nodesInfoValue.hasNext()) { - log.debug("*************Going to create resource instances from {}", yamlName); + log.debug("*************Going to create resource instances {}", yamlName); Entry uploadComponentInstanceInfoEntry = nodesInfoValue.next(); UploadComponentInstanceInfo uploadComponentInstanceInfo = uploadComponentInstanceInfoEntry.getValue(); // updating type if the type is node type name - we need to take the // updated name - log.debug("*************Going to create resource instance {}", uploadComponentInstanceInfo.getName()); + log.debug("*************Going to create resource instances {}", uploadComponentInstanceInfo.getName()); if (nodeNamespaceMap.containsKey(uploadComponentInstanceInfo.getType())) { uploadComponentInstanceInfo.setType(nodeNamespaceMap.get(uploadComponentInstanceInfo.getType()).getToscaResourceName()); } @@ -4362,7 +4447,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { ComponentTypeEnum containerComponentType = resource.getComponentType(); NodeTypeEnum containerNodeType = containerComponentType.getNodeType(); - //************ + if (containerNodeType.equals(NodeTypeEnum.Resource) && MapUtils.isNotEmpty(uploadComponentInstanceInfo.getCapabilities()) && MapUtils.isNotEmpty(refResource.getCapabilities())) { setCapabilityNamesTypes(refResource.getCapabilities(), uploadComponentInstanceInfo.getCapabilities()); Either>, ResponseFormat> getValidComponentInstanceCapabilitiesRes = getValidComponentInstanceCapabilities(refResource.getUniqueId(), refResource.getCapabilities(), uploadComponentInstanceInfo.getCapabilities()); @@ -4372,9 +4457,8 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { componentInstance.setCapabilities(getValidComponentInstanceCapabilitiesRes.left().value()); } } - //*********************** if (!existingnodeTypeMap.containsKey(uploadComponentInstanceInfo.getType())) { - log.debug("createResourceInstances - not found latest version for resource instance with name {} and type ", uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType()); + log.debug("createResourceInstances - not found lates version for resource instance with name {} and type ", uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType()); ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_NODE_TEMPLATE, yamlName, uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType()); return Either.right(responseFormat); } @@ -4424,30 +4508,28 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { return Either.left(eitherGerResource.left().value()); } - + private void setCapabilityNamesTypes(Map> originCapabilities, Map> uploadedCapabilities) { for(Entry> currEntry : uploadedCapabilities.entrySet()){ if(originCapabilities.containsKey(currEntry.getKey())){ currEntry.getValue().stream().forEach(cap -> cap.setType(currEntry.getKey())); } } - for(Map.Entry> capabilities : originCapabilities.entrySet()){ capabilities.getValue().stream().forEach(cap -> {if(uploadedCapabilities.containsKey(cap.getName())){uploadedCapabilities.get(cap.getName()).stream().forEach(c -> {c.setName(cap.getName());c.setType(cap.getType());});};}); - } + } + } - - private Either validateResourceInstanceBeforeCreate(String yamlName, UploadComponentInstanceInfo uploadComponentInstanceInfo, Map nodeNamespaceMap) { - log.debug("going to validate resource instance with name {} and type {} before create", uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType()); + log.debug("validateResourceInstanceBeforeCreate - going to validate resource instance with name {} and type before create", uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType()); Resource refResource = null; if (nodeNamespaceMap.containsKey(uploadComponentInstanceInfo.getType())) { refResource = nodeNamespaceMap.get(uploadComponentInstanceInfo.getType()); } else { Either findResourceEither = toscaOperationFacade.getLatestCertifiedNodeTypeByToscaResourceName(uploadComponentInstanceInfo.getType()); if (findResourceEither.isRight()) { - log.debug("not found lates version for resource instance with name {} and type {}", uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType()); + log.debug("validateResourceInstanceBeforeCreate - not found lates version for resource instance with name {} and type ", uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType()); ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(findResourceEither.right().value())); return Either.right(responseFormat); } @@ -4456,17 +4538,16 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { } String componentState = refResource.getComponentMetadataDefinition().getMetadataDataDefinition().getState(); if (componentState.equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name())) { - log.debug("component instance of component {} can not be created because the component is in an illegal state {}.", refResource.getName(), componentState); + log.debug("validateResourceInstanceBeforeCreate - component instance of component {} can not be created because the component is in an illegal state {}.", refResource.getName(), componentState); ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.ILLEGAL_COMPONENT_STATE, refResource.getComponentType().getValue(), refResource.getName(), componentState); return Either.right(responseFormat); } - if (!ToscaUtils.isAtomicType(refResource) && refResource.getResourceType() != ResourceTypeEnum.CVFC) { - log.debug("ref resource type is {}", refResource.getResourceType()); + if (!ModelConverter.isAtomicComponent(refResource) && refResource.getResourceType() != ResourceTypeEnum.CVFC) { + log.debug("validateResourceInstanceBeforeCreate - ref resource type is ", refResource.getResourceType()); ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_NODE_TEMPLATE, yamlName, uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType()); return Either.right(responseFormat); } - log.debug("validate resource instance with name {} and type {} before create, successful",uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType()); return Either.left(refResource); } @@ -4795,57 +4876,6 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { return result; } - - @SuppressWarnings("unchecked") - private Either>, ResponseFormat> createCapModuleFromYaml(UploadComponentInstanceInfo nodeTemplateInfo, Map nodeTemplateJsonMap) { - Map> moduleCap = new HashMap<>(); - Either>, ResponseFormat> response = Either.left(moduleCap); - Either, ResultStatusEnum> capabilitiesListRes = ImportUtils.findFirstToscaListElement(nodeTemplateJsonMap, ToscaTagNamesEnum.CAPABILITIES); - if (capabilitiesListRes.isLeft()) { - for (Object jsonCapObj : capabilitiesListRes.left().value()) { - String key = ((Map) jsonCapObj).keySet().iterator().next(); - Object capJson = ((Map) jsonCapObj).get(key); - Either eitherCap = addModuleNodeTemplateCap(nodeTemplateInfo, moduleCap, capJson, key); - if (eitherCap.isRight()) { - return Either.right(eitherCap.right().value()); - } - } - } else { - Either, ResultStatusEnum> capabilitiesMapRes = ImportUtils.findFirstToscaMapElement(nodeTemplateJsonMap, ToscaTagNamesEnum.CAPABILITIES); - if (capabilitiesMapRes.isLeft()) { - for (Map.Entry entry : capabilitiesMapRes.left().value().entrySet()) { - String capName = entry.getKey(); - Object capJson = entry.getValue(); - Either eitherCap = addModuleNodeTemplateCap(nodeTemplateInfo, moduleCap, capJson, capName); - if (eitherCap.isRight()) { - return Either.right(eitherCap.right().value()); - } - } - } - } - return response; - } - - private Either addModuleNodeTemplateCap(UploadComponentInstanceInfo nodeTemplateInfo, Map> moduleCap, Object capJson, String key) { - - Either eitherCap = createModuleNodeTemplateCap(capJson); - if (eitherCap.isRight()) { - log.info("error when creating Capability:{}, for node:{}", key, nodeTemplateInfo); - return Either.right(eitherCap.right().value()); - } else { - UploadCapInfo capabilityDef = eitherCap.left().value(); - capabilityDef.setKey(key); - if (moduleCap.containsKey(key)) { - moduleCap.get(key).add(capabilityDef); - } else { - List list = new ArrayList(); - list.add(capabilityDef); - moduleCap.put(key, list); - } - } - return Either.left( eitherCap.left().value()); - } - @SuppressWarnings("unchecked") private Either>, ResponseFormat> createReqModuleFromYaml(UploadComponentInstanceInfo nodeTemplateInfo, Map nodeTemplateJsonMap) { Map> moduleRequirements = new HashMap>(); @@ -4876,9 +4906,9 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { } return response; } - - private Either addModuleNodeTemplateReq(UploadComponentInstanceInfo nodeTemplateInfo, Map> moduleRequirements, Object requirementJson, String requirementName) { + private Either addModuleNodeTemplateReq(UploadComponentInstanceInfo nodeTemplateInfo,Map> moduleRequirements, Object requirementJson, String requirementName) { + Either eitherRequirement = createModuleNodeTemplateReg(requirementJson); if (eitherRequirement.isRight()) { log.info("error when creating Requirement:{}, for node:{}", requirementName, nodeTemplateInfo); @@ -4897,6 +4927,56 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { return Either.left(eitherRequirement.left().value()); } + @SuppressWarnings("unchecked") + private Either>, ResponseFormat> createCapModuleFromYaml(UploadComponentInstanceInfo nodeTemplateInfo, Map nodeTemplateJsonMap) { + Map> moduleCap = new HashMap<>(); + Either>, ResponseFormat> response = Either.left(moduleCap); + Either, ResultStatusEnum> capabilitiesListRes = ImportUtils.findFirstToscaListElement(nodeTemplateJsonMap, ToscaTagNamesEnum.CAPABILITIES); + if (capabilitiesListRes.isLeft()) { + for (Object jsonCapObj : capabilitiesListRes.left().value()) { + String key = ((Map) jsonCapObj).keySet().iterator().next(); + Object capJson = ((Map) jsonCapObj).get(key); + Either eitherCap = addModuleNodeTemplateCap(nodeTemplateInfo, moduleCap, capJson, key); + if (eitherCap.isRight()) { + return Either.right(eitherCap.right().value()); + } + } + } else { + Either, ResultStatusEnum> capabilitiesMapRes = ImportUtils.findFirstToscaMapElement(nodeTemplateJsonMap, ToscaTagNamesEnum.CAPABILITIES); + if (capabilitiesMapRes.isLeft()) { + for (Map.Entry entry: capabilitiesMapRes.left().value().entrySet()) { + String capName = entry.getKey(); + Object capJson = entry.getValue(); + Either eitherCap = addModuleNodeTemplateCap(nodeTemplateInfo, moduleCap, capJson, capName); + if (eitherCap.isRight()) { + return Either.right(eitherCap.right().value()); + } + } + } + } + return response; + } + + private Either addModuleNodeTemplateCap(UploadComponentInstanceInfo nodeTemplateInfo, Map> moduleCap, Object capJson, String key) { + + Either eitherCap = createModuleNodeTemplateCap(capJson); + if (eitherCap.isRight()) { + log.info("error when creating Capability:{}, for node:{}", key, nodeTemplateInfo); + return Either.right(eitherCap.right().value()); + } else { + UploadCapInfo capabilityDef = eitherCap.left().value(); + capabilityDef.setKey(key); + if (moduleCap.containsKey(key)) { + moduleCap.get(key).add(capabilityDef); + } else { + List list = new ArrayList(); + list.add(capabilityDef); + moduleCap.put(key, list); + } + } + return Either.left( eitherCap.left().value()); + } + @SuppressWarnings("unchecked") private Either createModuleNodeTemplateCap(Object capObject) { UploadCapInfo capTemplateInfo = new UploadCapInfo(); @@ -5109,6 +5189,11 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { result = Either.right(validateFieldsResponse.right().value()); return result; } + + validateFieldsResponse = validateCapabilityTypesCreate(user, getCapabilityTypeOperation(), newResource, AuditingActionEnum.IMPORT_RESOURCE, inTransaction); + if (validateFieldsResponse.isRight()) { + return Either.right(validateFieldsResponse.right().value()); + } // contact info normalization newResource.setContactId(newResource.getContactId().toLowerCase()); @@ -5245,7 +5330,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { resource.setCreatorUserId(user.getUserId()); resource.setCreatorFullName(user.getFirstName() + " " + user.getLastName()); resource.setContactId(resource.getContactId().toLowerCase()); - if (StringUtils.isEmpty(resource.getToscaResourceName()) && !ToscaUtils.isAtomicType(resource)) { + if (StringUtils.isEmpty(resource.getToscaResourceName()) && !ModelConverter.isAtomicComponent(resource)) { String resourceSystemName; if(csarInfo != null && StringUtils.isNotEmpty(csarInfo.getVfResourceName())){ resourceSystemName = ValidationUtils.convertToSystemName(csarInfo.getVfResourceName()); @@ -5414,11 +5499,9 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { } try { if (resource.deriveFromGeneric()) { - Either genericResourceEither = fetchAndSetDerivedFromGenericType(resource); + Either genericResourceEither = handleResourceGenericType(resource); if (genericResourceEither.isRight()) return genericResourceEither; - if (resource.shouldGenerateInputs()) - generateInputsFromGenericTypeProperties(resource, genericResourceEither.left().value()); } Either respStatus = createResourceTransaction(resource, user, isNormative, inTransaction); @@ -5783,7 +5866,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { // list // This code is not called from import resources, because of root // VF "derivedFrom" should be null (or ignored) - if (ToscaUtils.isAtomicType(currentResource)) { + if (ModelConverter.isAtomicComponent(currentResource)) { Either derivedFromNotEmptyEither = validateDerivedFromNotEmpty(null, newResource, null); if (derivedFromNotEmptyEither.isRight()) { log.debug("for updated resource {}, derived from field is empty", newResource.getName()); @@ -5997,7 +6080,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { // validate template (derived from) log.debug("validate derived from"); - if (!ToscaUtils.isAtomicType(resource) && resource.getResourceType() != ResourceTypeEnum.CVFC) { + if (!ModelConverter.isAtomicComponent(resource) && resource.getResourceType() != ResourceTypeEnum.CVFC) { resource.setDerivedFrom(null); } eitherValidation = validateDerivedFromExist(user, resource, actionEnum); @@ -6216,6 +6299,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { * * return Either.left(true); } */ + private boolean isResourceNameEquals(Resource currentResource, Resource updateInfoResource) { String resourceNameUpdated = updateInfoResource.getName(); String resourceNameCurrent = currentResource.getName(); @@ -6248,7 +6332,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { currentResource.setNormalizedName(ValidationUtils.normaliseComponentName(resourceNameUpdated)); currentResource.setSystemName(ValidationUtils.convertToSystemName(resourceNameUpdated)); - } else if(currentResource.getResourceType() != ResourceTypeEnum.CVFC) { + } else { log.info("Resource name: {}, cannot be updated once the resource has been certified once.", resourceNameUpdated); ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NAME_CANNOT_BE_CHANGED); return Either.right(errorResponse); @@ -7278,5 +7362,20 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { UiComponentDataTransfer dataTransfer = UiComponentDataConverter.getUiDataTransferFromResourceByParams(resource, dataParamsToReturn); return Either.left(dataTransfer); } + @Override + public Either shouldUpgradeToLatestDerived(Component clonedComponent) { + Resource resource = (Resource) clonedComponent; + if (ModelConverter.isAtomicComponent(resource.getResourceType())) { + Either shouldUpgradeToLatestDerived = toscaOperationFacade.shouldUpgradeToLatestDerived(resource); + if (shouldUpgradeToLatestDerived.isRight()) { + return Either.right(componentsUtils.convertFromStorageResponse(shouldUpgradeToLatestDerived.right().value())); + } + return Either.left(shouldUpgradeToLatestDerived.left().value()); + } else { + return super.shouldUpgradeToLatestDerived(clonedComponent); + } + } + + } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactTemplateInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactTemplateInfo.java index fa051a9e3d..0f1bf4603d 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactTemplateInfo.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactTemplateInfo.java @@ -49,11 +49,11 @@ public class ArtifactTemplateInfo { public static final String ENV = "env"; public static final String IS_BASE = "isBase"; - private static final String CSAR_HEAT = "HEAT"; - private static final String CSAR_ARTIFACT = "artifacts"; - private static final String CSAR_NETWORK = "network"; - private static final String CSAR_VOLUME = "volume"; - private static final String CSAR_NESTED = "nested"; + public static final String CSAR_HEAT = "HEAT"; + public static final String CSAR_ARTIFACT = "artifacts"; + public static final String CSAR_NETWORK = "network"; + public static final String CSAR_VOLUME = "volume"; + public static final String CSAR_NESTED = "nested"; private static final Object DESC = "description"; private static Logger log = LoggerFactory.getLogger(ArtifactTemplateInfo.class.getName()); String type; diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CapabiltyRequirementConvertor.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CapabiltyRequirementConvertor.java index 649f083903..0c93ee83cf 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CapabiltyRequirementConvertor.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CapabiltyRequirementConvertor.java @@ -1,5 +1,4 @@ /*- - * ============LICENSE_START======================================================= * SDC * ================================================================================ @@ -22,11 +21,16 @@ package org.openecomp.sdc.be.tosca; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.openecomp.sdc.be.datatypes.elements.CapabilityDataDefinition; import org.openecomp.sdc.be.datatypes.elements.RequirementDataDefinition; @@ -34,9 +38,14 @@ import org.openecomp.sdc.be.model.CapabilityDefinition; import org.openecomp.sdc.be.model.Component; import org.openecomp.sdc.be.model.ComponentInstance; import org.openecomp.sdc.be.model.ComponentInstanceProperty; +import org.openecomp.sdc.be.model.ComponentParametersView; import org.openecomp.sdc.be.model.DataTypeDefinition; import org.openecomp.sdc.be.model.PropertyDefinition; import org.openecomp.sdc.be.model.RequirementDefinition; +import org.openecomp.sdc.be.model.jsontitan.operations.ToscaOperationFacade; +import org.openecomp.sdc.be.model.jsontitan.utils.ModelConverter; +import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; +import org.openecomp.sdc.be.tosca.ToscaUtils.SubstituitionEntry; import org.openecomp.sdc.be.tosca.model.SubstitutionMapping; import org.openecomp.sdc.be.tosca.model.ToscaCapability; import org.openecomp.sdc.be.tosca.model.ToscaNodeTemplate; @@ -47,14 +56,29 @@ import org.openecomp.sdc.be.tosca.model.ToscaTemplateCapability; import org.openecomp.sdc.common.util.ValidationUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import fj.data.Either; - +/** + * Allows to convert requirements\capabilities of a component to requirements\capabilities of a substitution mappings section of a tosca template + * + */ +@org.springframework.stereotype.Component("capabilty-requirement-convertor") +@Scope(value = "singleton") public class CapabiltyRequirementConvertor { + + private static final String NO_CAPABILITIES = "No Capabilities for node type"; private static CapabiltyRequirementConvertor instance; - public final static String PATH_DELIMITER = "."; + private static Logger logger = LoggerFactory.getLogger(CapabiltyRequirementConvertor.class.getName()); + public static final String PATH_DELIMITER = "."; + + @Autowired + private ToscaOperationFacade toscaOperationFacade; protected CapabiltyRequirementConvertor() { @@ -66,9 +90,13 @@ public class CapabiltyRequirementConvertor { } return instance; } - - private static Logger log = LoggerFactory.getLogger(CapabiltyRequirementConvertor.class.getName()); - + /** + * Allows to convert capabilities of a component to capabilities of a substitution mappings section of a tosca template + * @param componentInstance + * @param dataTypes + * @param nodeTemplate + * @return + */ public Either convertComponentInstanceCapabilties(ComponentInstance componentInstance, Map dataTypes, ToscaNodeTemplate nodeTemplate) { Map> capabilitiesInst = componentInstance.getCapabilities(); @@ -77,12 +105,10 @@ public class CapabiltyRequirementConvertor { capabilitiesInst.entrySet().forEach(e -> { List capList = e.getValue(); if (capList != null && !capList.isEmpty()) { - capList.forEach(c -> { - convertOverridenProperties(componentInstance, dataTypes, capabilties, c); - }); + capList.forEach(c -> convertOverridenProperties(componentInstance, dataTypes, capabilties, c)); } }); - if (capabilties != null && !capabilties.isEmpty()) { + if (MapUtils.isNotEmpty(capabilties)) { nodeTemplate.setCapabilities(capabilties); } } @@ -92,15 +118,16 @@ public class CapabiltyRequirementConvertor { private void convertOverridenProperties(ComponentInstance componentInstance, Map dataTypes, Map capabilties, CapabilityDefinition c) { List properties = c.getProperties(); if (properties != null && !properties.isEmpty()) { - properties.stream().filter(p -> (p.getValueUniqueUid() != null)).forEach(p -> { - convertOverridenProperty(componentInstance, dataTypes, capabilties, c, p); - }); + properties + .stream() + .filter(p -> p.getValue() != null || p.getDefaultValue() != null) + .forEach(p -> convertOverridenProperty(componentInstance, dataTypes, capabilties, c, p)); } } private void convertOverridenProperty(ComponentInstance componentInstance, Map dataTypes, Map capabilties, CapabilityDefinition c, ComponentInstanceProperty p) { - if (log.isDebugEnabled()) { - log.debug("Exist overriden property {} for capabity {} with value {}", p.getName(), c.getName(), p.getValue()); + if (logger.isDebugEnabled()) { + logger.debug("Exist overriden property {} for capabity {} with value {}", p.getName(), c.getName(), p.getValue()); } ToscaTemplateCapability toscaTemplateCapability = capabilties.get(c.getName()); if (toscaTemplateCapability == null) { @@ -117,109 +144,224 @@ public class CapabiltyRequirementConvertor { } private Object convertInstanceProperty(Map dataTypes, ComponentInstance componentInstance, ComponentInstanceProperty prop) { - log.debug("Convert property {} for instance {}", prop.getName(), componentInstance.getUniqueId()); + logger.debug("Convert property {} for instance {}", prop.getName(), componentInstance.getUniqueId()); String propertyType = prop.getType(); String innerType = null; if (prop.getSchema() != null && prop.getSchema().getProperty() != null) { innerType = prop.getSchema().getProperty().getType(); } - Object convertedValue = PropertyConvertor.getInstance().convertToToscaObject(propertyType, prop.getValue(), innerType, dataTypes); - return convertedValue; + String propValue = prop.getValue() == null ? prop.getDefaultValue() : prop.getValue(); + return PropertyConvertor.getInstance().convertToToscaObject(propertyType, propValue, innerType, dataTypes); } - + /** + * Allows to convert requirements of a node type to tosca template requirements representation + * @param component + * @param nodeType + * @return + */ public Either convertRequirements(Component component, ToscaNodeType nodeType) { List> toscaRequirements = convertRequirementsAsList(component); if (!toscaRequirements.isEmpty()) { nodeType.setRequirements(toscaRequirements); } - log.debug("Finish convert Requirements for node type"); + logger.debug("Finish convert Requirements for node type"); return Either.left(nodeType); } - public Either convertSubstitutionMappingRequirements(Component component, SubstitutionMapping substitutionMapping) { - Map toscaRequirements = convertSubstitutionMappingRequirementsAsMap(component); - if (!toscaRequirements.isEmpty()) { - substitutionMapping.setRequirements(toscaRequirements); + /** + * Allows to convert component requirements to the tosca template substitution mappings requirements + * @param componentsCache + * @param component + * @param substitutionMappings + * @return + */ + public Either convertSubstitutionMappingRequirements(Map componentsCache, Component component, SubstitutionMapping substitutionMappings) { + Either result = Either.left(substitutionMappings); + Either, ToscaError> toscaRequirementsRes = convertSubstitutionMappingRequirementsAsMap(componentsCache, component); + if(toscaRequirementsRes.isRight()){ + result = Either.right(toscaRequirementsRes.right().value()); + logger.error("Failed convert requirements for the component {}. ", component.getName()); + } else if (MapUtils.isNotEmpty(toscaRequirementsRes.left().value())) { + substitutionMappings.setRequirements(toscaRequirementsRes.left().value()); + result = Either.left(substitutionMappings); + logger.debug("Finish convert requirements for the component {}. ", component.getName()); } - log.debug("Finish convert Requirements for node type"); - - return Either.left(substitutionMapping); + return result; } private List> convertRequirementsAsList(Component component) { Map> requirements = component.getRequirements(); List> toscaRequirements = new ArrayList<>(); if (requirements != null) { - boolean isNodeType = ToscaUtils.isAtomicType(component); for (Map.Entry> entry : requirements.entrySet()) { - entry.getValue().stream().filter(r -> (!isNodeType || (isNodeType && component.getUniqueId().equals(r.getOwnerId())) || (isNodeType && r.getOwnerId() == null))).forEach(r -> { - ImmutablePair pair = convertRequirement(component, isNodeType, r); + entry.getValue().stream().filter(r -> filter(component, r.getOwnerId())).forEach(r -> { + ImmutablePair pair = convertRequirement(component, ModelConverter.isAtomicComponent(component), r); Map requirement = new HashMap<>(); requirement.put(pair.left, pair.right); toscaRequirements.add(requirement); }); - - log.debug("Finish convert Requirements for node type"); + logger.debug("Finish convert Requirements for node type"); } } else { - log.debug("No Requirements for node type"); + logger.debug("No Requirements for node type"); } return toscaRequirements; } - private String getSubPathByFirstDelimiterAppearance(String path) { - return path.substring(path.indexOf(PATH_DELIMITER) + 1); + private boolean filter(Component component, String ownerId) { + return !ModelConverter.isAtomicComponent(component) || isNodeTypeOwner(component, ownerId) || (ModelConverter.isAtomicComponent(component) && ownerId == null); + } + + private boolean isNodeTypeOwner(Component component, String ownerId) { + return ModelConverter.isAtomicComponent(component) && component.getUniqueId().equals(ownerId); } private String getSubPathByLastDelimiterAppearance(String path) { return path.substring(path.lastIndexOf(PATH_DELIMITER) + 1); } - - //This function calls on Substitution Mapping region - the component is always non-atomic - private Map convertSubstitutionMappingRequirementsAsMap(Component component) { + + private Either, ToscaError> convertSubstitutionMappingRequirementsAsMap(Map componentsCache, Component component) { Map> requirements = component.getRequirements(); - Map toscaRequirements = new HashMap<>(); + Either, ToscaError> result; if (requirements != null) { - for (Map.Entry> entry : requirements.entrySet()) { - entry.getValue().stream().forEach(r -> { - String fullReqName; - String sourceCapName; - if(ToscaUtils.isComplexVfc(component)){ - fullReqName = r.getName(); - sourceCapName = r.getParentName(); - } else { - fullReqName = getRequirementPath(r); - sourceCapName = getSubPathByFirstDelimiterAppearance(fullReqName); - } - log.debug("the requirement {} belongs to resource {} ", fullReqName, component.getUniqueId()); - if(sourceCapName!= null){ - toscaRequirements.put(fullReqName, new String[]{r.getOwnerName(), sourceCapName}); - } - }); - log.debug("Finish convert Requirements for node type"); - } + result = buildAddSubstitutionMappingsRequirements(componentsCache, component, requirements); } else { - log.debug("No Requirements for node type"); + result = Either.left(Maps.newHashMap()); + logger.debug("No requirements for substitution mappings section of a tosca template of the component {}. ", component.getName()); } - return toscaRequirements; + return result; } - private String getRequirementPath(RequirementDefinition r) { - List pathArray = Lists.reverse(r.getPath().stream() - .map(path -> ValidationUtils.normalizeComponentInstanceName(getSubPathByLastDelimiterAppearance(path))) - .collect(Collectors.toList())); - return new StringBuilder().append(String.join(PATH_DELIMITER, pathArray)).append(PATH_DELIMITER).append(r.getName()).toString(); + private Either, ToscaError> buildAddSubstitutionMappingsRequirements(Map componentsCache, Component component, Map> requirements) { + Map toscaRequirements = new HashMap<>(); + Either, ToscaError> result = null; + for (Map.Entry> entry : requirements.entrySet()) { + Optional failedToAddRequirement = entry.getValue() + .stream() + .filter(r->!addEntry(componentsCache, toscaRequirements, component, r.getName(), r.getParentName(), r.getPath())) + .findAny(); + if(failedToAddRequirement.isPresent()){ + logger.error("Failed to convert requirement {} for substitution mappings section of a tosca template of the component {}. ", + failedToAddRequirement.get().getName(), component.getName()); + result = Either.right(ToscaError.NODE_TYPE_REQUIREMENT_ERROR); + } + logger.debug("Finish convert requirements for the component {}. ", component.getName()); + } + if(result == null){ + result = Either.left(toscaRequirements); + } + return result; } + private Either, ToscaError> buildAddSubstitutionMappingsCapabilities(Map componentsCache, Component component, Map> capabilities) { + + Map toscaRequirements = new HashMap<>(); + Either, ToscaError> result = null; + for (Map.Entry> entry : capabilities.entrySet()) { + Optional failedToAddRequirement = entry.getValue() + .stream() + .filter(c->!addEntry(componentsCache, toscaRequirements, component, c.getName(), c.getParentName(), c.getPath())) + .findAny(); + if(failedToAddRequirement.isPresent()){ + logger.error("Failed to convert capalility {} for substitution mappings section of a tosca template of the component {}. ", + failedToAddRequirement.get().getName(), component.getName()); + result = Either.right(ToscaError.NODE_TYPE_CAPABILITY_ERROR); + } + logger.debug("Finish convert capalilities for the component {}. ", component.getName()); + } + if(result == null){ + result = Either.left(toscaRequirements); + } + return result; + } + + private boolean addEntry(Map componentsCache, Map capReqMap, Component component, String name, String parentName, List path){ + + SubstituitionEntry entry = new SubstituitionEntry(name, parentName, ""); + + if(shouldBuildSubstitutionName(component, path) && !buildSubstitutedNamePerInstance(componentsCache, component, name, path, entry)){ + return false; + } + logger.debug("The requirement/capability {} belongs to the component {} ", entry.getFullName(), component.getUniqueId()); + if (entry.getSourceName() != null) { + addEntry(capReqMap, component, path, entry); + } + logger.debug("Finish convert the requirement/capability {} for the component {}. ", entry.getFullName(), component.getName()); + return true; + + } + + private boolean shouldBuildSubstitutionName(Component component, List path) { + return !ToscaUtils.isComplexVfc(component) && CollectionUtils.isNotEmpty(path) && path.iterator().hasNext(); + } + + private boolean buildSubstitutedNamePerInstance(Map componentsCache, Component component, String name, List path, SubstituitionEntry entry) { + Optional ci = component.getComponentInstances().stream().filter(c->c.getUniqueId().equals(Iterables.getLast(path))).findFirst(); + if(ci.isPresent()){ + Either buildSubstitutedName = buildSubstitutedName(componentsCache, name, path, ci.get()); + if(buildSubstitutedName.isRight()){ + return false; + } + entry.setFullName(ci.get().getNormalizedName() + '.' + buildSubstitutedName.left().value()); + entry.setSourceName(buildSubstitutedName.left().value()); + } else { + return false; + } + return true; + } + + private void addEntry(Map toscaRequirements, Component component, List capPath, SubstituitionEntry entry) { + Optional findFirst = component.getComponentInstances().stream().filter(ci -> ci.getUniqueId().equals(Iterables.getLast(capPath))).findFirst(); + if (findFirst.isPresent()) { + entry.setOwner(findFirst.get().getNormalizedName()); + } + toscaRequirements.put(entry.getFullName(), new String[] { entry.getOwner(), entry.getSourceName() }); + } + + private Either buildSubstitutedName(Map componentsCache, String name, List path, ComponentInstance instance) { + + Either result = null; + Either getOriginRes = getOriginComponent(componentsCache, instance); + if(getOriginRes.isRight()){ + logger.debug("Failed to build substituted name for the capability/requirement {}. Failed to get an origin component with uniqueId {}", name, instance.getComponentUid()); + result = Either.right(false); + } + if(result == null){ + result = buildSubstitutedName(componentsCache, getOriginRes.left().value(), Lists.newArrayList(path.subList(0, path.size()-1)), name); + } + return result; + } + + private String getRequirementPath(Component component, RequirementDefinition r) { + + // Evg : for the last in path take real instance name and not "decrypt" unique id. ( instance name can be change and not equal to id..) + // dirty quick fix. must be changed as capability redesign + List capPath = r.getPath(); + String lastInPath = capPath.get(capPath.size() - 1); + Optional findFirst = component.getComponentInstances().stream().filter(ci -> ci.getUniqueId().equals(lastInPath)).findFirst(); + if (findFirst.isPresent()) { + String lastInPathName = findFirst.get().getNormalizedName(); + + if (capPath.size() > 1) { + List pathArray = Lists.reverse(capPath.stream().map(path -> ValidationUtils.normalizeComponentInstanceName(getSubPathByLastDelimiterAppearance(path))).collect(Collectors.toList())); + + return new StringBuilder().append(lastInPathName).append(PATH_DELIMITER).append(String.join(PATH_DELIMITER, pathArray.subList(1, pathArray.size() ))).append(PATH_DELIMITER).append(r.getName()).toString(); + }else{ + return new StringBuilder().append(lastInPathName).append(PATH_DELIMITER).append(r.getName()).toString(); + } + } + return ""; + } + private ImmutablePair convertRequirement(Component component, boolean isNodeType, RequirementDefinition r) { String name = r.getName(); if (!isNodeType) { - name = getRequirementPath(r); + name = getRequirementPath(component, r); } - log.debug("the requirement {} belongs to resource {} ", name, component.getUniqueId()); + logger.debug("the requirement {} belongs to resource {} ", name, component.getUniqueId()); ToscaRequirement toscaRequirement = new ToscaRequirement(); List occurences = new ArrayList<>(); @@ -230,78 +372,101 @@ public class CapabiltyRequirementConvertor { occurences.add(Integer.valueOf(r.getMaxOccurrences())); } toscaRequirement.setOccurrences(occurences); - // toscaRequirement.setOccurrences(createOcurrencesRange(requirementDefinition.getMinOccurrences(), - // requirementDefinition.getMaxOccurrences())); toscaRequirement.setNode(r.getNode()); toscaRequirement.setCapability(r.getCapability()); toscaRequirement.setRelationship(r.getRelationship()); - ImmutablePair pair = new ImmutablePair(name, toscaRequirement); - return pair; + return new ImmutablePair<>(name, toscaRequirement); } + /** + * Allows to convert capabilities of a node type to tosca template capabilities + * @param component + * @param dataTypes + * @return + */ public Map convertCapabilities(Component component, Map dataTypes) { Map> capabilities = component.getCapabilities(); Map toscaCapabilities = new HashMap<>(); if (capabilities != null) { - boolean isNodeType = ToscaUtils.isAtomicType(component); + boolean isNodeType = ModelConverter.isAtomicComponent(component); for (Map.Entry> entry : capabilities.entrySet()) { - entry.getValue().stream().filter(c -> (!isNodeType || (isNodeType && component.getUniqueId().equals(c.getOwnerId())) || (isNodeType && c.getOwnerId() == null) )).forEach(c -> { - convertCapabilty(component, toscaCapabilities, isNodeType, c, dataTypes); - - }); + entry.getValue().stream().filter(c -> filter(component, c.getOwnerId())).forEach(c -> convertCapabilty(component, toscaCapabilities, isNodeType, c, dataTypes)); } } else { - log.debug("No Capabilities for node type"); + logger.debug(NO_CAPABILITIES); } return toscaCapabilities; } - - //This function calls on Substitution Mapping region - the component is always non-atomic - public Map convertSubstitutionMappingCapabilities(Component component, Map dataTypes) { - Map> capabilities = component.getCapabilities(); - Map toscaCapabilities = new HashMap<>(); + + /** + * Allows to convert capabilities of a server proxy node type to tosca template capabilities + * @param component + * @param proxyComponent + * @param instanceProxy + * @param dataTypes + * @return + */ + public Map convertProxyCapabilities(Component component, Component proxyComponent, ComponentInstance instanceProxy, Map dataTypes) { + Map> capabilities = instanceProxy.getCapabilities(); + Map toscaCapabilities = new HashMap<>(); if (capabilities != null) { + boolean isNodeType = ModelConverter.isAtomicComponent(component); for (Map.Entry> entry : capabilities.entrySet()) { - entry.getValue().stream().forEach(c -> { - String fullCapName; - String sourceReqName; - if(ToscaUtils.isComplexVfc(component)){ - fullCapName = c.getName(); - sourceReqName = c.getParentName(); - } else { - fullCapName = getCapabilityPath(c); - sourceReqName = getSubPathByFirstDelimiterAppearance(fullCapName); - } - log.debug("the capabilty {} belongs to resource {} ", fullCapName, component.getUniqueId()); - if(sourceReqName!= null){ - toscaCapabilities.put(fullCapName, new String[]{c.getOwnerName(), sourceReqName}); - } - }); + entry.getValue().stream().forEach(c -> convertCapabilty(proxyComponent, toscaCapabilities, isNodeType, c, dataTypes)); } } else { - log.debug("No Capabilities for node type"); + logger.debug(NO_CAPABILITIES); } return toscaCapabilities; } - - private String getCapabilityPath(CapabilityDefinition c) { - List pathArray = Lists.reverse(c.getPath().stream() - .map(path -> ValidationUtils.normalizeComponentInstanceName(getSubPathByLastDelimiterAppearance(path))) - .collect(Collectors.toList())); - return new StringBuilder().append(String.join(PATH_DELIMITER, pathArray)).append(PATH_DELIMITER).append(c.getName()).toString(); + + /** + * Allows to convert component capabilities to the tosca template substitution mappings capabilities + * @param componentsCache + * @param component + * @return + */ + public Either, ToscaError> convertSubstitutionMappingCapabilities(Map componentsCache, Component component) { + Map> capabilities = component.getCapabilities(); + Either, ToscaError> res = null; + if (capabilities != null) { + res = buildAddSubstitutionMappingsCapabilities(componentsCache, component, capabilities); + } else { + logger.debug(NO_CAPABILITIES); + res = Either.left(new HashMap<>()); + } + return res; } - - + private String getCapabilityPath(CapabilityDefinition c, Component component) { + // Evg : for the last in path take real instance name and not "decrypt" unique id. ( instance name can be change and not equal to id..) + // dirty quick fix. must be changed as capability redesign + List capPath = c.getPath(); + String lastInPath = capPath.get(capPath.size() - 1); + Optional findFirst = component.getComponentInstances().stream().filter(ci -> ci.getUniqueId().equals(lastInPath)).findFirst(); + if (findFirst.isPresent()) { + String lastInPathName = findFirst.get().getNormalizedName(); + + if (capPath.size() > 1) { + List pathArray = Lists.reverse(capPath.stream().map(path -> ValidationUtils.normalizeComponentInstanceName(getSubPathByLastDelimiterAppearance(path))).collect(Collectors.toList())); + + return new StringBuilder().append(lastInPathName).append(PATH_DELIMITER).append(String.join(PATH_DELIMITER, pathArray.subList(1, pathArray.size() ))).append(PATH_DELIMITER).append(c.getName()).toString(); + }else{ + return new StringBuilder().append(lastInPathName).append(PATH_DELIMITER).append(c.getName()).toString(); + } + } + return ""; + } + private void convertCapabilty(Component component, Map toscaCapabilities, boolean isNodeType, CapabilityDefinition c, Map dataTypes) { String name = c.getName(); if (!isNodeType) { - name = getCapabilityPath(c); + name = getCapabilityPath(c, component); } - log.debug("the capabilty {} belongs to resource {} ", name, component.getUniqueId()); + logger.debug("the capabilty {} belongs to resource {} ", name, component.getUniqueId()); ToscaCapability toscaCapability = new ToscaCapability(); toscaCapability.setDescription(c.getDescription()); toscaCapability.setType(c.getType()); @@ -327,5 +492,61 @@ public class CapabiltyRequirementConvertor { } toscaCapabilities.put(name, toscaCapability); } + + Either buildSubstitutedName(Map originComponents, Component originComponent, List path, String name) { + StringBuilder substitutedName = new StringBuilder(); + boolean nameBuiltSuccessfully = true; + Either result; + if(CollectionUtils.isNotEmpty(path) && !ToscaUtils.isComplexVfc(originComponent)){ + Collections.reverse(path); + Iterator instanceIdIter = path.iterator(); + nameBuiltSuccessfully = appendNameRecursively(originComponents, originComponent, instanceIdIter, substitutedName); + } + if(nameBuiltSuccessfully){ + result = Either.left(substitutedName.append(name).toString()); + } else { + result = Either.right(nameBuiltSuccessfully); + } + return result; + } + + private boolean appendNameRecursively(Map originComponents, Component originComponent, Iterator instanceIdIter, StringBuilder substitutedName) { + if(CollectionUtils.isNotEmpty(originComponent.getComponentInstances()) && instanceIdIter.hasNext()){ + String instanceId = instanceIdIter.next(); + Optional instanceOpt = originComponent.getComponentInstances().stream().filter(i -> i.getUniqueId().equals(instanceId)).findFirst(); + if(!instanceOpt.isPresent()){ + logger.debug("Failed to find an instance with uniqueId {} on a component with uniqueId {}", instanceId, originComponent.getUniqueId()); + return false; + } + Either getOriginRes = getOriginComponent(originComponents, instanceOpt.get()); + if(getOriginRes.isRight()){ + return false; + } + appendNameRecursively(originComponents, getOriginRes.left().value(), instanceIdIter, substitutedName); + substitutedName.append(instanceOpt.get().getNormalizedName()).append('.'); + return true; + } + return true; + } + + private Either getOriginComponent(Map originComponents, ComponentInstance instance) { + Either result; + Either getOriginRes; + if(originComponents.containsKey(instance.getComponentUid())){ + result = Either.left(originComponents.get(instance.getComponentUid())); + } else { + ComponentParametersView filter = new ComponentParametersView(true); + filter.setIgnoreComponentInstances(false); + getOriginRes = toscaOperationFacade.getToscaElement(instance.getComponentUid(), filter); + if(getOriginRes.isRight()){ + logger.debug("Failed to get an origin component with uniqueId {}", instance.getComponentUid()); + result = Either.right(false); + } else { + result = Either.left(getOriginRes.left().value()); + originComponents.put(getOriginRes.left().value().getUniqueId(), getOriginRes.left().value()); + } + } + return result; + } } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CsarUtils.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CsarUtils.java index fda199052e..2f4a385f69 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CsarUtils.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CsarUtils.java @@ -52,6 +52,7 @@ import org.openecomp.sdc.be.model.Service; import org.openecomp.sdc.be.model.User; import org.openecomp.sdc.be.model.jsontitan.operations.ToscaElementLifecycleOperation; import org.openecomp.sdc.be.model.jsontitan.operations.ToscaOperationFacade; +import org.openecomp.sdc.be.model.jsontitan.utils.ModelConverter; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter; import org.openecomp.sdc.be.resources.data.ESArtifactData; @@ -275,7 +276,7 @@ public class CsarUtils { zip.putNextEntry(new ZipEntry(DEFINITIONS_PATH + fileName)); zip.write(mainYaml); //US798487 - Abstraction of complex types - if (!ToscaUtils.isAtomicType(component)){ + if (!ModelConverter.isAtomicComponent(component)){ log.debug("Component {} is complex - generating abstract type for it..", component.getName()); writeComponentInterface(component, zip, fileName); } @@ -332,7 +333,7 @@ public class CsarUtils { zip.write(content); // add component interface to zip - if (!ToscaUtils.isAtomicType(innerComponent)) { + if (!ModelConverter.isAtomicComponent(innerComponent)) { writeComponentInterface(innerComponent, zip, icFileName); } } @@ -453,7 +454,7 @@ public class CsarUtils { } //if not atomic - insert inner components as well - if(!ToscaUtils.isAtomicType(componentRI)) { + if(!ModelConverter.isAtomicComponent(componentRI)) { addInnerComponentsToCache(componentCache, componentRI); } } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java index 8e0c312f1a..e65c4b5001 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java @@ -27,6 +27,7 @@ import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.function.Supplier; @@ -42,10 +43,29 @@ import org.openecomp.sdc.be.config.ConfigurationManager; import org.openecomp.sdc.be.dao.titan.TitanOperationStatus; import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition; import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum; -import org.openecomp.sdc.be.model.*; +import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum; +import org.openecomp.sdc.be.model.ArtifactDefinition; +import org.openecomp.sdc.be.model.CapabilityDefinition; +import org.openecomp.sdc.be.model.Component; +import org.openecomp.sdc.be.model.ComponentInstance; +import org.openecomp.sdc.be.model.ComponentInstanceInput; +import org.openecomp.sdc.be.model.ComponentInstanceProperty; +import org.openecomp.sdc.be.model.ComponentParametersView; +import org.openecomp.sdc.be.model.DataTypeDefinition; +import org.openecomp.sdc.be.model.GroupDefinition; +import org.openecomp.sdc.be.model.GroupInstance; +import org.openecomp.sdc.be.model.GroupProperty; +import org.openecomp.sdc.be.model.InputDefinition; +import org.openecomp.sdc.be.model.PropertyDefinition; +import org.openecomp.sdc.be.model.RequirementAndRelationshipPair; +import org.openecomp.sdc.be.model.RequirementCapabilityRelDef; +import org.openecomp.sdc.be.model.RequirementDefinition; +import org.openecomp.sdc.be.model.Resource; +import org.openecomp.sdc.be.model.Service; import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache; import org.openecomp.sdc.be.model.category.CategoryDefinition; import org.openecomp.sdc.be.model.jsontitan.operations.ToscaOperationFacade; +import org.openecomp.sdc.be.model.jsontitan.utils.ModelConverter; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; import org.openecomp.sdc.be.model.tosca.converters.ToscaValueBaseConverter; import org.openecomp.sdc.be.tosca.model.IToscaMetadata; @@ -87,10 +107,10 @@ public class ToscaExportHandler { @Autowired private ToscaOperationFacade toscaOperationFacade; - - private CapabiltyRequirementConvertor capabiltyRequirementConvertor = CapabiltyRequirementConvertor.getInstance(); + @Autowired + private CapabiltyRequirementConvertor capabiltyRequirementConvertor; private PropertyConvertor propertyConvertor = PropertyConvertor.getInstance(); - + Map originComponents = new HashMap<>(); private static Logger log = LoggerFactory.getLogger(ToscaExportHandler.class.getName()); @@ -105,9 +125,9 @@ public class ToscaExportHandler { public static final String VOLUME_GROUP_KEY = "volume_group"; public static final String VF_MODULE_TYPE_BASE = "Base"; public static final String VF_MODULE_TYPE_EXPANSION = "Expansion"; - public static final List>> DEFAULT_IMPORTS = ConfigurationManager.getConfigurationManager().getConfiguration().getDefaultImports(); - - + private static final String FAILED_TO_GET_DEFAULT_IMPORTS_CONFIGURATION = "convertToToscaTemplate - failed to get Default Imports section from configuration"; + private static final String NOT_SUPPORTED_COMPONENT_TYPE = "Not supported component type {}"; + protected static final List>> DEFAULT_IMPORTS = ConfigurationManager.getConfigurationManager().getConfiguration().getDefaultImports(); public Either exportComponent(Component component) { @@ -122,15 +142,16 @@ public class ToscaExportHandler { } public Either exportComponentInterface(Component component) { - if(null == DEFAULT_IMPORTS) { - log.debug("convertToToscaTemplate - failed to get Default Imports section from configuration"); + if (null == DEFAULT_IMPORTS) { + log.debug(FAILED_TO_GET_DEFAULT_IMPORTS_CONFIGURATION); return Either.right(ToscaError.GENERAL_ERROR); } ToscaTemplate toscaTemplate = new ToscaTemplate(TOSCA_VERSION); toscaTemplate.setImports(new ArrayList<>(DEFAULT_IMPORTS)); Map nodeTypes = new HashMap<>(); - Either toscaTemplateRes = convertInterfaceNodeType(component, toscaTemplate, nodeTypes); + Either toscaTemplateRes = convertInterfaceNodeType(component, toscaTemplate, + nodeTypes); if (toscaTemplateRes.isRight()) { return Either.right(toscaTemplateRes.right().value()); } @@ -179,8 +200,8 @@ public class ToscaExportHandler { } private Either convertToToscaTemplate(Component component) { - if(null == DEFAULT_IMPORTS) { - log.debug("convertToToscaTemplate - failed to get Default Imports section from configuration"); + if (null == DEFAULT_IMPORTS) { + log.debug(FAILED_TO_GET_DEFAULT_IMPORTS_CONFIGURATION); return Either.right(ToscaError.GENERAL_ERROR); } @@ -190,7 +211,7 @@ public class ToscaExportHandler { toscaTemplate.setMetadata(convertMetadata(component)); toscaTemplate.setImports(new ArrayList<>(DEFAULT_IMPORTS)); Map nodeTypes = new HashMap<>(); - if (ToscaUtils.isAtomicType(component)) { + if (ModelConverter.isAtomicComponent(component)) { log.trace("convert component as node type"); return convertNodeType(component, toscaTemplate, nodeTypes); } else { @@ -208,6 +229,15 @@ public class ToscaExportHandler { return Either.right(importsRes.right().value()); } toscaNode = importsRes.left().value().left; + /*Either, ToscaError> nodeTypesMapEither = createProxyNodeTypes(component); + if (nodeTypesMapEither.isRight()) { + log.debug("Failed to fetch normative service proxy resource by tosca name, error {}", + nodeTypesMapEither.right().value()); + return Either.right(nodeTypesMapEither.right().value()); + } + Map nodeTypesMap = nodeTypesMapEither.left().value(); + if (nodeTypesMap != null && !nodeTypesMap.isEmpty()) + toscaNode.setNode_types(nodeTypesMap);*/ Map componentCache = importsRes.left().value().right; Either, TitanOperationStatus> dataTypesEither = dataTypeCache.getAll(); @@ -240,9 +270,9 @@ public class ToscaExportHandler { topologyTemplate.setNode_templates(nodeTemplates.left().value()); } - Map groupsMap = null; + Map groupsMap; if (groups != null && !groups.isEmpty()) { - groupsMap = new HashMap(); + groupsMap = new HashMap<>(); for (GroupDefinition group : groups) { ToscaGroupTemplate toscaGroup = convertGroup(group); groupsMap.put(group.getName(), toscaGroup); @@ -252,7 +282,7 @@ public class ToscaExportHandler { topologyTemplate.addGroups(groupsMap); } SubstitutionMapping substitutionMapping = new SubstitutionMapping(); - String toscaResourceName = null; + String toscaResourceName; switch (component.getComponentType()) { case RESOURCE: toscaResourceName = ((ResourceMetadataDataDefinition) component.getComponentMetadataDefinition() @@ -263,19 +293,19 @@ public class ToscaExportHandler { + component.getComponentMetadataDefinition().getMetadataDataDefinition().getSystemName(); break; default: - log.debug("Not supported component type {}", component.getComponentType()); + log.debug(NOT_SUPPORTED_COMPONENT_TYPE, component.getComponentType()); return Either.right(ToscaError.NOT_SUPPORTED_TOSCA_TYPE); } substitutionMapping.setNode_type(toscaResourceName); - Either capabilities = convertCapabilities(component, substitutionMapping, - dataTypes); + Either capabilities = convertCapabilities(component, substitutionMapping); if (capabilities.isRight()) { return Either.right(capabilities.right().value()); } substitutionMapping = capabilities.left().value(); - Either requirements = capabiltyRequirementConvertor.convertSubstitutionMappingRequirements(component, substitutionMapping); + Either requirements = capabiltyRequirementConvertor + .convertSubstitutionMappingRequirements(originComponents, component, substitutionMapping); if (requirements.isRight()) { return Either.right(requirements.right().value()); } @@ -356,19 +386,19 @@ public class ToscaExportHandler { private Either>, ToscaError> fillImports(Component component, ToscaTemplate toscaTemplate) { - if(null == DEFAULT_IMPORTS) { - log.debug("convertToToscaTemplate - failed to get Default Imports section from configuration"); + if (null == DEFAULT_IMPORTS) { + log.debug(FAILED_TO_GET_DEFAULT_IMPORTS_CONFIGURATION); return Either.right(ToscaError.GENERAL_ERROR); } Map componentCache = new HashMap<>(); - if (!ToscaUtils.isAtomicType(component)) { + if (!ModelConverter.isAtomicComponent(component)) { List componentInstances = component.getComponentInstances(); if (componentInstances != null && !componentInstances.isEmpty()) { - List>> additionalImports = - toscaTemplate.getImports() == null ? new ArrayList<>(DEFAULT_IMPORTS) : new ArrayList<>(toscaTemplate.getImports()); + List>> additionalImports = toscaTemplate.getImports() == null + ? new ArrayList<>(DEFAULT_IMPORTS) : new ArrayList<>(toscaTemplate.getImports()); List> dependecies = new ArrayList<>(); @@ -386,9 +416,8 @@ public class ToscaExportHandler { importsListMember.put(keyNameBuilder.toString(), interfaceFiles); additionalImports.add(importsListMember); - componentInstances.forEach(ci -> { - createDependency(componentCache, additionalImports, dependecies, ci); - }); + componentInstances.forEach(ci -> createDependency(componentCache, additionalImports, dependecies, ci)); + originComponents.putAll(componentCache); toscaTemplate.setDependencies(dependecies); toscaTemplate.setImports(additionalImports); } @@ -407,7 +436,8 @@ public class ToscaExportHandler { Component componentRI = componentCache.get(ci.getComponentUid()); if (componentRI == null) { // all resource must be only once! - Either resource = toscaOperationFacade.getToscaFullElement(ci.getComponentUid()); + Either resource = toscaOperationFacade + .getToscaFullElement(ci.getComponentUid()); if (resource.isRight()) { log.debug("Failed to fetch resource with id {} for instance {}"); } @@ -429,7 +459,7 @@ public class ToscaExportHandler { dependecies.add(new ImmutableTriple(artifactName, artifactDefinition.getEsId(), fetchedComponent)); - if(!ToscaUtils.isAtomicType(componentRI)) { + if (!ModelConverter.isAtomicComponent(componentRI)) { importsListMember = new HashMap<>(); Map interfaceFiles = new HashMap<>(); interfaceFiles.put(IMPORTS_FILE_KEY, getInterfaceFilename(artifactName)); @@ -442,8 +472,7 @@ public class ToscaExportHandler { } public static String getInterfaceFilename(String artifactName) { - String interfaceFileName = artifactName.substring(0, artifactName.lastIndexOf('.')) + ToscaExportHandler.TOSCA_INTERFACE_NAME; - return interfaceFileName; + return artifactName.substring(0, artifactName.lastIndexOf('.')) + ToscaExportHandler.TOSCA_INTERFACE_NAME; } private Either convertNodeType(Component component, ToscaTemplate toscaNode, @@ -466,7 +495,7 @@ public class ToscaExportHandler { toscaNodeType = properties.left().value(); log.debug("Properties converted for {}", component.getUniqueId()); - //Extracted to method for code reuse + // Extracted to method for code reuse return convertReqCapAndTypeName(component, toscaNode, nodeTypes, toscaNodeType, dataTypes); } @@ -496,7 +525,7 @@ public class ToscaExportHandler { } } - //Extracted to method for code reuse + // Extracted to method for code reuse return convertReqCapAndTypeName(component, toscaNode, nodeTypes, toscaNodeType, dataTypes); } @@ -510,15 +539,13 @@ public class ToscaExportHandler { toscaNodeType = capabilities.left().value(); log.debug("Capabilities converted for {}", component.getUniqueId()); - Either requirements = capabiltyRequirementConvertor.convertRequirements(component, - toscaNodeType); + Either requirements = capabiltyRequirementConvertor.convertRequirements(component, toscaNodeType); if (requirements.isRight()) { return Either.right(requirements.right().value()); } toscaNodeType = requirements.left().value(); log.debug("Requirements converted for {}", component.getUniqueId()); - String toscaResourceName; switch (component.getComponentType()) { case RESOURCE: @@ -530,7 +557,7 @@ public class ToscaExportHandler { + component.getComponentMetadataDefinition().getMetadataDataDefinition().getSystemName(); break; default: - log.debug("Not supported component type {}", component.getComponentType()); + log.debug(NOT_SUPPORTED_COMPONENT_TYPE, component.getComponentType()); return Either.right(ToscaError.NOT_SUPPORTED_TOSCA_TYPE); } @@ -558,7 +585,8 @@ public class ToscaExportHandler { nodeTemplate.setType(componentInstance.getToscaComponentName()); Either requirements = convertComponentInstanceRequirements(component, - componentInstance, component.getComponentInstancesRelations(), nodeTemplate, componentCache.get(componentInstance.getComponentUid())); + componentInstance, component.getComponentInstancesRelations(), nodeTemplate, + componentCache.get(componentInstance.getComponentUid())); if (requirements.isRight()) { convertNodeTemplatesRes = Either.right(requirements.right().value()); break; @@ -634,8 +662,9 @@ public class ToscaExportHandler { List instanceInputsList = componentInstancesInputs.get(instanceUniqueId); if (instanceInputsList != null) { instanceInputsList.forEach(input -> { - - Supplier supplier = () -> input.getValue() != null && !input.getValue().isEmpty()? input.getValue(): input.getDefaultValue(); + + Supplier supplier = () -> input.getValue() != null && !input.getValue().isEmpty() + ? input.getValue() : input.getDefaultValue(); convertAndAddValue(dataTypes, componentInstance, props, input, supplier); }); } @@ -724,7 +753,8 @@ public class ToscaExportHandler { Supplier supplGroupType = () -> groupInstance.getType(); Supplier supplDescription = () -> groupInstance.getDescription(); - Supplier> supplProperties = () -> groupInstance.convertToGroupInstancesProperties(); + Supplier> supplProperties = () -> groupInstance + .convertToGroupInstancesProperties(); Supplier supplgroupName = () -> groupInstance.getGroupName(); Supplier supplInvariantUUID = () -> groupInstance.getInvariantUUID(); Supplier supplGroupUUID = () -> groupInstance.getGroupUUID(); @@ -751,7 +781,8 @@ public class ToscaExportHandler { toscaMetadata = new VfModuleToscaMetadata(); Map properties = fillGroupProperties(props.get()); - if(!properties.containsKey(VF_MODULE_DESC_KEY) || StringUtils.isEmpty((String) properties.get(VF_MODULE_DESC_KEY))){ + if (!properties.containsKey(VF_MODULE_DESC_KEY) + || StringUtils.isEmpty((String) properties.get(VF_MODULE_DESC_KEY))) { properties.put(VF_MODULE_DESC_KEY, description.get()); } toscaGroup.setProperties(properties); @@ -765,7 +796,7 @@ public class ToscaExportHandler { private Map fillGroupProperties(List groupProps) { Map properties = new HashMap<>(); - if(groupProps != null){ + if (groupProps != null) { for (GroupProperty gp : groupProps) { if (gp.getName().equals(Constants.IS_BASE)) { Boolean isBase = Boolean.parseBoolean(gp.getValue()); @@ -800,72 +831,211 @@ public class ToscaExportHandler { private ToscaNodeType createNodeType(Component component) { ToscaNodeType toscaNodeType = new ToscaNodeType(); - if (ToscaUtils.isAtomicType(component)){ - if (((Resource) component).getDerivedFrom() != null){ + if (ModelConverter.isAtomicComponent(component)) { + if (((Resource) component).getDerivedFrom() != null) { toscaNodeType.setDerived_from(((Resource) component).getDerivedFrom().get(0)); } - toscaNodeType.setDescription(component.getDescription()); // or name?? + toscaNodeType.setDescription(component.getDescription()); // or + // name?? } else { - String derivedFrom = null != component.getDerivedFromGenericType()? component.getDerivedFromGenericType() : "tosca.nodes.Root"; + String derivedFrom = null != component.getDerivedFromGenericType() ? component.getDerivedFromGenericType() + : "tosca.nodes.Root"; toscaNodeType.setDerived_from(derivedFrom); } return toscaNodeType; } - //TODO save the capability(type or name) info on relation data - private Either convertComponentInstanceRequirements(Component component, - ComponentInstance componentInstance, List relations, - ToscaNodeTemplate nodeTypeTemplate, Component originComponent) { + /*private Either, ToscaError> createProxyNodeTypes(Component container) { - List instancesList = component.getComponentInstances(); - List> toscaRequirements = new ArrayList<>(); - Map> reqMap = originComponent.getRequirements(); + Map nodeTypesMap = null; + Either, ToscaError> res = Either.left(nodeTypesMap); - relations.stream().filter(p -> componentInstance.getUniqueId().equals(p.getFromNode())).forEach(rel -> { - ComponentInstance toComponentInstance = instancesList.stream() - .filter(i -> rel.getToNode().equals(i.getUniqueId())).findFirst().orElse(null); - if (toComponentInstance == null) { - log.debug("Failed to find relation between node {} to node {}", componentInstance.getName(), - rel.getToNode()); - return; - } - RequirementAndRelationshipPair reqAndRelationshipPair = rel.getRelationships().get(0); - ToscaTemplateRequirement toscaRequirement = new ToscaTemplateRequirement(); - toscaRequirement.setNode(toComponentInstance.getName()); - Optional findAny = reqMap.values().stream().flatMap(e -> e.stream()) - .filter(e -> e.getName().equals(reqAndRelationshipPair.getRequirement())).findAny(); - if (findAny.isPresent()) { - RequirementDefinition reqDefinition = findAny.get(); - toscaRequirement.setCapability(reqDefinition.getCapability()); - toscaRequirement.setRelationship(reqDefinition.getRelationship()); - } else { - // reqMap represents calculated requirements! if not found there, export data directly from the relation definition - log.debug("Failed to find requirement {} definition for node {}", reqAndRelationshipPair.getRequirement(), componentInstance.getName()); - return; + List componetInstances = container.getComponentInstances(); + + if (componetInstances == null || componetInstances.isEmpty()) + return res; + Map serviceProxyInstanceList = new HashMap<>(); + List proxyInst = componetInstances.stream().filter(p -> p.getOriginType().name().equals(OriginTypeEnum.ServiceProxy.name())).collect(Collectors.toList()); + if(proxyInst != null && !proxyInst.isEmpty()){ + for(ComponentInstance inst: proxyInst){ + serviceProxyInstanceList.put(inst.getToscaComponentName(), inst); } - Map toscaReqMap = new HashMap<>(); - toscaReqMap.put(reqAndRelationshipPair.getRequirement(), toscaRequirement); - toscaRequirements.add(toscaReqMap); + } + + if (serviceProxyInstanceList.isEmpty()) + return res; + ComponentParametersView filter = new ComponentParametersView(true); + filter.setIgnoreCapabilities(false); + filter.setIgnoreComponentInstances(false); + Either serviceProxyOrigin = toscaOperationFacade + .getLatestByName("serviceProxy"); + if (serviceProxyOrigin.isRight()) { + log.debug("Failed to fetch normative service proxy resource by tosca name, error {}", + serviceProxyOrigin.right().value()); + return Either.right(ToscaError.NOT_SUPPORTED_TOSCA_TYPE); + } + Component origComponent = serviceProxyOrigin.left().value(); + + nodeTypesMap = new HashMap<>(); + for (Entry entryProxy : serviceProxyInstanceList.entrySet()) { + Component serviceComponent = null; + ComponentParametersView componentParametersView = new ComponentParametersView(); + componentParametersView.disableAll(); + componentParametersView.setIgnoreCategories(false); + Either service = toscaOperationFacade + .getToscaElement(entryProxy.getValue().getSourceModelUid(), componentParametersView); + if (service.isRight()) { + log.debug("Failed to fetch resource with id {} for instance {}"); + } else + serviceComponent = service.left().value(); + + ToscaNodeType toscaNodeType = createProxyNodeType(origComponent, serviceComponent, entryProxy.getValue()); + nodeTypesMap.put(entryProxy.getKey(), toscaNodeType); + } + + return Either.left(nodeTypesMap); + }*/ - }); + private ToscaNodeType createProxyNodeType(Component origComponent, Component proxyComponent, + ComponentInstance instance) { + ToscaNodeType toscaNodeType = new ToscaNodeType(); + String derivedFrom = ((Resource) origComponent).getToscaResourceName(); + + toscaNodeType.setDerived_from(derivedFrom); + Either, TitanOperationStatus> dataTypesEither = dataTypeCache.getAll(); + if (dataTypesEither.isRight()) { + log.debug("Failed to retrieve all data types {}", dataTypesEither.right().value()); + } + Map dataTypes = dataTypesEither.left().value(); + Map capabilities = this.capabiltyRequirementConvertor + .convertProxyCapabilities(origComponent, proxyComponent, instance, dataTypes); + toscaNodeType.setCapabilities(capabilities); + + return toscaNodeType; + } + + private Either convertComponentInstanceRequirements(Component component, + ComponentInstance componentInstance, List relations, + ToscaNodeTemplate nodeTypeTemplate, Component originComponent) { + + List> toscaRequirements = new ArrayList<>(); + if(!addRequirements(component, componentInstance, relations, originComponent, toscaRequirements)){ + log.debug("Failed to convert component instance requirements for the component instance {}. ", componentInstance.getName()); + return Either.right(ToscaError.NODE_TYPE_REQUIREMENT_ERROR); + } if (!toscaRequirements.isEmpty()) { nodeTypeTemplate.setRequirements(toscaRequirements); } - log.debug("Finish convert Requirements for node type"); + log.debug("Finished to convert requirements for the node type {} ", componentInstance.getName()); return Either.left(nodeTypeTemplate); } + private boolean addRequirements(Component component, ComponentInstance componentInstance, List relations, Component originComponent, + List> toscaRequirements) { + boolean result; + List filteredRelations = relations.stream().filter(p -> componentInstance.getUniqueId().equals(p.getFromNode())).collect(Collectors.toList()); + if(CollectionUtils.isEmpty(filteredRelations)){ + result = true; + } else { + result = !filteredRelations.stream().filter(rel -> !addRequirement(componentInstance, originComponent, component.getComponentInstances(), rel, toscaRequirements)).findFirst().isPresent(); + } + return result; + } + + private boolean addRequirement(ComponentInstance fromInstance, Component originComponent, List instancesList, RequirementCapabilityRelDef rel, List> toscaRequirements){ + + boolean result = true; + Map originComponents = new HashMap<>(); + Map> reqMap = originComponent.getRequirements(); + RequirementAndRelationshipPair reqAndRelationshipPair = rel.getRelationships().get(0); + Either getOriginRes = null; + Optional reqOpt = null; + Component toOriginComponent = null; + Optional cap = null; + Either buildCapNameRes = null; + Either buildReqNameRes = null; + + ComponentInstance toInstance = instancesList.stream().filter(i -> rel.getToNode().equals(i.getUniqueId())).findFirst().orElse(null); + if (toInstance == null) { + log.debug("Failed to find a relation from the node {} to the node {}", fromInstance.getName(), rel.getToNode()); + result = false; + } + if(result){ + reqOpt = findRequirement(reqMap, reqAndRelationshipPair.getRequirementUid()); + if(!reqOpt.isPresent()){ + log.debug("Failed to find a requirement with uniqueId {} on a component with uniqueId {}", reqAndRelationshipPair.getRequirementUid(), originComponent.getUniqueId()); + result = false; + } + } + if(result){ + ComponentParametersView filter = new ComponentParametersView(true); + filter.setIgnoreComponentInstances(false); + filter.setIgnoreCapabilities(false); + getOriginRes = toscaOperationFacade.getToscaElement(toInstance.getComponentUid(), filter); + if(getOriginRes.isRight()){ + log.debug("Failed to build substituted name for the requirement {}. Failed to get an origin component with uniqueId {}", reqOpt.get().getName(), toInstance.getComponentUid()); + result = false; + } + } + if(result){ + toOriginComponent = getOriginRes.left().value(); + cap = toOriginComponent.getCapabilities().get(reqOpt.get().getCapability()).stream().filter(c -> c.getName().equals(reqAndRelationshipPair.getCapability())).findFirst(); + if(!cap.isPresent()){ + log.debug("Failed to find a capability with name {} on a component with uniqueId {}", reqAndRelationshipPair.getCapability(), originComponent.getUniqueId()); + result = false; + } + } + if(result){ + buildCapNameRes = capabiltyRequirementConvertor.buildSubstitutedName(originComponents, toOriginComponent, cap.get().getPath(), reqAndRelationshipPair.getCapability()); + if(buildCapNameRes.isRight()){ + log.debug("Failed to build a substituted capability name for the capability with name {} on a component with uniqueId {}", reqAndRelationshipPair.getCapability(), originComponent.getUniqueId()); + result = false; + } + } + if(result){ + buildReqNameRes = capabiltyRequirementConvertor.buildSubstitutedName(originComponents, originComponent, reqOpt.get().getPath(), reqAndRelationshipPair.getRequirement()); + if(buildReqNameRes.isRight()){ + log.debug("Failed to build a substituted requirement name for the requirement with name {} on a component with uniqueId {}", reqAndRelationshipPair.getRequirement(), originComponent.getUniqueId()); + result = false; + } + } + if(result){ + ToscaTemplateRequirement toscaRequirement = new ToscaTemplateRequirement(); + Map toscaReqMap = new HashMap<>(); + toscaRequirement.setNode(toInstance.getName()); + toscaRequirement.setCapability(buildCapNameRes.left().value()); + toscaReqMap.put(buildReqNameRes.left().value(), toscaRequirement); + toscaRequirements.add(toscaReqMap); + } + return result; + } - private Either convertCapabilities(Component component, SubstitutionMapping substitutionMapping, Map dataTypes) { - Map toscaCapabilities = capabiltyRequirementConvertor.convertSubstitutionMappingCapabilities(component, dataTypes); - - if (!toscaCapabilities.isEmpty()) { - substitutionMapping.setCapabilities(toscaCapabilities); + private Optional findRequirement(Map> reqMap, String reqId) { + for(List reqList: reqMap.values()){ + Optional reqOpt = reqList.stream().filter(r -> r.getUniqueId().equals(reqId)).findFirst(); + if(reqOpt.isPresent()){ + return reqOpt; + } } - log.debug("Finish convert Capabilities for node type"); + return Optional.empty(); + } - return Either.left(substitutionMapping); + + private Either convertCapabilities(Component component, SubstitutionMapping substitutionMappings) { + + Either result = Either.left(substitutionMappings);; + Either, ToscaError> toscaCapabilitiesRes = capabiltyRequirementConvertor + .convertSubstitutionMappingCapabilities(originComponents, component); + if(toscaCapabilitiesRes.isRight()){ + result = Either.right(toscaCapabilitiesRes.right().value()); + log.error("Failed convert capabilities for the component {}. ", component.getName()); + } else if (MapUtils.isNotEmpty(toscaCapabilitiesRes.left().value())) { + substitutionMappings.setCapabilities(toscaCapabilitiesRes.left().value()); + log.debug("Finish convert capabilities for the component {}. ", component.getName()); + } + log.debug("Finished to convert capabilities for the component {}. ", component.getName()); + return result; } private Either convertCapabilities(Component component, ToscaNodeType nodeType, @@ -896,12 +1066,12 @@ public class ToscaExportHandler { return null; } else { // skip not relevant for Tosca property - if (property.getName().equals("dependencies")) { + if ("dependencies".equals(property.getName())) { return null; } NodeTuple defaultNode = super.representJavaBeanProperty(javaBean, property, propertyValue, customTag); - return property.getName().equals("_defaultp_") + return "_defaultp_".equals(property.getName()) ? new NodeTuple(representData("default"), defaultNode.getValueNode()) : defaultNode; } } @@ -928,7 +1098,7 @@ public class ToscaExportHandler { protected Set createPropertySet(Class type, BeanAccess bAccess) throws IntrospectionException { Collection fields = getPropertiesMap(type, BeanAccess.FIELD).values(); - return new LinkedHashSet(fields); + return new LinkedHashSet<>(fields); } } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaUtils.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaUtils.java index 24586d9ea0..0c54e7229e 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaUtils.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaUtils.java @@ -34,17 +34,6 @@ import org.openecomp.sdc.be.model.Component; public class ToscaUtils { - public static boolean isAtomicType(Component component) { - ComponentTypeEnum componentType = component.getComponentType(); - if (ComponentTypeEnum.RESOURCE.equals(componentType)) { - ResourceTypeEnum resourceType = ((ResourceMetadataDataDefinition) component.getComponentMetadataDefinition().getMetadataDataDefinition()).getResourceType(); - if (ResourceTypeEnum.CP == resourceType || ResourceTypeEnum.VL == resourceType || ResourceTypeEnum.VFC == resourceType || ResourceTypeEnum.VFCMT == resourceType || ResourceTypeEnum.ABSTRACT == resourceType) { - return true; - } - } - return false; - } - public static boolean isComplexVfc(Component component) { if (ComponentTypeEnum.RESOURCE == component.getComponentType()) { ResourceTypeEnum resourceType = ((ResourceMetadataDataDefinition) component.getComponentMetadataDefinition().getMetadataDataDefinition()).getResourceType(); @@ -55,7 +44,7 @@ public class ToscaUtils { return false; } - public static Map objectToMap(Object objectToConvert, Class clazz) throws IllegalArgumentException, IllegalAccessException { + public static Map objectToMap(Object objectToConvert, Class clazz) throws IllegalArgumentException, IllegalAccessException { Map map = new HashMap<>(); List fields = new ArrayList<>(); @@ -76,4 +65,47 @@ public class ToscaUtils { } return fields; } + + public static class SubstituitionEntry{ + + private String fullName = ""; + private String sourceName = ""; + private String owner = ""; + + public SubstituitionEntry() {} + + public SubstituitionEntry(String fullName, String sourceName, String owner) { + if(fullName != null) + this.fullName = fullName; + if(sourceName != null) + this.sourceName = sourceName; + if(owner != null) + this.owner = owner; + } + + public String getFullName() { + return fullName; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + public String getSourceName() { + return sourceName; + } + + public void setSourceName(String sourceName) { + this.sourceName = sourceName; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + } + } diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsontitan/operations/NodeTypeOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsontitan/operations/NodeTypeOperation.java index d986d77450..e530144fe0 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsontitan/operations/NodeTypeOperation.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsontitan/operations/NodeTypeOperation.java @@ -21,6 +21,7 @@ package org.openecomp.sdc.be.model.jsontitan.operations; import fj.data.Either; +import java.util.stream.Collectors; import org.apache.tinkerpop.gremlin.structure.Direction; import org.apache.tinkerpop.gremlin.structure.Edge; @@ -58,8 +59,10 @@ import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.regex.Pattern; @org.springframework.stereotype.Component("node-type-operation") @@ -757,25 +760,49 @@ public class NodeTypeOperation extends ToscaElementOperation { return DaoStatusConverter.convertTitanStatusToStorageStatus(error); } // must be only one - GraphVertex newDerived = getParentResources.left().value().get(0); - derivedResources.add(newDerived); - StorageOperationStatus updateStatus = updateDataFromNewDerived(derivedResources, nodeTypeV, (NodeType)toscaElementToUpdate); - if (updateStatus != StorageOperationStatus.OK) { - CommonUtility.addRecordToLog(log, LogLevelEnum.DEBUG, "Failed to update data for {} from new derived {} ", nodeTypeV.getUniqueId(), newDerived.getUniqueId(), updateStatus); - return updateStatus; - } + GraphVertex newDerivedV = getParentResources.left().value().get(0); + return updateDerived(toscaElementToUpdate, nodeTypeV, firstDerivedInChain, newDerivedV, false); + } + } + return StorageOperationStatus.OK; + } - Either deleteEdge = titanDao.deleteEdge(nodeTypeV, firstDerivedInChain, EdgeLabelEnum.DERIVED_FROM); - if (deleteEdge.isRight()) { - TitanOperationStatus deleteError = deleteEdge.right().value(); - log.debug("Failed to disassociate element {} from derived {} , error {}", nodeTypeV.getUniqueId(), firstDerivedInChain.getUniqueId(), deleteError); - return DaoStatusConverter.convertTitanStatusToStorageStatus(deleteError); - } + /** + * + * @param toscaElementToUpdate + * @param nodeTypeV + * @param preDerivedV + * @param newDerivedV + * @param mergeValues + * @return + */ + protected StorageOperationStatus updateDerived(T toscaElementToUpdate, GraphVertex nodeTypeV, GraphVertex preDerivedV, GraphVertex newDerivedV, boolean mergeValues) { + Set preDerivedChainIdList = new HashSet(); + preDerivedChainIdList.add(preDerivedV.getUniqueId()); + Either childVertex = titanDao.getChildVertex(preDerivedV, EdgeLabelEnum.DERIVED_FROM, JsonParseFlagEnum.NoParse); + while (childVertex.isLeft()) { + GraphVertex currentChield = childVertex.left().value(); + preDerivedChainIdList.add(currentChield.getUniqueId()); + childVertex = titanDao.getChildVertex(currentChield, EdgeLabelEnum.DERIVED_FROM, JsonParseFlagEnum.NoParse); + } - titanDao.createEdge(nodeTypeV, newDerived, EdgeLabelEnum.DERIVED_FROM, new HashMap<>()); - } + List derivedResources = new ArrayList<>(); + derivedResources.add(newDerivedV); + StorageOperationStatus updateStatus = updateDataFromNewDerived(derivedResources, nodeTypeV, (NodeType) toscaElementToUpdate, mergeValues, preDerivedChainIdList); + if (updateStatus != StorageOperationStatus.OK) { + CommonUtility.addRecordToLog(log, LogLevelEnum.DEBUG, "Failed to update data for {} from new derived {} ", nodeTypeV.getUniqueId(), newDerivedV.getUniqueId(), updateStatus); + return updateStatus; + } + + Either deleteEdge = titanDao.deleteEdge(nodeTypeV, preDerivedV, EdgeLabelEnum.DERIVED_FROM); + if (deleteEdge.isRight()) { + TitanOperationStatus deleteError = deleteEdge.right().value(); + log.debug("Failed to disassociate element {} from derived {} , error {}", nodeTypeV.getUniqueId(), preDerivedV.getUniqueId(), deleteError); + return DaoStatusConverter.convertTitanStatusToStorageStatus(deleteError); } + titanDao.createEdge(nodeTypeV, newDerivedV, EdgeLabelEnum.DERIVED_FROM, new HashMap<>()); + return StorageOperationStatus.OK; } @@ -800,20 +827,20 @@ public class NodeTypeOperation extends ToscaElementOperation { } - private StorageOperationStatus updateDataFromNewDerived(List newDerived, GraphVertex nodeTypeV, NodeType nodeToUpdate) { + private StorageOperationStatus updateDataFromNewDerived(List newDerived, GraphVertex nodeTypeV, NodeType nodeToUpdate, boolean mergeValues, Set preDerivedChainIdList) { EnumSet edgeLabels = EnumSet.of(EdgeLabelEnum.CAPABILITIES, EdgeLabelEnum.REQUIREMENTS, EdgeLabelEnum.PROPERTIES, EdgeLabelEnum.ATTRIBUTES, EdgeLabelEnum.CAPABILITIES_PROPERTIES, EdgeLabelEnum.ADDITIONAL_INFORMATION); StorageOperationStatus status = null; - for (EdgeLabelEnum edge : edgeLabels){ - status = updateDataByType(newDerived, nodeTypeV, edge, nodeToUpdate); + for (EdgeLabelEnum edge : edgeLabels) { + status = updateDataByType(newDerived, nodeTypeV, edge, nodeToUpdate, mergeValues, preDerivedChainIdList); if (status != StorageOperationStatus.OK) { break; } } return status; - + } - private StorageOperationStatus updateDataByType(List newDerivedList, GraphVertex nodeTypeV, EdgeLabelEnum label, NodeType nodeElement) { + private StorageOperationStatus updateDataByType(List newDerivedList, GraphVertex nodeTypeV, EdgeLabelEnum label, NodeType nodeElement, boolean mergeValues, Set preDerivedChainIdList) { log.debug("Update data from derived for element {} type {}", nodeTypeV.getUniqueId(), label); Either dataFromGraph = getDataVertex(nodeTypeV, label); if (dataFromGraph.isRight()) { @@ -824,9 +851,23 @@ public class NodeTypeOperation extends ToscaElementOperation { GraphVertex dataV = dataFromGraph.left().value(); Map mapFromGraph = (Map) dataV.getJson(); - mapFromGraph.entrySet().removeIf(e -> e.getValue().getOwnerId() != null); + Map valuesFrmPrev = null; + if (isSimpleHierarchy(label)) { + if (mergeValues) { + valuesFrmPrev = mapFromGraph.entrySet().stream().filter(e -> e.getValue().getOwnerId() != null).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + mapFromGraph.entrySet().removeIf(e -> preDerivedChainIdList.contains(e.getValue().getOwnerId())); + } else { + final Map valuesFrmPrevFinal = new HashMap<>(); + mapFromGraph.entrySet().stream().forEach(e -> { + T value = e.getValue(); + value = ToscaDataDefinition.removeAndCollectByOwnerId(value, preDerivedChainIdList); + valuesFrmPrevFinal.put(e.getKey(), value); + }); + valuesFrmPrev = valuesFrmPrevFinal; + mapFromGraph.entrySet().removeIf(e->e.getValue().isEmpty()); + } - Either, StorageOperationStatus> dataFromDerived = getDataFromDerived(newDerivedList, label); if (dataFromDerived.isRight()) { return dataFromDerived.right().value(); @@ -845,10 +886,74 @@ public class NodeTypeOperation extends ToscaElementOperation { } return StorageOperationStatus.OK; } + + private boolean isSimpleHierarchy(EdgeLabelEnum label) { + switch (label) { + case PROPERTIES: + case ATTRIBUTES: + case ADDITIONAL_INFORMATION: + case ARTIFACTS: + case GROUPS: + case INPUTS: + return true; + default: + return false; + } + } + @Override public void fillToscaElementVertexData(GraphVertex elementV, T toscaElementToUpdate, JsonParseFlagEnum flag) { fillMetadata(elementV, (NodeType) toscaElementToUpdate); } + public Either shouldUpdateDerivedVersion(ToscaElement toscaElementToUpdate, GraphVertex nodeTypeV) { + NodeType nodeType = (NodeType) toscaElementToUpdate; + + Either childVertex = titanDao.getChildVertex(nodeTypeV, EdgeLabelEnum.DERIVED_FROM, JsonParseFlagEnum.NoParse); + if (childVertex.isRight()) { + TitanOperationStatus getchildError = childVertex.right().value(); + if (getchildError == TitanOperationStatus.NOT_FOUND) { + log.debug("derived resource for element {} not found", nodeTypeV.getUniqueId()); + return Either.right(StorageOperationStatus.OK); + } + + log.debug("Failed to fetch derived resource for element {} error {}", nodeTypeV.getUniqueId(), getchildError); + return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(getchildError)); + } + GraphVertex firstDerivedInChain = childVertex.left().value(); + + String currentVersion = (String) firstDerivedInChain.getMetadataProperty(GraphPropertyEnum.VERSION); + + Map props = new HashMap<>(); + props.put(GraphPropertyEnum.TOSCA_RESOURCE_NAME, nodeType.getDerivedFrom().get(0)); + props.put(GraphPropertyEnum.STATE, LifecycleStateEnum.CERTIFIED.name()); + props.put(GraphPropertyEnum.IS_HIGHEST_VERSION, true); + + Map propsHasNot = new HashMap<>(); + propsHasNot.put(GraphPropertyEnum.IS_DELETED, true); + Either, TitanOperationStatus> byCriteria = titanDao.getByCriteria(VertexTypeEnum.NODE_TYPE, props, propsHasNot, JsonParseFlagEnum.NoParse); + if (byCriteria.isRight()) { + log.debug("Failed to fetch derived by props {} error {}", props, byCriteria.right().value()); + return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(byCriteria.right().value())); + } + List lastDerived = byCriteria.left().value(); + // now supported only one derived!!! Change in future!(Evg) + GraphVertex derivedFromHighest = lastDerived.get(0); + String highestVersion = (String) derivedFromHighest.getMetadataProperty(GraphPropertyEnum.VERSION); + if (!highestVersion.equals(currentVersion)) { + + // need to update to latest version of derived from + StorageOperationStatus updateDerived = updateDerived(toscaElementToUpdate, nodeTypeV, firstDerivedInChain, derivedFromHighest, true); + + if (updateDerived != StorageOperationStatus.OK) { + log.debug("Failed to update {} to highest derived {} from error {}", nodeTypeV.getUniqueId(), derivedFromHighest.getUniqueId(), updateDerived); + return Either.right(updateDerived); + } + return getToscaElement(nodeTypeV.getUniqueId(), new ComponentParametersView()); + } + // no version changes + return Either.right(StorageOperationStatus.OK); + } + } diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsontitan/operations/ToscaOperationFacade.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsontitan/operations/ToscaOperationFacade.java index b11036df49..394231938a 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsontitan/operations/ToscaOperationFacade.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsontitan/operations/ToscaOperationFacade.java @@ -275,8 +275,11 @@ public class ToscaOperationFacade { public Either getLatestByToscaResourceName(String toscaResourceName) { return getLatestByName(GraphPropertyEnum.TOSCA_RESOURCE_NAME, toscaResourceName); - } + + public Either getFullLatestComponentByToscaResourceName(String toscaResourceName) { + return getLatestByName(GraphPropertyEnum.TOSCA_RESOURCE_NAME, toscaResourceName, JsonParseFlagEnum.ParseAll); + } public Either getLatestByName(String resourceName) { return getLatestByName(GraphPropertyEnum.NAME, resourceName); @@ -536,6 +539,39 @@ public class ToscaOperationFacade { return getToscaElementByOperation(highestResource); } + private Either getLatestByName(GraphPropertyEnum property, String nodeName, JsonParseFlagEnum parseFlag) { + Either result; + + Map propertiesToMatch = new EnumMap<>(GraphPropertyEnum.class); + Map propertiesNotToMatch = new EnumMap<>(GraphPropertyEnum.class); + + propertiesToMatch.put(property, nodeName); + propertiesToMatch.put(GraphPropertyEnum.IS_HIGHEST_VERSION, true); + + propertiesNotToMatch.put(GraphPropertyEnum.IS_DELETED, true); + + Either, TitanOperationStatus> highestResources = titanDao.getByCriteria(null, propertiesToMatch, propertiesNotToMatch, parseFlag); + if (highestResources.isRight()) { + TitanOperationStatus status = highestResources.right().value(); + log.debug("failed to find resource with name {}. status={} ", nodeName, status); + result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status)); + return result; + } + + List resources = highestResources.left().value(); + double version = 0.0; + GraphVertex highestResource = null; + for (GraphVertex vertex : resources) { + Object versionObj = vertex.getMetadataProperty(GraphPropertyEnum.VERSION); + double resourceVersion = Double.valueOf((String) versionObj); + if (resourceVersion > version) { + version = resourceVersion; + highestResource = vertex; + } + } + return getToscaElementByOperation(highestResource); + } + public Either, StorageOperationStatus> getBySystemName(ComponentTypeEnum componentType, String systemName) { Either, StorageOperationStatus> result = null; @@ -2216,4 +2252,28 @@ public class ToscaOperationFacade { return status; } + public Either shouldUpgradeToLatestDerived(Resource clonedResource) { + String componentId = clonedResource.getUniqueId(); + Either getVertexEither = titanDao.getVertexById(componentId, JsonParseFlagEnum.NoParse); + if (getVertexEither.isRight()) { + log.debug("Couldn't fetch component with and unique id {}, error: {}", componentId, getVertexEither.right().value()); + return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(getVertexEither.right().value())); + + } + GraphVertex nodeTypeV = getVertexEither.left().value(); + + ToscaElement toscaElementToUpdate = ModelConverter.convertToToscaElement(clonedResource); + + Either shouldUpdateDerivedVersion = nodeTypeOperation.shouldUpdateDerivedVersion(toscaElementToUpdate, nodeTypeV); + if ( shouldUpdateDerivedVersion.isRight() && StorageOperationStatus.OK != shouldUpdateDerivedVersion.right().value() ){ + log.debug("Failed to update derived version for node type {} derived {}, error: {}", componentId, clonedResource.getDerivedFrom().get(0), shouldUpdateDerivedVersion.right().value()); + return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(getVertexEither.right().value())); + } + if ( shouldUpdateDerivedVersion.isLeft() ){ + return Either.left(ModelConverter.convertFromToscaElement(shouldUpdateDerivedVersion.left().value())); + } + return Either.left(clonedResource); + } + + } diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsontitan/utils/ModelConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsontitan/utils/ModelConverter.java index 03a5f41d55..973b0ce069 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsontitan/utils/ModelConverter.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsontitan/utils/ModelConverter.java @@ -94,6 +94,22 @@ public class ModelConverter { return null; } } + + public static boolean isAtomicComponent(Component component) { + ComponentTypeEnum componentType = component.getComponentType(); + if (!componentType.equals(ComponentTypeEnum.RESOURCE)) { + return false; + } + Resource resource = (Resource) component; + ResourceTypeEnum resType = resource.getResourceType(); + return isAtomicComponent(resType); + } + + public static boolean isAtomicComponent(ResourceTypeEnum resourceType) { + if (resourceType == null || resourceType == ResourceTypeEnum.VF || resourceType == ResourceTypeEnum.PNF || resourceType == ResourceTypeEnum.CVFC) + return false; + return true; + } // ********************************************************** public static VertexTypeEnum getVertexType(Component component) { @@ -117,11 +133,7 @@ public class ModelConverter { return vertexType; } - public static boolean isAtomicComponent(ResourceTypeEnum resourceType) { - if (resourceType == null || resourceType == ResourceTypeEnum.VF || resourceType == ResourceTypeEnum.PNF || resourceType == ResourceTypeEnum.CVFC) - return false; - return true; - } + private static Service convertToService(ToscaElement toscaElement) { Service service = new Service(); @@ -303,6 +315,7 @@ public class ModelConverter { relationshipPair.setCapabilityOwnerId(relation.getCapabilityOwnerId()); relationshipPair.setCapabilityUid(relation.getCapabilityId()); + relationshipPair.setCapability(relation.getCapability()); relationshipPair.setRequirementOwnerId(relation.getRequirementOwnerId()); relationshipPair.setRequirementUid(relation.getRequirementId()); relationshipPair.setRequirement(relation.getRequirement()); @@ -991,18 +1004,7 @@ public class ModelConverter { toscaElement.setMetadataValue(JsonPresentationFields.CONTACT_ID, component.getContactId()); } - public static boolean isAtomicComponent(Component component) { - ComponentTypeEnum componentType = component.getComponentType(); - if (!componentType.equals(ComponentTypeEnum.RESOURCE)) { - return false; - } - Resource resource = (Resource) component; - ResourceTypeEnum resType = resource.getResourceType(); - if (resType == ResourceTypeEnum.VFC || resType == ResourceTypeEnum.VFCMT || resType == ResourceTypeEnum.VL || resType == ResourceTypeEnum.CP || resType == ResourceTypeEnum.ABSTRACT) { - return true; - } - return false; - } + private static void setComponentInstancesToComponent(TopologyTemplate topologyTemplate, Component component) { diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/tosca/ToscaDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/tosca/ToscaDataDefinition.java index df73adaa4a..b8d164e4aa 100644 --- a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/tosca/ToscaDataDefinition.java +++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/tosca/ToscaDataDefinition.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.stream.Collectors; import org.codehaus.jackson.annotate.JsonCreator; @@ -104,4 +105,13 @@ public abstract class ToscaDataDefinition { public boolean findUidMatch(String uid){ return uid.equals(getToscaPresentationValue(JsonPresentationFields.UNIQUE_ID)); } + public T removeByOwnerId(Set ownerIdList) { + return (T) this; + } + public static T removeAndCollectByOwnerId(T complexStructure, Set ownerIdList) { + return complexStructure.removeByOwnerId(ownerIdList); + } + public boolean isEmpty(){ + return false; + } } diff --git a/sdc-os-chef/pom.xml b/sdc-os-chef/pom.xml index 896a75e503..04c2a9355f 100644 --- a/sdc-os-chef/pom.xml +++ b/sdc-os-chef/pom.xml @@ -259,6 +259,7 @@ + copy-resources-test-apis-ci validate diff --git a/sdc-os-chef/scripts/docker_sanity_run.sh b/sdc-os-chef/scripts/docker_sanity_run.sh new file mode 100644 index 0000000000..339b538f7a --- /dev/null +++ b/sdc-os-chef/scripts/docker_sanity_run.sh @@ -0,0 +1,122 @@ +#!/bin/bash + + +function usage { + echo "usage: docker_run.sh [ -r|--release ] [ -e|--environment ] [ -p|--port ] [ -l|--local ] [ -s|--skipTests ] [ -h|--help ]" +} + + +function cleanup { + echo "performing old dockers cleanup" + docker_ids=`docker ps -a | egrep -v "openecomp/sdc-simulator" | egrep "ecomp-nexus:${PORT}/sdc|sdc|Exit" | awk '{print $1}'` + for X in ${docker_ids} + do + docker rm -f ${X} + done +} + + +function dir_perms { + mkdir -p /data/logs/BE/SDC/SDC-BE + mkdir -p /data/logs/FE/SDC/SDC-FE + chmod -R 777 /data/logs +} + +function monitor_docker { + +echo monitor $1 Docker +sleep 10 +TIME_OUT=800 +INTERVAL=20 +TIME=0 +while [ "$TIME" -lt "$TIME_OUT" ]; do + +MATCH=`docker logs --tail 30 $1 | grep "DOCKER STARTED"` +echo MATCH is -- $MATCH + +if [ -n "$MATCH" ] + then + echo DOCKER start finished in $TIME seconds + break + fi + + echo Sleep: $INTERVAL seconds before testing if $1 DOCKER is up. Total wait time up now is: $TIME seconds. Timeout is: $TIME_OUT seconds + sleep $INTERVAL + TIME=$(($TIME+$INTERVAL)) +done + +if [ "$TIME" -ge "$TIME_OUT" ] + then + echo -e "\e[1;31mTIME OUT: DOCKER was NOT fully started in $TIME_OUT seconds... Could cause problems ...\e[0m" +fi + + +} + + +RELEASE=latest +LOCAL=false +SKIPTESTS=false +DEBUG_PORT="--publish 4000:4000" + +[ -f /opt/config/env_name.txt ] && DEP_ENV=$(cat /opt/config/env_name.txt) || DEP_ENV=__ENV-NAME__ +[ -f /opt/config/nexus_username.txt ] && NEXUS_USERNAME=$(cat /opt/config/nexus_username.txt) || NEXUS_USERNAME=release +[ -f /opt/config/nexus_password.txt ] && NEXUS_PASSWD=$(cat /opt/config/nexus_password.txt) || NEXUS_PASSWD=sfWU3DFVdBr7GVxB85mTYgAW +[ -f /opt/config/nexus_docker_repo.txt ] && NEXUS_DOCKER_REPO=$(cat /opt/config/nexus_docker_repo.txt) || NEXUS_DOCKER_REPO=ecomp-nexus:${PORT} + +while test $# -gt 0; do + case $1 in + -r | --release ) + shift + RELEASE=$1 + ;; + -e | --environment ) + shift + DEP_ENV=$1 + ;; + -p | --port ) + shift + PORT=$1 + ;; + -l | --local ) + shift + LOCAL=true + ;; + -s | --skipTests ) + shift + SKIPTESTS=true + ;; + -h | --help ) + usage + exit + ;; + * ) + usage + exit 1 + esac +done + +[ -f /opt/config/nexus_username.txt ] && docker login -u $NEXUS_USERNAME -p $NEXUS_PASSWD $NEXUS_DOCKER_REPO + + + + +export IP=`ifconfig eth0 | awk -F: '/inet addr/ {gsub(/ .*/,"",$2); print $2}'` +export PREFIX=${NEXUS_DOCKER_REPO}'/openecomp' + +if [ ${LOCAL} = true ]; then + PREFIX='openecomp' +fi + +echo "" + + +# sanityDocker +echo "docker run sdc-frontend..." +if [ ${SKIPTESTS} = false ]; then +echo "Triger sanity docker, please wait..." + if [ ${LOCAL} = false ]; then + docker pull ${PREFIX}/sdc-sanity:${RELEASE} + fi + docker run --detach --name sdc-sanity --env HOST_IP=${IP} --env ENVNAME="${DEP_ENV}" --env http_proxy=${http_proxy} --env https_proxy=${https_proxy} --env no_proxy=${no_proxy} --log-driver=json-file --log-opt max-size=100m --log-opt max-file=10 --ulimit memlock=-1:-1 --memory 1.2g --memory-swap=1.2g --ulimit nofile=4096:100000 --volume /etc/localtime:/etc/localtime:ro --volume /data/logs/sdc-sanity/target:/var/lib/tests/target --volume /data/logs/sdc-sanity/ExtentReport:/var/lib/tests/ExtentReport --volume /data/environments:/root/chef-solo/environments --publish 8849:8849 --publish 9560:9560 ${PREFIX}/sdc-sanity:${RELEASE} +fi diff --git a/sdc-os-chef/scripts/sanity_run.sh b/sdc-os-chef/scripts/sanity_run.sh new file mode 100644 index 0000000000..17d1642c73 --- /dev/null +++ b/sdc-os-chef/scripts/sanity_run.sh @@ -0,0 +1,117 @@ +#!/bin/bash + + +function usage { + echo "usage: docker_run.sh [ -r|--release ] [ -e|--environment ] [ -p|--port ] [ -l|--local ] [ -s|--skipTests ] [ -h|--help ]" +} + + +function cleanup { + echo "performing old dockers cleanup" + docker_ids=`docker ps -a | egrep -v "openecomp/sdc-simulator" | egrep "ecomp-nexus:${PORT}/sdc-sanity|sdc-sanity|Exit" | awk '{print $1}'` + for X in ${docker_ids} + do + docker rm -f ${X} + done +} + + +function dir_perms { + mkdir -p /data/logs/BE/SDC/SDC-BE + mkdir -p /data/logs/FE/SDC/SDC-FE + chmod -R 777 /data/logs +} + +function monitor_docker { + +echo monitor $1 Docker +sleep 5 +TIME_OUT=900 +INTERVAL=20 +TIME=0 +while [ "$TIME" -lt "$TIME_OUT" ]; do + +MATCH=`docker logs --tail 30 $1 | grep "DOCKER STARTED"` +echo MATCH is -- $MATCH + +if [ -n "$MATCH" ] + then + echo DOCKER start finished in $TIME seconds + break + fi + + echo Sleep: $INTERVAL seconds before testing if $1 DOCKER is up. Total wait time up now is: $TIME seconds. Timeout is: $TIME_OUT seconds + sleep $INTERVAL + TIME=$(($TIME+$INTERVAL)) +done + +if [ "$TIME" -ge "$TIME_OUT" ] + then + echo -e "\e[1;31mTIME OUT: DOCKER was NOT fully started in $TIME_OUT seconds... Could cause problems ...\e[0m" +fi + + +} + + +RELEASE=latest +LOCAL=false +SKIPTESTS=false +DEBUG_PORT="--publish 4000:4000" + +[ -f /opt/config/env_name.txt ] && DEP_ENV=$(cat /opt/config/env_name.txt) || DEP_ENV=__ENV-NAME__ +[ -f /opt/config/nexus_username.txt ] && NEXUS_USERNAME=$(cat /opt/config/nexus_username.txt) || NEXUS_USERNAME=release +[ -f /opt/config/nexus_password.txt ] && NEXUS_PASSWD=$(cat /opt/config/nexus_password.txt) || NEXUS_PASSWD=sfWU3DFVdBr7GVxB85mTYgAW +[ -f /opt/config/nexus_docker_repo.txt ] && NEXUS_DOCKER_REPO=$(cat /opt/config/nexus_docker_repo.txt) || NEXUS_DOCKER_REPO=ecomp-nexus:${PORT} + +while [ "$1" != "" ]; do + case $1 in + -r | --release ) + shift + RELEASE=${1} + ;; + -e | --environment ) + shift + DEP_ENV=${1} + ;; + -p | --port ) + shift + PORT=${1} + ;; + -l | --local ) + shift + LOCAL=true + ;; + -s | --skipTests ) + shift + SKIPTESTS=true + ;; + -h | --help ) + usage + exit + ;; + * ) + usage + exit 1 + esac + shift +done + +[ -f /opt/config/nexus_username.txt ] && docker login -u $NEXUS_USERNAME -p $NEXUS_PASSWD $NEXUS_DOCKER_REPO + +export IP=`ifconfig eth0 | awk -F: '/inet addr/ {gsub(/ .*/,"",$2); print $2}'` +export PREFIX=${NEXUS_DOCKER_REPO}'/openecomp' + +if [ ${LOCAL} = true ]; then + PREFIX='openecomp' +fi + +## sanityDocker +echo "docker run sdc-sanity..." +if [ ${SKIPTESTS} = false ]; then +echo "Triger sanity docker, please wait..." + if [ ${LOCAL} = false ]; then + docker pull ${PREFIX}/sdc-sanity:${RELEASE} + fi + docker run --detach --name sdc-sanity --env HOST_IP=${IP} --env ENVNAME="${DEP_ENV}" --env http_proxy=${http_proxy} --env https_proxy=${https_proxy} --env no_proxy=${no_proxy} --log-driver=json-file --log-opt max-size=100m --log-opt max-file=10 --ulimit memlock=-1:-1 --memory 1g --memory-swap=1g --ulimit nofile=4096:100000 --volume /etc/localtime:/etc/localtime:ro --volume /data/logs/sdc-sanity/target:/var/lib/tests/target --volume /data/logs/sdc-sanity/ExtentReport:/var/lib/tests/ExtentReport --volume /data/logs/sdc-sanity/outputCsar:/var/lib/tests/outputCsar --volume /data/environments:/root/chef-solo/environments --publish 9560:9560 ${PREFIX}/sdc-sanity:${RELEASE} +fi \ No newline at end of file diff --git a/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/Huawei_vPCRF_aligned_fixed.csar b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/Huawei_vPCRF.csar similarity index 100% rename from sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/Huawei_vPCRF_aligned_fixed.csar rename to sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/Huawei_vPCRF.csar diff --git a/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/Huawei_vSPGW.csar b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/Huawei_vSPGW.csar new file mode 100644 index 0000000000000000000000000000000000000000..e7eed4b7c674afcc62dfcff698131049a62c7050 GIT binary patch literal 3907 zcmeHKi9b~98$Tn588 zi)^JC49Su`OM@vz%5SEAuDNml1Mm5q^F7P+KHv8_=Y7A=d5j_JIK==~>rUMP`E~OD z10NH0ys&5#!V!y6G5*KyY&7&=cKl2`eIyEf5sgK=UtG2X1H9K=AkQ_2f&nf7s8$4k z9e-G!L7*?1BE7uPjz}}4n};g`i&XYSxVcVR!-+&yF@O5ASGRc{Ot_ZZ7??_Hx4A2V zNFCcw)a^JCB+L0i>!**<*ZkY+Ce{U{#%iMk%!k3oB2=SQzkQe776FBHCBun`8eY7y z$!P;;;kg>yfr^6k**1Au>5@1{@krr@Q=`uL+%vZH*dQJ7Slo$PZ=Q#$RZ3Cr zbb6%hVL3c`{`=&JuDQj9+#7`Oo?h}o^!}^&OCA-UTW^ro_H}_lAQ9#!F1lejP>x1s zeSCKz@z*$EeQ%qhNEQd{KfVmsjQY5b3s;sqlPL`L1XMD(6dlA!gqp;Q({)X|6c>K8=o7Q~48+LdyGy1A1tu`B;inZ^3F8E`< z^lPNi?N4ROhU+d9BWhl2e^9FRaay!$bIw<9dl*B3DUem(PlUsiqr{wIWV3HVMQ_A~ z9Y(v5YD^oU0-=v)q#wVY4xuAbaV5QituU6&Ct5?N-t#YmF-3Z zVq=we(%7Lskw(htz%q1M-yt)z`aB`>+2hbOsCA=;P#LZwIPqXV@=)G3wOjHPufsz) z59bflx$jV<%o}=g-VN>UC>AjjmHMKMu};FJJbiC;7@u)HIEmKaq-ZSXl>s*K2_@mY z0`l+DtZ{yS|J@a%tWlxP%fPJj75aWW-#r-z-{>R{?0I`i z+{Sc+H6!`qj|Y>5{8@FV#HNBHGUt^duS@5>B1z7gUa9r%xgWVTU7Xu|b}(fd{;AuH zyMfj1EXy%H*v?T12ReRFxiHTyhKqDaL<+dBKHF9W6+}Q?TC{k6BJXAbI%lu6xYo(< z@{#aiF;;?LQreXc9U z(Z|L<<+#}xGcZRk%Z54$^ehbK%D<;d%sWCEvX>?GV#m?^KU|MQCdtsz`qq+RAbUa~ zsj1X66!$aA0~!)G-s#wPPi;!69#N&$tBMd%GX$Ts715x?+O~(Ol(w{V`}WFBzkMLj z-+CX8`B?cn{<@%d`#JlZ676RDT_YMN{f4JDfBUTO(?=D39|V0wuSl=%4O(m#FO&=5 zLGbqKST0TrI#lx8U{7k&fy^^mt_OXU^Yrz2C*bMU6&Tux2shoMteIs*UO*P$6=8>g*9D9YYeh((=;kLTI z5TlD{VTYm&bv`AmTX#?BB8Sh&=L zm?vgXQ$3wMXOLKg69S9)=U*HQFigxKO)nd2u}mXYj@Y!)_*Y+LH)da(2pzsERbha> z{^wSij*fnvS9@TUkf((@Z*(jyoh>@Kx0V;2oIbd2Kl-7v!yCDQJ8m(t+_ZUat?<~R zj*07!dRPRPggrN>eQS5mKb3wdZ6Lbu;Y-C&a|((`-jP0k`|k554&Pth6NS<}JLlrl zMF7|omoWsyaTNHk0)tf$u$u61U@JHH0NG#nK!5}=G*bC2RgMZ;LO-8J8LVq|-z$p} zcHi<$U+g6cyZ6}cpyb-)RL~NhP4sK%D5S7?U#*Z?^3EFN*+0CmmY`WVj-7QQz_%R) zr-JtQ1cT*`mT@_z)sjCu>uqLHwHlKfik%05S;cvtrh;~APa$<7%gD~NGEcMfm~gMo z^Jhexp2TpoBD%!k4Pr$EBmHJvW?F4-FEO!ZK literal 0 HcmV?d00001 diff --git a/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/Huawei_vSPGW_fixed.csar b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/Huawei_vSPGW_fixed.csar deleted file mode 100644 index fa83c17d20fe0f5a1870ddf5f0f59d42dd9dc267..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3887 zcmeHKi9eLv8-GW(u@qSnu3Z@+OIJu3Ynp6hjWmXa8Ouy#iOdjDgG)#vQDm7exw2$w z;UaS_A&uJ%a>>?&5=ta>hki1@(qC{p@8^A=bKcMMKHu{_=e*x@9$VNJc41((3>GNB z)++yd@GvbjFc{;F^a>8zYx~s?o?`QVetb+nQFC;gGZD7Dkb0RLC6-N{#VgzM*>U7kO-&$rYoR>s~- z%Yi(B+qP9%bbz$*yqUkA@0%|EObFnP7BKc;x6JQSoh5YZpP8YNgsi=|pgRZ(3fYg< z&qKV+7jvVJ38%@-X*|pQlcW`v&z>p21785*h!Q5Lp~M$6bA66HJS!x>P)L`_vBFjR ziRMG_c*pcw*E|vUju}zUI<_e6!LHAy8eI*bC>@^@>4$XbYSF4}vOKq|wdg$Z=JJnp`lvp= zJF*ht^=@-LoLWj%x*<2qK-&vz++>?SE5tqrNcw8e!g%3LVP}c8a(S>Oa{FsGA>llc zS?~}#xSAk_c3qfg%NyY zvQwfC|Dtx2n`6zn)Y#1s@Cjh-fIrAr9s z+25mBA5{6}-Jwu;Z;?@ng?yW%H?VP<%$wn!$uFe9;=J( zBiN&%Q=*>Mv4KqmaxUu4XLkE`mJ{ti@W~5uJu>F6`b+(7wG{)G%EhZ;r5Q3fxm@Ak zdd#AZ@ziYSQt(tbBt~C1o-2H z)*8XngL)FFv{Fz!5mhNgEs$r{x0-%hm_YLVffbJ+7hsOjF6~Q#4ehSCZo`vLteDa3 zTQJ*_W2-%x+om(4>$>#h0BcZeT+0|*=5j6ZN8;=uTwis{o+W(@rfzv>3{4r{((f& zGNjKgoU_r)Is8r+qQ*hQKgmkd=_Alq zRi{Q+X)UxH>g|1uKEx;tY|c4IJZUl^%6uG1%5Fc=`rYI=egXPBV;p!?_0_u3j$7WIBa1cHbdY? z$;9g6f3*mFF)XPC4k;VHO~e91t1V|jsO4v diff --git a/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/resource-ZteEpcMmeVf-csar_fix.csar b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/ZteEpcMmeVf.csar similarity index 100% rename from sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/resource-ZteEpcMmeVf-csar_fix.csar rename to sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/ZteEpcMmeVf.csar diff --git a/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/ZteEpcSpgwVf-csar.csar b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/ZteEpcSpgwVf.csar similarity index 100% rename from sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/ZteEpcSpgwVf-csar.csar rename to sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/ZteEpcSpgwVf.csar diff --git a/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/cscf_si_fixed.csar b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/cscf_si.csar similarity index 96% rename from sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/cscf_si_fixed.csar rename to sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/cscf_si.csar index bc8397a86e5be698c34a970e1bfb841808c3bd7e..da19f0dd29e8f56103a491c77d4d4617c55ec552 100644 GIT binary patch delta 1239 zcmZ41$GohMd4v2uq3oV)@AeOuEABEfFvPGiFsLw0PC2Spe>B9m|FVI=-tXa0_yx8u zU4Cc5>R81KTpKT}etK;3(N%8^wL*|B{B}q7KxOALtw+mZp5IAt59FGmuvFo|zr^~}?_PfYSaWUC_pO^9 z=G-oi*GSya=JV>|GC|{&^;0f)Ci(sSe^qONYiqwO_ky*L7+3vM5HV1(N!7SA(Uzs; z&qnt-&Fv4KZ1nArVXQqHnU#|AWK(IzK~_tlUxv>Q$i)3)YUBMJx#`Bb>*b5R(w|@L zSatP?+^W!5OWvq|T$-6wx#MS;+!Aix@JOBh(AcGK1EZykA7|zL4Aiomue_{&apc?$ z$%&TVR9&0huJdk9ojOr^FYE5zlE)7Ai`6h3+#zsqb6ZuSL7<~DkW^vL#{L!KL@8-`=_CI;$s4b74Tb@hYy&$9Zb4_A{3n9+;^; zJAKxh%j%ha<_}&Q%j~=t&G_*5Gp^VzE!Bs*(@kFmd=+Qhb29(r>&(1($IoxMGnZWz zz2H$?*Jvwk_i5!5)!LTE(irV+iLbB5Pj)!)L?{R??j;x|pF1ix+2I(=X#5ZJ_}^@K zn3+k4^GDt97yVAanDf%+wm$J7jk?F~c zj%qQQOg?+mR0C!*W)ed-neQMh!J(Mn@%Y|kkh%PR)69_R$uY;Y7!@bC9}8j5I?OP6 V{$ZKPzm6$!y=7uxP(A{3EdYQRLQeny delta 1202 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 diff --git a/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/vCSCF_aligned.csar b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/vCSCF.csar similarity index 100% rename from sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/vCSCF_aligned.csar rename to sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/vCSCF.csar diff --git a/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/vCSCF_v3.0.csar b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/vCSCF_v3.0.csar index 1c52cca12d80b57b5a7cd3c1c32c666186c59b1e..094810a3f78d8344dbae838534999300d9fd95db 100644 GIT binary patch delta 1549 zcmdmdk7?t5rVSp;gcuk&7*_XYdwWcrbiIy=fuTlla?~=d`nM6M`)(PC-2E*2vr%b* z&Z;+27kb=smoL7wY3rLCj$(CJ%Yv4M{@;6DrKLrILpdZZKv(SG_j$i9p5I#F@yzh3 z-0^0OQ!^LcI(Bh!Trtmm;r6$aB9CwFT3llz6I$&b_I%E!HudEX45H7xyP#ZSmzPvp zc%SPqL-F^2+Bq6Jk*r+x@3fY#4S70GM=?uw9y{~;Ua_iBh4V`UZ)~<;xisafG+$Xl zrq0Df+1=p>Q#Wt;K5g=vl9ki0>mAGp_7o1f*?YAu@_c#6g%|VEx61rKI>B$^>Pc?- z?u&oN&O5Ya?M|_oEG4d@Tq;*l|NmVa;DNvaGA)di(Gf2ue$F4 zFLP$bj2!#9r&O|LEq<9Wy){~G=BLuX`L8_IS&CNw5s5Y{H=VkqtYt;`gT2;*O>?Yk zo4&}{Ij%FWZu%p?w|TMoyqNrN-`JE2J?npEh0OB#ztro!8DC6gnquDl$veN=7wHu@ z1~nGXy(uPtcJH}&%&e&kuHTxLu{7dQCEwPkIB#>>qzt6gUVwOw}X&+C&{I3)Ts>+2roFN_CfbTn@- zSD(yKUp8;L+J`EGv(r{T{&gWIEu-}IJl#%(?F%~;^aCdpY@Mg;$#Az#W8viK8r(AR z6)y31$7=qdyL3ZYyS-TWD-C zOFTVO`p4Gb&@#8kQ&*quU+w(Wd%~qB7JHT?tT+&~z)*^r_aD>b=PQnYlV=NR^4z;p zZZvt0CeMLRo)e@tN36+Yl(uIOVZbaz85mR;R3_h9YplY>pa7I-U|?jBV7RRNQG_M> zGGoO3JwnLzWd3zpOiU{$A6zM>mzx&g&Bz2Z6*cWM0L@SVo5lb(^#DkV6p9s-^b%h% n1Fewi_;nMRo;-7%7GufeBkOD!7fxneufY`yEbQ&pf(!!yzU<4! delta 1454 zcmdmZpK0?wrVSp;#F!X37=%|udA~4leVEL|!0=3ffkA~~viv%=`lk_r`L_%N>Yj%` z;pYiFc6iEm-YctaT?i3J@SU#`7)!_{dLce?Py%EEW~5Wl}Czq?v_9Qy>GL! ztC2hRs|+izNwZcm`OLa=ur2t{{OGK(GZB1RzOPn)RJrkv_sX7xxgK!|2Wl+$tCj!w z{i;~@z~3wr=QCfQ&(8=5*~IXv{^2T}Z{7=iw+U?hcz*FjW?rUiyoEklj_yFe zjGjKqvpeFmZS}{v?PsUWKK_r{wKnSTC!KF!MQ?80EFy4tvC5{;zLQSeJE{=td*0l$ z`qMKr{rf!6FW)?E`TvW+fvL%EfgjHV3qPK3VZ60oVd(z5JF`_YR=O1%`H#5uR{B=^36;>`2#g-f;Cd;lApWLvFWi&;O>=cf|tQ=veSAS#_Y-U>ovH5d4YjrEl3&wq3oq~ diff --git a/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/vSBC_aligned.csar b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/vSBC.csar similarity index 100% rename from sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/vSBC_aligned.csar rename to sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/vSBC.csar diff --git a/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/base_vfw.zip b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/vfw.zip similarity index 100% rename from sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/base_vfw.zip rename to sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/vfw.zip diff --git a/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/base_vvg.zip b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/vvg.zip similarity index 100% rename from sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/base_vvg.zip rename to sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default/Files/VNFs/vvg.zip diff --git a/test-apis-ci/src/main/java/org/openecomp/sdc/ci/tests/api/ComponentBaseTest.java b/test-apis-ci/src/main/java/org/openecomp/sdc/ci/tests/api/ComponentBaseTest.java index 9af3b6bc05..67091f3898 100644 --- a/test-apis-ci/src/main/java/org/openecomp/sdc/ci/tests/api/ComponentBaseTest.java +++ b/test-apis-ci/src/main/java/org/openecomp/sdc/ci/tests/api/ComponentBaseTest.java @@ -116,7 +116,7 @@ public abstract class ComponentBaseTest { config = Utils.getConfig(); myContext=context; ExtentManager.initReporter(getReportFolder(), REPORT_FILE_NAME, context); - AtomicOperationUtils.createDefaultConsumer(true); +// AtomicOperationUtils.createDefaultConsumer(true); openTitanLogic(); performClean(); diff --git a/test-apis-ci/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ResponseParser.java b/test-apis-ci/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ResponseParser.java index 95953838c9..6828a5ef2a 100644 --- a/test-apis-ci/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ResponseParser.java +++ b/test-apis-ci/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ResponseParser.java @@ -187,7 +187,7 @@ public class ResponseParser { Resource resource = null; try { // TODO Andrey L. uncomment line below in case to ignore on unknown properties, not recommended -// mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); resource = mapper.readValue(response, Resource.class); logger.debug(resource.toString()); -- 2.16.6