Import VSP with non-standard data types
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / csar / CsarInfo.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
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
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  * Modifications copyright (c) 2019 Nokia
20  * ================================================================================
21  */
22 package org.openecomp.sdc.be.components.csar;
23
24 import static org.openecomp.sdc.be.components.impl.ImportUtils.ResultStatusEnum;
25 import static org.openecomp.sdc.be.components.impl.ImportUtils.ToscaElementTypeEnum;
26 import static org.openecomp.sdc.be.components.impl.ImportUtils.findToscaElement;
27
28 import com.google.common.annotations.VisibleForTesting;
29 import fj.data.Either;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Optional;
37 import java.util.PriorityQueue;
38 import java.util.Queue;
39 import java.util.Set;
40 import java.util.regex.Pattern;
41 import lombok.Getter;
42 import lombok.Setter;
43 import org.apache.commons.collections.CollectionUtils;
44 import org.apache.commons.collections.MapUtils;
45 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
46 import org.openecomp.sdc.be.config.NonManoArtifactType;
47 import org.openecomp.sdc.be.config.NonManoConfiguration;
48 import org.openecomp.sdc.be.config.NonManoConfigurationManager;
49 import org.openecomp.sdc.be.config.NonManoFolderType;
50 import org.openecomp.sdc.be.dao.api.ActionStatus;
51 import org.openecomp.sdc.be.model.NodeTypeInfo;
52 import org.openecomp.sdc.be.model.Resource;
53 import org.openecomp.sdc.be.model.User;
54 import org.openecomp.sdc.be.tosca.CsarUtils;
55 import org.openecomp.sdc.be.utils.TypeUtils;
56 import org.openecomp.sdc.common.api.Constants;
57 import org.openecomp.sdc.common.log.wrappers.Logger;
58 import org.yaml.snakeyaml.Yaml;
59
60 public class CsarInfo {
61
62     private static final Logger log = Logger.getLogger(CsarInfo.class);
63     private final NonManoConfiguration nonManoConfiguration;
64     @Getter
65     @Setter
66     private String vfResourceName;
67     @Getter
68     @Setter
69     private User modifier;
70     @Getter
71     @Setter
72     private String csarUUID;
73     @Getter
74     @Setter
75     private Map<String, byte[]> csar;
76     @Getter
77     private String mainTemplateName;
78     @Getter
79     private String mainTemplateContent;
80     @Getter
81     private Map<String, Object> mappedToscaMainTemplate;
82     @Getter
83     private Map<String, String> createdNodesToscaResourceNames;
84     private Queue<String> cvfcToCreateQueue;
85     private boolean isUpdate;
86     @Getter
87     private Map<String, Resource> createdNodes;
88     private Map<String, Object> datatypeDefinitions;
89     private List<Map.Entry<String, byte[]>> globalSubstitutes;
90
91
92     @SuppressWarnings("unchecked")
93     public CsarInfo(User modifier, String csarUUID, Map<String, byte[]> csar, String vfResourceName, String mainTemplateName,
94                     String mainTemplateContent, boolean isUpdate) {
95         this.vfResourceName = vfResourceName;
96         this.modifier = modifier;
97         this.csarUUID = csarUUID;
98         this.csar = csar;
99         this.mainTemplateName = mainTemplateName;
100         this.mainTemplateContent = mainTemplateContent;
101         this.mappedToscaMainTemplate = (Map<String, Object>) new Yaml().load(mainTemplateContent);
102         this.createdNodesToscaResourceNames = new HashMap<>();
103         this.cvfcToCreateQueue = new PriorityQueue<>();
104         this.isUpdate = isUpdate;
105         this.createdNodes = new HashMap<>();
106         this.nonManoConfiguration = NonManoConfigurationManager.getInstance().getNonManoConfiguration();
107         this.globalSubstitutes = getGlobalSubstitutes(csar);
108     }
109     
110     private List<Map.Entry<String, byte[]>> getGlobalSubstitutes(final Map<String, byte[]> csar){
111         final List<Map.Entry<String, byte[]>> globalSubstitutesInCsar = new ArrayList<>();
112         for (Map.Entry<String, byte[]> entry : csar.entrySet()) {
113             if (isAServiceTemplate(entry.getKey()) && isGlobalSubstitute(entry.getKey())) {
114                 globalSubstitutesInCsar.add(entry);
115             }
116         }
117         return globalSubstitutesInCsar;
118     }    
119
120     @VisibleForTesting
121     CsarInfo(final NonManoConfiguration nonManoConfiguration) {
122         this.nonManoConfiguration = nonManoConfiguration;
123     }
124
125     @SuppressWarnings("unchecked")
126     public static void markNestedVfc(Map<String, Object> mappedToscaTemplate, Map<String, NodeTypeInfo> nodeTypesInfo) {
127         findToscaElement(mappedToscaTemplate, TypeUtils.ToscaTagNamesEnum.NODE_TEMPLATES, ToscaElementTypeEnum.MAP).right()
128             .on(nts -> processNodeTemplates((Map<String, Object>) nts, nodeTypesInfo));
129     }
130
131     @SuppressWarnings("unchecked")
132     private static ResultStatusEnum processNodeTemplates(Map<String, Object> nodeTemplates, Map<String, NodeTypeInfo> nodeTypesInfo) {
133         nodeTemplates.values().forEach(nt -> processNodeTemplate(nodeTypesInfo, (Map<String, Object>) nt));
134         return ResultStatusEnum.OK;
135     }
136
137     private static void processNodeTemplate(Map<String, NodeTypeInfo> nodeTypesInfo, Map<String, Object> nodeTemplate) {
138         if (nodeTemplate.containsKey(TypeUtils.ToscaTagNamesEnum.TYPE.getElementName())) {
139             String type = (String) nodeTemplate.get(TypeUtils.ToscaTagNamesEnum.TYPE.getElementName());
140             if (nodeTypesInfo.containsKey(type)) {
141                 NodeTypeInfo nodeTypeInfo = nodeTypesInfo.get(type);
142                 if (nodeTypeInfo.isSubstitutionMapping() && type.contains(Constants.USER_DEFINED_RESOURCE_NAMESPACE_PREFIX)) {
143                     nodeTypeInfo.setNested(true);
144                 }
145             }
146         }
147     }
148
149     public void addNodeToQueue(String nodeName) {
150         if (!cvfcToCreateQueue.contains(nodeName)) {
151             cvfcToCreateQueue.add(nodeName);
152         } else {
153             log.debug("Failed to validate complex VFC {}. Loop detected, VSP {}. ", nodeName, getVfResourceName());
154             throw new ByActionStatusComponentException(ActionStatus.CFVC_LOOP_DETECTED, getVfResourceName(), nodeName);
155         }
156     }
157
158     public void removeNodeFromQueue() {
159         cvfcToCreateQueue.remove();
160     }
161
162     public boolean isUpdate() {
163         return isUpdate;
164     }
165
166     public void setUpdate(boolean isUpdate) {
167         this.isUpdate = isUpdate;
168     }
169
170     public Map<String, NodeTypeInfo> extractTypesInfo() {
171         Map<String, NodeTypeInfo> nodeTypesInfo = new HashMap<>();
172         final Set<String> nodeTypesUsedInNodeTemplates = new HashSet<>();
173         for (Map.Entry<String, byte[]> entry : getCsar().entrySet()) {
174             extractNodeTypeInfo(nodeTypesInfo,  nodeTypesUsedInNodeTemplates, entry);
175         }
176         if (CollectionUtils.isNotEmpty(globalSubstitutes)) {
177             setDerivedFrom(nodeTypesInfo);
178             addGlobalSubstitutionsToNodeTypes(nodeTypesUsedInNodeTemplates, nodeTypesInfo);
179         }
180         
181         markNestedVfc(getMappedToscaMainTemplate(), nodeTypesInfo);
182         return nodeTypesInfo;
183     }
184     
185     public Map<String, Object> getDataTypes() {
186         if (datatypeDefinitions == null) {
187             datatypeDefinitions = new HashMap<>();
188             for (Map.Entry<String, byte[]> entry : globalSubstitutes) {
189                 final String yamlFileContents = new String(entry.getValue());
190                 final Map<String, Object> mappedToscaTemplate = new Yaml().load(yamlFileContents);
191                 datatypeDefinitions.putAll(getDataTypesFromTemplate(mappedToscaTemplate));
192             }
193             datatypeDefinitions.putAll(getDataTypesFromTemplate(mappedToscaMainTemplate));
194         }
195         return datatypeDefinitions;
196     }
197     
198     @SuppressWarnings("unchecked")    
199     private Map<String, Object> getDataTypesFromTemplate(final Map<String, Object> mappedToscaTemplate) {
200         final Either<Object, ResultStatusEnum> dataTypesEither = findToscaElement(mappedToscaTemplate, TypeUtils.ToscaTagNamesEnum.DATA_TYPES,
201                         ToscaElementTypeEnum.MAP);
202         if (dataTypesEither != null && dataTypesEither.isLeft()) {
203             return (Map<String, Object>) dataTypesEither.left().value();
204         }
205         return Collections.emptyMap();
206     }
207
208     @SuppressWarnings("unchecked")
209     private void extractNodeTypeInfo(Map<String, NodeTypeInfo> nodeTypesInfo,
210                                      final Set<String> nodeTypesUsedInNodeTemplates, Map.Entry<String, byte[]> entry) {
211         if (isAServiceTemplate(entry.getKey()) && !isGlobalSubstitute(entry.getKey())) {
212             Map<String, Object> mappedToscaTemplate = (Map<String, Object>) new Yaml().load(new String(entry.getValue()));
213             findToscaElement(mappedToscaTemplate, TypeUtils.ToscaTagNamesEnum.SUBSTITUTION_MAPPINGS, ToscaElementTypeEnum.MAP).right()
214                 .on(sub -> handleSubstitutionMappings(nodeTypesInfo, entry, mappedToscaTemplate, (Map<String, Object>) sub));
215             final Either<Object, ResultStatusEnum> nodeTypesEither = findToscaElement(mappedToscaTemplate,
216                 TypeUtils.ToscaTagNamesEnum.NODE_TEMPLATES, ToscaElementTypeEnum.MAP);
217             if (nodeTypesEither.isLeft()) {
218                 final Map<String, Map<String, Object>> nodeTemplates = (Map<String, Map<String, Object>>) nodeTypesEither.left().value();
219                 nodeTypesUsedInNodeTemplates.addAll(findNodeTypesUsedInNodeTemplates(nodeTemplates));
220             }
221         }
222     }
223
224     private boolean isAServiceTemplate(final String filePath) {
225         return Pattern.compile(CsarUtils.SERVICE_TEMPLATE_PATH_PATTERN).matcher(filePath).matches();
226     }
227
228     private Set<String> findNodeTypesUsedInNodeTemplates(final Map<String, Map<String, Object>> nodeTemplates) {
229         final Set<String> nodeTypes = new HashSet<>();
230         for (final Map<String, Object> nodeTemplate : nodeTemplates.values()) {
231             nodeTypes.add((String) nodeTemplate.get(TypeUtils.ToscaTagNamesEnum.TYPE.getElementName()));
232         }
233         return nodeTypes;
234     }
235
236     private ResultStatusEnum handleSubstitutionMappings(Map<String, NodeTypeInfo> nodeTypesInfo, Map.Entry<String, byte[]> entry,
237                                                         Map<String, Object> mappedToscaTemplate, Map<String, Object> substitutionMappings) {
238         final Set<String> nodeTypesDefinedInTemplate = findNodeTypesDefinedInTemplate(mappedToscaTemplate);
239         if (substitutionMappings.containsKey(TypeUtils.ToscaTagNamesEnum.NODE_TYPE.getElementName()) && !nodeTypesDefinedInTemplate
240             .contains(substitutionMappings.get(TypeUtils.ToscaTagNamesEnum.NODE_TYPE.getElementName()))) {
241             NodeTypeInfo nodeTypeInfo = new NodeTypeInfo();
242             nodeTypeInfo.setSubstitutionMapping(true);
243             nodeTypeInfo.setType((String) substitutionMappings.get(TypeUtils.ToscaTagNamesEnum.NODE_TYPE.getElementName()));
244             nodeTypeInfo.setTemplateFileName(entry.getKey());
245             nodeTypeInfo.setMappedToscaTemplate(mappedToscaTemplate);
246             nodeTypesInfo.put(nodeTypeInfo.getType(), nodeTypeInfo);
247         }
248         return ResultStatusEnum.OK;
249     }
250
251     @SuppressWarnings("unchecked")
252     private Set<String> findNodeTypesDefinedInTemplate(final Map<String, Object> mappedToscaTemplate) {
253         final Either<Object, ResultStatusEnum> nodeTypesEither = findToscaElement(mappedToscaTemplate, TypeUtils.ToscaTagNamesEnum.NODE_TYPES,
254             ToscaElementTypeEnum.MAP);
255         if (nodeTypesEither.isLeft()) {
256             final Map<String, Object> nodeTypes = (Map<String, Object>) nodeTypesEither.left().value();
257             return nodeTypes.keySet();
258         }
259         return Collections.emptySet();
260     }
261
262     private boolean isGlobalSubstitute(String fileName) {
263         return fileName.equalsIgnoreCase(Constants.GLOBAL_SUBSTITUTION_TYPES_SERVICE_TEMPLATE) || fileName
264             .equalsIgnoreCase(Constants.ABSTRACT_SUBSTITUTE_GLOBAL_TYPES_SERVICE_TEMPLATE);
265     }
266
267     @SuppressWarnings("unchecked")
268     private void setDerivedFrom(Map<String, NodeTypeInfo> nodeTypesInfo) {
269         for (Map.Entry<String, byte[]> entry : globalSubstitutes) {
270             String yamlFileContents = new String(entry.getValue());
271             Map<String, Object> mappedToscaTemplate = (Map<String, Object>) new Yaml().load(yamlFileContents);
272             Either<Object, ResultStatusEnum> nodeTypesEither = findToscaElement(mappedToscaTemplate, TypeUtils.ToscaTagNamesEnum.NODE_TYPES,
273                 ToscaElementTypeEnum.MAP);
274             if (nodeTypesEither.isLeft()) {
275                 Map<String, Object> nodeTypes = (Map<String, Object>) nodeTypesEither.left().value();
276                 for (Map.Entry<String, Object> nodeType : nodeTypes.entrySet()) {
277                     processNodeType(nodeTypesInfo, nodeType);
278                 }
279             }
280         }
281     }
282
283     @SuppressWarnings("unchecked")
284     private void processNodeType(Map<String, NodeTypeInfo> nodeTypesInfo, Map.Entry<String, Object> nodeType) {
285         Map<String, Object> nodeTypeMap = (Map<String, Object>) nodeType.getValue();
286         if (nodeTypeMap.containsKey(TypeUtils.ToscaTagNamesEnum.DERIVED_FROM.getElementName()) && nodeTypesInfo.containsKey(nodeType.getKey())) {
287             NodeTypeInfo nodeTypeInfo = nodeTypesInfo.get(nodeType.getKey());
288             List<String> derivedFrom = new ArrayList<>();
289             derivedFrom.add((String) nodeTypeMap.get(TypeUtils.ToscaTagNamesEnum.DERIVED_FROM.getElementName()));
290             nodeTypeInfo.setDerivedFrom(derivedFrom);
291         }
292     }
293
294     @SuppressWarnings("unchecked")
295     private void addGlobalSubstitutionsToNodeTypes(final Set<String> nodeTypesUsedInNodeTemplates, final Map<String, NodeTypeInfo> nodeTypesInfo) {
296         for (Map.Entry<String, byte[]> entry : globalSubstitutes) {
297             final String yamlFileContents = new String(entry.getValue());
298             final Map<String, Object> mappedToscaTemplate = (Map<String, Object>) new Yaml().load(yamlFileContents);
299             final Either<Object, ResultStatusEnum> nodeTypesEither = findToscaElement(mappedToscaTemplate, TypeUtils.ToscaTagNamesEnum.NODE_TYPES,
300                 ToscaElementTypeEnum.MAP);
301             if (nodeTypesEither.isLeft()) {
302                 final Map<String, Object> nodeTypes = (Map<String, Object>) nodeTypesEither.left().value();
303                 for (final Map.Entry<String, Object> nodeType : nodeTypes.entrySet()) {
304                     if (!nodeTypesInfo.containsKey(nodeType.getKey()) && nodeTypesUsedInNodeTemplates.contains(nodeType.getKey())) {
305                         nodeTypesInfo.put(nodeType.getKey(), buildNodeTypeInfo(nodeType, entry.getKey(), mappedToscaTemplate));
306                     }
307                 }
308             }
309         }
310     }
311
312     @SuppressWarnings("unchecked")
313     private NodeTypeInfo buildNodeTypeInfo(final Map.Entry<String, Object> nodeType, final String templateFileName,
314                                            final Map<String, Object> mappedToscaTemplate) {
315         final NodeTypeInfo nodeTypeInfo = new NodeTypeInfo();
316         nodeTypeInfo.setSubstitutionMapping(false);
317         nodeTypeInfo.setNested(true);
318         nodeTypeInfo.setType(nodeType.getKey());
319         nodeTypeInfo.setTemplateFileName(templateFileName);
320         nodeTypeInfo.setMappedToscaTemplate(buildToscaTemplateForNode(nodeType.getKey(), mappedToscaTemplate));
321         final Map<String, Object> nodeTypeMap = (Map<String, Object>) nodeType.getValue();
322         final List<String> derivedFrom = new ArrayList<>();
323         derivedFrom.add((String) nodeTypeMap.get(TypeUtils.ToscaTagNamesEnum.DERIVED_FROM.getElementName()));
324         nodeTypeInfo.setDerivedFrom(derivedFrom);
325         return nodeTypeInfo;
326     }
327
328     @SuppressWarnings("unchecked")
329     private Map<String, Object> buildToscaTemplateForNode(final String nodeTypeName, final Map<String, Object> mappedToscaTemplate) {
330         final Map<String, Object> mappedToscaTemplateforNode = new HashMap<>(mappedToscaTemplate);
331         final Either<Object, ResultStatusEnum> nodeTypesEither = findToscaElement(mappedToscaTemplate, TypeUtils.ToscaTagNamesEnum.NODE_TYPES,
332             ToscaElementTypeEnum.MAP);
333         final Map<String, Object> nodeTypes = new HashMap<>();
334         if (nodeTypesEither.isLeft()) {
335             final Map<String, Object> allNodeTypes = (Map<String, Object>) nodeTypesEither.left().value();
336             nodeTypes.put(nodeTypeName, allNodeTypes.get(nodeTypeName));
337         }
338         mappedToscaTemplateforNode.put(TypeUtils.ToscaTagNamesEnum.NODE_TYPES.getElementName(), nodeTypes);
339         return mappedToscaTemplateforNode;
340     }
341
342     /**
343      * Gets the software information yaml path from the csar file map.
344      *
345      * @return the software information yaml path if it is present in the csar file map
346      */
347     public Optional<String> getSoftwareInformationPath() {
348         if (MapUtils.isEmpty(csar)) {
349             return Optional.empty();
350         }
351         final NonManoFolderType softwareInformationType = nonManoConfiguration.getNonManoType(NonManoArtifactType.ONAP_SW_INFORMATION);
352         return csar.keySet().stream().filter(filePath -> filePath.startsWith(softwareInformationType.getPath())).findFirst();
353     }
354 }