[VVP] Updating vm_type class test to proceed if no cinder
[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     if isinstance(param, dict) and "get_param" in param:
164         parameter = param.get("get_param")
165     else:
166         return (
167             "Unexpected parameter format for {} {} property {}: {}. "
168             "Please consult the heat guidelines documentation for details."
169         ).format(resource_type, rid, properties, param)
170
171     # getting parameter if the get_param uses list, and getting official
172     # HEAT parameter type
173     parameter_type = parameter_type_to_heat_type(parameter)
174     if parameter_type == "comma_delimited_list":
175         parameter = parameter[0]
176     elif parameter_type != "string":
177         return None
178
179     if exemptions_allowed and parameter in get_aap_exemptions(resource):
180         return None
181
182     # if parameter type is not in regx dict, then it is not supported
183     # by automation
184     regx_dict = regx[resource_intext].get(parameter_type)
185     if not regx_dict:
186         msg = (
187             "{} {} {} parameter {} defined as type {} "
188             "which is required by platform data model for proper "
189             "assignment and inventory."
190         ).format(resource_type, rid, properties, parameter, parameter_type)
191         if exemptions_allowed:
192             msg = "WARNING: {} {}".format(msg, AAP_EXEMPT_CAVEAT)
193         return msg
194
195     msg = validate_parameter_format(
196         regx, parameter_type, resource_intext, parameter, rid, exemptions_allowed
197     )
198     if msg:
199         return msg
200
201     # checking that parameter includes correct vm_type/network_role
202     parameter_checks = regx.get("parameter_to_resource_comparisons", [])
203     for check in parameter_checks:
204         msg = mismatch_resource_and_parameter_attribute(
205             check, port_match, parameter, rid
206         )
207         if msg:
208             return msg
209
210     return None
211
212
213 def validate_parameter_format(
214     regx, parameter_type, resource_intext, parameter, rid, exemptions_allowed
215 ):
216     """Checks if a parameter format matches the expected format
217     from input format dictionary"""
218     msg = None
219     regexp = regx[resource_intext][parameter_type]["machine"]
220     readable_format = regx[resource_intext][parameter_type]["readable"]
221     match = regexp.match(parameter)
222     if not match:
223         msg = (
224             "{} property parameter {} does not follow {} "
225             "format {} which is required by platform data model for proper "
226             "assignment and inventory."
227         ).format(rid, parameter, resource_intext, readable_format)
228         if exemptions_allowed:
229             msg = "WARNING: {} {}".format(msg, AAP_EXEMPT_CAVEAT)
230
231     return msg
232
233
234 def mismatch_resource_and_parameter_attribute(check, resource_re_match, parameter, rid):
235     """Compares vm_type or network_role from resource
236     is the same as found in parameter"""
237     resource_match = resource_re_match.group(check)
238     if (
239         resource_match
240         and not parameter.startswith(resource_match)
241         and parameter.find("_{}_".format(resource_match)) == -1
242     ):
243         return ("{0} {1} does not match parameter {2} {1}").format(
244             rid, check, parameter
245         )
246
247
248 def get_list_of_ports_attached_to_nova_server(nova_server):
249     networks_list = nova_server.get("properties", {}).get("networks")
250
251     port_ids = []
252     if networks_list:
253         for network in networks_list:
254             network_prop = network.get("port")
255             if network_prop:
256                 pid = network_prop.get("get_param")
257                 if not pid:
258                     pid = network_prop.get("get_resource")
259                 port_ids.append(pid)
260
261     return port_ids