39362c7e10e979d15fd689e9e0812e9fff2be43a
[sdc.git] /
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       logger.debug("",exception);
229       mdcDataDebugMessage.debugExitMessage("file", parentFileName);
230       return;
231     }
232
233     try {
234       Optional<InputStream> fileContent = globalContext.getFileContent(parentFileName);
235       if (fileContent.isPresent()) {
236         parentHeatOrchestrationTemplate =
237             new YamlUtil().yamlToObject(fileContent.get(), HeatOrchestrationTemplate.class);
238       } else {
239         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_API,
240             LoggerTragetServiceName.VALIDATE_PROPERTIES_MATCH_NESTED_PARAMETERS,
241             ErrorLevel.ERROR.name(), LoggerErrorCode.DATA_ERROR.getErrorCode(),
242             LoggerErrorDescription.EMPTY_FILE);
243         throw new Exception("The file '" + parentFileName + "' has no content");
244       }
245     } catch (Exception exception) {
246       logger.debug("",exception);
247       mdcDataDebugMessage.debugExitMessage("file", parentFileName);
248       return;
249     }
250     Map<String, Parameter> parentParameters = parentHeatOrchestrationTemplate.getParameters();
251     Map<String, Parameter> nestedParameters = nestedHeatOrchestrationTemplate.getParameters();
252     Set<String> nestedParametersNames =
253         nestedParameters == null ? null : nestedHeatOrchestrationTemplate.getParameters().keySet();
254
255     checkNoMissingParameterInNested(parentFileName, nestedFileName, resourceName,
256         resourceFileProperties, nestedParametersNames, globalContext);
257     checkNestedInputValuesAlignWithType(parentFileName, nestedFileName, parentParameters,
258         nestedParameters, resourceName, resource, indexVarValue, globalContext);
259
260     mdcDataDebugMessage.debugExitMessage("file", parentFileName);
261
262   }
263
264   private static void checkNoMissingParameterInNested(String parentFileName, String nestedFileName,
265                                                       String resourceName,
266                                                       Set<String> resourceFileProperties,
267                                                       Set<String> nestedParametersNames,
268                                                       GlobalValidationContext globalContext) {
269
270     mdcDataDebugMessage.debugEntryMessage("nested file", nestedFileName);
271
272     if (CollectionUtils.isNotEmpty(nestedParametersNames)) {
273       resourceFileProperties
274           .stream()
275           .filter(propertyName -> !nestedParametersNames.contains(propertyName))
276           .forEach(propertyName -> globalContext
277               .addMessage(parentFileName, ErrorLevel.ERROR, ErrorMessagesFormatBuilder
278                       .getErrorWithParameters(Messages
279                               .MISSING_PARAMETER_IN_NESTED.getErrorMessage(),
280                           nestedFileName, resourceName, propertyName),
281                   LoggerTragetServiceName.VALIDATE_PROPERTIES_MATCH_NESTED_PARAMETERS,
282                   LoggerErrorDescription.MISSING_PARAMETER_IN_NESTED));
283     }
284
285     mdcDataDebugMessage.debugExitMessage("nested file", nestedFileName);
286   }
287
288
289   private static void checkNestedInputValuesAlignWithType(String parentFileName,
290                                                           String nestedFileName,
291                                                           Map<String, Parameter> parentParameters,
292                                                           Map<String, Parameter> nestedParameters,
293                                                           String resourceName, Resource resource,
294                                                           Optional<String> indexVarValue,
295                                                           GlobalValidationContext globalContext) {
296
297
298     mdcDataDebugMessage.debugEntryMessage("nested file", nestedFileName);
299
300     Map<String, Object> properties = resource.getProperties();
301     for (Map.Entry<String, Object> propertyEntry : properties.entrySet()) {
302       String parameterName = propertyEntry.getKey();
303       Object parameterInputValue = propertyEntry.getValue();
304
305       if (Objects.nonNull(parameterInputValue)) {
306         if (parameterInputValue instanceof String) {
307           if (indexVarValue.isPresent() && indexVarValue.get().equals(parameterInputValue)) {
308             parameterInputValue = 3; //indexVarValue is actually number value in runtime
309           }
310           validateStaticValueForNestedInputParameter(parentFileName, nestedFileName, resourceName,
311               parameterName, parameterInputValue, nestedParameters.get(parameterName),
312               globalContext);
313         }
314       }
315     }
316
317     mdcDataDebugMessage.debugExitMessage("nested file", nestedFileName);
318   }
319
320   private static void validateStaticValueForNestedInputParameter(String parentFileName,
321                                                                  String nestedFileName,
322                                                                  String resourceName,
323                                                                  String parameterName,
324                                                                  Object staticValue,
325                                                                  Parameter parameterInNested,
326                                                                  GlobalValidationContext
327                                                                      globalContext) {
328
329
330     mdcDataDebugMessage.debugEntryMessage("nested file", nestedFileName);
331
332     if (parameterInNested == null) {
333       return;
334     }
335     if (!DefinedHeatParameterTypes
336         .isValueIsFromGivenType(staticValue, parameterInNested.getType())) {
337       globalContext.addMessage(parentFileName, ErrorLevel.WARNING, ErrorMessagesFormatBuilder
338               .getErrorWithParameters(Messages
339                       .WRONG_VALUE_TYPE_ASSIGNED_NESTED_INPUT.getErrorMessage(),
340                   resourceName, parameterName, nestedFileName),
341           LoggerTragetServiceName.VALIDATE_PROPERTIES_MATCH_NESTED_PARAMETERS,
342           LoggerErrorDescription.WRONG_VALUE_ASSIGNED_NESTED_PARAMETER);
343     }
344
345     mdcDataDebugMessage.debugExitMessage("nested file", nestedFileName);
346   }
347
348
349   /**
350    * Is nested loop exist in file boolean.
351    *
352    * @param callingFileName the calling file name
353    * @param nestedFileName  the nested file name
354    * @param filesInLoop     the files in loop
355    * @param globalContext   the global context
356    * @return the boolean
357    */
358   public static boolean isNestedLoopExistInFile(String callingFileName, String nestedFileName,
359                                                 List<String> filesInLoop,
360                                                 GlobalValidationContext globalContext) {
361
362
363     mdcDataDebugMessage.debugEntryMessage("file", callingFileName);
364
365     HeatOrchestrationTemplate nestedHeatOrchestrationTemplate;
366     try {
367       Optional<InputStream> fileContent = globalContext.getFileContent(nestedFileName);
368       if (fileContent.isPresent()) {
369         nestedHeatOrchestrationTemplate =
370             new YamlUtil().yamlToObject(fileContent.get(), HeatOrchestrationTemplate.class);
371       } else {
372         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_API,
373             LoggerTragetServiceName.VALIDATE_NESTING_LOOPS, ErrorLevel.ERROR.name(),
374             LoggerErrorCode.DATA_ERROR.getErrorCode(), LoggerErrorDescription.EMPTY_FILE);
375         throw new Exception("The file '" + nestedFileName + "' has no content");
376       }
377
378     } catch (Exception exception) {
379       logger.debug("",exception);
380       logger.warn("HEAT Validator will not be executed on file " + nestedFileName
381           + " due to illegal HEAT format");
382
383       mdcDataDebugMessage.debugExitMessage("file", callingFileName);
384       return false;
385     }
386     filesInLoop.add(nestedFileName);
387     Collection<Resource> nestedResources =
388         nestedHeatOrchestrationTemplate.getResources() == null ? null
389             : nestedHeatOrchestrationTemplate.getResources().values();
390     if (CollectionUtils.isNotEmpty(nestedResources)) {
391       for (Resource resource : nestedResources) {
392         String resourceType = resource.getType();
393
394         if (Objects.nonNull(resourceType) && isNestedResource(resourceType)) {
395           mdcDataDebugMessage.debugExitMessage("file", callingFileName);
396           return resourceType.equals(callingFileName) || !filesInLoop.contains(resourceType)
397               && isNestedLoopExistInFile(callingFileName, resourceType, filesInLoop, globalContext);
398         }
399       }
400     }
401
402     mdcDataDebugMessage.debugExitMessage("file", callingFileName);
403     return false;
404   }
405
406
407   /**
408    * Loop over output map and validate get attr from nested.
409    *
410    * @param fileName                  the file name
411    * @param outputMap                 the output map
412    * @param heatOrchestrationTemplate the heat orchestration template
413    * @param globalContext             the global context
414    */
415   @SuppressWarnings("unchecked")
416   public static void loopOverOutputMapAndValidateGetAttrFromNested(String fileName,
417                                                                    Map<String, Output> outputMap,
418                                                                    HeatOrchestrationTemplate
419                                                                        heatOrchestrationTemplate,
420                                                                    GlobalValidationContext
421                                                                        globalContext) {
422     for (Output output : outputMap.values()) {
423       Object outputValue = output.getValue();
424       if (outputValue != null && outputValue instanceof Map) {
425         Map<String, Object> outputValueMap = (Map<String, Object>) outputValue;
426         List<String> getAttrValue =
427             (List<String>) outputValueMap.get(ResourceReferenceFunctions.GET_ATTR.getFunction());
428         if (!CollectionUtils.isEmpty(getAttrValue)) {
429           String resourceName = getAttrValue.get(0);
430           Object attNameObject = getAttrValue.get(1);
431           if (!(attNameObject instanceof String)) {
432             return;
433           }
434           String attName = getAttrValue.get(1);
435           String resourceType =
436               getResourceTypeFromResourcesMap(resourceName, heatOrchestrationTemplate);
437
438           if (Objects.nonNull(resourceType)
439               && isNestedResource(resourceType)) {
440             handleGetAttrNestedResource(fileName, globalContext, resourceName, attName,
441                 resourceType);
442             }
443           }
444         }
445       }
446     }
447
448   private static void handleGetAttrNestedResource(String fileName,
449                                                      GlobalValidationContext globalContext,
450                                                      String resourceName, String attName,
451                                                      String resourceType) {
452     Map<String, Output> nestedOutputMap;
453     HeatOrchestrationTemplate nestedHeatOrchestrationTemplate;
454     try {
455       Optional<InputStream> fileContent = globalContext.getFileContent(resourceType);
456       if (fileContent.isPresent()) {
457         nestedHeatOrchestrationTemplate =
458             new YamlUtil().yamlToObject(fileContent.get(), HeatOrchestrationTemplate.class);
459       } else {
460         MdcDataErrorMessage
461             .createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_API,
462                 LoggerTragetServiceName.VALIDATE_GET_ATTR_FROM_NESTED,
463                 ErrorLevel.ERROR.name(), LoggerErrorCode.DATA_ERROR.getErrorCode(),
464                 LoggerErrorDescription.EMPTY_FILE);
465         throw new Exception("The file '" + resourceType + "' has no content");
466       }
467     } catch (Exception exception) {
468       logger.debug("",exception);
469       return;
470     }
471     nestedOutputMap = nestedHeatOrchestrationTemplate.getOutputs();
472
473     if (MapUtils.isEmpty(nestedOutputMap) || !nestedOutputMap.containsKey(attName)) {
474       globalContext.addMessage(fileName, ErrorLevel.ERROR, ErrorMessagesFormatBuilder
475               .getErrorWithParameters(Messages.GET_ATTR_NOT_FOUND.getErrorMessage(),
476                   attName, resourceName),
477           LoggerTragetServiceName.VALIDATE_GET_ATTR_FROM_NESTED,
478           LoggerErrorDescription.GET_ATTR_NOT_FOUND);
479     }
480   }
481
482   public static boolean isNestedResource(String resourceType) {
483     return resourceType.contains(".yaml") || resourceType.contains(".yml");
484   }
485
486   private static String getResourceTypeFromResourcesMap(String resourceName,
487                                                         HeatOrchestrationTemplate
488                                                             heatOrchestrationTemplate) {
489     return heatOrchestrationTemplate.getResources().get(resourceName).getType();
490   }
491
492   /**
493    * Validate env content environment.
494    *
495    * @param fileName      the file name
496    * @param envFileName   the env file name
497    * @param globalContext the global context
498    * @return the environment
499    */
500   public static Environment validateEnvContent(String fileName, String envFileName,
501                                                GlobalValidationContext globalContext) {
502
503
504     mdcDataDebugMessage.debugEntryMessage("env file", envFileName);
505
506     Environment envContent = null;
507     try {
508       Optional<InputStream> fileContent = globalContext.getFileContent(envFileName);
509       if (fileContent.isPresent()) {
510         envContent = new YamlUtil().yamlToObject(fileContent.get(), Environment.class);
511       } else {
512         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_API,
513             LoggerTragetServiceName.VALIDATE_ENV_FILE, ErrorLevel.ERROR.name(),
514             LoggerErrorCode.DATA_ERROR.getErrorCode(), LoggerErrorDescription.EMPTY_FILE);
515         throw new Exception("The file '" + envFileName + "' has no content");
516       }
517     } catch (Exception exception) {
518       logger.debug("",exception);
519       mdcDataDebugMessage.debugExitMessage("env file", envFileName);
520       return null;
521     }
522     return envContent;
523   }
524
525
526   public static String getResourceGroupResourceName(String resourceCallingToResourceGroup) {
527     return "OS::Heat::ResourceGroup in " + resourceCallingToResourceGroup;
528   }
529
530 }