2 * ============LICENSE_START=======================================================
3 * Copyright (c) 2017 AT&T Intellectual Property.
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 * ============LICENSE_END=========================================================
17 * Modifications copyright (c) 2019 Fujitsu Limited.
18 * ================================================================================
20 package org.onap.sdc.toscaparser.api;
23 import java.io.FileInputStream;
24 import java.io.FileNotFoundException;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.nio.file.Files;
28 import java.nio.file.Paths;
29 import java.util.ArrayList;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.Iterator;
33 import java.util.LinkedHashMap;
34 import java.util.List;
37 import java.util.concurrent.ConcurrentHashMap;
38 import java.util.function.Predicate;
40 import org.onap.sdc.toscaparser.api.common.JToscaException;
41 import org.onap.sdc.toscaparser.api.common.JToscaValidationIssue;
42 import org.onap.sdc.toscaparser.api.common.ValidationIssueCollector;
43 import org.onap.sdc.toscaparser.api.elements.EntityType;
44 import org.onap.sdc.toscaparser.api.elements.DataType;
45 import org.onap.sdc.toscaparser.api.elements.Metadata;
46 import org.onap.sdc.toscaparser.api.extensions.ExtTools;
47 import org.onap.sdc.toscaparser.api.parameters.Input;
48 import org.onap.sdc.toscaparser.api.parameters.Output;
49 import org.onap.sdc.toscaparser.api.prereq.CSAR;
50 import org.onap.sdc.toscaparser.api.utils.JToscaErrorCodes;
51 import org.onap.sdc.toscaparser.api.utils.ThreadLocalsHolder;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54 import org.yaml.snakeyaml.Yaml;
56 public class ToscaTemplate extends Object {
58 public static final int MAX_LEVELS = 20;
59 private static Logger log = LoggerFactory.getLogger(ToscaTemplate.class.getName());
61 // TOSCA template key names
62 private static final String DEFINITION_VERSION = "tosca_definitions_version";
63 private static final String DEFAULT_NAMESPACE = "tosca_default_namespace";
64 private static final String TEMPLATE_NAME = "template_name";
65 private static final String TOPOLOGY_TEMPLATE = "topology_template";
66 private static final String TEMPLATE_AUTHOR = "template_author";
67 private static final String TEMPLATE_VERSION = "template_version";
68 private static final String DESCRIPTION = "description";
69 private static final String IMPORTS = "imports";
70 private static final String DSL_DEFINITIONS = "dsl_definitions";
71 private static final String NODE_TYPES = "node_types";
72 private static final String RELATIONSHIP_TYPES = "relationship_types";
73 private static final String RELATIONSHIP_TEMPLATES = "relationship_templates";
74 private static final String CAPABILITY_TYPES = "capability_types";
75 private static final String ARTIFACT_TYPES = "artifact_types";
76 private static final String DATA_TYPES = "data_types";
77 private static final String INTERFACE_TYPES = "interface_types";
78 private static final String POLICY_TYPES = "policy_types";
79 private static final String GROUP_TYPES = "group_types";
80 private static final String REPOSITORIES = "repositories";
82 private static String SECTIONS[] = {
83 DEFINITION_VERSION, DEFAULT_NAMESPACE, TEMPLATE_NAME,
84 TOPOLOGY_TEMPLATE, TEMPLATE_AUTHOR, TEMPLATE_VERSION,
85 DESCRIPTION, IMPORTS, DSL_DEFINITIONS, NODE_TYPES,
86 RELATIONSHIP_TYPES, RELATIONSHIP_TEMPLATES,
87 CAPABILITY_TYPES, ARTIFACT_TYPES, DATA_TYPES,
88 INTERFACE_TYPES, POLICY_TYPES, GROUP_TYPES, REPOSITORIES
91 // Sections that are specific to individual template definitions
92 private static final String METADATA = "metadata";
93 private static ArrayList<String> SPECIAL_SECTIONS;
95 private ExtTools exttools = new ExtTools();
97 private ArrayList<String> VALID_TEMPLATE_VERSIONS;
98 private LinkedHashMap<String, ArrayList<String>> ADDITIONAL_SECTIONS;
100 private boolean isFile;
102 private String inputPath;
103 private String rootPath;
104 private LinkedHashMap<String, Object> parsedParams;
105 private boolean resolveGetInput;
106 private LinkedHashMap<String, Object> tpl;
107 private String version;
108 private ArrayList<Object> imports;
109 private LinkedHashMap<String, Object> relationshipTypes;
110 private Metadata metaData;
111 private String description;
112 private TopologyTemplate topologyTemplate;
113 private ArrayList<Repository> repositories;
114 private ArrayList<Input> inputs;
115 private ArrayList<RelationshipTemplate> relationshipTemplates;
116 private ArrayList<NodeTemplate> nodeTemplates;
117 private ArrayList<Output> outputs;
118 private ArrayList<Policy> policies;
119 private ArrayList<Group> groups;
120 private ConcurrentHashMap<String, Object> nestedToscaTplsWithTopology;
121 private ArrayList<TopologyTemplate> nestedToscaTemplatesWithTopology;
122 private ToscaGraph graph;
123 private String csarTempDir;
124 private int nestingLoopCounter;
125 private LinkedHashMap<String, LinkedHashMap<String, Object>> metaProperties;
126 private Set<String> processedImports;
127 private LinkedHashMap<String, Object> customDefsFinal = new LinkedHashMap<>();
128 private HashSet<DataType> dataTypes;
130 public ToscaTemplate(String _path,
131 LinkedHashMap<String, Object> _parsedParams,
133 LinkedHashMap<String, Object> yamlDictTpl) throws JToscaException {
134 init(_path, _parsedParams, aFile, yamlDictTpl, true);
137 public ToscaTemplate(String _path,
138 LinkedHashMap<String, Object> _parsedParams,
140 LinkedHashMap<String, Object> yamlDictTpl, boolean resolveGetInput) throws JToscaException {
141 init(_path, _parsedParams, aFile, yamlDictTpl, resolveGetInput);
144 @SuppressWarnings("unchecked")
145 private void init(String _path,
146 LinkedHashMap<String, Object> _parsedParams,
148 LinkedHashMap<String, Object> yamlDictTpl, boolean _resolveGetInput) throws JToscaException {
150 ThreadLocalsHolder.setCollector(new ValidationIssueCollector());
152 VALID_TEMPLATE_VERSIONS = new ArrayList<>();
153 VALID_TEMPLATE_VERSIONS.add("tosca_simple_yaml_1_0");
154 VALID_TEMPLATE_VERSIONS.add("tosca_simple_yaml_1_1");
155 VALID_TEMPLATE_VERSIONS.addAll(exttools.getVersions());
156 ADDITIONAL_SECTIONS = new LinkedHashMap<>();
157 SPECIAL_SECTIONS = new ArrayList<>();
158 SPECIAL_SECTIONS.add(METADATA);
159 ADDITIONAL_SECTIONS.put("tosca_simple_yaml_1_0", SPECIAL_SECTIONS);
160 ADDITIONAL_SECTIONS.put("tosca_simple_yaml_1_1", SPECIAL_SECTIONS);
161 ADDITIONAL_SECTIONS.putAll(exttools.getSections());
163 //long startTime = System.nanoTime();
171 nestedToscaTplsWithTopology = new ConcurrentHashMap<>();
172 nestedToscaTemplatesWithTopology = new ArrayList<TopologyTemplate>();
173 resolveGetInput = _resolveGetInput;
174 metaProperties = new LinkedHashMap<>();
176 if (_path != null && !_path.isEmpty()) {
177 // save the original input path
179 // get the actual path (will change with CSAR)
180 path = _getPath(_path);
181 // load the YAML template
182 if (path != null && !path.isEmpty()) {
183 try (InputStream input = new FileInputStream(new File(path));) {
184 //System.out.println("Loading YAML file " + path);
185 log.debug("ToscaTemplate Loading YAMEL file {}", path);
186 Yaml yaml = new Yaml();
187 Object data = yaml.load(input);
188 this.tpl = (LinkedHashMap<String, Object>) data;
189 } catch (FileNotFoundException e) {
190 log.error("ToscaTemplate - Exception loading yaml: {}", e.getMessage());
191 log.error("Exception", e);
192 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE275",
193 "ToscaTemplate - Exception loading yaml: -> " + e.getMessage()));
195 } catch (Exception e) {
196 log.error("ToscaTemplate - Error loading yaml, aborting -> ", e.getMessage());
197 log.error("Exception", e);
198 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE275",
199 "ToscaTemplate - Error loading yaml, aborting -> " + e.getMessage()));
203 if (yamlDictTpl != null) {
204 //msg = (_('Both path and yaml_dict_tpl arguments were '
205 // 'provided. Using path and ignoring yaml_dict_tpl.'))
207 log.debug("ToscaTemplate - Both path and yaml_dict_tpl arguments were provided. Using path and ignoring yaml_dict_tpl");
210 // no input to process...
214 if (yamlDictTpl != null) {
217 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE244",
218 "ValueError: No path or yaml_dict_tpl was provided. There is nothing to parse"));
219 log.debug("ToscaTemplate ValueError: No path or yaml_dict_tpl was provided. There is nothing to parse");
225 parsedParams = _parsedParams;
227 this.rootPath = path;
228 this.processedImports = new HashSet<String>();
229 this.imports = _tplImports();
230 this.version = _tplVersion();
231 this.metaData = _tplMetaData();
232 this.relationshipTypes = _tplRelationshipTypes();
233 this.description = _tplDescription();
234 this.dataTypes = getTopologyDataTypes();
235 this.topologyTemplate = _topologyTemplate();
236 this.repositories = _tplRepositories();
237 if (topologyTemplate.getTpl() != null) {
238 this.inputs = _inputs();
239 this.relationshipTemplates = _relationshipTemplates();
240 this.nodeTemplates = _nodeTemplates();
241 this.outputs = _outputs();
242 this.policies = _policies();
243 this.groups = _groups();
244 // _handleNestedToscaTemplatesWithTopology();
245 _handleNestedToscaTemplatesWithTopology(topologyTemplate);
246 graph = new ToscaGraph(nodeTemplates);
250 if (csarTempDir != null) {
251 CSAR.deleteDir(new File(csarTempDir));
259 private void _abort() throws JToscaException {
260 // print out all exceptions caught
262 throw new JToscaException("jtosca aborting", JToscaErrorCodes.PATH_NOT_VALID.getValue());
265 private TopologyTemplate _topologyTemplate() {
266 return new TopologyTemplate(
267 _tplTopologyTemplate(),
268 _getAllCustomDefs(imports),
275 private ArrayList<Input> _inputs() {
276 return topologyTemplate.getInputs();
279 private ArrayList<NodeTemplate> _nodeTemplates() {
280 return topologyTemplate.getNodeTemplates();
283 private ArrayList<RelationshipTemplate> _relationshipTemplates() {
284 return topologyTemplate.getRelationshipTemplates();
287 private ArrayList<Output> _outputs() {
288 return topologyTemplate.getOutputs();
291 private String _tplVersion() {
292 return (String) tpl.get(DEFINITION_VERSION);
295 @SuppressWarnings("unchecked")
296 private Metadata _tplMetaData() {
297 Object mdo = tpl.get(METADATA);
298 if (mdo instanceof LinkedHashMap) {
299 return new Metadata((Map<String, Object>) mdo);
305 private String _tplDescription() {
306 return (String) tpl.get(DESCRIPTION);
309 @SuppressWarnings("unchecked")
310 private ArrayList<Object> _tplImports() {
311 return (ArrayList<Object>) tpl.get(IMPORTS);
314 @SuppressWarnings("unchecked")
315 private ArrayList<Repository> _tplRepositories() {
316 LinkedHashMap<String, Object> repositories =
317 (LinkedHashMap<String, Object>) tpl.get(REPOSITORIES);
318 ArrayList<Repository> reposit = new ArrayList<>();
319 if (repositories != null) {
320 for (Map.Entry<String, Object> me : repositories.entrySet()) {
321 Repository reposits = new Repository(me.getKey(), me.getValue());
322 reposit.add(reposits);
328 private LinkedHashMap<String, Object> _tplRelationshipTypes() {
329 return (LinkedHashMap<String, Object>) _getCustomTypes(RELATIONSHIP_TYPES, null);
332 @SuppressWarnings("unchecked")
333 private LinkedHashMap<String, Object> _tplTopologyTemplate() {
334 return (LinkedHashMap<String, Object>) tpl.get(TOPOLOGY_TEMPLATE);
337 private ArrayList<Policy> _policies() {
338 return topologyTemplate.getPolicies();
341 private ArrayList<Group> _groups() {
342 return topologyTemplate.getGroups();
346 * Read datatypes field
348 * @return return list of datatypes.
350 @SuppressWarnings("unchecked")
351 private HashSet<DataType> getTopologyDataTypes() {
352 LinkedHashMap<String, Object> value =
353 (LinkedHashMap<String, Object>) tpl.get(DATA_TYPES);
354 HashSet<DataType> datatypes = new HashSet<>();
356 customDefsFinal.putAll(value);
357 for (Map.Entry<String, Object> me : value.entrySet()) {
358 DataType datatype = new DataType(me.getKey(), value);
359 datatypes.add(datatype);
368 * This method is used to get consolidated custom definitions from all imports
369 * It is logically divided in two parts to handle imports; map and list formats.
370 * Before processing the imports; it sorts them to make sure the current directory imports are
371 * being processed first and then others. Once sorted; it processes each import one by one in
373 * To avoid cyclic dependency among imports; this method uses a set to keep track of all
374 * imports which are already processed and filters the imports which occurs more than once.
376 * @param alImports all imports which needs to be processed
377 * @return the linked hash map containing all import definitions
380 @SuppressWarnings("unchecked")
381 private LinkedHashMap<String, Object> _getAllCustomDefs(Object alImports) {
385 IMPORTS, NODE_TYPES, CAPABILITY_TYPES, RELATIONSHIP_TYPES,
386 DATA_TYPES, INTERFACE_TYPES, POLICY_TYPES, GROUP_TYPES
389 List<Map<String, Object>> imports = (List<Map<String, Object>>) alImports;
390 if (imports != null && !imports.isEmpty()) {
391 if (imports.get(0) instanceof LinkedHashMap) {
392 imports = sortImports(imports);
394 for (Map<String, Object> map : imports) {
395 List<Map<String, Object>> singleImportList = new ArrayList<>();
396 singleImportList.add(map);
398 Map<String, String> importNameDetails = getValidFileNameForImportReference(singleImportList);
399 singleImportList = filterImportsForRecursion(singleImportList, importNameDetails);
401 if (!singleImportList.get(0).isEmpty()) {
402 LinkedHashMap<String, Object> customDefs = _getCustomTypes(types, new ArrayList<>(singleImportList));
403 processedImports.add(importNameDetails.get("importFileName"));
405 if (customDefs != null) {
406 customDefsFinal.putAll(customDefs);
408 if (customDefs.get(IMPORTS) != null) {
409 resetPathForRecursiveImports(importNameDetails.get("importRelativeName"));
410 LinkedHashMap<String, Object> importDefs = _getAllCustomDefs(customDefs.get(IMPORTS));
411 customDefsFinal.putAll(importDefs);
417 LinkedHashMap<String, Object> customDefs = _getCustomTypes(types, new ArrayList<>(imports));
418 if (customDefs != null) {
419 customDefsFinal.putAll(customDefs);
421 if (customDefs.get(IMPORTS) != null) {
422 LinkedHashMap<String, Object> importDefs = _getAllCustomDefs(customDefs.get(IMPORTS));
423 customDefsFinal.putAll(importDefs);
429 // As imports are not custom_types, remove from the dict
430 customDefsFinal.remove(IMPORTS);
432 return customDefsFinal;
436 * This method is used to sort the imports in order so that same directory
437 * imports will be processed first
439 * @param customImports the custom imports
440 * @return the sorted list of imports
442 private List<Map<String, Object>> sortImports(List<Map<String, Object>> customImports) {
443 List<Map<String, Object>> finalList1 = new ArrayList<>();
444 List<Map<String, Object>> finalList2 = new ArrayList<>();
445 Iterator<Map<String, Object>> itr = customImports.iterator();
446 while (itr.hasNext()) {
447 Map<String, Object> innerMap = itr.next();
448 if (innerMap.toString().contains("../")) {
449 finalList2.add(innerMap);
451 } else if (innerMap.toString().contains("/")) {
452 finalList1.add(innerMap);
457 customImports.addAll(finalList1);
458 customImports.addAll(finalList2);
459 return customImports;
463 * This method is used to reset PATH variable after processing of current import file is done
464 * This is required because of relative path nature of imports present in files.
466 * @param currImportRelativeName the current import relative name
468 private void resetPathForRecursiveImports(String currImportRelativeName) {
469 path = getPath(path, currImportRelativeName);
473 * This is a recursive method which starts from current import and then recursively finds a
474 * valid path relative to current import file name.
475 * By doing this it handles all nested hierarchy of imports defined in CSARs
477 * @param path the path
478 * @param importFileName the import file name
479 * @return the string containing updated path value
481 private String getPath(String path, String importFileName) {
482 String tempFullPath = (Paths.get(path).toAbsolutePath().getParent()
483 .toString() + File.separator + importFileName.replace("../", "")).replace('\\', '/');
484 String tempPartialPath = (Paths.get(path).toAbsolutePath().getParent().toString()).replace('\\', '/');
485 if (Files.exists(Paths.get(tempFullPath)))
488 return getPath(tempPartialPath, importFileName);
492 * This method is used to get full path name for the file which needs to be processed. It helps
493 * in situation where files are present in different directory and are references as relative
496 * @param customImports the custom imports
497 * @return the map containing import file full and relative paths
499 private Map<String, String> getValidFileNameForImportReference(List<Map<String, Object>> customImports) {
500 String importFileName;
501 Map<String, String> retMap = new HashMap<>();
502 for (Map<String, Object> map1 : customImports) {
503 for (Map.Entry<String, Object> entry : map1.entrySet()) {
504 Map innerMostMap = (Map) entry.getValue();
505 Iterator<Map.Entry<String, String>> it = innerMostMap.entrySet().iterator();
506 while (it.hasNext()) {
507 Map.Entry<String, String> val = it.next();
508 if (val.getValue().contains("/")) {
509 importFileName = (Paths.get(rootPath).toAbsolutePath().getParent().toString() + File
510 .separator + val.getValue().replace("../", "")).replace('\\', '/');
512 importFileName = (Paths.get(path).toAbsolutePath().getParent().toString() + File
513 .separator + val.getValue().replace("../", "")).replace('\\', '/');
515 retMap.put("importFileName", importFileName);
516 retMap.put("importRelativeName", val.getValue());
524 * This method is used to filter the imports which already gets processed in previous step.
525 * It handles the use case of cyclic dependency in imports which may cause Stack Overflow
528 * @param customImports the custom imports
529 * @param importNameDetails the import name details
530 * @return the list containing filtered imports
532 private List<Map<String, Object>> filterImportsForRecursion(List<Map<String, Object>>
533 customImports, Map<String,
534 String> importNameDetails) {
535 for (Map<String, Object> map1 : customImports) {
536 for (Map.Entry<String, Object> entry : map1.entrySet()) {
537 Map innerMostMap = (Map) entry.getValue();
538 Iterator<Map.Entry<String, String>> it = innerMostMap.entrySet().iterator();
539 while (it.hasNext()) {
541 if (processedImports.contains(importNameDetails.get("importFileName"))) {
548 // Remove Empty elements
549 Iterator<Map<String, Object>> itr = customImports.iterator();
550 while (itr.hasNext()) {
551 Map innerMap = itr.next();
552 Predicate<Map> predicate = p -> p.values().isEmpty();
553 innerMap.values().removeIf(predicate);
556 return customImports;
559 @SuppressWarnings("unchecked")
560 private LinkedHashMap<String, Object> _getCustomTypes(Object typeDefinitions, ArrayList<Object> alImports) {
562 // Handle custom types defined in imported template files
563 // This method loads the custom type definitions referenced in "imports"
564 // section of the TOSCA YAML template.
566 LinkedHashMap<String, Object> customDefs = new LinkedHashMap<String, Object>();
567 ArrayList<String> typeDefs = new ArrayList<String>();
568 if (typeDefinitions instanceof String[]) {
569 for (String s : (String[]) typeDefinitions) {
573 typeDefs.add((String) typeDefinitions);
576 if (alImports == null) {
577 alImports = _tplImports();
580 if (alImports != null) {
581 ImportsLoader customService = new ImportsLoader(alImports, path, typeDefs, tpl);
582 ArrayList<LinkedHashMap<String, Object>> nestedToscaTpls = customService.getNestedToscaTpls();
583 _updateNestedToscaTplsWithTopology(nestedToscaTpls);
585 customDefs = customService.getCustomDefs();
586 if (customDefs == null) {
591 //Handle custom types defined in current template file
592 for (String td : typeDefs) {
593 if (!td.equals(IMPORTS)) {
594 LinkedHashMap<String, Object> innerCustomTypes = (LinkedHashMap<String, Object>) tpl.get(td);
595 if (innerCustomTypes != null) {
596 customDefs.putAll(innerCustomTypes);
603 private void _updateNestedToscaTplsWithTopology(ArrayList<LinkedHashMap<String, Object>> nestedToscaTpls) {
604 for (LinkedHashMap<String, Object> ntpl : nestedToscaTpls) {
605 // there is just one key:value pair in ntpl
606 for (Map.Entry<String, Object> me : ntpl.entrySet()) {
607 String fileName = me.getKey();
608 @SuppressWarnings("unchecked")
609 LinkedHashMap<String, Object> toscaTpl = (LinkedHashMap<String, Object>) me.getValue();
610 if (toscaTpl.get(TOPOLOGY_TEMPLATE) != null) {
611 if (nestedToscaTplsWithTopology.get(fileName) == null) {
612 nestedToscaTplsWithTopology.putAll(ntpl);
619 // multi level nesting - RECURSIVE
620 @SuppressWarnings("unchecked")
621 private void _handleNestedToscaTemplatesWithTopology(TopologyTemplate tt) {
622 if (++nestingLoopCounter > MAX_LEVELS) {
623 log.error("ToscaTemplate - _handleNestedToscaTemplatesWithTopology - Nested Topologies Loop: too many levels, aborting");
626 // Reset Processed Imports for nested templates
627 this.processedImports = new HashSet<>();
628 for (Map.Entry<String, Object> me : nestedToscaTplsWithTopology.entrySet()) {
629 LinkedHashMap<String, Object> toscaTpl =
630 (LinkedHashMap<String, Object>) me.getValue();
631 for (NodeTemplate nt : tt.getNodeTemplates()) {
632 if (_isSubMappedNode(nt, toscaTpl)) {
633 parsedParams = _getParamsForNestedTemplate(nt);
634 ArrayList<Object> alim = (ArrayList<Object>) toscaTpl.get(IMPORTS);
635 LinkedHashMap<String, Object> topologyTpl =
636 (LinkedHashMap<String, Object>) toscaTpl.get(TOPOLOGY_TEMPLATE);
637 TopologyTemplate topologyWithSubMapping =
638 new TopologyTemplate(topologyTpl,
639 _getAllCustomDefs(alim),
644 nt.setOriginComponentTemplate(topologyWithSubMapping);
645 if (topologyWithSubMapping.getSubstitutionMappings() != null) {
646 // Record nested topology templates in top level template
647 //nestedToscaTemplatesWithTopology.add(topologyWithSubMapping);
648 // Set substitution mapping object for mapped node
649 nt.setSubMappingToscaTemplate(
650 topologyWithSubMapping.getSubstitutionMappings());
651 _handleNestedToscaTemplatesWithTopology(topologyWithSubMapping);
658 // private void _handleNestedToscaTemplatesWithTopology() {
659 // for(Map.Entry<String,Object> me: nestedToscaTplsWithTopology.entrySet()) {
660 // String fname = me.getKey();
661 // LinkedHashMap<String,Object> toscaTpl =
662 // (LinkedHashMap<String,Object>)me.getValue();
663 // for(NodeTemplate nt: nodeTemplates) {
664 // if(_isSubMappedNode(nt,toscaTpl)) {
665 // parsedParams = _getParamsForNestedTemplate(nt);
666 // ArrayList<Object> alim = (ArrayList<Object>)toscaTpl.get(IMPORTS);
667 // LinkedHashMap<String,Object> topologyTpl =
668 // (LinkedHashMap<String,Object>)toscaTpl.get(TOPOLOGY_TEMPLATE);
669 // TopologyTemplate topologyWithSubMapping =
670 // new TopologyTemplate(topologyTpl,
671 // //_getAllCustomDefs(null),
672 // _getAllCustomDefs(alim),
673 // relationshipTypes,
676 // if(topologyWithSubMapping.getSubstitutionMappings() != null) {
677 // // Record nested topology templates in top level template
678 // nestedToscaTemplatesWithTopology.add(topologyWithSubMapping);
679 // // Set substitution mapping object for mapped node
680 // nt.setSubMappingToscaTemplate(
681 // topologyWithSubMapping.getSubstitutionMappings());
688 private void _validateField() {
689 String sVersion = _tplVersion();
690 if (sVersion == null) {
691 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE245", String.format(
692 "MissingRequiredField: Template is missing required field \"%s\"", DEFINITION_VERSION)));
694 _validateVersion(sVersion);
695 this.version = sVersion;
698 for (String sKey : tpl.keySet()) {
699 boolean bFound = false;
700 for (String sSection : SECTIONS) {
701 if (sKey.equals(sSection)) {
706 // check ADDITIONAL_SECTIONS
708 if (ADDITIONAL_SECTIONS.get(version) != null &&
709 ADDITIONAL_SECTIONS.get(version).contains(sKey)) {
714 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE246", String.format(
715 "UnknownFieldError: Template contains unknown field \"%s\"",
721 private void _validateVersion(String sVersion) {
722 boolean bFound = false;
723 for (String vtv : VALID_TEMPLATE_VERSIONS) {
724 if (sVersion.equals(vtv)) {
730 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE247", String.format(
731 "InvalidTemplateVersion: \"%s\" is invalid. Valid versions are %s",
732 sVersion, VALID_TEMPLATE_VERSIONS.toString())));
733 } else if ((!sVersion.equals("tosca_simple_yaml_1_0") && !sVersion.equals("tosca_simple_yaml_1_1"))) {
734 EntityType.updateDefinitions(sVersion);
739 private String _getPath(String _path) throws JToscaException {
740 if (_path.toLowerCase().endsWith(".yaml") || _path.toLowerCase().endsWith(".yml")) {
742 } else if (_path.toLowerCase().endsWith(".zip") || _path.toLowerCase().endsWith(".csar")) {
744 CSAR csar = new CSAR(_path, isFile);
745 if (csar.validate()) {
748 metaProperties = csar.getMetaProperties();
749 } catch (IOException e) {
750 log.error("ToscaTemplate - _getPath - IOException trying to decompress {}", _path);
753 isFile = true; // the file has been decompressed locally
755 csarTempDir = csar.getTempDir();
756 return csar.getTempDir() + File.separator + csar.getMainTemplate();
759 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE248", "ValueError: " + _path + " is not a valid file"));
765 private void verifyTemplate() throws JToscaException {
767 int validationIssuesCaught = ThreadLocalsHolder.getCollector().validationIssuesCaught();
768 if (validationIssuesCaught > 0) {
769 List<String> validationIssueStrings = ThreadLocalsHolder.getCollector().getValidationIssueReport();
770 log.trace("####################################################################################################");
771 log.trace("ToscaTemplate - verifyTemplate - {} Parsing Critical{} occurred...", validationIssuesCaught, (validationIssuesCaught > 1 ? "s" : ""));
772 for (String s : validationIssueStrings) {
773 log.trace("{}. CSAR name - {}", s, inputPath);
775 log.trace("####################################################################################################");
780 public String getPath() {
784 public String getVersion() {
788 public String getDescription() {
792 public TopologyTemplate getTopologyTemplate() {
793 return topologyTemplate;
796 public Metadata getMetaData() {
800 public ArrayList<Input> getInputs() {
801 if (inputs != null) {
802 inputs.stream().forEach(Input::resetAnnotaions);
807 public ArrayList<Output> getOutputs() {
811 public ArrayList<Policy> getPolicies() {
815 public ArrayList<Group> getGroups() {
819 public ArrayList<NodeTemplate> getNodeTemplates() {
820 return nodeTemplates;
823 public LinkedHashMap<String, Object> getMetaProperties(String propertiesFile) {
824 return metaProperties.get(propertiesFile);
827 // private boolean _isSubMappedNode(NodeTemplate nt,LinkedHashMap<String,Object> toscaTpl) {
828 // // Return True if the nodetemple is substituted
829 // if(nt != null && nt.getSubMappingToscaTemplate() == null &&
830 // getSubMappingNodeType(toscaTpl).equals(nt.getType()) &&
831 // nt.getInterfaces().size() < 1) {
837 private boolean _isSubMappedNode(NodeTemplate nt, LinkedHashMap<String, Object> toscaTpl) {
838 // Return True if the nodetemple is substituted
839 if (nt != null && nt.getSubMappingToscaTemplate() == null &&
840 getSubMappingNodeType(toscaTpl).equals(nt.getType()) &&
841 nt.getInterfaces().size() < 1) {
847 private LinkedHashMap<String, Object> _getParamsForNestedTemplate(NodeTemplate nt) {
848 // Return total params for nested_template
849 LinkedHashMap<String, Object> pparams;
850 if (parsedParams != null) {
851 pparams = parsedParams;
853 pparams = new LinkedHashMap<String, Object>();
856 for (String pname : nt.getProperties().keySet()) {
857 pparams.put(pname, nt.getPropertyValue(pname));
863 @SuppressWarnings("unchecked")
864 private String getSubMappingNodeType(LinkedHashMap<String, Object> toscaTpl) {
865 // Return substitution mappings node type
866 if (toscaTpl != null) {
867 return TopologyTemplate.getSubMappingNodeType(
868 (LinkedHashMap<String, Object>) toscaTpl.get(TOPOLOGY_TEMPLATE));
873 public boolean hasNestedTemplates() {
874 // Return True if the tosca template has nested templates
875 return nestedToscaTemplatesWithTopology != null &&
876 nestedToscaTemplatesWithTopology.size() >= 1;
880 public ArrayList<TopologyTemplate> getNestedTemplates() {
881 return nestedToscaTemplatesWithTopology;
884 public ConcurrentHashMap<String, Object> getNestedTopologyTemplates() {
885 return nestedToscaTplsWithTopology;
891 * @return return list of datatypes.
893 public HashSet<DataType> getDataTypes() {
898 public String toString() {
899 return "ToscaTemplate{" +
900 "exttools=" + exttools +
901 ", VALID_TEMPLATE_VERSIONS=" + VALID_TEMPLATE_VERSIONS +
902 ", ADDITIONAL_SECTIONS=" + ADDITIONAL_SECTIONS +
903 ", isFile=" + isFile +
904 ", path='" + path + '\'' +
905 ", inputPath='" + inputPath + '\'' +
906 ", parsedParams=" + parsedParams +
908 ", version='" + version + '\'' +
909 ", imports=" + imports +
910 ", relationshipTypes=" + relationshipTypes +
911 ", metaData=" + metaData +
912 ", description='" + description + '\'' +
913 ", topologyTemplate=" + topologyTemplate +
914 ", repositories=" + repositories +
915 ", inputs=" + inputs +
916 ", relationshipTemplates=" + relationshipTemplates +
917 ", nodeTemplates=" + nodeTemplates +
918 ", outputs=" + outputs +
919 ", policies=" + policies +
920 ", nestedToscaTplsWithTopology=" + nestedToscaTplsWithTopology +
921 ", nestedToscaTemplatesWithTopology=" + nestedToscaTemplatesWithTopology +
923 ", csarTempDir='" + csarTempDir + '\'' +
924 ", nestingLoopCounter=" + nestingLoopCounter +
925 ", dataTypes=" + dataTypes +
929 public List<Input> getInputs(boolean annotationsRequired) {
930 if (inputs != null && annotationsRequired) {
931 inputs.stream().forEach(Input::parseAnnotations);
943 from copy import deepcopy
944 from toscaparser.common.exception import ValidationIssueCollector.collector
945 from toscaparser.common.exception import InvalidTemplateVersion
946 from toscaparser.common.exception import MissingRequiredFieldError
947 from toscaparser.common.exception import UnknownFieldError
948 from toscaparser.common.exception import ValidationError
949 from toscaparser.elements.entity_type import update_definitions
950 from toscaparser.extensions.exttools import ExtTools
951 import org.openecomp.sdc.toscaparser.api.imports
952 from toscaparser.prereq.csar import CSAR
953 from toscaparser.repositories import Repository
954 from toscaparser.topology_template import TopologyTemplate
955 from toscaparser.tpl_relationship_graph import ToscaGraph
956 from toscaparser.utils.gettextutils import _
957 import org.openecomp.sdc.toscaparser.api.utils.yamlparser
960 # TOSCA template key names
961 SECTIONS = (DEFINITION_VERSION, DEFAULT_NAMESPACE, TEMPLATE_NAME,
962 TOPOLOGY_TEMPLATE, TEMPLATE_AUTHOR, TEMPLATE_VERSION,
963 DESCRIPTION, IMPORTS, DSL_DEFINITIONS, NODE_TYPES,
964 RELATIONSHIP_TYPES, RELATIONSHIP_TEMPLATES,
965 CAPABILITY_TYPES, ARTIFACT_TYPES, DATA_TYPES, INTERFACE_TYPES,
966 POLICY_TYPES, GROUP_TYPES, REPOSITORIES) = \
967 ('tosca_definitions_version', 'tosca_default_namespace',
968 'template_name', 'topology_template', 'template_author',
969 'template_version', 'description', 'imports', 'dsl_definitions',
970 'node_types', 'relationship_types', 'relationship_templates',
971 'capability_types', 'artifact_types', 'data_types',
972 'interface_types', 'policy_types', 'group_types', 'repositories')
973 # Sections that are specific to individual template definitions
974 SPECIAL_SECTIONS = (METADATA) = ('metadata')
976 log = logging.getLogger("tosca.model")
978 YAML_LOADER = toscaparser.utils.yamlparser.load_yaml
981 class ToscaTemplate(object):
982 exttools = ExtTools()
984 VALID_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0']
986 VALID_TEMPLATE_VERSIONS.extend(exttools.get_versions())
988 ADDITIONAL_SECTIONS = {'tosca_simple_yaml_1_0': SPECIAL_SECTIONS}
990 ADDITIONAL_SECTIONS.update(exttools.get_sections())
992 '''Load the template data.'''
993 def __init__(self, path=None, parsed_params=None, a_file=True,
996 ValidationIssueCollector.collector.start()
998 self.input_path = None
1001 self.nested_tosca_tpls_with_topology = {}
1002 self.nested_tosca_templates_with_topology = []
1004 self.input_path = path
1005 self.path = self._get_path(path)
1007 self.tpl = YAML_LOADER(self.path, self.a_file)
1009 msg = (_('Both path and yaml_dict_tpl arguments were '
1010 'provided. Using path and ignoring yaml_dict_tpl.'))
1015 self.tpl = yaml_dict_tpl
1017 ValidationIssueCollector.collector.appendException(
1018 ValueError(_('No path or yaml_dict_tpl was provided. '
1019 'There is nothing to parse.')))
1022 self.parsed_params = parsed_params
1023 self._validate_field()
1024 self.version = self._tpl_version()
1025 self.relationship_types = self._tpl_relationship_types()
1026 self.description = self._tpl_description()
1027 self.topology_template = self._topology_template()
1028 self.repositories = self._tpl_repositories()
1029 if self.topology_template.tpl:
1030 self.inputs = self._inputs()
1031 self.relationship_templates = self._relationship_templates()
1032 self.nodetemplates = self._nodetemplates()
1033 self.outputs = self._outputs()
1034 self._handle_nested_tosca_templates_with_topology()
1035 self.graph = ToscaGraph(self.nodetemplates)
1037 ValidationIssueCollector.collector.stop()
1038 self.verify_template()
1040 def _topology_template(self):
1041 return TopologyTemplate(self._tpl_topology_template(),
1042 self._get_all_custom_defs(),
1043 self.relationship_types,
1048 return self.topology_template.inputs
1050 def _nodetemplates(self):
1051 return self.topology_template.nodetemplates
1053 def _relationship_templates(self):
1054 return self.topology_template.relationship_templates
1057 return self.topology_template.outputs
1059 def _tpl_version(self):
1060 return self.tpl.get(DEFINITION_VERSION)
1062 def _tpl_description(self):
1063 desc = self.tpl.get(DESCRIPTION)
1065 return desc.rstrip()
1067 def _tpl_imports(self):
1068 return self.tpl.get(IMPORTS)
1070 def _tpl_repositories(self):
1071 repositories = self.tpl.get(REPOSITORIES)
1074 for name, val in repositories.items():
1075 reposits = Repository(name, val)
1076 reposit.append(reposits)
1079 def _tpl_relationship_types(self):
1080 return self._get_custom_types(RELATIONSHIP_TYPES)
1082 def _tpl_relationship_templates(self):
1083 topology_template = self._tpl_topology_template()
1084 return topology_template.get(RELATIONSHIP_TEMPLATES)
1086 def _tpl_topology_template(self):
1087 return self.tpl.get(TOPOLOGY_TEMPLATE)
1089 def _get_all_custom_defs(self, imports=None):
1090 types = [IMPORTS, NODE_TYPES, CAPABILITY_TYPES, RELATIONSHIP_TYPES,
1091 DATA_TYPES, INTERFACE_TYPES, POLICY_TYPES, GROUP_TYPES]
1092 custom_defs_final = {}
1093 custom_defs = self._get_custom_types(types, imports)
1095 custom_defs_final.update(custom_defs)
1096 if custom_defs.get(IMPORTS):
1097 import_defs = self._get_all_custom_defs(
1098 custom_defs.get(IMPORTS))
1099 custom_defs_final.update(import_defs)
1101 # As imports are not custom_types, removing from the dict
1102 custom_defs_final.pop(IMPORTS, None)
1103 return custom_defs_final
1105 def _get_custom_types(self, type_definitions, imports=None):
1106 """Handle custom types defined in imported template files
1108 This method loads the custom type definitions referenced in "imports"
1109 section of the TOSCA YAML template.
1113 if not isinstance(type_definitions, list):
1114 type_defs.append(type_definitions)
1116 type_defs = type_definitions
1119 imports = self._tpl_imports()
1122 custom_service = toscaparser.imports.\
1123 ImportsLoader(imports, self.path,
1124 type_defs, self.tpl)
1126 nested_tosca_tpls = custom_service.get_nested_tosca_tpls()
1127 self._update_nested_tosca_tpls_with_topology(nested_tosca_tpls)
1129 custom_defs = custom_service.get_custom_defs()
1133 # Handle custom types defined in current template file
1134 for type_def in type_defs:
1135 if type_def != IMPORTS:
1136 inner_custom_types = self.tpl.get(type_def) or {}
1137 if inner_custom_types:
1138 custom_defs.update(inner_custom_types)
1141 def _update_nested_tosca_tpls_with_topology(self, nested_tosca_tpls):
1142 for tpl in nested_tosca_tpls:
1143 filename, tosca_tpl = list(tpl.items())[0]
1144 if (tosca_tpl.get(TOPOLOGY_TEMPLATE) and
1145 filename not in list(
1146 self.nested_tosca_tpls_with_topology.keys())):
1147 self.nested_tosca_tpls_with_topology.update(tpl)
1149 def _handle_nested_tosca_templates_with_topology(self):
1150 for fname, tosca_tpl in self.nested_tosca_tpls_with_topology.items():
1151 for nodetemplate in self.nodetemplates:
1152 if self._is_sub_mapped_node(nodetemplate, tosca_tpl):
1153 parsed_params = self._get_params_for_nested_template(
1155 topology_tpl = tosca_tpl.get(TOPOLOGY_TEMPLATE)
1156 topology_with_sub_mapping = TopologyTemplate(
1158 self._get_all_custom_defs(),
1159 self.relationship_types,
1162 if topology_with_sub_mapping.substitution_mappings:
1163 # Record nested topo templates in top level template
1164 self.nested_tosca_templates_with_topology.\
1165 append(topology_with_sub_mapping)
1166 # Set substitution mapping object for mapped node
1167 nodetemplate.sub_mapping_tosca_template = \
1168 topology_with_sub_mapping.substitution_mappings
1170 def _validate_field(self):
1171 version = self._tpl_version()
1173 ValidationIssueCollector.collector.appendException(
1174 MissingRequiredFieldError(what='Template',
1175 required=DEFINITION_VERSION))
1177 self._validate_version(version)
1178 self.version = version
1180 for name in self.tpl:
1181 if (name not in SECTIONS and
1182 name not in self.ADDITIONAL_SECTIONS.get(version, ())):
1183 ValidationIssueCollector.collector.appendException(
1184 UnknownFieldError(what='Template', field=name))
1186 def _validate_version(self, version):
1187 if version not in self.VALID_TEMPLATE_VERSIONS:
1188 ValidationIssueCollector.collector.appendException(
1189 InvalidTemplateVersion(
1191 valid_versions=', '. join(self.VALID_TEMPLATE_VERSIONS)))
1193 if version != 'tosca_simple_yaml_1_0':
1194 update_definitions(version)
1196 def _get_path(self, path):
1197 if path.lower().endswith(('.yaml','.yml')):
1199 elif path.lower().endswith(('.zip', '.csar')):
1201 csar = CSAR(path, self.a_file)
1204 self.a_file = True # the file has been decompressed locally
1205 return os.path.join(csar.temp_dir, csar.get_main_template())
1207 ValidationIssueCollector.collector.appendException(
1208 ValueError(_('"%(path)s" is not a valid file.')
1211 def verify_template(self):
1212 if ValidationIssueCollector.collector.exceptionsCaught():
1214 raise ValidationError(
1215 message=(_('\nThe input "%(path)s" failed validation with '
1216 'the following error(s): \n\n\t')
1217 % {'path': self.input_path}) +
1218 '\n\t'.join(ValidationIssueCollector.collector.getExceptionsReport()))
1220 raise ValidationError(
1221 message=_('\nThe pre-parsed input failed validation with '
1222 'the following error(s): \n\n\t') +
1223 '\n\t'.join(ValidationIssueCollector.collector.getExceptionsReport()))
1226 msg = (_('The input "%(path)s" successfully passed '
1227 'validation.') % {'path': self.input_path})
1229 msg = _('The pre-parsed input successfully passed validation.')
1233 def _is_sub_mapped_node(self, nodetemplate, tosca_tpl):
1234 """Return True if the nodetemple is substituted."""
1235 if (nodetemplate and not nodetemplate.sub_mapping_tosca_template and
1236 self.get_sub_mapping_node_type(tosca_tpl) == nodetemplate.type
1237 and len(nodetemplate.interfaces) < 1):
1242 def _get_params_for_nested_template(self, nodetemplate):
1243 """Return total params for nested_template."""
1244 parsed_params = deepcopy(self.parsed_params) \
1245 if self.parsed_params else {}
1247 for pname in nodetemplate.get_properties():
1248 parsed_params.update({pname:
1249 nodetemplate.get_property_value(pname)})
1250 return parsed_params
1252 def get_sub_mapping_node_type(self, tosca_tpl):
1253 """Return substitution mappings node type."""
1255 return TopologyTemplate.get_sub_mapping_node_type(
1256 tosca_tpl.get(TOPOLOGY_TEMPLATE))
1258 def _has_substitution_mappings(self):
1259 """Return True if the template has valid substitution mappings."""
1260 return self.topology_template is not None and \
1261 self.topology_template.substitution_mappings is not None
1263 def has_nested_templates(self):
1264 """Return True if the tosca template has nested templates."""
1265 return self.nested_tosca_templates_with_topology is not None and \
1266 len(self.nested_tosca_templates_with_topology) >= 1