2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.openecomp.sdc.asdctool.impl.migration.v1610;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.HashMap;
26 import java.util.List;
28 import java.util.Optional;
30 import java.util.function.Function;
31 import java.util.function.Supplier;
32 import java.util.stream.Collectors;
33 import java.util.stream.Stream;
35 import org.apache.commons.collections.CollectionUtils;
36 import org.apache.commons.collections.MapUtils;
37 import org.apache.commons.lang3.tuple.ImmutablePair;
38 import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
39 import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
40 import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
41 import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
42 import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
43 import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
44 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
45 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
46 import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
47 import org.openecomp.sdc.be.model.ArtifactDefinition;
48 import org.openecomp.sdc.be.model.Component;
49 import org.openecomp.sdc.be.model.LifecycleStateEnum;
50 import org.openecomp.sdc.be.model.Operation;
51 import org.openecomp.sdc.be.model.Resource;
52 import org.openecomp.sdc.be.model.Service;
53 import org.openecomp.sdc.be.model.User;
54 import org.openecomp.sdc.be.model.operations.api.IArtifactOperation;
55 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
56 import org.openecomp.sdc.be.model.operations.impl.AbstractOperation;
57 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
58 import org.openecomp.sdc.be.resources.data.ArtifactData;
59 import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
60 import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
61 import org.openecomp.sdc.be.resources.data.ServiceMetadataData;
62 import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
63 import org.openecomp.sdc.common.api.ArtifactTypeEnum;
64 import org.openecomp.sdc.common.datastructure.Wrapper;
65 import org.openecomp.sdc.common.util.StreamUtils;
66 import org.openecomp.sdc.exception.ResponseFormat;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69 import org.springframework.beans.factory.annotation.Autowired;
71 import fj.data.Either;
74 * This Class holds the logic to add Tosca Artifacts placeholder and payload.<br>
75 * This addition is done for old version of Services and Resources (pre 1610) that weren't created with them.<br>
77 * @author mshitrit <br>
81 public class ToscaArtifactsAlignment extends AbstractOperation {
83 private IArtifactOperation artifactOperation;
86 private ServiceBusinessLogic serviceBusinessLogic;
88 private static Logger log = LoggerFactory.getLogger(ToscaArtifactsAlignment.class.getName());
90 private static final String ERROR_PREFIX = "Tosca Artifact Alignment Error: ";
92 // API that Fetches Resource
93 private final Function<ComponentMetadataData, Resource> resourceFetcher = componentMD -> getComponent(componentMD, ComponentTypeEnum.RESOURCE);
94 // API that Fetches Service
95 private final Function<ComponentMetadataData, Service> serviceFetcher = componentMD -> getComponent(componentMD, ComponentTypeEnum.SERVICE);
97 private final Supplier<Class<ResourceMetadataData>> resourceClassGetter = () -> ResourceMetadataData.class;
98 private final Supplier<Class<ServiceMetadataData>> serviceClassGetter = () -> ServiceMetadataData.class;
101 * This method holds the logic to add Tosca Artifacts placeholder and payload.<br>
103 * @return true if succeed otherwise returns false
105 public boolean alignToscaArtifacts() {
106 Wrapper<TitanOperationStatus> errorWrapper = new Wrapper<>();
107 List<ResourceMetadataData> allResources = new ArrayList<>();
108 List<ResourceMetadataData> resourcesWithoutToscaPlaceHolder = new ArrayList<>();
109 List<ServiceMetadataData> allServices = new ArrayList<>();
110 List<ServiceMetadataData> servicesWithoutToscaPlaceHolder = new ArrayList<>();
111 log.debug("alignToscaArtifacts Start");
114 if (errorWrapper.isEmpty()) {
115 log.info("Fetching all resources");
116 fillAllComponetOfSpecificType(allResources, NodeTypeEnum.Resource, resourceClassGetter, errorWrapper);
119 if (errorWrapper.isEmpty()) {
120 // Filter Resources Without Tosca Artifacts
121 log.info("filtering resources to add tosca placeholder");
122 Either<List<ResourceMetadataData>, TitanOperationStatus> eitherRelevantResources = getComponentsWithMissingToscaArtifacts(resourceClassGetter, NodeTypeEnum.Resource, allResources);
123 fillListOrWrapper(errorWrapper, resourcesWithoutToscaPlaceHolder, eitherRelevantResources);
126 if (errorWrapper.isEmpty()) {
127 // Add PlaceHolders To Resources
128 log.info("adding tosca placeholders artifacts to resources");
129 addToscaArtifactToComponents(resourcesWithoutToscaPlaceHolder, resourceFetcher, NodeTypeEnum.Resource, errorWrapper);
131 if (errorWrapper.isEmpty()) {
132 // Add payload to Resources
133 log.info("generating payload to tosca artifacts on resources");
134 fillResourcesPayload(allResources, errorWrapper);
137 if (errorWrapper.isEmpty()) {
138 log.info("Fetching all services");
139 fillAllComponetOfSpecificType(allServices, NodeTypeEnum.Service, serviceClassGetter, errorWrapper);
141 if (errorWrapper.isEmpty()) {
142 // Filter Services Without Tosca Artifacts
143 log.info("filtering services to add tosca placeholder");
144 Either<List<ServiceMetadataData>, TitanOperationStatus> eitherRelevantServices = getComponentsWithMissingToscaArtifacts(serviceClassGetter, NodeTypeEnum.Service, allServices);
145 fillListOrWrapper(errorWrapper, servicesWithoutToscaPlaceHolder, eitherRelevantServices);
148 if (errorWrapper.isEmpty()) {
149 // Add PlaceHolders To Services
150 log.info("adding tosca placeholders artifacts to services");
151 addToscaArtifactToComponents(servicesWithoutToscaPlaceHolder, serviceFetcher, NodeTypeEnum.Service, errorWrapper);
154 if (errorWrapper.isEmpty()) {
155 // Filter Services for Payload Add
156 // Add payload to Services
157 log.info("generating payload to tosca artifacts on services");
158 fillToscaArtifactPayload(allServices, serviceFetcher, errorWrapper);
161 titanGenericDao.commit();
163 return errorWrapper.isEmpty();
167 private void fillResourcesPayload(List<ResourceMetadataData> allResources, Wrapper<TitanOperationStatus> errorWrapper) {
168 if (errorWrapper.isEmpty()) {
169 // First Only Non VF (CP, VL & VFC)
170 List<ResourceMetadataData> basicResources = allResources.stream().filter(e -> isBasicResource((ResourceMetadataDataDefinition) e.getMetadataDataDefinition())).collect(Collectors.toList());
171 // Filter resources for Payload Add
172 // Add payload to resources
173 fillToscaArtifactPayload(basicResources, resourceFetcher, errorWrapper);
175 if (errorWrapper.isEmpty()) {
177 List<ResourceMetadataData> complexResource = allResources.stream().filter(e -> ((ResourceMetadataDataDefinition) e.getMetadataDataDefinition()).getResourceType() == ResourceTypeEnum.VF).collect(Collectors.toList());
178 // Filter resources for Payload Add
179 // Add payload to resources
180 fillToscaArtifactPayload(complexResource, resourceFetcher, errorWrapper);
184 private boolean isBasicResource(ResourceMetadataDataDefinition resourceMetadataDataDefinition) {
185 final ResourceTypeEnum resourceType = resourceMetadataDataDefinition.getResourceType();
186 boolean isBasicResource = resourceType == ResourceTypeEnum.CP || resourceType == ResourceTypeEnum.VL || resourceType == ResourceTypeEnum.VFC;
187 return isBasicResource;
190 private <T extends ComponentMetadataData> void fillAllComponetOfSpecificType(List<T> components, NodeTypeEnum nodeType, Supplier<Class<T>> classGetter, Wrapper<TitanOperationStatus> errorWrapper) {
192 Map<String, Object> props = new HashMap<String, Object>();
193 props.put(GraphPropertiesDictionary.IS_DELETED.getProperty(), true);
194 Either<List<T>, TitanOperationStatus> eitherComponentMD = titanGenericDao.getByCriteria(nodeType, null, props, classGetter.get());
195 if (eitherComponentMD.isLeft()) {
196 components.addAll(eitherComponentMD.left().value());
198 final TitanOperationStatus errorType = eitherComponentMD.right().value();
199 if (errorType != TitanOperationStatus.NOT_FOUND) {
200 log.error("{} When fetching all components of type:{} a titan error occured:{}", ERROR_PREFIX, nodeType.getName(), errorType.name());
201 errorWrapper.setInnerElement(errorType);
207 private <T extends ComponentMetadataData, R extends Component> void addToscaArtifactToComponents(List<T> relevantResources, Function<ComponentMetadataData, R> componentConvertor, NodeTypeEnum nodeType,
208 Wrapper<TitanOperationStatus> errorWrapper) {
210 // This Stream contains all create tosca placeholder results
211 Stream<StorageOperationStatus> addToscaToComponentsResultsStream = relevantResources.stream().map(e -> addToscaArtifacts(e, nodeType, componentConvertor));
212 // Execute the stream, and collect error
213 Optional<StorageOperationStatus> optionalError = addToscaToComponentsResultsStream.filter(e -> e != StorageOperationStatus.OK).findFirst();
216 if (optionalError.isPresent()) {
217 errorWrapper.setInnerElement(TitanOperationStatus.NOT_CREATED);
221 private <R extends Component> R getComponent(ComponentMetadataData md, ComponentTypeEnum componentTypeEnum) {
223 Either<R, StorageOperationStatus> eitherComponent = serviceBusinessLogic.getComponent(md.getMetadataDataDefinition().getUniqueId(), componentTypeEnum);
224 if (eitherComponent.isRight()) {
225 log.error("{} When fetching component {} of type:{} with uniqueId:{}", ERROR_PREFIX, md.getMetadataDataDefinition().getName(), componentTypeEnum.getValue(), md.getMetadataDataDefinition().getUniqueId());
227 result = eitherComponent.left().value();
232 private Either<Either<ArtifactDefinition, Operation>, ResponseFormat> populateToscaArtifactsWithLog(Component component, User user, boolean isInCertificationRequest, boolean inTransaction, boolean shouldLock) {
233 Either<Either<ArtifactDefinition, Operation>, ResponseFormat> ret;
235 ret = serviceBusinessLogic.populateToscaArtifacts(component, user, isInCertificationRequest, inTransaction, shouldLock);
237 log.debug("Added payload to tosca artifacts of component {} of type:{} with uniqueId:{}", component.getName(), component.getComponentType().getValue(), component.getUniqueId());
240 } catch (Exception e) {
241 log.error("{} Exception Occured When filling tosca artifact payload for component {} of type:{} with uniqueId:{}", ERROR_PREFIX, component.getName(), component.getComponentType().name(), component.getUniqueId(), e);
246 private <R extends Component, T extends ComponentMetadataData> void fillToscaArtifactPayload(List<T> relevantComponents, Function<ComponentMetadataData, R> componentCreator, Wrapper<TitanOperationStatus> errorWrapper) {
248 final User dummyUser = buildDummyUser();
249 // Stream for all fill payload results
250 Stream<ImmutablePair<Component, Either<Either<ArtifactDefinition, Operation>, ResponseFormat>>>
251 // Filter elements that needs generation of tosca payload
252 fillToscaPayloadResultsStream = relevantComponents.stream().filter(e -> isGenerateToscaPayload(e))
253 // Converts ComponentMetadataData to Component
254 .map(e -> componentCreator.apply(e))
255 // For each component generate payload for tosca
258 return new ImmutablePair<Component, Either<Either<ArtifactDefinition, Operation>, ResponseFormat>>(component, populateToscaArtifactsWithLog(component, dummyUser, true, true, false));
262 // execute and the stream
263 Optional<Component> optionalError = fillToscaPayloadResultsStream.
265 filter(e -> e.getRight().isRight())
266 // convert the result to error and execute the stream
267 .map(e -> e.getLeft()).findFirst();
269 // Check if error occurred
270 if (optionalError.isPresent()) {
271 Component component = optionalError.get();
272 log.error("{} When filling tosca artifact payload for component {} of type:{} with uniqueId:{}", ERROR_PREFIX, component.getName(), component.getComponentType().name(), component.getUniqueId());
274 errorWrapper.setInnerElement(TitanOperationStatus.GENERAL_ERROR);
276 } catch (Exception e) {
277 log.error("{} When filling tosca artifact payload for components : {}", ERROR_PREFIX, e.getMessage(), e);
278 errorWrapper.setInnerElement(TitanOperationStatus.GENERAL_ERROR);
282 private <R extends Component> StorageOperationStatus addToscaArtifacts(ComponentMetadataData component, NodeTypeEnum nodeType, Function<ComponentMetadataData, R> componentCreator) {
284 StorageOperationStatus result = StorageOperationStatus.OK;
285 R componentDefinition = componentCreator.apply(component);
287 // Fetch artifacts to be Added
288 Either<List<ArtifactDefinition>, StorageOperationStatus> eitherToscaArtifacts = getToscaArtifactsToAdd(componentDefinition);
289 if (eitherToscaArtifacts.isRight()) {
290 result = eitherToscaArtifacts.right().value();
292 List<ArtifactDefinition> toscaArtifactsToAdd = eitherToscaArtifacts.left().value();
293 if (!CollectionUtils.isEmpty(eitherToscaArtifacts.left().value())) {
294 final Stream<ImmutablePair<ArtifactDefinition, Either<ArtifactDefinition, StorageOperationStatus>>> createdToscaPlaceHolderStream = toscaArtifactsToAdd.stream()
295 // creates the artifact in the graph
296 .map(artifactDef -> new ImmutablePair<ArtifactDefinition, Either<ArtifactDefinition, StorageOperationStatus>>(artifactDef,
297 artifactOperation.addArifactToComponent(artifactDef, componentDefinition.getUniqueId(), nodeType, false, true)));
299 // Execute the stream, and collect error
300 Optional<ImmutablePair<ArtifactDefinition, StorageOperationStatus>> optionalError = createdToscaPlaceHolderStream.filter(e -> e.getRight().isRight()).map(e -> new ImmutablePair<>(e.getLeft(), e.getRight().right().value()))
303 // In case error occurred
304 if (optionalError.isPresent()) {
305 ArtifactDefinition toscaArtifact = optionalError.get().getLeft();
306 StorageOperationStatus storageError = optionalError.get().getRight();
307 log.error("{} When adding tosca artifact of type {} to component {} of type:{} with uniqueId:{} a storageError occurred:{}", ERROR_PREFIX, toscaArtifact.getArtifactType(), component.getMetadataDataDefinition().getName(),
308 nodeType.getName(), component.getMetadataDataDefinition().getUniqueId(), storageError.name());
310 result = storageError;
312 log.debug("Added tosca artifacts to component {} of type:{} with uniqueId:{}", component.getMetadataDataDefinition().getName(), nodeType.getName(), component.getMetadataDataDefinition().getUniqueId());
321 private <R extends Component> Either<List<ArtifactDefinition>, StorageOperationStatus> getToscaArtifactsToAdd(R componentDefinition) {
323 Either<List<ArtifactDefinition>, StorageOperationStatus> result;
324 List<ArtifactDefinition> toscaArtifactsAlreadyExist = new ArrayList<>();
325 if (!MapUtils.isEmpty(componentDefinition.getToscaArtifacts())) {
326 toscaArtifactsAlreadyExist.addAll(componentDefinition.getToscaArtifacts().values());
329 // Set Tosca Artifacts on component
330 serviceBusinessLogic.setToscaArtifactsPlaceHolders(componentDefinition, buildDummyUser());
332 List<ArtifactDefinition> toscaArtifactsToAdd = new ArrayList<>();
333 if (!MapUtils.isEmpty(componentDefinition.getToscaArtifacts())) {
334 final Collection<ArtifactDefinition> allToscaArtifacts = componentDefinition.getToscaArtifacts().values();
335 Set<String> artifactTypesExist = toscaArtifactsAlreadyExist.stream().map(e -> e.getArtifactType()).collect(Collectors.toSet());
336 toscaArtifactsToAdd = allToscaArtifacts.stream().filter(e -> !artifactTypesExist.contains(e.getArtifactType())).collect(Collectors.toList());
337 result = Either.left(toscaArtifactsToAdd);
339 log.error("{} failed to add tosca artifacts in bussiness logic to component {} of type:{} with uniqueId:{}", ERROR_PREFIX, componentDefinition.getName(), componentDefinition.getComponentType().getValue(),
340 componentDefinition.getUniqueId());
341 result = Either.right(StorageOperationStatus.ARTIFACT_NOT_FOUND);
346 private User buildDummyUser() {
347 User user = new User();
348 user.setUserId("migrationTask");
352 private boolean isGenerateToscaPayload(ComponentMetadataData component) {
353 final String state = component.getMetadataDataDefinition().getState();
354 boolean componentLifeCycleStateIsValid = LifecycleStateEnum.NOT_CERTIFIED_CHECKIN.name().equals(state) || LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name().equals(state);
356 return !componentLifeCycleStateIsValid;
360 private <T> void fillListOrWrapper(Wrapper<TitanOperationStatus> wrapper, List<T> listToFill, Either<List<T>, TitanOperationStatus> either) {
361 if (either.isRight()) {
362 final TitanOperationStatus errorType = either.right().value();
363 if (errorType != TitanOperationStatus.NOT_FOUND) {
364 wrapper.setInnerElement(errorType);
367 listToFill.addAll(either.left().value());
371 private <T extends ComponentMetadataData> Either<List<T>, TitanOperationStatus> getComponentsWithMissingToscaArtifacts(Supplier<Class<T>> classGetter, NodeTypeEnum nodeType, List<T> allComponents) {
373 Either<List<T>, TitanOperationStatus> result;
374 Stream<ImmutablePair<T, Either<List<ArtifactData>, TitanOperationStatus>>> componentsWithToscaStream =
375 // Create a Stream of pairs : component and its Tosca Artifacts
376 allComponents.stream().map(e -> new ImmutablePair<>(e, getToscaArtifatcs(e, nodeType)));
378 List<ImmutablePair<T, Either<List<ArtifactData>, TitanOperationStatus>>> componentsWithToscaArtifacts =
379 // Collect the stream to list.
380 // in case getToscaArtifatcs failed, the first failure is
382 // (the collection stops after first failure)
383 StreamUtils.takeWhilePlusOneNoEval(componentsWithToscaStream, e -> e.getRight().isLeft()).collect(Collectors.toList());
385 // retrieve the failure optional (it may or may not exist)
386 Optional<TitanOperationStatus> isErrorOccured = componentsWithToscaArtifacts.stream()
387 // convert to the right side of the pair of type Either
388 .map(e -> e.getRight())
389 // Filter in only the errors
390 .filter(e -> e.isRight()).
391 // map the error from Either to TitanOperationStatus
392 map(e -> e.right().value()).findFirst();
394 // In case failure occurred
395 if (isErrorOccured.isPresent()) {
396 result = Either.right(isErrorOccured.get());
397 // In case NO failure occurred
399 List<T> filteredComponents = componentsWithToscaArtifacts.stream()
400 // Filter in only elements that does NOT have tosca
402 .filter(e -> isNotContainAllToscaArtifacts(e))
403 // Convert back to Components List & collect
404 .map(e -> e.getLeft()).collect(Collectors.toList());
406 result = Either.left(filteredComponents);
412 private <T extends ComponentMetadataData> boolean isNotContainAllToscaArtifacts(ImmutablePair<T, Either<List<ArtifactData>, TitanOperationStatus>> pair) {
414 final List<ArtifactData> artifactList = pair.getRight().left().value();
416 Set<ArtifactTypeEnum> filteredToscaList = artifactList.stream().
417 // Convert to ArtifactDataDefinition
418 map(e -> e.getArtifactDataDefinition()).
419 // Filter in Only Tosca Artifacts
420 filter(e -> e.getArtifactGroupType() == ArtifactGroupTypeEnum.TOSCA).
421 // Convert To ArtifactTypeEnum
422 map(e -> ArtifactTypeEnum.findType(e.getArtifactType())).
423 // Filter Out nulls in case of Type not found
424 filter(e -> e != null).collect(Collectors.toSet());
426 boolean toscaArifactContained = filteredToscaList.contains(ArtifactTypeEnum.TOSCA_CSAR) && filteredToscaList.contains(ArtifactTypeEnum.TOSCA_TEMPLATE);
427 return !toscaArifactContained;
430 private <T extends ComponentMetadataData> Either<List<ArtifactData>, TitanOperationStatus> getToscaArtifatcs(T component, NodeTypeEnum nodeType) {
432 Either<List<ArtifactData>, TitanOperationStatus> result;
433 // All The Artifacts of the Component
434 Either<List<ImmutablePair<ArtifactData, GraphEdge>>, TitanOperationStatus> eitherComponentArtifacts = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(nodeType), component.getMetadataDataDefinition().getUniqueId(),
435 GraphEdgeLabels.ARTIFACT_REF, NodeTypeEnum.ArtifactRef, ArtifactData.class);
437 if (eitherComponentArtifacts.isLeft()) {
438 // Convert Artifact Edge Pair to Artifact
439 List<ArtifactData> toscaArtifacts = eitherComponentArtifacts.left().value().stream()
440 // map ImmutablePair<ArtifactData, GraphEdge> to
442 .map(e -> e.getLeft())
443 // Filter in only Tosca Artifacts
444 .filter(artifact -> artifact.getArtifactDataDefinition().getArtifactGroupType() == ArtifactGroupTypeEnum.TOSCA)
446 .collect(Collectors.toList());
447 result = Either.left(toscaArtifacts);
448 } else if (eitherComponentArtifacts.right().value() == TitanOperationStatus.NOT_FOUND) {
449 result = Either.left(new ArrayList<>());
451 final TitanOperationStatus titanError = eitherComponentArtifacts.right().value();
452 log.error("{} When fetching artifacts for component {} of type:{} with uniqueId:{} a titanError occurred:{}", ERROR_PREFIX, component.getMetadataDataDefinition().getName(), nodeType.getName(),
453 component.getMetadataDataDefinition().getUniqueId(), titanError.name());
455 result = Either.right(titanError);