Add collaboration feature
[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  * Copyright © 2016-2017 European Support Limited
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.openecomp.sdc.validation.impl.util;
18
19 import org.apache.commons.collections4.CollectionUtils;
20 import org.openecomp.core.validation.errors.ErrorMessagesFormatBuilder;
21 import org.openecomp.core.validation.types.GlobalValidationContext;
22 import org.openecomp.sdc.common.errors.Messages;
23 import org.openecomp.sdc.datatypes.error.ErrorLevel;
24 import org.openecomp.sdc.heat.datatypes.DefinedHeatParameterTypes;
25 import org.openecomp.sdc.heat.datatypes.model.Environment;
26 import org.openecomp.sdc.heat.datatypes.model.HeatOrchestrationTemplate;
27 import org.openecomp.sdc.heat.datatypes.model.Parameter;
28 import org.openecomp.sdc.heat.datatypes.model.Resource;
29 import org.openecomp.sdc.logging.api.Logger;
30 import org.openecomp.sdc.logging.api.LoggerFactory;
31 import org.openecomp.sdc.logging.context.impl.MdcDataDebugMessage;
32 import org.openecomp.sdc.logging.context.impl.MdcDataErrorMessage;
33 import org.openecomp.sdc.logging.types.LoggerConstants;
34 import org.openecomp.sdc.logging.types.LoggerErrorCode;
35 import org.openecomp.sdc.logging.types.LoggerErrorDescription;
36 import org.openecomp.sdc.logging.types.LoggerTragetServiceName;
37 import org.openecomp.sdc.tosca.services.YamlUtil;
38 import org.openecomp.sdc.validation.impl.validators.HeatValidator;
39
40 import java.io.InputStream;
41 import java.util.Collection;
42 import java.util.HashMap;
43 import java.util.HashSet;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Objects;
47 import java.util.Optional;
48 import java.util.Set;
49
50
51 public class HeatValidationService {
52
53   private static final Logger LOGGER = LoggerFactory.getLogger(HeatValidator.class);
54   private static final String NESTED_FILE = "nested file";
55   private static final String NO_CONTENT_IN_FILE_MSG = "The file ' %s ' has no content";
56   private static final MdcDataDebugMessage MDC_DATA_DEBUG_MESSAGE = new MdcDataDebugMessage();
57   private HeatValidationService(){
58
59   }
60   /**
61    * Check artifacts existence.
62    *
63    * @param fileName the file name
64    * @param artifactsNames the artifacts names
65    * @param globalContext the global context
66    */
67   public static void checkArtifactsExistence(String fileName, Set<String> artifactsNames,
68                                              GlobalValidationContext globalContext) {
69
70
71     MDC_DATA_DEBUG_MESSAGE.debugEntryMessage("file", fileName);
72     artifactsNames
73             .stream()
74             .filter(artifactName -> !globalContext.getFileContextMap().containsKey(artifactName))
75             .forEach(artifactName ->
76               globalContext.addMessage(fileName,
77                       ErrorLevel.ERROR, ErrorMessagesFormatBuilder
78                               .getErrorWithParameters(
79                                       globalContext.getMessageCode(),
80                                       Messages.MISSING_ARTIFACT.getErrorMessage(), artifactName),
81                       LoggerTragetServiceName.VALIDATE_ARTIFACTS_EXISTENCE,
82                       LoggerErrorDescription.MISSING_FILE));
83
84     MDC_DATA_DEBUG_MESSAGE.debugExitMessage("file", fileName);
85   }
86
87   /**
88    * Draw files loop string.
89    *
90    * @param filesInPath the files in path
91    * @return the string
92    */
93   public static String drawFilesLoop(List<String> filesInPath) {
94     StringBuilder stringBuilder = new StringBuilder();
95     stringBuilder.append("[");
96     int pathSize = filesInPath.size();
97
98     for (int i = 0; i < pathSize; i++) {
99       stringBuilder.append(filesInPath.get(i));
100       if (i != pathSize - 1) {
101         stringBuilder.append(" -- ");
102       }
103     }
104     if (!filesInPath.get(0).equals(filesInPath.get(pathSize - 1))) {
105       stringBuilder.append(" -- ");
106       stringBuilder.append(filesInPath.get(0));
107     }
108     stringBuilder.append("]");
109
110     return stringBuilder.toString();
111   }
112
113   /**
114    * Check nested parameters.
115    *
116    * @param parentFileName the calling nested file name
117    * @param nestedFileName the nested file name
118    * @param globalContext the global context
119    * @param parentParameters parent parameters.
120    * @param nestedParameters nested parameters.
121    * @param nestedParametersNames nested parameter names.
122    */
123   public static void checkNestedParameters(String parentFileName, String nestedFileName,
124                                            GlobalValidationContext globalContext,
125                                            Map<String, Parameter> parentParameters,
126                                            Map<String, Parameter> nestedParameters,
127                                            Set<String> nestedParametersNames) {
128
129     MDC_DATA_DEBUG_MESSAGE.debugEntryMessage("file", parentFileName);
130
131     HeatOrchestrationTemplate parentHeatOrchestrationTemplate;
132     HeatOrchestrationTemplate nestedHeatOrchestrationTemplate;
133
134     try {
135       nestedHeatOrchestrationTemplate = getHeatOrchestrationTemplate(nestedFileName, globalContext);
136       parentHeatOrchestrationTemplate = getHeatOrchestrationTemplate(parentFileName, globalContext);
137     } catch (Exception exception) {
138       MDC_DATA_DEBUG_MESSAGE.debugExitMessage("file", parentFileName);
139       return;
140     }
141
142     parentParameters.putAll(parentHeatOrchestrationTemplate.getParameters());
143     nestedParameters.putAll(nestedHeatOrchestrationTemplate.getParameters());
144     if (!nestedParameters.isEmpty()) {
145       nestedParametersNames.addAll(nestedHeatOrchestrationTemplate.getParameters().keySet());
146     }
147
148     MDC_DATA_DEBUG_MESSAGE.debugExitMessage("file", parentFileName);
149   }
150
151   private static HeatOrchestrationTemplate getHeatOrchestrationTemplate(String fileName,
152                                                                         GlobalValidationContext globalContext)
153           throws Exception {
154
155     Optional<InputStream> fileContent = globalContext.getFileContent(fileName);
156     if (fileContent.isPresent()) {
157       return new YamlUtil().yamlToObject(fileContent.get(), HeatOrchestrationTemplate.class);
158     } else {
159       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_API,
160               LoggerTragetServiceName.VALIDATE_PROPERTIES_MATCH_NESTED_PARAMETERS,
161               ErrorLevel.ERROR.name(), LoggerErrorCode.DATA_ERROR.getErrorCode(),
162               LoggerErrorDescription.EMPTY_FILE);
163       Exception exception = new Exception(String.format(NO_CONTENT_IN_FILE_MSG, fileName));
164       LOGGER.error("Error while reading file : " + fileName , exception);
165       throw exception;
166     }
167   }
168
169   public static void checkNestedParametersNoMissingParameterInNested(String parentFileName,
170                                                                      String nestedFileName,
171                                                                      String resourceName,
172                                                                      Set<String> resourceFileProperties,
173                                                                      GlobalValidationContext globalContext) {
174     MDC_DATA_DEBUG_MESSAGE.debugEntryMessage("file", parentFileName);
175
176     Map<String, Parameter> parentParameters = new HashMap<>();
177     Map<String, Parameter> nestedParameters = new HashMap<>();
178     Set<String> nestedParametersNames = new HashSet<>();
179     checkNestedParameters(parentFileName, nestedFileName, globalContext, parentParameters,
180             nestedParameters, nestedParametersNames);
181
182     checkNoMissingParameterInNested(parentFileName, nestedFileName, resourceName,
183             resourceFileProperties, nestedParametersNames, globalContext);
184
185     MDC_DATA_DEBUG_MESSAGE.debugExitMessage("file", parentFileName);
186   }
187
188   public static void checkNestedInputValuesAlignWithType(String parentFileName,
189                                                          String nestedFileName,
190                                                          String resourceName, Resource resource,
191                                                          Optional<String> indexVarValue,
192                                                          GlobalValidationContext globalContext) {
193     MDC_DATA_DEBUG_MESSAGE.debugEntryMessage("file", parentFileName);
194
195     Map<String, Parameter> parentParameters = new HashMap<>();
196     Map<String, Parameter> nestedParameters = new HashMap<>();
197     Set<String> nestedParametersNames = new HashSet<>();
198     checkNestedParameters(parentFileName, nestedFileName, globalContext, parentParameters,
199             nestedParameters, nestedParametersNames);
200
201     checkNestedInputValuesAlignWithType(parentFileName, nestedFileName,
202             nestedParameters, resourceName, resource, indexVarValue, globalContext);
203
204     MDC_DATA_DEBUG_MESSAGE.debugExitMessage("file", parentFileName);
205   }
206
207   private static void checkNoMissingParameterInNested(String parentFileName, String nestedFileName,
208                                                       String resourceName,
209                                                       Set<String> resourceFileProperties,
210                                                       Set<String> nestedParametersNames,
211                                                       GlobalValidationContext globalContext) {
212
213     MDC_DATA_DEBUG_MESSAGE.debugEntryMessage("nested file", nestedFileName);
214
215     if (CollectionUtils.isNotEmpty(nestedParametersNames)) {
216       resourceFileProperties
217               .stream()
218               .filter(propertyName -> !nestedParametersNames.contains(propertyName))
219               .forEach(propertyName -> globalContext
220                       .addMessage(parentFileName, ErrorLevel.ERROR, ErrorMessagesFormatBuilder
221                                       .getErrorWithParameters(
222                                               globalContext.getMessageCode(),
223                                               Messages.MISSING_PARAMETER_IN_NESTED.getErrorMessage(),
224                                               nestedFileName, resourceName, propertyName),
225                               LoggerTragetServiceName.VALIDATE_PROPERTIES_MATCH_NESTED_PARAMETERS,
226                               LoggerErrorDescription.MISSING_PARAMETER_IN_NESTED));
227     }
228
229     MDC_DATA_DEBUG_MESSAGE.debugExitMessage(NESTED_FILE, nestedFileName);
230   }
231
232   private static void checkNestedInputValuesAlignWithType(String parentFileName,
233                                                           String nestedFileName,
234                                                           Map<String, Parameter> nestedParameters,
235                                                           String resourceName, Resource resource,
236                                                           Optional<String> indexVarValue,
237                                                           GlobalValidationContext globalContext) {
238
239     MDC_DATA_DEBUG_MESSAGE.debugEntryMessage(NESTED_FILE, nestedFileName);
240
241     Map<String, Object> properties = resource.getProperties();
242     for (Map.Entry<String, Object> propertyEntry : properties.entrySet()) {
243       String parameterName = propertyEntry.getKey();
244       Object parameterInputValue = propertyEntry.getValue();
245       if (parameterInputValue instanceof String) {
246         if (indexVarValue.isPresent() && indexVarValue.get().equals(parameterInputValue)) {
247           parameterInputValue = 3; //indexVarValue is actually number value in runtime
248         }
249         validateStaticValueForNestedInputParameter(parentFileName, nestedFileName, resourceName,
250                 parameterName, parameterInputValue, nestedParameters.get(parameterName),
251                 globalContext);
252       }
253     }
254
255     MDC_DATA_DEBUG_MESSAGE.debugExitMessage(NESTED_FILE, nestedFileName);
256   }
257
258   private static void validateStaticValueForNestedInputParameter(String parentFileName,
259                                                                  String nestedFileName,
260                                                                  String resourceName,
261                                                                  String parameterName,
262                                                                  Object staticValue,
263                                                                  Parameter parameterInNested,
264                                                                  GlobalValidationContext
265                                                                          globalContext) {
266
267     MDC_DATA_DEBUG_MESSAGE.debugEntryMessage(NESTED_FILE, nestedFileName);
268
269     if (parameterInNested == null) {
270       return;
271     }
272     if (!DefinedHeatParameterTypes
273             .isValueIsFromGivenType(staticValue, parameterInNested.getType())) {
274       globalContext.addMessage(parentFileName, ErrorLevel.WARNING, ErrorMessagesFormatBuilder
275                       .getErrorWithParameters(globalContext.getMessageCode(),
276                               Messages.WRONG_VALUE_TYPE_ASSIGNED_NESTED_INPUT.getErrorMessage(),
277                               resourceName, parameterName, nestedFileName),
278               LoggerTragetServiceName.VALIDATE_PROPERTIES_MATCH_NESTED_PARAMETERS,
279               LoggerErrorDescription.WRONG_VALUE_ASSIGNED_NESTED_PARAMETER);
280     }
281
282     MDC_DATA_DEBUG_MESSAGE.debugExitMessage(NESTED_FILE, nestedFileName);
283   }
284
285
286   /**
287    * Is nested loop exist in file boolean.
288    *
289    * @param callingFileName the calling file name
290    * @param nestedFileName the nested file name
291    * @param filesInLoop the files in loop
292    * @param globalContext the global context
293    * @return the boolean
294    */
295   public static boolean isNestedLoopExistInFile(String callingFileName, String nestedFileName,
296                                                 List<String> filesInLoop,
297                                                 GlobalValidationContext globalContext) {
298
299     MDC_DATA_DEBUG_MESSAGE.debugEntryMessage("file", callingFileName);
300
301     HeatOrchestrationTemplate nestedHeatOrchestrationTemplate;
302     try {
303       nestedHeatOrchestrationTemplate = getNestedHeatOrchestrationTemplate(nestedFileName,
304                                           globalContext);
305     } catch (Exception exception) {
306       LOGGER.error("Error while reading file :  " + nestedFileName, exception);
307       LOGGER.warn("HEAT Validator will not be executed on file " + nestedFileName
308               + " due to illegal HEAT format");
309
310       MDC_DATA_DEBUG_MESSAGE.debugExitMessage("file", callingFileName);
311       return false;
312     }
313     filesInLoop.add(nestedFileName);
314     Collection<Resource> nestedResources =
315             nestedHeatOrchestrationTemplate.getResources() == null ? null
316                     : nestedHeatOrchestrationTemplate.getResources().values();
317     boolean isNestedLoopExist = addNestedFilesInLoopAndCheckIfNestedLoopExist(nestedResources,
318                     callingFileName, filesInLoop, globalContext);
319
320     MDC_DATA_DEBUG_MESSAGE.debugExitMessage("file", callingFileName);
321     return isNestedLoopExist;
322   }
323   private static boolean addNestedFilesInLoopAndCheckIfNestedLoopExist(
324                 Collection<Resource> nestedResources,String callingFileName,
325                 List<String> filesInLoop,
326                 GlobalValidationContext globalContext){
327     if (CollectionUtils.isNotEmpty(nestedResources)) {
328       for (Resource resource : nestedResources) {
329         String resourceType = resource.getType();
330
331         if (Objects.nonNull(resourceType) && isNestedResource(resourceType)) {
332           MDC_DATA_DEBUG_MESSAGE.debugExitMessage("file", callingFileName);
333           return resourceType.equals(callingFileName) || !filesInLoop.contains(resourceType)
334                   && isNestedLoopExistInFile(callingFileName, resourceType, filesInLoop, globalContext);
335         }
336       }
337     }
338     return false;
339   }
340   private static HeatOrchestrationTemplate getNestedHeatOrchestrationTemplate( String nestedFileName,
341                                           GlobalValidationContext globalContext) throws Exception {
342     Optional<InputStream> fileContent = globalContext.getFileContent(nestedFileName);
343     HeatOrchestrationTemplate nestedHeatOrchestrationTemplate;
344     if (fileContent.isPresent()) {
345       nestedHeatOrchestrationTemplate =
346               new YamlUtil().yamlToObject(fileContent.get(), HeatOrchestrationTemplate.class);
347     } else {
348       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_API,
349               LoggerTragetServiceName.VALIDATE_NESTING_LOOPS, ErrorLevel.ERROR.name(),
350               LoggerErrorCode.DATA_ERROR.getErrorCode(), LoggerErrorDescription.EMPTY_FILE);
351       throw new Exception(String.format(NO_CONTENT_IN_FILE_MSG, nestedFileName));
352     }
353
354     return nestedHeatOrchestrationTemplate;
355   }
356
357   public static boolean isNestedResource(String resourceType) {
358     return resourceType.contains(".yaml") || resourceType.contains(".yml");
359   }
360
361   /**
362    * Validate env content environment.
363    *
364    * @param fileName the file name
365    * @param envFileName the env file name
366    * @param globalContext the global context
367    * @return the environment
368    */
369   public static Environment validateEnvContent(String fileName, String envFileName,
370                                                GlobalValidationContext globalContext) {
371
372     MDC_DATA_DEBUG_MESSAGE.debugEntryMessage("env file", envFileName);
373
374     Environment envContent;
375     try {
376       Optional<InputStream> fileContent = globalContext.getFileContent(envFileName);
377       if (fileContent.isPresent()) {
378         envContent = new YamlUtil().yamlToObject(fileContent.get(), Environment.class);
379       } else {
380         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_API,
381                 LoggerTragetServiceName.VALIDATE_ENV_FILE, ErrorLevel.ERROR.name(),
382                 LoggerErrorCode.DATA_ERROR.getErrorCode(), LoggerErrorDescription.EMPTY_FILE);
383         throw new Exception(String.format(NO_CONTENT_IN_FILE_MSG, envFileName));
384       }
385     } catch (Exception exception) {
386       LOGGER.error("Error while reading env file : " + envFileName, exception);
387       MDC_DATA_DEBUG_MESSAGE.debugExitMessage("env file", envFileName);
388       return null;
389     }
390     return envContent;
391   }
392
393
394   public static String getResourceGroupResourceName(String resourceCallingToResourceGroup) {
395     return "OS::Heat::ResourceGroup in " + resourceCallingToResourceGroup;
396   }
397
398 }