Fix checkstyle violations in sdc/jtosca
[sdc/sdc-tosca.git] / src / main / java / org / onap / sdc / toscaparser / api / SubstitutionMappings.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.sdc.toscaparser.api;
22
23 import org.onap.sdc.toscaparser.api.common.JToscaValidationIssue;
24 import org.onap.sdc.toscaparser.api.elements.NodeType;
25 import org.onap.sdc.toscaparser.api.elements.PropertyDef;
26 import org.onap.sdc.toscaparser.api.parameters.Input;
27 import org.onap.sdc.toscaparser.api.parameters.Output;
28 import org.onap.sdc.toscaparser.api.utils.ThreadLocalsHolder;
29
30 import java.util.ArrayList;
31 import java.util.HashSet;
32 import java.util.LinkedHashMap;
33 import java.util.List;
34
35
36 public class SubstitutionMappings {
37     // SubstitutionMappings class declaration
38
39     // SubstitutionMappings exports the topology template as an
40     // implementation of a Node type.
41
42     private static final String NODE_TYPE = "node_type";
43     private static final String REQUIREMENTS = "requirements";
44     private static final String CAPABILITIES = "capabilities";
45
46     private static final String SECTIONS[] = {NODE_TYPE, REQUIREMENTS, CAPABILITIES};
47
48     private static final String OPTIONAL_OUTPUTS[] = {"tosca_id", "tosca_name", "state"};
49
50     private LinkedHashMap<String, Object> subMappingDef;
51     private ArrayList<NodeTemplate> nodetemplates;
52     private ArrayList<Input> inputs;
53     private ArrayList<Output> outputs;
54     private ArrayList<Group> groups;
55     private NodeTemplate subMappedNodeTemplate;
56     private LinkedHashMap<String, Object> customDefs;
57     private LinkedHashMap<String, Object> _capabilities;
58     private LinkedHashMap<String, Object> _requirements;
59
60     public SubstitutionMappings(LinkedHashMap<String, Object> smsubMappingDef,
61                                 ArrayList<NodeTemplate> smnodetemplates,
62                                 ArrayList<Input> sminputs,
63                                 ArrayList<Output> smoutputs,
64                                 ArrayList<Group> smgroups,
65                                 NodeTemplate smsubMappedNodeTemplate,
66                                 LinkedHashMap<String, Object> smcustomDefs) {
67
68         subMappingDef = smsubMappingDef;
69         nodetemplates = smnodetemplates;
70         inputs = sminputs != null ? sminputs : new ArrayList<Input>();
71         outputs = smoutputs != null ? smoutputs : new ArrayList<Output>();
72         groups = smgroups != null ? smgroups : new ArrayList<Group>();
73         subMappedNodeTemplate = smsubMappedNodeTemplate;
74         customDefs = smcustomDefs != null ? smcustomDefs : new LinkedHashMap<String, Object>();
75         _validate();
76
77         _capabilities = null;
78         _requirements = null;
79     }
80
81     public String getType() {
82         if (subMappingDef != null) {
83             return (String) subMappingDef.get(NODE_TYPE);
84         }
85         return null;
86     }
87
88     public ArrayList<NodeTemplate> getNodeTemplates() {
89         return nodetemplates;
90     }
91
92         /*
93     @classmethod
94     def get_node_type(cls, sub_mapping_def):
95         if isinstance(sub_mapping_def, dict):
96             return sub_mapping_def.get(cls.NODE_TYPE)
97         */
98
99     public static String stGetNodeType(LinkedHashMap<String, Object> _subMappingDef) {
100         if (_subMappingDef instanceof LinkedHashMap) {
101             return (String) _subMappingDef.get(NODE_TYPE);
102         }
103         return null;
104     }
105
106     public String getNodeType() {
107         return (String) subMappingDef.get(NODE_TYPE);
108     }
109
110     public ArrayList<Input> getInputs() {
111         return inputs;
112     }
113
114     public ArrayList<Group> getGroups() {
115         return groups;
116     }
117
118     public LinkedHashMap<String, Object> getCapabilities() {
119         return (LinkedHashMap<String, Object>) subMappingDef.get(CAPABILITIES);
120     }
121
122     public LinkedHashMap<String, Object> getRequirements() {
123         return (LinkedHashMap<String, Object>) subMappingDef.get(REQUIREMENTS);
124     }
125
126     public NodeType getNodeDefinition() {
127         return new NodeType(getNodeType(), customDefs);
128     }
129
130     private void _validate() {
131         // Basic validation
132         _validateKeys();
133         _validateType();
134
135         // SubstitutionMapping class syntax validation
136         _validateInputs();
137         _validateCapabilities();
138         _validateRequirements();
139         _validateOutputs();
140     }
141
142     private void _validateKeys() {
143         // validate the keys of substitution mappings
144         for (String key : subMappingDef.keySet()) {
145             boolean bFound = false;
146             for (String s : SECTIONS) {
147                 if (s.equals(key)) {
148                     bFound = true;
149                     break;
150                 }
151             }
152             if (!bFound) {
153                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE232", String.format(
154                         "UnknownFieldError: SubstitutionMappings contain unknown field \"%s\"",
155                         key)));
156             }
157         }
158     }
159
160     private void _validateType() {
161         // validate the node_type of substitution mappings
162         String nodeType = (String) subMappingDef.get(NODE_TYPE);
163         if (nodeType == null) {
164             ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE233", String.format(
165                     "MissingRequiredFieldError: SubstitutionMappings used in topology_template is missing required field \"%s\"",
166                     NODE_TYPE)));
167         }
168         Object nodeTypeDef = customDefs.get(nodeType);
169         if (nodeTypeDef == null) {
170             ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE234", String.format(
171                     "InvalidNodeTypeError: \"%s\" is invalid", nodeType)));
172         }
173     }
174
175     private void _validateInputs() {
176         // validate the inputs of substitution mappings.
177
178         // The inputs defined by the topology template have to match the
179         // properties of the node type or the substituted node. If there are
180         // more inputs than the substituted node has properties, default values
181         //must be defined for those inputs.
182
183         HashSet<String> allInputs = new HashSet<>();
184         for (Input inp : inputs) {
185             allInputs.add(inp.getName());
186         }
187         HashSet<String> requiredProperties = new HashSet<>();
188         for (PropertyDef pd : getNodeDefinition().getPropertiesDefObjects()) {
189             if (pd.isRequired() && pd.getDefault() == null) {
190                 requiredProperties.add(pd.getName());
191             }
192         }
193         // Must provide inputs for required properties of node type.
194         for (String property : requiredProperties) {
195             // Check property which is 'required' and has no 'default' value
196             if (!allInputs.contains(property)) {
197                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE235", String.format(
198                         "MissingRequiredInputError: SubstitutionMappings with node_type \"%s\" is missing required input \"%s\"",
199                         getNodeType(), property)));
200             }
201         }
202         // If the optional properties of node type need to be customized by
203         // substituted node, it also is necessary to define inputs for them,
204         // otherwise they are not mandatory to be defined.
205         HashSet<String> customizedParameters = new HashSet<>();
206         if (subMappedNodeTemplate != null) {
207             customizedParameters.addAll(subMappedNodeTemplate.getProperties().keySet());
208         }
209         HashSet<String> allProperties = new HashSet<String>(
210                 getNodeDefinition().getPropertiesDef().keySet());
211         HashSet<String> diffset = customizedParameters;
212         diffset.removeAll(allInputs);
213         for (String parameter : diffset) {
214             if (allProperties.contains(parameter)) {
215                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE236", String.format(
216                         "MissingRequiredInputError: SubstitutionMappings with node_type \"%s\" is missing required input \"%s\"",
217                         getNodeType(), parameter)));
218             }
219         }
220         // Additional inputs are not in the properties of node type must
221         // provide default values. Currently the scenario may not happen
222         // because of parameters validation in nodetemplate, here is a
223         // guarantee.
224         for (Input inp : inputs) {
225             diffset = allInputs;
226             diffset.removeAll(allProperties);
227             if (diffset.contains(inp.getName()) && inp.getDefault() == null) {
228                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE237", String.format(
229                         "MissingRequiredInputError: SubstitutionMappings with node_type \"%s\" is missing rquired input \"%s\"",
230                         getNodeType(), inp.getName())));
231             }
232         }
233     }
234
235     private void _validateCapabilities() {
236         // validate the capabilities of substitution mappings
237
238         // The capabilities must be in node template which be mapped.
239         LinkedHashMap<String, Object> tplsCapabilities =
240                 (LinkedHashMap<String, Object>) subMappingDef.get(CAPABILITIES);
241         List<CapabilityAssignment> nodeCapabilities = null;
242         if (subMappedNodeTemplate != null) {
243             nodeCapabilities = subMappedNodeTemplate.getCapabilities().getAll();
244         }
245         if (nodeCapabilities != null) {
246             for (CapabilityAssignment cap : nodeCapabilities) {
247                 if (tplsCapabilities != null && tplsCapabilities.get(cap.getName()) == null) {
248                     ; //pass
249                     // ValidationIssueCollector.appendException(
250                     //    UnknownFieldError(what='SubstitutionMappings',
251                     //                      field=cap))
252                 }
253             }
254         }
255     }
256
257     private void _validateRequirements() {
258         // validate the requirements of substitution mappings
259         //*****************************************************
260         //TO-DO - Different from Python code!! one is a bug...
261         //*****************************************************
262         // The requirements must be in node template which be mapped.
263         LinkedHashMap<String, Object> tplsRequirements =
264                 (LinkedHashMap<String, Object>) subMappingDef.get(REQUIREMENTS);
265         List<RequirementAssignment> nodeRequirements = null;
266         if (subMappedNodeTemplate != null) {
267             nodeRequirements = subMappedNodeTemplate.getRequirements().getAll();
268         }
269         if (nodeRequirements != null) {
270             for (RequirementAssignment ro : nodeRequirements) {
271                 String cap = ro.getName();
272                 if (tplsRequirements != null && tplsRequirements.get(cap) == null) {
273                     ; //pass
274                     // ValidationIssueCollector.appendException(
275                     //    UnknownFieldError(what='SubstitutionMappings',
276                     //                      field=cap))
277                 }
278             }
279         }
280     }
281
282     private void _validateOutputs() {
283         // validate the outputs of substitution mappings.
284
285         // The outputs defined by the topology template have to match the
286         // attributes of the node type or the substituted node template,
287         // and the observable attributes of the substituted node template
288         // have to be defined as attributes of the node type or outputs in
289         // the topology template.
290
291         // The outputs defined by the topology template have to match the
292         // attributes of the node type according to the specification, but
293         // it's reasonable that there are more inputs than the node type
294         // has properties, the specification will be amended?
295
296         for (Output output : outputs) {
297             Object ado = getNodeDefinition().getAttributesDef();
298             if (ado != null && ((LinkedHashMap<String, Object>) ado).get(output.getName()) == null) {
299                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE238", String.format(
300                         "UnknownOutputError: Unknown output \"%s\" in SubstitutionMappings with node_type \"%s\"",
301                         output.getName(), getNodeType())));
302             }
303         }
304     }
305
306     @Override
307     public String toString() {
308         return "SubstitutionMappings{" +
309 //                              "subMappingDef=" + subMappingDef +
310 //                              ", nodetemplates=" + nodetemplates +
311 //                              ", inputs=" + inputs +
312 //                              ", outputs=" + outputs +
313 //                              ", groups=" + groups +
314                 ", subMappedNodeTemplate=" + (subMappedNodeTemplate == null ? "" : subMappedNodeTemplate.getName()) +
315 //                              ", customDefs=" + customDefs +
316 //                              ", _capabilities=" + _capabilities +
317 //                              ", _requirements=" + _requirements +
318                 '}';
319     }
320
321     @Deprecated
322     public String toLimitedString() {
323         return "SubstitutionMappings{" +
324                 "subMappingDef=" + subMappingDef +
325                 ", nodetemplates=" + nodetemplates +
326                 ", inputs=" + inputs +
327                 ", outputs=" + outputs +
328                 ", groups=" + groups +
329                 ", subMappedNodeTemplate=" + (subMappedNodeTemplate == null ? "" : subMappedNodeTemplate.getName()) +
330                 ", customDefs=" + customDefs +
331                 ", _capabilities=" + _capabilities +
332                 ", _requirements=" + _requirements +
333                 '}';
334     }
335 }
336
337
338 /*python
339
340 from toscaparser.common.exception import ValidationIssueCollector
341 from toscaparser.common.exception import InvalidNodeTypeError
342 from toscaparser.common.exception import MissingDefaultValueError
343 from toscaparser.common.exception import MissingRequiredFieldError
344 from toscaparser.common.exception import MissingRequiredInputError
345 from toscaparser.common.exception import UnknownFieldError
346 from toscaparser.common.exception import UnknownOutputError
347 from toscaparser.elements.nodetype import NodeType
348 from toscaparser.utils.gettextutils import _
349
350 log = logging.getLogger('tosca')
351
352
353 class SubstitutionMappings(object):
354     '''SubstitutionMappings class declaration
355
356     SubstitutionMappings exports the topology template as an
357     implementation of a Node type.
358     '''
359
360     SECTIONS = (NODE_TYPE, REQUIREMENTS, CAPABILITIES) = \
361                ('node_type', 'requirements', 'capabilities')
362
363     OPTIONAL_OUTPUTS = ['tosca_id', 'tosca_name', 'state']
364
365     def __init__(self, sub_mapping_def, nodetemplates, inputs, outputs,
366                  sub_mapped_node_template, custom_defs):
367         self.nodetemplates = nodetemplates
368         self.sub_mapping_def = sub_mapping_def
369         self.inputs = inputs or []
370         self.outputs = outputs or []
371         self.sub_mapped_node_template = sub_mapped_node_template
372         self.custom_defs = custom_defs or {}
373         self._validate()
374
375         self._capabilities = None
376         self._requirements = None
377
378     @property
379     def type(self):
380         if self.sub_mapping_def:
381             return self.sub_mapping_def.get(self.NODE_TYPE)
382
383     @classmethod
384     def get_node_type(cls, sub_mapping_def):
385         if isinstance(sub_mapping_def, dict):
386             return sub_mapping_def.get(cls.NODE_TYPE)
387
388     @property
389     def node_type(self):
390         return self.sub_mapping_def.get(self.NODE_TYPE)
391
392     @property
393     def capabilities(self):
394         return self.sub_mapping_def.get(self.CAPABILITIES)
395
396     @property
397     def requirements(self):
398         return self.sub_mapping_def.get(self.REQUIREMENTS)
399
400     @property
401     def node_definition(self):
402         return NodeType(self.node_type, self.custom_defs)
403
404     def _validate(self):
405         # Basic validation
406         self._validate_keys()
407         self._validate_type()
408
409         # SubstitutionMapping class syntax validation
410         self._validate_inputs()
411         self._validate_capabilities()
412         self._validate_requirements()
413         self._validate_outputs()
414
415     def _validate_keys(self):
416         """validate the keys of substitution mappings."""
417         for key in self.sub_mapping_def.keys():
418             if key not in self.SECTIONS:
419                 ValidationIssueCollector.appendException(
420                     UnknownFieldError(what=_('SubstitutionMappings'),
421                                       field=key))
422
423     def _validate_type(self):
424         """validate the node_type of substitution mappings."""
425         node_type = self.sub_mapping_def.get(self.NODE_TYPE)
426         if not node_type:
427             ValidationIssueCollector.appendException(
428                 MissingRequiredFieldError(
429                     what=_('SubstitutionMappings used in topology_template'),
430                     required=self.NODE_TYPE))
431
432         node_type_def = self.custom_defs.get(node_type)
433         if not node_type_def:
434             ValidationIssueCollector.appendException(
435                 InvalidNodeTypeError(what=node_type))
436
437     def _validate_inputs(self):
438         """validate the inputs of substitution mappings.
439
440         The inputs defined by the topology template have to match the
441         properties of the node type or the substituted node. If there are
442         more inputs than the substituted node has properties, default values
443         must be defined for those inputs.
444         """
445
446         all_inputs = set([input.name for input in self.inputs])
447         required_properties = set([p.name for p in
448                                    self.node_definition.
449                                    get_properties_def_objects()
450                                    if p.required and p.default is None])
451         # Must provide inputs for required properties of node type.
452         for property in required_properties:
453             # Check property which is 'required' and has no 'default' value
454             if property not in all_inputs:
455                 ValidationIssueCollector.appendException(
456                     MissingRequiredInputError(
457                         what=_('SubstitutionMappings with node_type ')
458                         + self.node_type,
459                         input_name=property))
460
461         # If the optional properties of node type need to be customized by
462         # substituted node, it also is necessary to define inputs for them,
463         # otherwise they are not mandatory to be defined.
464         customized_parameters = set(self.sub_mapped_node_template
465                                     .get_properties().keys()
466                                     if self.sub_mapped_node_template else [])
467         all_properties = set(self.node_definition.get_properties_def())
468         for parameter in customized_parameters - all_inputs:
469             if parameter in all_properties:
470                 ValidationIssueCollector.appendException(
471                     MissingRequiredInputError(
472                         what=_('SubstitutionMappings with node_type ')
473                         + self.node_type,
474                         input_name=parameter))
475
476         # Additional inputs are not in the properties of node type must
477         # provide default values. Currently the scenario may not happen
478         # because of parameters validation in nodetemplate, here is a
479         # guarantee.
480         for input in self.inputs:
481             if input.name in all_inputs - all_properties \
482                and input.default is None:
483                 ValidationIssueCollector.appendException(
484                     MissingDefaultValueError(
485                         what=_('SubstitutionMappings with node_type ')
486                         + self.node_type,
487                         input_name=input.name))
488
489     def _validate_capabilities(self):
490         """validate the capabilities of substitution mappings."""
491
492         # The capabilites must be in node template wchich be mapped.
493         tpls_capabilities = self.sub_mapping_def.get(self.CAPABILITIES)
494         node_capabiliteys = self.sub_mapped_node_template.get_capabilities() \
495             if self.sub_mapped_node_template else None
496         for cap in node_capabiliteys.keys() if node_capabiliteys else []:
497             if (tpls_capabilities and
498                     cap not in list(tpls_capabilities.keys())):
499                 pass
500                 # ValidationIssueCollector.appendException(
501                 #    UnknownFieldError(what='SubstitutionMappings',
502                 #                      field=cap))
503
504     def _validate_requirements(self):
505         """validate the requirements of substitution mappings."""
506
507         # The requirements must be in node template wchich be mapped.
508         tpls_requirements = self.sub_mapping_def.get(self.REQUIREMENTS)
509         node_requirements = self.sub_mapped_node_template.requirements \
510             if self.sub_mapped_node_template else None
511         for req in node_requirements if node_requirements else []:
512             if (tpls_requirements and
513                     req not in list(tpls_requirements.keys())):
514                 pass
515                 # ValidationIssueCollector.appendException(
516                 #    UnknownFieldError(what='SubstitutionMappings',
517                 #                      field=req))
518
519     def _validate_outputs(self):
520         """validate the outputs of substitution mappings.
521
522         The outputs defined by the topology template have to match the
523         attributes of the node type or the substituted node template,
524         and the observable attributes of the substituted node template
525         have to be defined as attributes of the node type or outputs in
526         the topology template.
527         """
528
529         # The outputs defined by the topology template have to match the
530         # attributes of the node type according to the specification, but
531         # it's reasonable that there are more inputs than the node type
532         # has properties, the specification will be amended?
533         for output in self.outputs:
534             if output.name not in self.node_definition.get_attributes_def():
535                 ValidationIssueCollector.appendException(
536                     UnknownOutputError(
537                         where=_('SubstitutionMappings with node_type ')
538                         + self.node_type,
539                         output_name=output.name))*/