diff options
Diffstat (limited to 'jtosca/src/main/java/org/onap/sdc/toscaparser/api/ToscaTemplate.java')
-rw-r--r-- | jtosca/src/main/java/org/onap/sdc/toscaparser/api/ToscaTemplate.java | 1267 |
1 files changed, 1267 insertions, 0 deletions
diff --git a/jtosca/src/main/java/org/onap/sdc/toscaparser/api/ToscaTemplate.java b/jtosca/src/main/java/org/onap/sdc/toscaparser/api/ToscaTemplate.java new file mode 100644 index 0000000..ddb8ddb --- /dev/null +++ b/jtosca/src/main/java/org/onap/sdc/toscaparser/api/ToscaTemplate.java @@ -0,0 +1,1267 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (c) 2017 AT&T Intellectual Property. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * Modifications copyright (c) 2019 Fujitsu Limited. + * ================================================================================ + */ +package org.onap.sdc.toscaparser.api; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; + +import org.onap.sdc.toscaparser.api.common.JToscaException; +import org.onap.sdc.toscaparser.api.common.JToscaValidationIssue; +import org.onap.sdc.toscaparser.api.common.ValidationIssueCollector; +import org.onap.sdc.toscaparser.api.elements.EntityType; +import org.onap.sdc.toscaparser.api.elements.DataType; +import org.onap.sdc.toscaparser.api.elements.Metadata; +import org.onap.sdc.toscaparser.api.extensions.ExtTools; +import org.onap.sdc.toscaparser.api.parameters.Input; +import org.onap.sdc.toscaparser.api.parameters.Output; +import org.onap.sdc.toscaparser.api.prereq.CSAR; +import org.onap.sdc.toscaparser.api.utils.JToscaErrorCodes; +import org.onap.sdc.toscaparser.api.utils.ThreadLocalsHolder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.yaml.snakeyaml.Yaml; + +public class ToscaTemplate extends Object { + + public static final int MAX_LEVELS = 20; + private static Logger log = LoggerFactory.getLogger(ToscaTemplate.class.getName()); + + // TOSCA template key names + private static final String DEFINITION_VERSION = "tosca_definitions_version"; + private static final String DEFAULT_NAMESPACE = "tosca_default_namespace"; + private static final String TEMPLATE_NAME = "template_name"; + private static final String TOPOLOGY_TEMPLATE = "topology_template"; + private static final String TEMPLATE_AUTHOR = "template_author"; + private static final String TEMPLATE_VERSION = "template_version"; + private static final String DESCRIPTION = "description"; + private static final String IMPORTS = "imports"; + private static final String DSL_DEFINITIONS = "dsl_definitions"; + private static final String NODE_TYPES = "node_types"; + private static final String RELATIONSHIP_TYPES = "relationship_types"; + private static final String RELATIONSHIP_TEMPLATES = "relationship_templates"; + private static final String CAPABILITY_TYPES = "capability_types"; + private static final String ARTIFACT_TYPES = "artifact_types"; + private static final String DATA_TYPES = "data_types"; + private static final String INTERFACE_TYPES = "interface_types"; + private static final String POLICY_TYPES = "policy_types"; + private static final String GROUP_TYPES = "group_types"; + private static final String REPOSITORIES = "repositories"; + + private static String SECTIONS[] = { + DEFINITION_VERSION, DEFAULT_NAMESPACE, TEMPLATE_NAME, + TOPOLOGY_TEMPLATE, TEMPLATE_AUTHOR, TEMPLATE_VERSION, + DESCRIPTION, IMPORTS, DSL_DEFINITIONS, NODE_TYPES, + RELATIONSHIP_TYPES, RELATIONSHIP_TEMPLATES, + CAPABILITY_TYPES, ARTIFACT_TYPES, DATA_TYPES, + INTERFACE_TYPES, POLICY_TYPES, GROUP_TYPES, REPOSITORIES + }; + + // Sections that are specific to individual template definitions + private static final String METADATA = "metadata"; + private static ArrayList<String> SPECIAL_SECTIONS; + + private ExtTools exttools = new ExtTools(); + + private ArrayList<String> VALID_TEMPLATE_VERSIONS; + private LinkedHashMap<String, ArrayList<String>> ADDITIONAL_SECTIONS; + + private boolean isFile; + private String path; + private String inputPath; + private String rootPath; + private LinkedHashMap<String, Object> parsedParams; + private boolean resolveGetInput; + private LinkedHashMap<String, Object> tpl; + private String version; + private ArrayList<Object> imports; + private LinkedHashMap<String, Object> relationshipTypes; + private Metadata metaData; + private String description; + private TopologyTemplate topologyTemplate; + private ArrayList<Repository> repositories; + private ArrayList<Input> inputs; + private ArrayList<RelationshipTemplate> relationshipTemplates; + private ArrayList<NodeTemplate> nodeTemplates; + private ArrayList<Output> outputs; + private ArrayList<Policy> policies; + private ArrayList<Group> groups; + private ConcurrentHashMap<String, Object> nestedToscaTplsWithTopology; + private ArrayList<TopologyTemplate> nestedToscaTemplatesWithTopology; + private ToscaGraph graph; + private String csarTempDir; + private int nestingLoopCounter; + private LinkedHashMap<String, LinkedHashMap<String, Object>> metaProperties; + private Set<String> processedImports; + private LinkedHashMap<String, Object> customDefsFinal = new LinkedHashMap<>(); + private HashSet<DataType> dataTypes; + + public ToscaTemplate(String _path, + LinkedHashMap<String, Object> _parsedParams, + boolean aFile, + LinkedHashMap<String, Object> yamlDictTpl) throws JToscaException { + init(_path, _parsedParams, aFile, yamlDictTpl, true); + } + + public ToscaTemplate(String _path, + LinkedHashMap<String, Object> _parsedParams, + boolean aFile, + LinkedHashMap<String, Object> yamlDictTpl, boolean resolveGetInput) throws JToscaException { + init(_path, _parsedParams, aFile, yamlDictTpl, resolveGetInput); + } + + @SuppressWarnings("unchecked") + private void init(String _path, + LinkedHashMap<String, Object> _parsedParams, + boolean aFile, + LinkedHashMap<String, Object> yamlDictTpl, boolean _resolveGetInput) throws JToscaException { + + ThreadLocalsHolder.setCollector(new ValidationIssueCollector()); + + VALID_TEMPLATE_VERSIONS = new ArrayList<>(); + VALID_TEMPLATE_VERSIONS.add("tosca_simple_yaml_1_0"); + VALID_TEMPLATE_VERSIONS.add("tosca_simple_yaml_1_1"); + VALID_TEMPLATE_VERSIONS.addAll(exttools.getVersions()); + ADDITIONAL_SECTIONS = new LinkedHashMap<>(); + SPECIAL_SECTIONS = new ArrayList<>(); + SPECIAL_SECTIONS.add(METADATA); + ADDITIONAL_SECTIONS.put("tosca_simple_yaml_1_0", SPECIAL_SECTIONS); + ADDITIONAL_SECTIONS.put("tosca_simple_yaml_1_1", SPECIAL_SECTIONS); + ADDITIONAL_SECTIONS.putAll(exttools.getSections()); + + //long startTime = System.nanoTime(); + + + isFile = aFile; + inputPath = null; + path = null; + tpl = null; + csarTempDir = null; + nestedToscaTplsWithTopology = new ConcurrentHashMap<>(); + nestedToscaTemplatesWithTopology = new ArrayList<TopologyTemplate>(); + resolveGetInput = _resolveGetInput; + metaProperties = new LinkedHashMap<>(); + + if (_path != null && !_path.isEmpty()) { + // save the original input path + inputPath = _path; + // get the actual path (will change with CSAR) + path = _getPath(_path); + // load the YAML template + if (path != null && !path.isEmpty()) { + try (InputStream input = new FileInputStream(new File(path));) { + //System.out.println("Loading YAML file " + path); + log.debug("ToscaTemplate Loading YAMEL file {}", path); + Yaml yaml = new Yaml(); + Object data = yaml.load(input); + this.tpl = (LinkedHashMap<String, Object>) data; + } catch (FileNotFoundException e) { + log.error("ToscaTemplate - Exception loading yaml: {}", e.getMessage()); + log.error("Exception", e); + ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE275", + "ToscaTemplate - Exception loading yaml: -> " + e.getMessage())); + return; + } catch (Exception e) { + log.error("ToscaTemplate - Error loading yaml, aborting -> ", e.getMessage()); + log.error("Exception", e); + ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE275", + "ToscaTemplate - Error loading yaml, aborting -> " + e.getMessage())); + return; + } + + if (yamlDictTpl != null) { + //msg = (_('Both path and yaml_dict_tpl arguments were ' + // 'provided. Using path and ignoring yaml_dict_tpl.')) + //log.info(msg) + log.debug("ToscaTemplate - Both path and yaml_dict_tpl arguments were provided. Using path and ignoring yaml_dict_tpl"); + } + } else { + // no input to process... + _abort(); + } + } else { + if (yamlDictTpl != null) { + tpl = yamlDictTpl; + } else { + ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE244", + "ValueError: No path or yaml_dict_tpl was provided. There is nothing to parse")); + log.debug("ToscaTemplate ValueError: No path or yaml_dict_tpl was provided. There is nothing to parse"); + + } + } + + if (tpl != null) { + parsedParams = _parsedParams; + _validateField(); + this.rootPath = path; + this.processedImports = new HashSet<String>(); + this.imports = _tplImports(); + this.version = _tplVersion(); + this.metaData = _tplMetaData(); + this.relationshipTypes = _tplRelationshipTypes(); + this.description = _tplDescription(); + this.dataTypes = getTopologyDataTypes(); + this.topologyTemplate = _topologyTemplate(); + this.repositories = _tplRepositories(); + if (topologyTemplate.getTpl() != null) { + this.inputs = _inputs(); + this.relationshipTemplates = _relationshipTemplates(); + this.nodeTemplates = _nodeTemplates(); + this.outputs = _outputs(); + this.policies = _policies(); + this.groups = _groups(); +// _handleNestedToscaTemplatesWithTopology(); + _handleNestedToscaTemplatesWithTopology(topologyTemplate); + graph = new ToscaGraph(nodeTemplates); + } + } + + if (csarTempDir != null) { + CSAR.deleteDir(new File(csarTempDir)); + csarTempDir = null; + } + + verifyTemplate(); + + } + + private void _abort() throws JToscaException { + // print out all exceptions caught + verifyTemplate(); + throw new JToscaException("jtosca aborting", JToscaErrorCodes.PATH_NOT_VALID.getValue()); + } + + private TopologyTemplate _topologyTemplate() { + return new TopologyTemplate( + _tplTopologyTemplate(), + _getAllCustomDefs(imports), + relationshipTypes, + parsedParams, + null, + resolveGetInput); + } + + private ArrayList<Input> _inputs() { + return topologyTemplate.getInputs(); + } + + private ArrayList<NodeTemplate> _nodeTemplates() { + return topologyTemplate.getNodeTemplates(); + } + + private ArrayList<RelationshipTemplate> _relationshipTemplates() { + return topologyTemplate.getRelationshipTemplates(); + } + + private ArrayList<Output> _outputs() { + return topologyTemplate.getOutputs(); + } + + private String _tplVersion() { + return (String) tpl.get(DEFINITION_VERSION); + } + + @SuppressWarnings("unchecked") + private Metadata _tplMetaData() { + Object mdo = tpl.get(METADATA); + if (mdo instanceof LinkedHashMap) { + return new Metadata((Map<String, Object>) mdo); + } else { + return null; + } + } + + private String _tplDescription() { + return (String) tpl.get(DESCRIPTION); + } + + @SuppressWarnings("unchecked") + private ArrayList<Object> _tplImports() { + return (ArrayList<Object>) tpl.get(IMPORTS); + } + + @SuppressWarnings("unchecked") + private ArrayList<Repository> _tplRepositories() { + LinkedHashMap<String, Object> repositories = + (LinkedHashMap<String, Object>) tpl.get(REPOSITORIES); + ArrayList<Repository> reposit = new ArrayList<>(); + if (repositories != null) { + for (Map.Entry<String, Object> me : repositories.entrySet()) { + Repository reposits = new Repository(me.getKey(), me.getValue()); + reposit.add(reposits); + } + } + return reposit; + } + + private LinkedHashMap<String, Object> _tplRelationshipTypes() { + return (LinkedHashMap<String, Object>) _getCustomTypes(RELATIONSHIP_TYPES, null); + } + + @SuppressWarnings("unchecked") + private LinkedHashMap<String, Object> _tplTopologyTemplate() { + return (LinkedHashMap<String, Object>) tpl.get(TOPOLOGY_TEMPLATE); + } + + private ArrayList<Policy> _policies() { + return topologyTemplate.getPolicies(); + } + + private ArrayList<Group> _groups() { + return topologyTemplate.getGroups(); + } + + /** + * Read datatypes field + * + * @return return list of datatypes. + */ + @SuppressWarnings("unchecked") + private HashSet<DataType> getTopologyDataTypes() { + LinkedHashMap<String, Object> value = + (LinkedHashMap<String, Object>) tpl.get(DATA_TYPES); + HashSet<DataType> datatypes = new HashSet<>(); + if (value != null) { + customDefsFinal.putAll(value); + for (Map.Entry<String, Object> me : value.entrySet()) { + DataType datatype = new DataType(me.getKey(), value); + datatypes.add(datatype); + } + } + + + return datatypes; + } + + /** + * This method is used to get consolidated custom definitions from all imports + * It is logically divided in two parts to handle imports; map and list formats. + * Before processing the imports; it sorts them to make sure the current directory imports are + * being processed first and then others. Once sorted; it processes each import one by one in + * recursive manner. + * To avoid cyclic dependency among imports; this method uses a set to keep track of all + * imports which are already processed and filters the imports which occurs more than once. + * + * @param alImports all imports which needs to be processed + * @return the linked hash map containing all import definitions + */ + + @SuppressWarnings("unchecked") + private LinkedHashMap<String, Object> _getAllCustomDefs(Object alImports) { + + + String types[] = { + IMPORTS, NODE_TYPES, CAPABILITY_TYPES, RELATIONSHIP_TYPES, + DATA_TYPES, INTERFACE_TYPES, POLICY_TYPES, GROUP_TYPES + }; + + List<Map<String, Object>> imports = (List<Map<String, Object>>) alImports; + if (imports != null && !imports.isEmpty()) { + if (imports.get(0) instanceof LinkedHashMap) { + imports = sortImports(imports); + + for (Map<String, Object> map : imports) { + List<Map<String, Object>> singleImportList = new ArrayList<>(); + singleImportList.add(map); + + Map<String, String> importNameDetails = getValidFileNameForImportReference(singleImportList); + singleImportList = filterImportsForRecursion(singleImportList, importNameDetails); + + if (!singleImportList.get(0).isEmpty()) { + LinkedHashMap<String, Object> customDefs = _getCustomTypes(types, new ArrayList<>(singleImportList)); + processedImports.add(importNameDetails.get("importFileName")); + + if (customDefs != null) { + customDefsFinal.putAll(customDefs); + + if (customDefs.get(IMPORTS) != null) { + resetPathForRecursiveImports(importNameDetails.get("importRelativeName")); + LinkedHashMap<String, Object> importDefs = _getAllCustomDefs(customDefs.get(IMPORTS)); + customDefsFinal.putAll(importDefs); + } + } + } + } + } else { + LinkedHashMap<String, Object> customDefs = _getCustomTypes(types, new ArrayList<>(imports)); + if (customDefs != null) { + customDefsFinal.putAll(customDefs); + + if (customDefs.get(IMPORTS) != null) { + LinkedHashMap<String, Object> importDefs = _getAllCustomDefs(customDefs.get(IMPORTS)); + customDefsFinal.putAll(importDefs); + } + } + } + } + + // As imports are not custom_types, remove from the dict + customDefsFinal.remove(IMPORTS); + + return customDefsFinal; + } + + /** + * This method is used to sort the imports in order so that same directory + * imports will be processed first + * + * @param customImports the custom imports + * @return the sorted list of imports + */ + private List<Map<String, Object>> sortImports(List<Map<String, Object>> customImports) { + List<Map<String, Object>> finalList1 = new ArrayList<>(); + List<Map<String, Object>> finalList2 = new ArrayList<>(); + Iterator<Map<String, Object>> itr = customImports.iterator(); + while (itr.hasNext()) { + Map<String, Object> innerMap = itr.next(); + if (innerMap.toString().contains("../")) { + finalList2.add(innerMap); + itr.remove(); + } else if (innerMap.toString().contains("/")) { + finalList1.add(innerMap); + itr.remove(); + } + } + + customImports.addAll(finalList1); + customImports.addAll(finalList2); + return customImports; + } + + /** + * This method is used to reset PATH variable after processing of current import file is done + * This is required because of relative path nature of imports present in files. + * + * @param currImportRelativeName the current import relative name + */ + private void resetPathForRecursiveImports(String currImportRelativeName) { + path = getPath(path, currImportRelativeName); + } + + /** + * This is a recursive method which starts from current import and then recursively finds a + * valid path relative to current import file name. + * By doing this it handles all nested hierarchy of imports defined in CSARs + * + * @param path the path + * @param importFileName the import file name + * @return the string containing updated path value + */ + private String getPath(String path, String importFileName) { + String tempFullPath = (Paths.get(path).toAbsolutePath().getParent() + .toString() + File.separator + importFileName.replace("../", "")).replace('\\', '/'); + String tempPartialPath = (Paths.get(path).toAbsolutePath().getParent().toString()).replace('\\', '/'); + if (Files.exists(Paths.get(tempFullPath))) + return tempFullPath; + else + return getPath(tempPartialPath, importFileName); + } + + /** + * This method is used to get full path name for the file which needs to be processed. It helps + * in situation where files are present in different directory and are references as relative + * paths. + * + * @param customImports the custom imports + * @return the map containing import file full and relative paths + */ + private Map<String, String> getValidFileNameForImportReference(List<Map<String, Object>> customImports) { + String importFileName; + Map<String, String> retMap = new HashMap<>(); + for (Map<String, Object> map1 : customImports) { + for (Map.Entry<String, Object> entry : map1.entrySet()) { + Map innerMostMap = (Map) entry.getValue(); + Iterator<Map.Entry<String, String>> it = innerMostMap.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<String, String> val = it.next(); + if (val.getValue().contains("/")) { + importFileName = (Paths.get(rootPath).toAbsolutePath().getParent().toString() + File + .separator + val.getValue().replace("../", "")).replace('\\', '/'); + } else { + importFileName = (Paths.get(path).toAbsolutePath().getParent().toString() + File + .separator + val.getValue().replace("../", "")).replace('\\', '/'); + } + retMap.put("importFileName", importFileName); + retMap.put("importRelativeName", val.getValue()); + } + } + } + return retMap; + } + + /** + * This method is used to filter the imports which already gets processed in previous step. + * It handles the use case of cyclic dependency in imports which may cause Stack Overflow + * exception + * + * @param customImports the custom imports + * @param importNameDetails the import name details + * @return the list containing filtered imports + */ + private List<Map<String, Object>> filterImportsForRecursion(List<Map<String, Object>> + customImports, Map<String, + String> importNameDetails) { + for (Map<String, Object> map1 : customImports) { + for (Map.Entry<String, Object> entry : map1.entrySet()) { + Map innerMostMap = (Map) entry.getValue(); + Iterator<Map.Entry<String, String>> it = innerMostMap.entrySet().iterator(); + while (it.hasNext()) { + it.next(); + if (processedImports.contains(importNameDetails.get("importFileName"))) { + it.remove(); + } + } + } + } + + // Remove Empty elements + Iterator<Map<String, Object>> itr = customImports.iterator(); + while (itr.hasNext()) { + Map innerMap = itr.next(); + Predicate<Map> predicate = p -> p.values().isEmpty(); + innerMap.values().removeIf(predicate); + } + + return customImports; + } + + @SuppressWarnings("unchecked") + private LinkedHashMap<String, Object> _getCustomTypes(Object typeDefinitions, ArrayList<Object> alImports) { + + // Handle custom types defined in imported template files + // This method loads the custom type definitions referenced in "imports" + // section of the TOSCA YAML template. + + LinkedHashMap<String, Object> customDefs = new LinkedHashMap<String, Object>(); + ArrayList<String> typeDefs = new ArrayList<String>(); + if (typeDefinitions instanceof String[]) { + for (String s : (String[]) typeDefinitions) { + typeDefs.add(s); + } + } else { + typeDefs.add((String) typeDefinitions); + } + + if (alImports == null) { + alImports = _tplImports(); + } + + if (alImports != null) { + ImportsLoader customService = new ImportsLoader(alImports, path, typeDefs, tpl); + ArrayList<LinkedHashMap<String, Object>> nestedToscaTpls = customService.getNestedToscaTpls(); + _updateNestedToscaTplsWithTopology(nestedToscaTpls); + + customDefs = customService.getCustomDefs(); + if (customDefs == null) { + return null; + } + } + + //Handle custom types defined in current template file + for (String td : typeDefs) { + if (!td.equals(IMPORTS)) { + LinkedHashMap<String, Object> innerCustomTypes = (LinkedHashMap<String, Object>) tpl.get(td); + if (innerCustomTypes != null) { + customDefs.putAll(innerCustomTypes); + } + } + } + return customDefs; + } + + private void _updateNestedToscaTplsWithTopology(ArrayList<LinkedHashMap<String, Object>> nestedToscaTpls) { + for (LinkedHashMap<String, Object> ntpl : nestedToscaTpls) { + // there is just one key:value pair in ntpl + for (Map.Entry<String, Object> me : ntpl.entrySet()) { + String fileName = me.getKey(); + @SuppressWarnings("unchecked") + LinkedHashMap<String, Object> toscaTpl = (LinkedHashMap<String, Object>) me.getValue(); + if (toscaTpl.get(TOPOLOGY_TEMPLATE) != null) { + if (nestedToscaTplsWithTopology.get(fileName) == null) { + nestedToscaTplsWithTopology.putAll(ntpl); + } + } + } + } + } + + // multi level nesting - RECURSIVE + @SuppressWarnings("unchecked") + private void _handleNestedToscaTemplatesWithTopology(TopologyTemplate tt) { + if (++nestingLoopCounter > MAX_LEVELS) { + log.error("ToscaTemplate - _handleNestedToscaTemplatesWithTopology - Nested Topologies Loop: too many levels, aborting"); + return; + } + // Reset Processed Imports for nested templates + this.processedImports = new HashSet<>(); + for (Map.Entry<String, Object> me : nestedToscaTplsWithTopology.entrySet()) { + LinkedHashMap<String, Object> toscaTpl = + (LinkedHashMap<String, Object>) me.getValue(); + for (NodeTemplate nt : tt.getNodeTemplates()) { + if (_isSubMappedNode(nt, toscaTpl)) { + parsedParams = _getParamsForNestedTemplate(nt); + ArrayList<Object> alim = (ArrayList<Object>) toscaTpl.get(IMPORTS); + LinkedHashMap<String, Object> topologyTpl = + (LinkedHashMap<String, Object>) toscaTpl.get(TOPOLOGY_TEMPLATE); + TopologyTemplate topologyWithSubMapping = + new TopologyTemplate(topologyTpl, + _getAllCustomDefs(alim), + relationshipTypes, + parsedParams, + nt, + resolveGetInput); + nt.setOriginComponentTemplate(topologyWithSubMapping); + if (topologyWithSubMapping.getSubstitutionMappings() != null) { + // Record nested topology templates in top level template + //nestedToscaTemplatesWithTopology.add(topologyWithSubMapping); + // Set substitution mapping object for mapped node + nt.setSubMappingToscaTemplate( + topologyWithSubMapping.getSubstitutionMappings()); + _handleNestedToscaTemplatesWithTopology(topologyWithSubMapping); + } + } + } + } + } + +// private void _handleNestedToscaTemplatesWithTopology() { +// for(Map.Entry<String,Object> me: nestedToscaTplsWithTopology.entrySet()) { +// String fname = me.getKey(); +// LinkedHashMap<String,Object> toscaTpl = +// (LinkedHashMap<String,Object>)me.getValue(); +// for(NodeTemplate nt: nodeTemplates) { +// if(_isSubMappedNode(nt,toscaTpl)) { +// parsedParams = _getParamsForNestedTemplate(nt); +// ArrayList<Object> alim = (ArrayList<Object>)toscaTpl.get(IMPORTS); +// LinkedHashMap<String,Object> topologyTpl = +// (LinkedHashMap<String,Object>)toscaTpl.get(TOPOLOGY_TEMPLATE); +// TopologyTemplate topologyWithSubMapping = +// new TopologyTemplate(topologyTpl, +// //_getAllCustomDefs(null), +// _getAllCustomDefs(alim), +// relationshipTypes, +// parsedParams, +// nt); +// if(topologyWithSubMapping.getSubstitutionMappings() != null) { +// // Record nested topology templates in top level template +// nestedToscaTemplatesWithTopology.add(topologyWithSubMapping); +// // Set substitution mapping object for mapped node +// nt.setSubMappingToscaTemplate( +// topologyWithSubMapping.getSubstitutionMappings()); +// } +// } +// } +// } +// } + + private void _validateField() { + String sVersion = _tplVersion(); + if (sVersion == null) { + ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE245", String.format( + "MissingRequiredField: Template is missing required field \"%s\"", DEFINITION_VERSION))); + } else { + _validateVersion(sVersion); + this.version = sVersion; + } + + for (String sKey : tpl.keySet()) { + boolean bFound = false; + for (String sSection : SECTIONS) { + if (sKey.equals(sSection)) { + bFound = true; + break; + } + } + // check ADDITIONAL_SECTIONS + if (!bFound) { + if (ADDITIONAL_SECTIONS.get(version) != null && + ADDITIONAL_SECTIONS.get(version).contains(sKey)) { + bFound = true; + } + } + if (!bFound) { + ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE246", String.format( + "UnknownFieldError: Template contains unknown field \"%s\"", + sKey))); + } + } + } + + private void _validateVersion(String sVersion) { + boolean bFound = false; + for (String vtv : VALID_TEMPLATE_VERSIONS) { + if (sVersion.equals(vtv)) { + bFound = true; + break; + } + } + if (!bFound) { + ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE247", String.format( + "InvalidTemplateVersion: \"%s\" is invalid. Valid versions are %s", + sVersion, VALID_TEMPLATE_VERSIONS.toString()))); + } else if ((!sVersion.equals("tosca_simple_yaml_1_0") && !sVersion.equals("tosca_simple_yaml_1_1"))) { + EntityType.updateDefinitions(sVersion); + + } + } + + private String _getPath(String _path) throws JToscaException { + if (_path.toLowerCase().endsWith(".yaml") || _path.toLowerCase().endsWith(".yml")) { + return _path; + } else if (_path.toLowerCase().endsWith(".zip") || _path.toLowerCase().endsWith(".csar")) { + // a CSAR archive + CSAR csar = new CSAR(_path, isFile); + if (csar.validate()) { + try { + csar.decompress(); + metaProperties = csar.getMetaProperties(); + } catch (IOException e) { + log.error("ToscaTemplate - _getPath - IOException trying to decompress {}", _path); + return null; + } + isFile = true; // the file has been decompressed locally + csar.cleanup(); + csarTempDir = csar.getTempDir(); + return csar.getTempDir() + File.separator + csar.getMainTemplate(); + } + } else { + ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE248", "ValueError: " + _path + " is not a valid file")); + return null; + } + return null; + } + + private void verifyTemplate() throws JToscaException { + //Criticals + int validationIssuesCaught = ThreadLocalsHolder.getCollector().validationIssuesCaught(); + if (validationIssuesCaught > 0) { + List<String> validationIssueStrings = ThreadLocalsHolder.getCollector().getValidationIssueReport(); + log.trace("####################################################################################################"); + log.trace("ToscaTemplate - verifyTemplate - {} Parsing Critical{} occurred...", validationIssuesCaught, (validationIssuesCaught > 1 ? "s" : "")); + for (String s : validationIssueStrings) { + log.trace("{}. CSAR name - {}", s, inputPath); + } + log.trace("####################################################################################################"); + } + + } + + public String getPath() { + return path; + } + + public String getVersion() { + return version; + } + + public String getDescription() { + return description; + } + + public TopologyTemplate getTopologyTemplate() { + return topologyTemplate; + } + + public Metadata getMetaData() { + return metaData; + } + + public ArrayList<Input> getInputs() { + if (inputs != null) { + inputs.stream().forEach(Input::resetAnnotaions); + } + return inputs; + } + + public ArrayList<Output> getOutputs() { + return outputs; + } + + public ArrayList<Policy> getPolicies() { + return policies; + } + + public ArrayList<Group> getGroups() { + return groups; + } + + public ArrayList<NodeTemplate> getNodeTemplates() { + return nodeTemplates; + } + + public LinkedHashMap<String, Object> getMetaProperties(String propertiesFile) { + return metaProperties.get(propertiesFile); + } + +// private boolean _isSubMappedNode(NodeTemplate nt,LinkedHashMap<String,Object> toscaTpl) { +// // Return True if the nodetemple is substituted +// if(nt != null && nt.getSubMappingToscaTemplate() == null && +// getSubMappingNodeType(toscaTpl).equals(nt.getType()) && +// nt.getInterfaces().size() < 1) { +// return true; +// } +// return false; +// } + + private boolean _isSubMappedNode(NodeTemplate nt, LinkedHashMap<String, Object> toscaTpl) { + // Return True if the nodetemple is substituted + if (nt != null && nt.getSubMappingToscaTemplate() == null && + getSubMappingNodeType(toscaTpl).equals(nt.getType()) && + nt.getInterfaces().size() < 1) { + return true; + } + return false; + } + + private LinkedHashMap<String, Object> _getParamsForNestedTemplate(NodeTemplate nt) { + // Return total params for nested_template + LinkedHashMap<String, Object> pparams; + if (parsedParams != null) { + pparams = parsedParams; + } else { + pparams = new LinkedHashMap<String, Object>(); + } + if (nt != null) { + for (String pname : nt.getProperties().keySet()) { + pparams.put(pname, nt.getPropertyValue(pname)); + } + } + return pparams; + } + + @SuppressWarnings("unchecked") + private String getSubMappingNodeType(LinkedHashMap<String, Object> toscaTpl) { + // Return substitution mappings node type + if (toscaTpl != null) { + return TopologyTemplate.getSubMappingNodeType( + (LinkedHashMap<String, Object>) toscaTpl.get(TOPOLOGY_TEMPLATE)); + } + return null; + } + + public boolean hasNestedTemplates() { + // Return True if the tosca template has nested templates + return nestedToscaTemplatesWithTopology != null && + nestedToscaTemplatesWithTopology.size() >= 1; + + } + + public ArrayList<TopologyTemplate> getNestedTemplates() { + return nestedToscaTemplatesWithTopology; + } + + public ConcurrentHashMap<String, Object> getNestedTopologyTemplates() { + return nestedToscaTplsWithTopology; + } + + /** + * Get datatypes. + * + * @return return list of datatypes. + */ + public HashSet<DataType> getDataTypes() { + return dataTypes; + } + + @Override + public String toString() { + return "ToscaTemplate{" + + "exttools=" + exttools + + ", VALID_TEMPLATE_VERSIONS=" + VALID_TEMPLATE_VERSIONS + + ", ADDITIONAL_SECTIONS=" + ADDITIONAL_SECTIONS + + ", isFile=" + isFile + + ", path='" + path + '\'' + + ", inputPath='" + inputPath + '\'' + + ", parsedParams=" + parsedParams + + ", tpl=" + tpl + + ", version='" + version + '\'' + + ", imports=" + imports + + ", relationshipTypes=" + relationshipTypes + + ", metaData=" + metaData + + ", description='" + description + '\'' + + ", topologyTemplate=" + topologyTemplate + + ", repositories=" + repositories + + ", inputs=" + inputs + + ", relationshipTemplates=" + relationshipTemplates + + ", nodeTemplates=" + nodeTemplates + + ", outputs=" + outputs + + ", policies=" + policies + + ", nestedToscaTplsWithTopology=" + nestedToscaTplsWithTopology + + ", nestedToscaTemplatesWithTopology=" + nestedToscaTemplatesWithTopology + + ", graph=" + graph + + ", csarTempDir='" + csarTempDir + '\'' + + ", nestingLoopCounter=" + nestingLoopCounter + + ", dataTypes=" + dataTypes + + '}'; + } + + public List<Input> getInputs(boolean annotationsRequired) { + if (inputs != null && annotationsRequired) { + inputs.stream().forEach(Input::parseAnnotations); + return inputs; + } + return getInputs(); + } +} + +/*python + +import logging +import os + +from copy import deepcopy +from toscaparser.common.exception import ValidationIssueCollector.collector +from toscaparser.common.exception import InvalidTemplateVersion +from toscaparser.common.exception import MissingRequiredFieldError +from toscaparser.common.exception import UnknownFieldError +from toscaparser.common.exception import ValidationError +from toscaparser.elements.entity_type import update_definitions +from toscaparser.extensions.exttools import ExtTools +import org.openecomp.sdc.toscaparser.api.imports +from toscaparser.prereq.csar import CSAR +from toscaparser.repositories import Repository +from toscaparser.topology_template import TopologyTemplate +from toscaparser.tpl_relationship_graph import ToscaGraph +from toscaparser.utils.gettextutils import _ +import org.openecomp.sdc.toscaparser.api.utils.yamlparser + + +# TOSCA template key names +SECTIONS = (DEFINITION_VERSION, DEFAULT_NAMESPACE, TEMPLATE_NAME, + TOPOLOGY_TEMPLATE, TEMPLATE_AUTHOR, TEMPLATE_VERSION, + DESCRIPTION, IMPORTS, DSL_DEFINITIONS, NODE_TYPES, + RELATIONSHIP_TYPES, RELATIONSHIP_TEMPLATES, + CAPABILITY_TYPES, ARTIFACT_TYPES, DATA_TYPES, INTERFACE_TYPES, + POLICY_TYPES, GROUP_TYPES, REPOSITORIES) = \ + ('tosca_definitions_version', 'tosca_default_namespace', + 'template_name', 'topology_template', 'template_author', + 'template_version', 'description', 'imports', 'dsl_definitions', + 'node_types', 'relationship_types', 'relationship_templates', + 'capability_types', 'artifact_types', 'data_types', + 'interface_types', 'policy_types', 'group_types', 'repositories') +# Sections that are specific to individual template definitions +SPECIAL_SECTIONS = (METADATA) = ('metadata') + +log = logging.getLogger("tosca.model") + +YAML_LOADER = toscaparser.utils.yamlparser.load_yaml + + +class ToscaTemplate(object): + exttools = ExtTools() + + VALID_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0'] + + VALID_TEMPLATE_VERSIONS.extend(exttools.get_versions()) + + ADDITIONAL_SECTIONS = {'tosca_simple_yaml_1_0': SPECIAL_SECTIONS} + + ADDITIONAL_SECTIONS.update(exttools.get_sections()) + + '''Load the template data.''' + def __init__(self, path=None, parsed_params=None, a_file=True, + yaml_dict_tpl=None): + + ValidationIssueCollector.collector.start() + self.a_file = a_file + self.input_path = None + self.path = None + self.tpl = None + self.nested_tosca_tpls_with_topology = {} + self.nested_tosca_templates_with_topology = [] + if path: + self.input_path = path + self.path = self._get_path(path) + if self.path: + self.tpl = YAML_LOADER(self.path, self.a_file) + if yaml_dict_tpl: + msg = (_('Both path and yaml_dict_tpl arguments were ' + 'provided. Using path and ignoring yaml_dict_tpl.')) + log.info(msg) + print(msg) + else: + if yaml_dict_tpl: + self.tpl = yaml_dict_tpl + else: + ValidationIssueCollector.collector.appendException( + ValueError(_('No path or yaml_dict_tpl was provided. ' + 'There is nothing to parse.'))) + + if self.tpl: + self.parsed_params = parsed_params + self._validate_field() + self.version = self._tpl_version() + self.relationship_types = self._tpl_relationship_types() + self.description = self._tpl_description() + self.topology_template = self._topology_template() + self.repositories = self._tpl_repositories() + if self.topology_template.tpl: + self.inputs = self._inputs() + self.relationship_templates = self._relationship_templates() + self.nodetemplates = self._nodetemplates() + self.outputs = self._outputs() + self._handle_nested_tosca_templates_with_topology() + self.graph = ToscaGraph(self.nodetemplates) + + ValidationIssueCollector.collector.stop() + self.verify_template() + + def _topology_template(self): + return TopologyTemplate(self._tpl_topology_template(), + self._get_all_custom_defs(), + self.relationship_types, + self.parsed_params, + None) + + def _inputs(self): + return self.topology_template.inputs + + def _nodetemplates(self): + return self.topology_template.nodetemplates + + def _relationship_templates(self): + return self.topology_template.relationship_templates + + def _outputs(self): + return self.topology_template.outputs + + def _tpl_version(self): + return self.tpl.get(DEFINITION_VERSION) + + def _tpl_description(self): + desc = self.tpl.get(DESCRIPTION) + if desc: + return desc.rstrip() + + def _tpl_imports(self): + return self.tpl.get(IMPORTS) + + def _tpl_repositories(self): + repositories = self.tpl.get(REPOSITORIES) + reposit = [] + if repositories: + for name, val in repositories.items(): + reposits = Repository(name, val) + reposit.append(reposits) + return reposit + + def _tpl_relationship_types(self): + return self._get_custom_types(RELATIONSHIP_TYPES) + + def _tpl_relationship_templates(self): + topology_template = self._tpl_topology_template() + return topology_template.get(RELATIONSHIP_TEMPLATES) + + def _tpl_topology_template(self): + return self.tpl.get(TOPOLOGY_TEMPLATE) + + def _get_all_custom_defs(self, imports=None): + types = [IMPORTS, NODE_TYPES, CAPABILITY_TYPES, RELATIONSHIP_TYPES, + DATA_TYPES, INTERFACE_TYPES, POLICY_TYPES, GROUP_TYPES] + custom_defs_final = {} + custom_defs = self._get_custom_types(types, imports) + if custom_defs: + custom_defs_final.update(custom_defs) + if custom_defs.get(IMPORTS): + import_defs = self._get_all_custom_defs( + custom_defs.get(IMPORTS)) + custom_defs_final.update(import_defs) + + # As imports are not custom_types, removing from the dict + custom_defs_final.pop(IMPORTS, None) + return custom_defs_final + + def _get_custom_types(self, type_definitions, imports=None): + """Handle custom types defined in imported template files + + This method loads the custom type definitions referenced in "imports" + section of the TOSCA YAML template. + """ + custom_defs = {} + type_defs = [] + if not isinstance(type_definitions, list): + type_defs.append(type_definitions) + else: + type_defs = type_definitions + + if not imports: + imports = self._tpl_imports() + + if imports: + custom_service = toscaparser.imports.\ + ImportsLoader(imports, self.path, + type_defs, self.tpl) + + nested_tosca_tpls = custom_service.get_nested_tosca_tpls() + self._update_nested_tosca_tpls_with_topology(nested_tosca_tpls) + + custom_defs = custom_service.get_custom_defs() + if not custom_defs: + return + + # Handle custom types defined in current template file + for type_def in type_defs: + if type_def != IMPORTS: + inner_custom_types = self.tpl.get(type_def) or {} + if inner_custom_types: + custom_defs.update(inner_custom_types) + return custom_defs + + def _update_nested_tosca_tpls_with_topology(self, nested_tosca_tpls): + for tpl in nested_tosca_tpls: + filename, tosca_tpl = list(tpl.items())[0] + if (tosca_tpl.get(TOPOLOGY_TEMPLATE) and + filename not in list( + self.nested_tosca_tpls_with_topology.keys())): + self.nested_tosca_tpls_with_topology.update(tpl) + + def _handle_nested_tosca_templates_with_topology(self): + for fname, tosca_tpl in self.nested_tosca_tpls_with_topology.items(): + for nodetemplate in self.nodetemplates: + if self._is_sub_mapped_node(nodetemplate, tosca_tpl): + parsed_params = self._get_params_for_nested_template( + nodetemplate) + topology_tpl = tosca_tpl.get(TOPOLOGY_TEMPLATE) + topology_with_sub_mapping = TopologyTemplate( + topology_tpl, + self._get_all_custom_defs(), + self.relationship_types, + parsed_params, + nodetemplate) + if topology_with_sub_mapping.substitution_mappings: + # Record nested topo templates in top level template + self.nested_tosca_templates_with_topology.\ + append(topology_with_sub_mapping) + # Set substitution mapping object for mapped node + nodetemplate.sub_mapping_tosca_template = \ + topology_with_sub_mapping.substitution_mappings + + def _validate_field(self): + version = self._tpl_version() + if not version: + ValidationIssueCollector.collector.appendException( + MissingRequiredFieldError(what='Template', + required=DEFINITION_VERSION)) + else: + self._validate_version(version) + self.version = version + + for name in self.tpl: + if (name not in SECTIONS and + name not in self.ADDITIONAL_SECTIONS.get(version, ())): + ValidationIssueCollector.collector.appendException( + UnknownFieldError(what='Template', field=name)) + + def _validate_version(self, version): + if version not in self.VALID_TEMPLATE_VERSIONS: + ValidationIssueCollector.collector.appendException( + InvalidTemplateVersion( + what=version, + valid_versions=', '. join(self.VALID_TEMPLATE_VERSIONS))) + else: + if version != 'tosca_simple_yaml_1_0': + update_definitions(version) + + def _get_path(self, path): + if path.lower().endswith(('.yaml','.yml')): + return path + elif path.lower().endswith(('.zip', '.csar')): + # a CSAR archive + csar = CSAR(path, self.a_file) + if csar.validate(): + csar.decompress() + self.a_file = True # the file has been decompressed locally + return os.path.join(csar.temp_dir, csar.get_main_template()) + else: + ValidationIssueCollector.collector.appendException( + ValueError(_('"%(path)s" is not a valid file.') + % {'path': path})) + + def verify_template(self): + if ValidationIssueCollector.collector.exceptionsCaught(): + if self.input_path: + raise ValidationError( + message=(_('\nThe input "%(path)s" failed validation with ' + 'the following error(s): \n\n\t') + % {'path': self.input_path}) + + '\n\t'.join(ValidationIssueCollector.collector.getExceptionsReport())) + else: + raise ValidationError( + message=_('\nThe pre-parsed input failed validation with ' + 'the following error(s): \n\n\t') + + '\n\t'.join(ValidationIssueCollector.collector.getExceptionsReport())) + else: + if self.input_path: + msg = (_('The input "%(path)s" successfully passed ' + 'validation.') % {'path': self.input_path}) + else: + msg = _('The pre-parsed input successfully passed validation.') + + log.info(msg) + + def _is_sub_mapped_node(self, nodetemplate, tosca_tpl): + """Return True if the nodetemple is substituted.""" + if (nodetemplate and not nodetemplate.sub_mapping_tosca_template and + self.get_sub_mapping_node_type(tosca_tpl) == nodetemplate.type + and len(nodetemplate.interfaces) < 1): + return True + else: + return False + + def _get_params_for_nested_template(self, nodetemplate): + """Return total params for nested_template.""" + parsed_params = deepcopy(self.parsed_params) \ + if self.parsed_params else {} + if nodetemplate: + for pname in nodetemplate.get_properties(): + parsed_params.update({pname: + nodetemplate.get_property_value(pname)}) + return parsed_params + + def get_sub_mapping_node_type(self, tosca_tpl): + """Return substitution mappings node type.""" + if tosca_tpl: + return TopologyTemplate.get_sub_mapping_node_type( + tosca_tpl.get(TOPOLOGY_TEMPLATE)) + + def _has_substitution_mappings(self): + """Return True if the template has valid substitution mappings.""" + return self.topology_template is not None and \ + self.topology_template.substitution_mappings is not None + + def has_nested_templates(self): + """Return True if the tosca template has nested templates.""" + return self.nested_tosca_templates_with_topology is not None and \ + len(self.nested_tosca_templates_with_topology) >= 1 +*/
\ No newline at end of file |