1f5f658e829bf0927c35a29403dfca36a75a8729
[vvp/validation-scripts.git] / ice_validator / tests / utils / ports.py
1 # -*- coding: utf8 -*-
2 # ============LICENSE_START=======================================================
3 # org.onap.vvp/validation-scripts
4 # ===================================================================
5 # Copyright © 2019 AT&T Intellectual Property. All rights reserved.
6 # ===================================================================
7 #
8 # Unless otherwise specified, all software contained herein is licensed
9 # under the Apache License, Version 2.0 (the "License");
10 # you may not use this software except in compliance with the License.
11 # You may obtain a copy of the License at
12 #
13 #             http://www.apache.org/licenses/LICENSE-2.0
14 #
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS,
17 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 # See the License for the specific language governing permissions and
19 # limitations under the License.
20 #
21 #
22 #
23 # Unless otherwise specified, all documentation contained herein is licensed
24 # under the Creative Commons License, Attribution 4.0 Intl. (the "License");
25 # you may not use this documentation except in compliance with the License.
26 # You may obtain a copy of the License at
27 #
28 #             https://creativecommons.org/licenses/by/4.0/
29 #
30 # Unless required by applicable law or agreed to in writing, documentation
31 # distributed under the License is distributed on an "AS IS" BASIS,
32 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
33 # See the License for the specific language governing permissions and
34 # limitations under the License.
35 #
36 # ============LICENSE_END============================================
37 #
38 #
39 from tests.structures import Heat
40 from tests.helpers import parameter_type_to_heat_type, prop_iterator
41 from . import nested_dict
42
43
44 AAP_EXEMPT_CAVEAT = (
45     "If this VNF is not able to adhere to this requirement, please consult the Heat "
46     "Orchestration Template guidelines for more information. If you are knowingly "
47     "violating this requirement after reading the guidelines, then add the parameter "
48     "to the aap_exempt list under this resources metadata to suppress this warning."
49 )
50
51
52 def get_aap_exemptions(resource_props):
53     """
54     Gets the list of parameters that the Heat author has exempted from following
55     the naming conventions associated with AAP.
56
57     :param resource_props: dict of properties under the resource ID
58     :return: list of all parameters to exempt or an empty list
59     """
60     metadata = resource_props.get("metadata") or {}
61     return metadata.get("aap_exempt") or []
62
63
64 def check_parameter_format(
65     yaml_file, regx, intext, resource_processor, *properties, exemptions_allowed=False
66 ):
67     """
68     yaml_file: input file to check
69     regx: dictionary containing the regex to use to validate parameter
70     intext: internal or external
71     resource_processor: resource type specific helper, defined in structures.py
72     properties: arg list of property that is being checked
73     exemptions_allowed: If True, then parameters in the aap_exempt list are allowed to
74                         not follow the rules
75     """
76
77     invalid_parameters = []
78     heat = Heat(filepath=yaml_file)
79     resource_type = resource_processor.resource_type
80     resources = heat.get_resource_by_type(resource_type)
81     for rid, resource in resources.items():
82         resource_intext, port_match = resource_processor.get_rid_match_tuple(rid)
83         if not port_match:
84             continue  # port resource ID not formatted correctely
85
86         if (
87             resource_intext != intext
88         ):  # skipping if type (internal/external) doesn't match
89             continue
90         for param in prop_iterator(resource, *properties):
91             if (
92                 param
93                 and isinstance(param, dict)
94                 and "get_resource" not in param
95                 and "get_attr" not in param
96             ):
97                 template_parameters = []
98                 if "str_replace" in param:
99                     # print(param)
100                     template_parameters.extend(
101                         v
102                         for k, v in nested_dict.get(
103                             param, "str_replace", "params", default={}
104                         ).items()
105                     )
106                 else:
107                     template_parameters.append(param)
108
109                 invalid_template_parameters = []
110                 for template_parameter in template_parameters:
111                     # Looping through each parameter to check
112                     # the only case where there can be more than 1 is
113                     # if using str_replace
114                     msg = validate_port_parameter(
115                         resource_type,
116                         rid,
117                         properties,
118                         template_parameter,
119                         resource_intext,
120                         resource,
121                         regx,
122                         port_match,
123                         exemptions_allowed,
124                     )
125
126                     if not msg:
127                         # if we found a valid parameter then
128                         # reset invalide_template_parameters
129                         # and break out of loop
130                         invalid_template_parameters = []
131                         break
132                     else:
133                         # haven't found a valid parameter yet
134                         invalid_template_parameters.append(msg)
135
136                 invalid_parameters.extend(x for x in invalid_template_parameters)
137
138     assert not invalid_parameters, "%s" % "\n".join(invalid_parameters)
139
140
141 def validate_port_parameter(
142     resource_type,
143     rid,
144     properties,
145     param,
146     resource_intext,
147     resource,
148     regx,
149     port_match,
150     exemptions_allowed,
151 ):
152     """
153     Performs 4 validations
154
155     1) param actually uses get_param
156     2) parameter_type + network_type (internal/external) is a valid combination
157     3) parameter format matches expected format from input dictionary
158     4) the vm_type or network role from resource matches parameter
159
160     If the parameter is present in the resource metadata
161     and exemptions are allowed, then the validation will be skipped.
162     """
163     parameter = param.get("get_param")
164     if not parameter:
165         return (
166             "Unexpected parameter format for {} {} property {}: {}. "
167             "Please consult the heat guidelines documentation for details."
168         ).format(resource_type, rid, properties, param)
169
170     # getting parameter if the get_param uses list, and getting official
171     # HEAT parameter type
172     parameter_type = parameter_type_to_heat_type(parameter)
173     if parameter_type == "comma_delimited_list":
174         parameter = parameter[0]
175     elif parameter_type != "string":
176         return None
177
178     if exemptions_allowed and parameter in get_aap_exemptions(resource):
179         return None
180
181     # if parameter type is not in regx dict, then it is not supported
182     # by automation
183     regx_dict = regx[resource_intext].get(parameter_type)
184     if not regx_dict:
185         msg = (
186             "{} {} {} parameter {} defined as type {} "
187             "which is required by platform data model for proper "
188             "assignment and inventory."
189         ).format(resource_type, rid, properties, parameter, parameter_type)
190         if exemptions_allowed:
191             msg = "WARNING: {} {}".format(msg, AAP_EXEMPT_CAVEAT)
192         return msg
193
194     msg = validate_parameter_format(
195         regx, parameter_type, resource_intext, parameter, rid, exemptions_allowed
196     )
197     if msg:
198         return msg
199
200     # checking that parameter includes correct vm_type/network_role
201     parameter_checks = regx.get("parameter_to_resource_comparisons", [])
202     for check in parameter_checks:
203         msg = mismatch_resource_and_parameter_attribute(
204             check, port_match, parameter, rid
205         )
206         if msg:
207             return msg
208
209     return None
210
211
212 def validate_parameter_format(
213     regx, parameter_type, resource_intext, parameter, rid, exemptions_allowed
214 ):
215     """Checks if a parameter format matches the expected format
216     from input format dictionary"""
217     msg = None
218     regexp = regx[resource_intext][parameter_type]["machine"]
219     readable_format = regx[resource_intext][parameter_type]["readable"]
220     match = regexp.match(parameter)
221     if not match:
222         msg = (
223             "{} property parameter {} does not follow {} "
224             "format {} which is required by platform data model for proper "
225             "assignment and inventory."
226         ).format(rid, parameter, resource_intext, readable_format)
227         if exemptions_allowed:
228             msg = "WARNING: {} {}".format(msg, AAP_EXEMPT_CAVEAT)
229
230     return msg
231
232
233 def mismatch_resource_and_parameter_attribute(check, resource_re_match, parameter, rid):
234     """Compares vm_type or network_role from resource
235     is the same as found in parameter"""
236     resource_match = resource_re_match.group(check)
237     if (
238         resource_match
239         and not parameter.startswith(resource_match)
240         and parameter.find("_{}_".format(resource_match)) == -1
241     ):
242         return ("{0} {1} does not match parameter {2} {1}").format(
243             rid, check, parameter
244         )
245
246
247 def get_list_of_ports_attached_to_nova_server(nova_server):
248     networks_list = nova_server.get("properties", {}).get("networks")
249
250     port_ids = []
251     if networks_list:
252         for network in networks_list:
253             network_prop = network.get("port")
254             if network_prop:
255                 pid = network_prop.get("get_param")
256                 if not pid:
257                     pid = network_prop.get("get_resource")
258                 port_ids.append(pid)
259
260     return port_ids