34808b9d1697dd24d04d852d89869cd002955927
[vvp/validation-scripts.git] / ice_validator / tests / test_environment_file_parameters.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 """ environment file structure
40 """
41 import os
42
43 from tests.structures import Heat
44 from tests.utils import nested_dict
45 from .helpers import (
46     validates,
47     categories,
48     get_environment_pair,
49     find_environment_file,
50     get_param,
51 )
52 import re
53 import pytest
54 from tests import cached_yaml as yaml
55
56 VERSION = "1.0.0"
57
58 # pylint: disable=invalid-name
59
60
61 def check_parameter_exists(pattern, parameters):
62     if not parameters:
63         return False
64
65     for param in parameters:
66         if pattern.search(param):
67             return True
68
69     return False
70
71
72 def check_param_in_env_file(environment_pair, param, DESIRED, exclude_parameter=None):
73
74     # workaround for internal/external parameters
75     if exclude_parameter and re.match(exclude_parameter, param):
76         return False
77
78     if not environment_pair:
79         pytest.skip("No heat/env pair could be identified")
80
81     env_file = environment_pair.get("eyml")
82
83     pattern = re.compile(r"^{}$".format(param))
84
85     if "parameters" not in env_file:
86         pytest.skip("No parameters specified in the environment file")
87
88     return (
89         check_parameter_exists(pattern, env_file.get("parameters", {})) is not DESIRED
90     )
91
92
93 """
94 This function supports this structure, deviations
95 may or may not work without enhancement
96
97 resource_id:
98     type: <resource_type>
99     properties:
100         prop0: { get_param: parameter_0 }
101         prop1:  # this is a list of dicts
102             - nested_prop_0: { get_param: parameter_1 }
103             - nested_prop_1: { get_param: [parameter_2, {index}] }
104         prop2:  # this is a dict of dicts
105             nested_prop_0: { get_param: parameter_1 }
106         prop3: { get_param: [parameter_3, 0]}
107 """
108
109
110 def check_resource_parameter(
111     environment_pair,
112     prop,
113     DESIRED,
114     resource_type,
115     resource_type_inverse=False,
116     nested_prop="",
117     exclude_resource="",
118     exclude_parameter="",
119 ):
120     if not environment_pair:
121         pytest.skip("No heat/env pair could be identified")
122
123     env_file = environment_pair.get("eyml")
124     template_file = environment_pair.get("yyml")
125
126     if "parameters" not in env_file:
127         pytest.skip("No parameters specified in the environment file")
128
129     invalid_parameters = []
130     if template_file:
131         for resource, resource_prop in template_file.get("resources", {}).items():
132
133             # workaround for subinterface resource groups
134             if exclude_resource and re.match(exclude_resource, resource):
135                 continue
136
137             if (
138                 resource_prop.get("type") == resource_type and not resource_type_inverse
139             ) or (resource_prop.get("type") != resource_type and resource_type_inverse):
140
141                 pattern = False
142
143                 if not resource_prop.get("properties"):
144                     continue
145
146                 resource_parameter = resource_prop.get("properties").get(prop)
147
148                 if not resource_parameter:
149                     continue
150                 if isinstance(resource_parameter, list) and nested_prop:
151                     for param in resource_parameter:
152                         nested_param = param.get(nested_prop)
153                         if not nested_param:
154                             continue
155
156                         if isinstance(nested_param, dict):
157                             pattern = nested_param.get("get_param")
158                         else:
159                             pattern = ""
160
161                         if not pattern:
162                             continue
163
164                         if isinstance(pattern, list):
165                             pattern = pattern[0]
166
167                         if check_param_in_env_file(
168                             environment_pair,
169                             pattern,
170                             DESIRED,
171                             exclude_parameter=exclude_parameter,
172                         ):
173                             invalid_parameters.append(pattern)
174
175                 elif isinstance(resource_parameter, dict):
176                     if nested_prop and nested_prop in resource_parameter:
177                         resource_parameter = resource_parameter.get(nested_prop)
178
179                     pattern = resource_parameter.get("get_param")
180                     if not pattern:
181                         continue
182
183                     if isinstance(pattern, list):
184                         pattern = pattern[0]
185
186                     if check_param_in_env_file(
187                         environment_pair,
188                         pattern,
189                         DESIRED,
190                         exclude_parameter=exclude_parameter,
191                     ):
192                         invalid_parameters.append(pattern)
193                 else:
194                     continue
195
196     return set(invalid_parameters)
197
198
199 def run_check_resource_parameter(
200     yaml_file, prop, DESIRED, resource_type, check_resource=True, **kwargs
201 ):
202     filepath, filename = os.path.split(yaml_file)
203     environment_pair = get_environment_pair(yaml_file)
204
205     if not environment_pair:
206         # this is a nested file
207
208         if not check_resource:
209             # dont check env for nested files
210             # This will be tested separately for parent template
211             pytest.skip("This test doesn't apply to nested files")
212
213         environment_pair = find_environment_file(yaml_file)
214         if environment_pair:
215             with open(yaml_file, "r") as f:
216                 yml = yaml.load(f)
217             environment_pair["yyml"] = yml
218         else:
219             pytest.skip("unable to determine environment file for nested yaml file")
220
221     if check_resource:
222         invalid_parameters = check_resource_parameter(
223             environment_pair, prop, DESIRED, resource_type, **kwargs
224         )
225     else:
226         invalid_parameters = check_param_in_env_file(environment_pair, prop, DESIRED)
227
228     if kwargs.get("resource_type_inverse"):
229         resource_type = "non-{}".format(resource_type)
230
231     assert not invalid_parameters, (
232         "{} {} parameters in template {}{}"
233         " found in {} environment file: {}".format(
234             resource_type,
235             prop,
236             filename,
237             " not" if DESIRED else "",
238             environment_pair.get("name"),
239             ", ".join(invalid_parameters),
240         )
241     )
242
243
244 @validates("R-91125")
245 def test_nova_server_image_parameter_exists_in_environment_file(yaml_file):
246     run_check_resource_parameter(yaml_file, "image", True, "OS::Nova::Server")
247
248
249 @validates("R-69431")
250 def test_nova_server_flavor_parameter_exists_in_environment_file(yaml_file):
251     run_check_resource_parameter(yaml_file, "flavor", True, "OS::Nova::Server")
252
253
254 @categories("environment_file")
255 @validates("R-22838")
256 def test_nova_server_name_parameter_doesnt_exist_in_environment_file(yaml_file):
257     run_check_resource_parameter(yaml_file, "name", False, "OS::Nova::Server")
258
259
260 @categories("environment_file")
261 @validates("R-59568")
262 def test_nova_server_az_parameter_doesnt_exist_in_environment_file(yaml_file):
263     run_check_resource_parameter(
264         yaml_file, "availability_zone", False, "OS::Nova::Server"
265     )
266
267
268 @categories("environment_file")
269 @validates("R-20856")
270 def test_nova_server_vnf_id_parameter_doesnt_exist_in_environment_file(yaml_file):
271     run_check_resource_parameter(yaml_file, "vnf_id", False, "", check_resource=False)
272
273
274 @categories("environment_file")
275 @validates("R-72871")
276 def test_nova_server_vf_module_id_parameter_doesnt_exist_in_environment_file(yaml_file):
277     run_check_resource_parameter(
278         yaml_file, "vf_module_id", False, "", check_resource=False
279     )
280
281
282 @categories("environment_file")
283 @validates("R-37039")
284 def test_nova_server_vf_module_index_parameter_doesnt_exist_in_environment_file(
285     yaml_file
286 ):
287     run_check_resource_parameter(
288         yaml_file, "vf_module_index", False, "", check_resource=False
289     )
290
291
292 @categories("environment_file")
293 @validates("R-36542")
294 def test_nova_server_vnf_name_parameter_doesnt_exist_in_environment_file(yaml_file):
295     run_check_resource_parameter(yaml_file, "vnf_name", False, "", check_resource=False)
296
297
298 @categories("environment_file")
299 @validates("R-80374")
300 def test_nova_server_vf_module_name_parameter_doesnt_exist_in_environment_file(
301     yaml_file
302 ):
303     run_check_resource_parameter(
304         yaml_file, "vf_module_name", False, "", check_resource=False
305     )
306
307
308 @categories("environment_file")
309 @validates("R-02691")
310 def test_nova_server_workload_context_parameter_doesnt_exist_in_environment_file(
311     yaml_file
312 ):
313     run_check_resource_parameter(
314         yaml_file, "workload_context", False, "", check_resource=False
315     )
316
317
318 @categories("environment_file")
319 @validates("R-13194")
320 def test_nova_server_environment_context_parameter_doesnt_exist_in_environment_file(
321     yaml_file
322 ):
323     run_check_resource_parameter(
324         yaml_file, "environment_context", False, "", check_resource=False
325     )
326
327
328 @categories("environment_file")
329 @validates("R-29872")
330 def test_neutron_port_network_parameter_doesnt_exist_in_environment_file(yaml_file):
331     run_check_resource_parameter(yaml_file, "network", False, "OS::Neutron::Port")
332
333
334 @categories("environment_file")
335 @validates("R-39841", "R-87123", "R-62590", "R-98905", "R-93030", "R-62590")
336 def test_neutron_port_external_fixedips_ipaddress_parameter_doesnt_exist_in_environment_file(
337     yaml_file
338 ):
339     run_check_resource_parameter(
340         yaml_file,
341         "fixed_ips",
342         False,
343         "OS::Neutron::Port",
344         nested_prop="ip_address",
345         exclude_parameter=re.compile(r"^(.+?)_int_(.+?)$"),
346     )
347
348
349 @validates("R-28795", "R-97201", "R-93496", "R-90206", "R-98569", "R-93496")
350 def test_neutron_port_internal_fixedips_ipaddress_parameter_exists_in_environment_file(
351     yaml_file
352 ):
353     run_check_resource_parameter(
354         yaml_file,
355         "fixed_ips",
356         True,
357         "OS::Neutron::Port",
358         nested_prop="ip_address",
359         exclude_parameter=re.compile(r"^((?!_int_).)*$"),
360     )
361
362
363 @categories("environment_file")
364 @validates("R-83677", "R-80829", "R-69634", "R-22288")
365 def test_neutron_port_fixedips_subnet_parameter_doesnt_exist_in_environment_file(
366     yaml_file
367 ):
368     run_check_resource_parameter(
369         yaml_file, "fixed_ips", False, "OS::Neutron::Port", nested_prop="subnet"
370     )
371
372
373 @categories("environment_file")
374 @validates("R-83412", "R-83418")
375 def test_neutron_port_external_aap_ip_parameter_doesnt_exist_in_environment_file(
376     yaml_file
377 ):
378     run_check_resource_parameter(
379         yaml_file,
380         "allowed_address_pairs",
381         False,
382         "OS::Neutron::Port",
383         nested_prop="ip_address",
384         exclude_parameter=re.compile(r"^(.+?)_int_(.+?)$"),
385     )
386
387
388 @categories("environment_file")
389 @validates("R-99812")
390 def test_non_nova_server_name_parameter_doesnt_exist_in_environment_file(yaml_file):
391     run_check_resource_parameter(
392         yaml_file, "name", False, "OS::Nova::Server", resource_type_inverse=True
393     )
394
395
396 @categories("environment_file")
397 @validates("R-92193")
398 def test_network_fqdn_parameter_doesnt_exist_in_environment_file(yaml_file):
399     run_check_resource_parameter(
400         yaml_file, r"^(.+?)_net_fqdn$", False, "", check_resource=False
401     )
402
403
404 @categories("environment_file")
405 @validates("R-76682")
406 def test_contrail_route_prefixes_parameter_doesnt_exist_in_environment_file(yaml_file):
407     run_check_resource_parameter(
408         yaml_file,
409         "interface_route_table_routes",
410         False,
411         "OS::ContrailV2::InterfaceRouteTable",
412         nested_prop="interface_route_table_routes_route",
413     )
414
415
416 @validates("R-50011")
417 def test_heat_rg_count_parameter_exists_in_environment_file(yaml_file):
418     run_check_resource_parameter(
419         yaml_file,
420         "count",
421         True,
422         "OS::Heat::ResourceGroup",
423         exclude_resource=re.compile(r"^(.+?)_subint_(.+?)_port_(.+?)_subinterfaces$"),
424     )
425
426
427 @categories("environment_file")
428 @validates("R-100020", "R-100040", "R-100060", "R-100080", "R-100170")
429 def test_contrail_external_instance_ip_does_not_exist_in_environment_file(yaml_file):
430     run_check_resource_parameter(
431         yaml_file,
432         "instance_ip_address",
433         False,
434         "OS::ContrailV2::InstanceIp",
435         exclude_resource=re.compile(r"^.*_int_.*$"),  # exclude internal IPs
436     )
437
438
439 @validates("R-100100", "R-100120", "R-100140", "R-100160", "R-100180")
440 def test_contrail_internal_instance_ip_does_exist_in_environment_file(yaml_file):
441     run_check_resource_parameter(
442         yaml_file,
443         "instance_ip_address",
444         True,
445         "OS::ContrailV2::InstanceIp",
446         exclude_resource=re.compile(r"(?!.*_int_.*)"),  # exclude external IPs
447     )
448
449
450 @categories("environment_file")
451 @validates("R-100210", "R-100230", "R-100250", "R-100270")
452 def test_contrail_subnet_uuid_does_not_exist_in_environment_file(yaml_file):
453     run_check_resource_parameter(
454         yaml_file, "subnet_uuid", False, "OS::ContrailV2::InstanceIp"
455     )
456
457
458 @categories("environment_file")
459 @validates("R-100320", "R-100340")
460 def test_contrail_vmi_aap_does_not_exist_in_environment_file(yaml_file):
461     # This test needs to check a more complex structure.  Rather than try to force
462     # that into the existing run_check_resource_parameter logic we'll just check it
463     # directly
464     pairs = get_environment_pair(yaml_file)
465     if not pairs:
466         pytest.skip("No matching env file found")
467     heat = Heat(filepath=yaml_file)
468     env_parameters = pairs["eyml"].get("parameters") or {}
469     vmis = heat.get_resource_by_type("OS::ContrailV2::VirtualMachineInterface")
470     external_vmis = {rid: data for rid, data in vmis.items() if "_int_" not in rid}
471     invalid_params = []
472     for r_id, vmi in external_vmis.items():
473         aap_value = nested_dict.get(
474             vmi,
475             "properties",
476             "virtual_machine_interface_allowed_address_pairs",
477             "virtual_machine_interface_allowed_address_pairs_allowed_address_pair",
478         )
479         if not aap_value or not isinstance(aap_value, list):
480             # Skip if aap not used or is not a list.
481             continue
482         for pair_ip in aap_value:
483             if not isinstance(pair_ip, dict):
484                 continue  # Invalid Heat will be detected by another test
485             settings = (
486                 pair_ip.get(
487                     "virtual_machine_interface_allowed_address"
488                     "_pairs_allowed_address_pair_ip"
489                 )
490                 or {}
491             )
492             if isinstance(settings, dict):
493                 ip_prefix = (
494                     settings.get(
495                         "virtual_machine_interface_allowed_address"
496                         "_pairs_allowed_address_pair_ip_ip_prefix"
497                     )
498                     or {}
499                 )
500                 ip_prefix_param = get_param(ip_prefix)
501                 if ip_prefix_param and ip_prefix_param in env_parameters:
502                     invalid_params.append(ip_prefix_param)
503
504     msg = (
505         "OS::ContrailV2::VirtualMachineInterface "
506         "virtual_machine_interface_allowed_address_pairs"
507         "_allowed_address_pair_ip_ip_prefix "
508         "parameters found in environment file {}: {}"
509     ).format(pairs.get("name"), ", ".join(invalid_params))
510     assert not invalid_params, msg