a5f819d9b07c5e050524be9784a0fa2b7eebafb8
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / OutputsBusinessLogic.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2021, Nordix Foundation. 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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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=========================================================
19  */
20 package org.openecomp.sdc.be.components.impl;
21
22 import fj.data.Either;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Optional;
28 import org.apache.commons.collections4.CollectionUtils;
29 import org.openecomp.sdc.be.components.attribute.AttributeDeclarationOrchestrator;
30 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
31 import org.openecomp.sdc.be.components.impl.exceptions.ByResponseFormatComponentException;
32 import org.openecomp.sdc.be.components.impl.exceptions.ComponentException;
33 import org.openecomp.sdc.be.components.validation.ComponentValidations;
34 import org.openecomp.sdc.be.dao.api.ActionStatus;
35 import org.openecomp.sdc.be.dao.utils.MapUtil;
36 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
37 import org.openecomp.sdc.be.datatypes.tosca.ToscaGetFunctionType;
38 import org.openecomp.sdc.be.model.Component;
39 import org.openecomp.sdc.be.model.ComponentInstOutputsMap;
40 import org.openecomp.sdc.be.model.ComponentInstance;
41 import org.openecomp.sdc.be.model.ComponentInstanceAttribOutput;
42 import org.openecomp.sdc.be.model.ComponentInstanceAttribute;
43 import org.openecomp.sdc.be.model.ComponentInstanceOutput;
44 import org.openecomp.sdc.be.model.ComponentParametersView;
45 import org.openecomp.sdc.be.model.OutputDefinition;
46 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ArtifactsOperations;
47 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.InterfaceOperation;
48 import org.openecomp.sdc.be.model.operations.api.IElementOperation;
49 import org.openecomp.sdc.be.model.operations.api.IGroupInstanceOperation;
50 import org.openecomp.sdc.be.model.operations.api.IGroupOperation;
51 import org.openecomp.sdc.be.model.operations.api.IGroupTypeOperation;
52 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
53 import org.openecomp.sdc.be.model.operations.impl.InterfaceLifecycleOperation;
54 import org.openecomp.sdc.common.log.elements.LoggerSupportability;
55 import org.openecomp.sdc.common.log.enums.LoggerSupportabilityActions;
56 import org.openecomp.sdc.common.log.enums.StatusCode;
57 import org.openecomp.sdc.common.log.wrappers.Logger;
58 import org.openecomp.sdc.exception.ResponseFormat;
59 import org.springframework.beans.factory.annotation.Autowired;
60 import org.springframework.http.HttpStatus;
61
62 @org.springframework.stereotype.Component("outputsBusinessLogic")
63 public class OutputsBusinessLogic extends BaseBusinessLogic {
64
65     private static final String CREATE_OUTPUT = "CreateOutput";
66     private static final Logger log = Logger.getLogger(OutputsBusinessLogic.class);
67     private static final String FAILED_TO_FOUND_COMPONENT_ERROR = "Failed to found component {}, error: {}";
68     private static final String GOING_TO_EXECUTE_ROLLBACK_ON_CREATE_OUTPUTS = "Going to execute rollback on create outputs.";
69     private static final String GOING_TO_EXECUTE_COMMIT_ON_CREATE_OUTPUTS = "Going to execute commit on create outputs.";
70     private static final LoggerSupportability loggerSupportability = LoggerSupportability.getLogger(OutputsBusinessLogic.class);
71     private static final String FAILED_TO_FOUND_COMPONENT_INSTANCE_OUTPUTS_COMPONENT_INSTANCE_ID = "Failed to found component instance outputs componentInstanceId: {}";
72     private static final String FAILED_TO_FOUND_COMPONENT_INSTANCE_OUTPUTS_ERROR = "Failed to found component instance outputs {}, error: {}";
73     private final AttributeDeclarationOrchestrator attributeDeclarationOrchestrator;
74
75     @Autowired
76     public OutputsBusinessLogic(final IElementOperation elementDao, final IGroupOperation groupOperation,
77                                 final IGroupInstanceOperation groupInstanceOperation, final IGroupTypeOperation groupTypeOperation,
78                                 final InterfaceOperation interfaceOperation, final InterfaceLifecycleOperation interfaceLifecycleTypeOperation,
79                                 final AttributeDeclarationOrchestrator attributeDeclarationOrchestrator,
80                                 final ArtifactsOperations artifactToscaOperation) {
81         super(elementDao, groupOperation, groupInstanceOperation, groupTypeOperation, interfaceOperation, interfaceLifecycleTypeOperation,
82             artifactToscaOperation);
83         this.attributeDeclarationOrchestrator = attributeDeclarationOrchestrator;
84     }
85
86     public Either<List<ComponentInstanceOutput>, ResponseFormat> getComponentInstanceOutputs(final String userId, final String componentId,
87                                                                                              final String componentInstanceId) {
88         validateUserExists(userId);
89         final ComponentParametersView filters = new ComponentParametersView();
90         filters.disableAll();
91         filters.setIgnoreOutputs(false);
92         filters.setIgnoreComponentInstances(false);
93         filters.setIgnoreComponentInstancesOutputs(false);
94         final Either<Component, StorageOperationStatus> getComponentEither = toscaOperationFacade.getToscaElement(componentId, filters);
95         if (getComponentEither.isRight()) {
96             ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(getComponentEither.right().value());
97             log.debug(FAILED_TO_FOUND_COMPONENT_ERROR, componentId, actionStatus);
98             return Either.right(componentsUtils.getResponseFormat(actionStatus));
99         }
100         final Component component = getComponentEither.left().value();
101         if (!ComponentValidations.validateComponentInstanceExist(component, componentInstanceId)) {
102             final ActionStatus actionStatus = ActionStatus.COMPONENT_INSTANCE_NOT_FOUND;
103             log.debug(FAILED_TO_FOUND_COMPONENT_INSTANCE_OUTPUTS_ERROR, componentInstanceId, actionStatus);
104             loggerSupportability.log(LoggerSupportabilityActions.CREATE_OUTPUTS, component.getComponentMetadataForSupportLog(), StatusCode.ERROR,
105                 FAILED_TO_FOUND_COMPONENT_INSTANCE_OUTPUTS_COMPONENT_INSTANCE_ID, componentInstanceId);
106             return Either.right(componentsUtils.getResponseFormat(actionStatus));
107         }
108         final Map<String, List<ComponentInstanceOutput>> ciOutputs = Optional.ofNullable(component.getComponentInstancesOutputs())
109             .orElse(Collections.emptyMap());
110         return Either.left(ciOutputs.getOrDefault(componentInstanceId, Collections.emptyList()));
111     }
112
113     @Override
114     public Either<List<OutputDefinition>, ResponseFormat> declareAttributes(final String userId, final String componentId,
115                                                                             final ComponentTypeEnum componentTypeEnum,
116                                                                             final ComponentInstOutputsMap componentInstOutputsMap) {
117         return createMultipleOutputs(userId, componentId, componentTypeEnum, componentInstOutputsMap, true, false);
118     }
119
120     private Either<List<OutputDefinition>, ResponseFormat> createMultipleOutputs(final String userId, final String componentId,
121                                                                                  final ComponentTypeEnum componentType,
122                                                                                  final ComponentInstOutputsMap componentInstOutputsMapUi,
123                                                                                  final boolean shouldLockComp, final boolean inTransaction) {
124         Either<List<OutputDefinition>, ResponseFormat> result = Either.right(new ResponseFormat(HttpStatus.BAD_REQUEST.value()));
125         org.openecomp.sdc.be.model.Component component = null;
126         try {
127             validateUserExists(userId);
128             component = getAndValidateComponentForCreate(userId, componentId, componentType, shouldLockComp);
129             result = attributeDeclarationOrchestrator.declareAttributesToOutputs(component, componentInstOutputsMapUi).left()
130                 .bind(outputsToCreate -> prepareOutputsForCreation(userId, componentId, outputsToCreate)).right()
131                 .map(componentsUtils::getResponseFormat);
132             return result;
133         } catch (final ByResponseFormatComponentException e) {
134             log.error("#createMultipleOutputs: Exception thrown: ", e);
135             result = Either.right(e.getResponseFormat());
136             return result;
137         } finally {
138             if (!inTransaction) {
139                 if (result.isRight()) {
140                     log.debug(GOING_TO_EXECUTE_ROLLBACK_ON_CREATE_OUTPUTS);
141                     janusGraphDao.rollback();
142                 } else {
143                     log.debug(GOING_TO_EXECUTE_COMMIT_ON_CREATE_OUTPUTS);
144                     janusGraphDao.commit();
145                 }
146             }
147             // unlock resource
148             if (shouldLockComp && component != null) {
149                 graphLockOperation.unlockComponent(componentId, componentType.getNodeType());
150             }
151         }
152     }
153
154     private Component getAndValidateComponentForCreate(final String userId, final String componentId,
155                                                        final ComponentTypeEnum componentType,
156                                                        final boolean shouldLockComp) {
157         final ComponentParametersView componentParametersView = getBaseComponentParametersView();
158         final Component component = validateComponentExists(componentId, componentType, componentParametersView);
159         if (shouldLockComp) {
160             // lock the component
161             lockComponent(component, CREATE_OUTPUT);
162         }
163         validateCanWorkOnComponent(component, userId);
164         return component;
165     }
166
167     private Either<List<OutputDefinition>, StorageOperationStatus> prepareOutputsForCreation(final String userId, final String cmptId,
168                                                                                              final List<OutputDefinition> outputsToCreate) {
169         final Map<String, OutputDefinition> outputsToPersist = MapUtil.toMap(outputsToCreate, OutputDefinition::getName);
170         assignOwnerIdToOutputs(userId, outputsToPersist);
171         final var statusEither = toscaOperationFacade.addOutputsToComponent(outputsToPersist, cmptId);
172         if (statusEither.isRight()) {
173             return statusEither;
174         }
175         return statusEither.left().map(persistedOutputs -> outputsToCreate);
176     }
177
178     private void assignOwnerIdToOutputs(final String userId, final Map<String, OutputDefinition> outputsToCreate) {
179         outputsToCreate.values().forEach(outputDefinition -> outputDefinition.setOwnerId(userId));
180     }
181
182     private ComponentParametersView getBaseComponentParametersView() {
183         final ComponentParametersView componentParametersView = new ComponentParametersView();
184         componentParametersView.disableAll();
185         componentParametersView.setIgnoreOutputs(false);
186         componentParametersView.setIgnoreAttributes(false);
187         componentParametersView.setIgnoreComponentInstances(false);
188         componentParametersView.setIgnoreComponentInstancesOutputs(false);
189         componentParametersView.setIgnoreComponentInstancesAttributes(false);
190         componentParametersView.setIgnoreUsers(false);
191         return componentParametersView;
192     }
193
194     /**
195      * Delete output from component
196      *
197      * @param componentId
198      * @param userId
199      * @param outputId
200      * @return
201      */
202     public OutputDefinition deleteOutput(final String componentId, final String userId, final String outputId) {
203         if (log.isDebugEnabled()) {
204             log.debug("Going to delete output id: {}", outputId);
205         }
206         validateUserExists(userId);
207         final ComponentParametersView componentParametersView = getBaseComponentParametersView();
208         componentParametersView.setIgnoreAttributes(false);
209         final Either<org.openecomp.sdc.be.model.Component, StorageOperationStatus> componentEither = toscaOperationFacade
210             .getToscaElement(componentId, componentParametersView);
211         if (componentEither.isRight()) {
212             throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(componentEither.right().value()));
213         }
214         final org.openecomp.sdc.be.model.Component component = componentEither.left().value();
215         // Validate outputId is child of the component
216         final Optional<OutputDefinition> optionalOutput = component.getOutputs().stream().
217             // filter by ID
218                 filter(output -> output.getUniqueId().equals(outputId)).
219             // Get the output
220                 findAny();
221         if (!optionalOutput.isPresent()) {
222             throw new ByActionStatusComponentException(ActionStatus.OUTPUT_IS_NOT_CHILD_OF_COMPONENT, outputId, componentId);
223         }
224         final OutputDefinition outputForDelete = optionalOutput.get();
225         // Lock component
226         lockComponent(componentId, component, "deleteOutput");
227         // Delete output operations
228         boolean failed = false;
229         try {
230             final StorageOperationStatus status = toscaOperationFacade.deleteOutputOfResource(component, outputForDelete.getName());
231             if (status != StorageOperationStatus.OK) {
232                 log.debug("Component id: {} delete output id: {} failed", componentId, outputId);
233                 throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(status), component.getName());
234             }
235             final StorageOperationStatus storageOperationStatus = attributeDeclarationOrchestrator
236                 .unDeclareAttributesAsOutputs(component, outputForDelete);
237             if (storageOperationStatus != StorageOperationStatus.OK) {
238                 log.debug("Component id: {} update attributes declared as output for outputId: {} failed", componentId, outputId);
239                 throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(storageOperationStatus), component.getName());
240             }
241             return outputForDelete;
242         } catch (final ComponentException e) {
243             failed = true;
244             throw e;
245         } finally {
246             unlockComponent(failed, component);
247         }
248     }
249
250     public Either<List<OutputDefinition>, ResponseFormat> createOutputsInGraph(final Map<String, OutputDefinition> outputs,
251                                                                                final Component component,
252                                                                                final String userId) {
253
254         final List<OutputDefinition> result = new ArrayList<>();
255         for (final Map.Entry<String, OutputDefinition> outputDefinition : outputs.entrySet()) {
256             final var outputDefinitionValue = outputDefinition.getValue();
257             outputDefinitionValue.setName(outputDefinition.getKey());
258
259             final String value = outputDefinitionValue.getValue();
260             if (value != null) {
261                 final List<String> getAttribute = (List<String>) ImportUtils.loadYamlAsStrictMap(value)
262                     .get(ToscaGetFunctionType.GET_ATTRIBUTE.getFunctionName());
263                 if (getAttribute.size() == 2) {
264                     final var optionalComponentInstance = component.getComponentInstanceByName(getAttribute.get(0));
265                     if (optionalComponentInstance.isPresent()) {
266                         final var createdOutputs
267                             = createOutputs(component.getUniqueId(), userId, getAttribute.get(1), optionalComponentInstance.get());
268                         if (createdOutputs.isRight()) {
269                             return Either.right((createdOutputs.right().value()));
270                         }
271                         result.addAll(createdOutputs.left().value());
272                     } else {
273                         // From SELF
274                         outputDefinitionValue.setInstanceUniqueId(component.getUniqueId());
275                     }
276                 }
277             }
278         }
279         return Either.left(result);
280
281     }
282
283     private Either<List<OutputDefinition>, ResponseFormat> createOutputs(final String componentUniqueId, final String userId,
284                                                                          final String attributeName,
285                                                                          final ComponentInstance componentInstance) {
286         // From Instance
287         final List<OutputDefinition> result = new ArrayList<>();
288         final var componentInstanceAttributes = componentInstance.getAttributes();
289         if (CollectionUtils.isNotEmpty(componentInstanceAttributes)) {
290             final var componentInstanceAttributeOptional = componentInstanceAttributes.stream()
291                 .filter(ad -> ad.getName().equals(attributeName)).map(ComponentInstanceAttribute::new).findFirst();
292             if (componentInstanceAttributeOptional.isPresent()) {
293                 final var componentInstOutputsMap = new ComponentInstOutputsMap();
294                 componentInstOutputsMap.setComponentInstanceAttributes(Collections.singletonMap(componentInstance.getUniqueId(),
295                     Collections.singletonList(new ComponentInstanceAttribOutput(componentInstanceAttributeOptional.get()))));
296                 final var createdOutputs = createMultipleOutputs(userId, componentUniqueId, ComponentTypeEnum.SERVICE,
297                     componentInstOutputsMap, true, false);
298                 if (createdOutputs.isRight()) {
299                     return Either.right((createdOutputs.right().value()));
300                 }
301                 result.addAll(createdOutputs.left().value());
302             }
303         }
304         return Either.left(result);
305     }
306
307 }