[VVP] Misc tweaks and fixes to preload generation
[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 import re
44 import pytest
45 from tests.helpers import (
46     prop_iterator,
47     get_param,
48     get_environment_pair,
49     validates,
50     find_environment_file,
51     categories,
52 )
53 from tests.structures import Heat
54 from tests.utils.nested_files import file_is_a_nested_template
55
56
57 # Whats persistent mean? It means it goes in env.
58 # When adding an additional case, note the ","
59 # at the end of a property to make it a tuple.
60 ENV_PARAMETER_SPEC = {
61     "PLATFORM PROVIDED": [
62         {"property": ("metadata", "vnf_id",), "persistent": False, "kwargs": {}},
63         {"property": ("metadata", "vnf_name",), "persistent": False, "kwargs": {}},
64         {"property": ("metadata", "vf_module_id",), "persistent": False, "kwargs": {}},
65         {"property": ("metadata", "vf_module_index",), "persistent": False, "kwargs": {}},
66         {"property": ("metadata", "vf_module_name",), "persistent": False, "kwargs": {}},
67         {"property": ("metadata", "workload_context",), "persistent": False, "kwargs": {}},
68         {"property": ("metadata", "environment_context",), "persistent": False, "kwargs": {}},
69         {"property": (r"^(.+?)_net_fqdn$",), "persistent": False, "kwargs": {}},
70     ],
71     "ALL": [{"property": ("name",), "persistent": False, "kwargs": {}}],
72     "OS::Nova::Server": [
73         {"property": ("image",), "persistent": True, "kwargs": {}},
74         {"property": ("flavor",), "persistent": True, "kwargs": {}},
75         {"property": ("availability_zone",), "persistent": False, "kwargs": {}},
76     ],
77     "OS::Neutron::Port": [
78         {"property": ("network",), "persistent": False, "kwargs": {}},
79         {
80             "property": ("fixed_ips", "ip_address"),
81             "persistent": False,
82             "network_type": "external",
83             "kwargs": {"exclude_parameter": re.compile(r"^(.+?)_int_(.+?)$")},
84         },
85         {
86             "property": ("fixed_ips", "ip_address"),
87             "persistent": True,
88             "network_type": "internal",
89             "kwargs": {"exclude_parameter": re.compile(r"^((?!_int_).)*$")},
90         },
91         {"property": ("fixed_ips", "subnet"), "persistent": False, "kwargs": {}},
92         {
93             "property": ("allowed_address_pairs", "ip_address"),
94             "persistent": False,
95             "network_type": "external",
96             "kwargs": {"exclude_parameter": re.compile(r"^(.+?)_int_(.+?)$")},
97         },
98         {
99             "property": ("allowed_address_pairs", "ip_address"),
100             "persistent": True,
101             "network_type": "internal",
102             "kwargs": {"exclude_parameter": re.compile(r"^((?!_int_).)*$")},
103         },
104     ],
105     "OS::ContrailV2::InterfaceRouteTable": [
106         {
107             "property": (
108                 "interface_route_table_routes",
109                 "interface_route_table_routes_route",
110             ),
111             "persistent": False,
112             "kwargs": {},
113         }
114     ],
115     "OS::Heat::ResourceGroup": [
116         {
117             "property": ("count",),
118             "persistent": True,
119             "kwargs": {
120                 "exclude_resource": re.compile(
121                     r"^(.+?)_subint_(.+?)_port_(.+?)_subinterfaces$"
122                 )
123             },
124         }
125     ],
126     "OS::ContrailV2::InstanceIp": [
127         {
128             "property": ("instance_ip_address",),
129             "persistent": False,
130             "network_type": "external",
131             "kwargs": {"exclude_resource": re.compile(r"^.*_int_.*$")},
132         },
133         {
134             "property": ("instance_ip_address",),
135             "persistent": True,
136             "network_type": "internal",
137             "kwargs": {"exclude_resource": re.compile(r"(?!.*_int_.*)")},
138         },
139         {
140             "property": ("subnet_uuid",),
141             "persistent": False,
142             "network_type": "internal",
143             "kwargs": {"exclude_resource": re.compile(r"(?!.*_int_.*)")},
144         },
145     ],
146     "OS::ContrailV2::VirtualMachineInterface": [
147         {
148             "property": (
149                 "virtual_machine_interface_allowed_address_pairs",
150                 "virtual_machine_interface_allowed_address_pairs_allowed_address_pair",
151                 "virtual_machine_interface_allowed_address_pairs_allowed_address_pair_ip",
152                 "virtual_machine_interface_allowed_address_pairs_allowed_address_pair_ip_ip_prefix",
153             ),
154             "persistent": False,
155             "network_type": "external",
156             "kwargs": {"exclude_resource": re.compile(r"(?!.*_int_.*)")},
157         }
158     ],
159 }
160
161
162 def run_test_parameter(yaml_file, resource_type, *prop, **kwargs):
163     template_parameters = []
164     invalid_parameters = []
165     param_spec = {}
166     parameter_spec = ENV_PARAMETER_SPEC.get(
167         resource_type
168     )  # matching spec dict on resource type
169     for spec in parameter_spec:
170         # iterating through spec dict and trying to match on property
171         if spec.get("property") == prop:
172             yep = True
173             for (
174                 k,
175                 v,
176             ) in (
177                 kwargs.items()
178             ):  # now matching on additional kwargs passed in from test (i.e. network_type)
179                 if not spec.get(k) or spec.get(k) != v:
180                     yep = False
181             if yep:
182                 param_spec = spec
183                 if resource_type == "PLATFORM PROVIDED":
184                     if file_is_a_nested_template(yaml_file):
185                         pytest.skip(
186                             "Not checking nested files for PLATFORM PROVIDED params"
187                         )
188                     template_parameters.append(
189                         {"resource": "", "param": param_spec.get("property")[0]}
190                     )
191                 else:
192                     all_resources = False
193                     if resource_type == "ALL":
194                         all_resources = True
195                     template_parameters = get_template_parameters(
196                         yaml_file,
197                         resource_type,
198                         param_spec,
199                         all_resources=all_resources,
200                     )  # found the correct spec, proceeding w/ test
201                 break
202
203     for parameter in template_parameters:
204         param = parameter.get("param")
205         persistence = param_spec.get("persistent")
206
207         if env_violation(yaml_file, param, spec.get("persistent")):
208             human_text = "must" if persistence else "must not"
209             human_text2 = "was not" if persistence else "was"
210
211             invalid_parameters.append(
212                 "{} parameter {} {} be enumerated in an environment file, but "
213                 "parameter {} for {} {} found.".format(
214                     resource_type, prop, human_text, param, yaml_file, human_text2
215                 )
216             )
217
218     assert not invalid_parameters, "\n".join(invalid_parameters)
219
220
221 def get_preload_excluded_parameters(yaml_file, persistent_only=False, env_spec=None):
222     """
223     Returns set of all parameters that should not be included in the preload's
224     vnf parameters/tag-values section.
225
226     if persistent_only only parameters that are marked as persistent will
227     be excluded
228     """
229     env_spec = env_spec or ENV_PARAMETER_SPEC
230     results = []
231     for resource_type, specs in env_spec.items():
232         # apply to all resources if not in the format of an OpenStack resource
233         all_resources = "::" not in resource_type
234         for spec in specs:
235             if persistent_only and not spec.get("persistent"):
236                 continue
237             results.extend(get_template_parameters(yaml_file, resource_type,
238                                                    spec, all_resources, nested_resources=True))
239     results = {item["param"] for item in results}
240     for param in Heat(yaml_file).parameters:
241         # AZs often are manipulated and passed into nested templates making
242         # them difficult to detect by looking for the assignment.  We'll
243         # just extract them from the parameters if they are there to be safe
244         if re.match(r"availability_zone_\d+", param):
245             results.add(param)
246     return results
247
248
249 def get_template_parameters(yaml_file, resource_type, spec, all_resources=False, nested_resources=False):
250     parameters = []
251
252     heat = Heat(yaml_file)
253     if all_resources:
254         resources = heat.resources if not nested_resources else heat.get_all_resources()
255     else:
256         resources = heat.get_resource_by_type(resource_type, all_resources=nested_resources)
257     for rid, resource_props in resources.items():
258         for param in prop_iterator(resource_props, *spec.get("property")):
259             if param and get_param(param) and param_helper(spec, get_param(param), rid):
260                 # this is first getting the param
261                 # then checking if its actually using get_param
262                 # then checking a custom helper function (mostly for internal vs external networks)
263                 parameters.append({"resource": rid, "param": get_param(param)})
264     return parameters
265
266
267 def env_violation(yaml_file, parameter, persistent):
268     # Returns True IF there's a violation, False if everything looks good.
269
270     filepath, filename = os.path.split(yaml_file)
271     environment_pair = get_environment_pair(yaml_file)
272     if not environment_pair:  # this is a nested file perhaps?
273         environment_pair = find_environment_file(
274             yaml_file
275         )  # we want to check parent env
276         if not environment_pair:
277             pytest.skip("unable to determine environment file for nested yaml file")
278
279     env_yaml = environment_pair.get("eyml")
280     parameters = env_yaml.get("parameters", {})
281     in_env = False
282     if parameters:  # env file can be just parameters:
283         for param, value in parameters.items():
284             if re.match(parameter, param):
285                 in_env = True
286                 break
287
288     # confusing return. This function is looking for a violation.
289     return not persistent == in_env
290
291
292 def param_helper(spec, param, rid):
293     # helper function that has some predefined additional
294     # checkers, mainly to figure out if internal/external network
295     keeper = True
296     for k, v in spec.get("kwargs").items():
297         if k == "exclude_resource" and re.match(v, rid):
298             keeper = False
299             break
300         elif k == "exclude_parameter" and re.match(v, param):
301             keeper = False
302             break
303
304     return keeper
305
306
307 @validates("R-91125")
308 def test_nova_server_image_parameter_exists_in_environment_file(yaml_file):
309     run_test_parameter(yaml_file, "OS::Nova::Server", "image")
310
311
312 @validates("R-69431")
313 def test_nova_server_flavor_parameter_exists_in_environment_file(yaml_file):
314     run_test_parameter(yaml_file, "OS::Nova::Server", "flavor")
315
316
317 @categories("environment_file")
318 @validates("R-22838", "R-99812")
319 def test_nova_server_name_parameter_doesnt_exist_in_environment_file(yaml_file):
320     run_test_parameter(yaml_file, "ALL", "name")
321
322
323 @categories("environment_file")
324 @validates("R-59568")
325 def test_nova_server_az_parameter_doesnt_exist_in_environment_file(yaml_file):
326     run_test_parameter(yaml_file, "OS::Nova::Server", "availability_zone")
327
328
329 @categories("environment_file")
330 @validates("R-20856")
331 def test_nova_server_vnf_id_parameter_doesnt_exist_in_environment_file(yaml_file):
332     run_test_parameter(yaml_file, "PLATFORM PROVIDED", "vnf_id")
333
334
335 @categories("environment_file")
336 @validates("R-72871")
337 def test_nova_server_vf_module_id_parameter_doesnt_exist_in_environment_file(yaml_file):
338     run_test_parameter(yaml_file, "PLATFORM PROVIDED", "vf_module_id")
339
340
341 @categories("environment_file")
342 @validates("R-37039")
343 def test_nova_server_vf_module_index_parameter_doesnt_exist_in_environment_file(
344     yaml_file
345 ):
346     run_test_parameter(yaml_file, "PLATFORM PROVIDED", "vf_module_index")
347
348
349 @categories("environment_file")
350 @validates("R-36542")
351 def test_nova_server_vnf_name_parameter_doesnt_exist_in_environment_file(yaml_file):
352     run_test_parameter(yaml_file, "PLATFORM PROVIDED", "vnf_name")
353
354
355 @categories("environment_file")
356 @validates("R-80374")
357 def test_nova_server_vf_module_name_parameter_doesnt_exist_in_environment_file(
358     yaml_file
359 ):
360     run_test_parameter(yaml_file, "PLATFORM PROVIDED", "vf_module_name")
361
362
363 @categories("environment_file")
364 @validates("R-02691")
365 def test_nova_server_workload_context_parameter_doesnt_exist_in_environment_file(
366     yaml_file
367 ):
368     run_test_parameter(yaml_file, "PLATFORM PROVIDED", "workload_context")
369
370
371 @categories("environment_file")
372 @validates("R-13194")
373 def test_nova_server_environment_context_parameter_doesnt_exist_in_environment_file(
374     yaml_file
375 ):
376     run_test_parameter(yaml_file, "PLATFORM PROVIDED", "environment_context")
377
378
379 @categories("environment_file")
380 @validates("R-29872")
381 def test_neutron_port_network_parameter_doesnt_exist_in_environment_file(yaml_file):
382     run_test_parameter(yaml_file, "OS::Neutron::Port", "network")
383
384
385 @categories("environment_file")
386 @validates("R-39841", "R-87123", "R-62590", "R-98905", "R-93030", "R-62590")
387 def test_neutron_port_external_fixedips_ipaddress_parameter_doesnt_exist_in_environment_file(
388     yaml_file
389 ):
390     run_test_parameter(
391         yaml_file,
392         "OS::Neutron::Port",
393         "fixed_ips",
394         "ip_address",
395         network_type="external",
396     )
397
398
399 @validates("R-28795", "R-97201", "R-93496", "R-90206", "R-98569", "R-93496")
400 def test_neutron_port_internal_fixedips_ipaddress_parameter_exists_in_environment_file(
401     yaml_file
402 ):
403     run_test_parameter(
404         yaml_file,
405         "OS::Neutron::Port",
406         "fixed_ips",
407         "ip_address",
408         network_type="internal",
409     )
410
411
412 @categories("environment_file")
413 @validates("R-83677", "R-80829", "R-69634", "R-22288")
414 def test_neutron_port_fixedips_subnet_parameter_doesnt_exist_in_environment_file(
415     yaml_file
416 ):
417     run_test_parameter(
418         yaml_file, "OS::Neutron::Port", "fixed_ips", "subnet", network_type="internal"
419     )
420
421
422 @categories("environment_file")
423 @validates("R-83412", "R-83418")
424 def test_neutron_port_external_aap_ip_parameter_doesnt_exist_in_environment_file(
425     yaml_file
426 ):
427     run_test_parameter(
428         yaml_file,
429         "OS::Neutron::Port",
430         "allowed_address_pairs",
431         "subnet",
432         network_type="external",
433     )
434
435
436 @categories("environment_file")
437 @validates("R-92193")
438 def test_network_fqdn_parameter_doesnt_exist_in_environment_file(yaml_file):
439     run_test_parameter(yaml_file, "PLATFORM PROVIDED", r"^(.+?)_net_fqdn$")
440
441
442 @categories("environment_file")
443 @validates("R-76682")
444 def test_contrail_route_prefixes_parameter_doesnt_exist_in_environment_file(yaml_file):
445     run_test_parameter(
446         yaml_file,
447         "OS::ContrailV2::InterfaceRouteTable",
448         "interface_route_table_routes",
449         "interface_route_table_routes_route",
450     )
451
452
453 @validates("R-50011")
454 def test_heat_rg_count_parameter_exists_in_environment_file(yaml_file):
455     run_test_parameter(yaml_file, "OS::Heat::ResourceGroup", "count")
456
457
458 @categories("environment_file")
459 @validates("R-100020", "R-100040", "R-100060", "R-100080", "R-100170")
460 def test_contrail_external_instance_ip_does_not_exist_in_environment_file(yaml_file):
461     run_test_parameter(
462         yaml_file,
463         "OS::ContrailV2::InstanceIp",
464         "instance_ip_address",
465         network_type="external",
466     )
467
468
469 @validates("R-100100", "R-100120", "R-100140", "R-100160", "R-100180")
470 def test_contrail_internal_instance_ip_does_exist_in_environment_file(yaml_file):
471     run_test_parameter(
472         yaml_file,
473         "OS::ContrailV2::InstanceIp",
474         "instance_ip_address",
475         network_type="internal",
476     )
477
478
479 @categories("environment_file")
480 @validates("R-100210", "R-100230", "R-100250", "R-100270")
481 def test_contrail_subnet_uuid_does_not_exist_in_environment_file(yaml_file):
482     run_test_parameter(yaml_file, "OS::ContrailV2::InstanceIp", "subnet_uuid")
483
484
485 @categories("environment_file")
486 @validates("R-100320", "R-100340")
487 def test_contrail_vmi_aap_does_not_exist_in_environment_file(yaml_file):
488     run_test_parameter(
489         yaml_file,
490         "OS::ContrailV2::VirtualMachineInterface",
491         "virtual_machine_interface_allowed_address_pairs",
492         "virtual_machine_interface_allowed_address_pairs_allowed_address_pair",
493         "virtual_machine_interface_allowed_address_pairs_allowed_address_pair_ip",
494         "virtual_machine_interface_allowed_address_pairs_allowed_address_pair_ip_ip_prefix",
495     )