[SDC] Onboarding 1710 rebase.
[sdc.git] / openecomp-be / lib / openecomp-sdc-validation-lib / openecomp-sdc-validation-impl / src / main / java / org / openecomp / sdc / validation / impl / util / HeatValidationService.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  */
20
21 package org.openecomp.sdc.validation.impl.util;
22
23 import org.apache.commons.collections4.CollectionUtils;
24 import org.apache.commons.collections4.MapUtils;
25 import org.openecomp.sdc.tosca.services.YamlUtil;
26 import org.openecomp.core.validation.errors.ErrorMessagesFormatBuilder;
27 import org.openecomp.core.validation.types.GlobalValidationContext;
28 import org.openecomp.sdc.common.errors.Messages;
29 import org.openecomp.sdc.datatypes.error.ErrorLevel;
30 import org.openecomp.sdc.heat.datatypes.DefinedHeatParameterTypes;
31 import org.openecomp.sdc.heat.datatypes.model.Environment;
32 import org.openecomp.sdc.heat.datatypes.model.HeatOrchestrationTemplate;
33 import org.openecomp.sdc.heat.datatypes.model.Output;
34 import org.openecomp.sdc.heat.datatypes.model.Parameter;
35 import org.openecomp.sdc.heat.datatypes.model.Resource;
36 import org.openecomp.sdc.heat.datatypes.model.ResourceReferenceFunctions;
37 import org.openecomp.sdc.heat.services.HeatStructureUtil;
38 import org.openecomp.sdc.logging.api.Logger;
39 import org.openecomp.sdc.logging.api.LoggerFactory;
40 import org.openecomp.sdc.logging.context.impl.MdcDataDebugMessage;
41 import org.openecomp.sdc.logging.context.impl.MdcDataErrorMessage;
42 import org.openecomp.sdc.logging.types.LoggerConstants;
43 import org.openecomp.sdc.logging.types.LoggerErrorCode;
44 import org.openecomp.sdc.logging.types.LoggerErrorDescription;
45 import org.openecomp.sdc.logging.types.LoggerTragetServiceName;
46 import org.openecomp.sdc.validation.impl.validators.HeatValidator;
47
48 import java.io.InputStream;
49 import java.util.Collection;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.Objects;
53 import java.util.Optional;
54 import java.util.Set;
55
56
57 public class HeatValidationService {
58
59   private static final Logger logger = (Logger) LoggerFactory.getLogger(HeatValidator.class);
60   private static MdcDataDebugMessage mdcDataDebugMessage = new MdcDataDebugMessage();
61
62   /**
63    * Check artifacts existence.
64    *
65    * @param fileName       the file name
66    * @param artifactsNames the artifacts names
67    * @param globalContext  the global context
68    */
69   public static void checkArtifactsExistence(String fileName, Set<String> artifactsNames,
70                                              GlobalValidationContext globalContext) {
71
72
73     mdcDataDebugMessage.debugEntryMessage("file", fileName);
74     artifactsNames
75         .stream()
76         .filter(artifactName -> !globalContext.getFileContextMap().containsKey(artifactName))
77         .forEach(artifactName -> {
78           globalContext.addMessage(fileName,
79               ErrorLevel.ERROR, ErrorMessagesFormatBuilder
80                   .getErrorWithParameters(Messages.MISSING_ARTIFACT.getErrorMessage(),
81                       artifactName), LoggerTragetServiceName.VALIDATE_ARTIFACTS_EXISTENCE,
82               LoggerErrorDescription.MISSING_FILE);
83         });
84
85     mdcDataDebugMessage.debugExitMessage("file", fileName);
86   }
87
88   /**
89    * Check resource existence from resources map.
90    *
91    * @param fileName         the file name
92    * @param resourcesNames   the resources names
93    * @param valuesToSearchIn the values to search in
94    * @param globalContext    the global context
95    */
96   public static void checkResourceExistenceFromResourcesMap(String fileName,
97                                                             Set<String> resourcesNames,
98                                                             Collection<?> valuesToSearchIn,
99                                                             GlobalValidationContext globalContext) {
100
101
102     mdcDataDebugMessage.debugEntryMessage("file", fileName);
103
104     if (CollectionUtils.isNotEmpty(valuesToSearchIn)) {
105       for (Object value : valuesToSearchIn) {
106         if (value instanceof Resource) {
107           Resource resource = (Resource) value;
108
109           Collection<Object> resourcePropertiesValues =
110               resource.getProperties() == null ? null : resource.getProperties().values();
111           if (CollectionUtils.isNotEmpty(resourcePropertiesValues)) {
112             for (Object propertyValue : resourcePropertiesValues) {
113               handleReferencedResources(fileName, propertyValue, resourcesNames, globalContext);
114             }
115           }
116         } else if (value instanceof Output) {
117           Output output = (Output) value;
118           Object outputsValue = output.getValue();
119           handleReferencedResources(fileName, outputsValue, resourcesNames, globalContext);
120         }
121       }
122     }
123
124     mdcDataDebugMessage.debugExitMessage("file", fileName);
125   }
126
127   private static void handleReferencedResources(String fileName, Object valueToSearchReferencesIn,
128                                                 Set<String> resourcesNames,
129                                                 GlobalValidationContext globalContext) {
130
131
132     mdcDataDebugMessage.debugEntryMessage("file", fileName);
133
134     Set<String> referencedResourcesNames = HeatStructureUtil
135         .getReferencedValuesByFunctionName(fileName,
136             ResourceReferenceFunctions.GET_RESOURCE.getFunction(), valueToSearchReferencesIn,
137             globalContext);
138     if (CollectionUtils.isNotEmpty(referencedResourcesNames)) {
139       checkIfResourceReferenceExist(fileName, resourcesNames, referencedResourcesNames,
140           globalContext);
141     }
142
143     mdcDataDebugMessage.debugExitMessage("file", fileName);
144   }
145
146   private static void checkIfResourceReferenceExist(String fileName,
147                                                     Set<String> referencedResourcesNames,
148                                                     Set<String> referencedResources,
149                                                     GlobalValidationContext globalContext) {
150
151
152     mdcDataDebugMessage.debugEntryMessage("file", fileName);
153
154     referencedResources.stream()
155         .filter(referencedResource -> !referencedResourcesNames.contains(referencedResource))
156         .forEach(referencedResource -> {
157           globalContext.addMessage(fileName,
158               ErrorLevel.ERROR, ErrorMessagesFormatBuilder
159                   .getErrorWithParameters(Messages.REFERENCED_RESOURCE_NOT_FOUND.getErrorMessage(),
160                       referencedResource),
161               LoggerTragetServiceName.VALIDATE_RESOURCE_REFERENCE_EXISTENCE,
162               LoggerErrorDescription.RESOURCE_NOT_FOUND);
163         });
164
165     mdcDataDebugMessage.debugExitMessage("file", fileName);
166   }
167
168   /**
169    * Draw files loop string.
170    *
171    * @param filesInPath the files in path
172    * @return the string
173    */
174   public static String drawFilesLoop(List<String> filesInPath) {
175     StringBuilder stringBuilder = new StringBuilder();
176     stringBuilder.append("[");
177     int pathSize = filesInPath.size();
178
179     for (int i = 0; i < pathSize; i++) {
180       stringBuilder.append(filesInPath.get(i));
181       if (i != pathSize - 1) {
182         stringBuilder.append(" -- ");
183       }
184     }
185     if (!filesInPath.get(0).equals(filesInPath.get(pathSize - 1))) {
186       stringBuilder.append(" -- ");
187       stringBuilder.append(filesInPath.get(0));
188     }
189     stringBuilder.append("]");
190
191     return stringBuilder.toString();
192   }
193
194
195   /**
196    * Check nested parameters.
197    *
198    * @param parentFileName         the calling nested file name
199    * @param nestedFileName         the nested file name
200    * @param resourceName           the resource name
201    * @param globalContext          the global context
202    * @param resourceFileProperties the resource file properties
203    */
204   public static void checkNestedParameters(String parentFileName, String nestedFileName,
205                                            String resourceName, Resource resource,
206                                            Set<String> resourceFileProperties,
207                                            Optional<String> indexVarValue,
208                                            GlobalValidationContext globalContext) {
209
210
211     mdcDataDebugMessage.debugEntryMessage("file", parentFileName);
212
213     HeatOrchestrationTemplate parentHeatOrchestrationTemplate;
214     HeatOrchestrationTemplate nestedHeatOrchestrationTemplate;
215     try {
216       Optional<InputStream> fileContent = globalContext.getFileContent(nestedFileName);
217       if (fileContent.isPresent()) {
218         nestedHeatOrchestrationTemplate =
219             new YamlUtil().yamlToObject(fileContent.get(), HeatOrchestrationTemplate.class);
220       } else {
221         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_API,
222             LoggerTragetServiceName.VALIDATE_PROPERTIES_MATCH_NESTED_PARAMETERS,
223             ErrorLevel.ERROR.name(), LoggerErrorCode.DATA_ERROR.getErrorCode(),
224             LoggerErrorDescription.EMPTY_FILE);
225         throw new Exception("The file '" + nestedFileName + "' has no content");
226       }
227     } catch (Exception exception) {
228       mdcDataDebugMessage.debugExitMessage("file", parentFileName);
229       return;
230     }
231
232     try {
233       Optional<InputStream> fileContent = globalContext.getFileContent(parentFileName);
234       if (fileContent.isPresent()) {
235         parentHeatOrchestrationTemplate =
236             new YamlUtil().yamlToObject(fileContent.get(), HeatOrchestrationTemplate.class);
237       } else {
238         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_API,
239             LoggerTragetServiceName.VALIDATE_PROPERTIES_MATCH_NESTED_PARAMETERS,
240             ErrorLevel.ERROR.name(), LoggerErrorCode.DATA_ERROR.getErrorCode(),
241             LoggerErrorDescription.EMPTY_FILE);
242         throw new Exception("The file '" + parentFileName + "' has no content");
243       }
244     } catch (Exception exception) {
245       mdcDataDebugMessage.debugExitMessage("file", parentFileName);
246       return;
247     }
248     Map<String, Parameter> parentParameters = parentHeatOrchestrationTemplate.getParameters();
249     Map<String, Parameter> nestedParameters = nestedHeatOrchestrationTemplate.getParameters();
250     Set<String> nestedParametersNames =
251         nestedParameters == null ? null : nestedHeatOrchestrationTemplate.getParameters().keySet();
252
253     checkNoMissingParameterInNested(parentFileName, nestedFileName, resourceName,
254         resourceFileProperties, nestedParametersNames, globalContext);
255     checkNestedInputValuesAlignWithType(parentFileName, nestedFileName, parentParameters,
256         nestedParameters, resourceName, resource, indexVarValue, globalContext);
257
258     mdcDataDebugMessage.debugExitMessage("file", parentFileName);
259
260   }
261
262   private static void checkNoMissingParameterInNested(String parentFileName, String nestedFileName,
263                                                       String resourceName,
264                                                       Set<String> resourceFileProperties,
265                                                       Set<String> nestedParametersNames,
266                                                       GlobalValidationContext globalContext) {
267
268     mdcDataDebugMessage.debugEntryMessage("nested file", nestedFileName);
269
270     if (CollectionUtils.isNotEmpty(nestedParametersNames)) {
271       resourceFileProperties
272           .stream()
273           .filter(propertyName -> !nestedParametersNames.contains(propertyName))
274           .forEach(propertyName -> globalContext
275               .addMessage(parentFileName, ErrorLevel.ERROR, ErrorMessagesFormatBuilder
276                       .getErrorWithParameters(Messages
277                               .MISSING_PARAMETER_IN_NESTED.getErrorMessage(),
278                           nestedFileName, resourceName, propertyName),
279                   LoggerTragetServiceName.VALIDATE_PROPERTIES_MATCH_NESTED_PARAMETERS,
280                   LoggerErrorDescription.MISSING_PARAMETER_IN_NESTED));
281     }
282
283     mdcDataDebugMessage.debugExitMessage("nested file", nestedFileName);
284   }
285
286
287   private static void checkNestedInputValuesAlignWithType(String parentFileName,
288                                                           String nestedFileName,
289                                                           Map<String, Parameter> parentParameters,
290                                                           Map<String, Parameter> nestedParameters,
291                                                           String resourceName, Resource resource,
292                                                           Optional<String> indexVarValue,
293                                                           GlobalValidationContext globalContext) {
294
295
296     mdcDataDebugMessage.debugEntryMessage("nested file", nestedFileName);
297
298     Map<String, Object> properties = resource.getProperties();
299     for (Map.Entry<String, Object> propertyEntry : properties.entrySet()) {
300       String parameterName = propertyEntry.getKey();
301       Object parameterInputValue = propertyEntry.getValue();
302
303       if (Objects.nonNull(parameterInputValue)) {
304         if (parameterInputValue instanceof String) {
305           if (indexVarValue.isPresent() && indexVarValue.get().equals(parameterInputValue)) {
306             parameterInputValue = 3; //indexVarValue is actually number value in runtime
307           }
308           validateStaticValueForNestedInputParameter(parentFileName, nestedFileName, resourceName,
309               parameterName, parameterInputValue, nestedParameters.get(parameterName),
310               globalContext);
311         }
312       }
313     }
314
315     mdcDataDebugMessage.debugExitMessage("nested file", nestedFileName);
316   }
317
318   private static void validateStaticValueForNestedInputParameter(String parentFileName,
319                                                                  String nestedFileName,
320                                                                  String resourceName,
321                                                                  String parameterName,
322                                                                  Object staticValue,
323                                                                  Parameter parameterInNested,
324                                                                  GlobalValidationContext
325                                                                      globalContext) {
326
327
328     mdcDataDebugMessage.debugEntryMessage("nested file", nestedFileName);
329
330     if (parameterInNested == null) {
331       return;
332     }
333     if (!DefinedHeatParameterTypes
334         .isValueIsFromGivenType(staticValue, parameterInNested.getType())) {
335       globalContext.addMessage(parentFileName, ErrorLevel.WARNING, ErrorMessagesFormatBuilder
336               .getErrorWithParameters(Messages
337                       .WRONG_VALUE_TYPE_ASSIGNED_NESTED_INPUT.getErrorMessage(),
338                   resourceName, parameterName, nestedFileName),
339           LoggerTragetServiceName.VALIDATE_PROPERTIES_MATCH_NESTED_PARAMETERS,
340           LoggerErrorDescription.WRONG_VALUE_ASSIGNED_NESTED_PARAMETER);
341     }
342
343     mdcDataDebugMessage.debugExitMessage("nested file", nestedFileName);
344   }
345
346
347   /**
348    * Is nested loop exist in file boolean.
349    *
350    * @param callingFileName the calling file name
351    * @param nestedFileName  the nested file name
352    * @param filesInLoop     the files in loop
353    * @param globalContext   the global context
354    * @return the boolean
355    */
356   public static boolean isNestedLoopExistInFile(String callingFileName, String nestedFileName,
357                                                 List<String> filesInLoop,
358                                                 GlobalValidationContext globalContext) {
359
360
361     mdcDataDebugMessage.debugEntryMessage("file", callingFileName);
362
363     HeatOrchestrationTemplate nestedHeatOrchestrationTemplate;
364     try {
365       Optional<InputStream> fileContent = globalContext.getFileContent(nestedFileName);
366       if (fileContent.isPresent()) {
367         nestedHeatOrchestrationTemplate =
368             new YamlUtil().yamlToObject(fileContent.get(), HeatOrchestrationTemplate.class);
369       } else {
370         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_API,
371             LoggerTragetServiceName.VALIDATE_NESTING_LOOPS, ErrorLevel.ERROR.name(),
372             LoggerErrorCode.DATA_ERROR.getErrorCode(), LoggerErrorDescription.EMPTY_FILE);
373         throw new Exception("The file '" + nestedFileName + "' has no content");
374       }
375
376     } catch (Exception exception) {
377       logger.warn("HEAT Validator will not be executed on file " + nestedFileName
378           + " due to illegal HEAT format");
379
380       mdcDataDebugMessage.debugExitMessage("file", callingFileName);
381       return false;
382     }
383     filesInLoop.add(nestedFileName);
384     Collection<Resource> nestedResources =
385         nestedHeatOrchestrationTemplate.getResources() == null ? null
386             : nestedHeatOrchestrationTemplate.getResources().values();
387     if (CollectionUtils.isNotEmpty(nestedResources)) {
388       for (Resource resource : nestedResources) {
389         String resourceType = resource.getType();
390
391         if (Objects.nonNull(resourceType) && isNestedResource(resourceType)) {
392           mdcDataDebugMessage.debugExitMessage("file", callingFileName);
393           return resourceType.equals(callingFileName) || !filesInLoop.contains(resourceType)
394               && isNestedLoopExistInFile(callingFileName, resourceType, filesInLoop, globalContext);
395         }
396       }
397     }
398
399     mdcDataDebugMessage.debugExitMessage("file", callingFileName);
400     return false;
401   }
402
403
404   /**
405    * Loop over output map and validate get attr from nested.
406    *
407    * @param fileName                  the file name
408    * @param outputMap                 the output map
409    * @param heatOrchestrationTemplate the heat orchestration template
410    * @param globalContext             the global context
411    */
412   @SuppressWarnings("unchecked")
413   public static void loopOverOutputMapAndValidateGetAttrFromNested(String fileName,
414                                                                    Map<String, Output> outputMap,
415                                                                    HeatOrchestrationTemplate
416                                                                        heatOrchestrationTemplate,
417                                                                    GlobalValidationContext
418                                                                        globalContext) {
419     for (Output output : outputMap.values()) {
420       Object outputValue = output.getValue();
421       if (outputValue != null && outputValue instanceof Map) {
422         Map<String, Object> outputValueMap = (Map<String, Object>) outputValue;
423         List<String> getAttrValue =
424             (List<String>) outputValueMap.get(ResourceReferenceFunctions.GET_ATTR.getFunction());
425         if (!CollectionUtils.isEmpty(getAttrValue)) {
426           String resourceName = getAttrValue.get(0);
427           Object attNameObject = getAttrValue.get(1);
428           if (!(attNameObject instanceof String)) {
429             return;
430           }
431           String attName = getAttrValue.get(1);
432           String resourceType =
433               getResourceTypeFromResourcesMap(resourceName, heatOrchestrationTemplate);
434
435           if (Objects.nonNull(resourceType)
436               && isNestedResource(resourceType)) {
437             handleGetAttrNestedResource(fileName, globalContext, resourceName, attName,
438                 resourceType);
439             }
440           }
441         }
442       }
443     }
444
445   private static void handleGetAttrNestedResource(String fileName,
446                                                      GlobalValidationContext globalContext,
447                                                      String resourceName, String attName,
448                                                      String resourceType) {
449     Map<String, Output> nestedOutputMap;
450     HeatOrchestrationTemplate nestedHeatOrchestrationTemplate;
451     try {
452       Optional<InputStream> fileContent = globalContext.getFileContent(resourceType);
453       if (fileContent.isPresent()) {
454         nestedHeatOrchestrationTemplate =
455             new YamlUtil().yamlToObject(fileContent.get(), HeatOrchestrationTemplate.class);
456       } else {
457         MdcDataErrorMessage
458             .createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_API,
459                 LoggerTragetServiceName.VALIDATE_GET_ATTR_FROM_NESTED,
460                 ErrorLevel.ERROR.name(), LoggerErrorCode.DATA_ERROR.getErrorCode(),
461                 LoggerErrorDescription.EMPTY_FILE);
462         throw new Exception("The file '" + resourceType + "' has no content");
463       }
464     } catch (Exception exception) {
465       return;
466     }
467     nestedOutputMap = nestedHeatOrchestrationTemplate.getOutputs();
468
469     if (MapUtils.isEmpty(nestedOutputMap) || !nestedOutputMap.containsKey(attName)) {
470       globalContext.addMessage(fileName, ErrorLevel.ERROR, ErrorMessagesFormatBuilder
471               .getErrorWithParameters(Messages.GET_ATTR_NOT_FOUND.getErrorMessage(),
472                   attName, resourceName),
473           LoggerTragetServiceName.VALIDATE_GET_ATTR_FROM_NESTED,
474           LoggerErrorDescription.GET_ATTR_NOT_FOUND);
475     }
476   }
477
478   public static boolean isNestedResource(String resourceType) {
479     return resourceType.contains(".yaml") || resourceType.contains(".yml");
480   }
481
482   private static String getResourceTypeFromResourcesMap(String resourceName,
483                                                         HeatOrchestrationTemplate
484                                                             heatOrchestrationTemplate) {
485     return heatOrchestrationTemplate.getResources().get(resourceName).getType();
486   }
487
488   /**
489    * Validate env content environment.
490    *
491    * @param fileName      the file name
492    * @param envFileName   the env file name
493    * @param globalContext the global context
494    * @return the environment
495    */
496   public static Environment validateEnvContent(String fileName, String envFileName,
497                                                GlobalValidationContext globalContext) {
498
499
500     mdcDataDebugMessage.debugEntryMessage("env file", envFileName);
501
502     Environment envContent = null;
503     try {
504       Optional<InputStream> fileContent = globalContext.getFileContent(envFileName);
505       if (fileContent.isPresent()) {
506         envContent = new YamlUtil().yamlToObject(fileContent.get(), Environment.class);
507       } else {
508         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_API,
509             LoggerTragetServiceName.VALIDATE_ENV_FILE, ErrorLevel.ERROR.name(),
510             LoggerErrorCode.DATA_ERROR.getErrorCode(), LoggerErrorDescription.EMPTY_FILE);
511         throw new Exception("The file '" + envFileName + "' has no content");
512       }
513     } catch (Exception exception) {
514       mdcDataDebugMessage.debugExitMessage("env file", envFileName);
515       return null;
516     }
517     return envContent;
518   }
519
520
521   public static String getResourceGroupResourceName(String resourceCallingToResourceGroup) {
522     return "OS::Heat::ResourceGroup in " + resourceCallingToResourceGroup;
523   }
524
525 }