6 from tests.helpers import validates, get_base_template_from_yaml_files
7 from tests.parametrizers import get_nested_files
8 from tests.structures import Heat
10 INTERNAL_UUID_PATTERN = re.compile(r"^int_(?P<network_role>.+?)_net_id$")
11 INTERNAL_NAME_PATTERN = re.compile(r"^int_(?P<network_role>.+?)_net_name$")
12 INTERNAL_PORT = re.compile(r"^(?P<vm_type>.+)_(?P<vm_type_index>\d+)_int_"
13 r"(?P<network_role>.+)_port_(?P<port_index>\d+)$")
15 EXTERNAL_PORT = re.compile(r"^(?P<vm_type>.+)_(?P<vm_type_index>\d+)_(?!int_)"
16 r"(?P<network_role>.+)_port_(?P<port_index>\d+)$")
18 EXTERNAL_UUID_PATTERN = re.compile(r"^(?!int_)(?P<network_role>.+?)_net_id$")
19 EXTERNAL_NAME_PATTERN = re.compile(r"^(?!int_)(?P<network_role>.+?)_net_name$")
21 INTERNAL_NETWORK_PATTERN = re.compile(r"^int_(?P<network_role>.+?)"
25 def is_incremental_module(yaml_file, base_path, nested_paths):
26 return yaml_file != base_path and yaml_file not in nested_paths
29 def get_param(prop_val):
30 if not isinstance(prop_val, dict):
32 param = prop_val.get("get_param")
33 return param if isinstance(param, str) else None
36 @validates("R-86182", "R-22688")
37 def test_internal_network_parameters(yaml_files):
38 base_path = get_base_template_from_yaml_files(yaml_files)
40 pytest.skip("No base module found")
41 base_heat = Heat(filepath=base_path)
42 nested_paths = get_nested_files(yaml_files)
43 incremental_modules = [f for f in yaml_files
44 if is_incremental_module(f, base_path, nested_paths)]
46 for module in incremental_modules:
47 heat = Heat(filepath=module)
48 for rid, port in heat.neutron_port_resources.items():
49 rid_match = INTERNAL_PORT.match(rid)
53 network = (port.get("properties") or {}).get("network") or {}
54 if isinstance(network, dict) and (
55 "get_resource" in network or "get_attr" in network):
58 param = get_param(network)
61 "The internal port ({}) must either connect to a network "
62 "in the base module using get_param or to a network "
63 "created in this module ({})"
64 ).format(rid, os.path.split(module)[1]))
68 INTERNAL_UUID_PATTERN.match(param)
69 or INTERNAL_NAME_PATTERN.match(param)
73 "The internal port ({}) network parameter ({}) does not "
74 "match one of the required naming conventions of "
75 "int_{{network-role}}_net_id or "
76 "int_{{network-role}}_net_name "
77 "for connecting to an internal network. "
78 "If this is not an internal port, then change the resource "
79 "ID to adhere to the external port naming convention."
83 if param not in base_heat.yml.get("outputs", {}):
84 base_module = os.path.split(base_path)[1]
86 "The internal network parameter ({}) attached to port ({}) "
87 "must be defined in the output section of the base module ({})."
88 ).format(param, rid, base_module))
91 param_network_role = param_match.groupdict().get("network_role")
92 rid_network_role = rid_match.groupdict().get("network_role")
93 if param_network_role.lower() != rid_network_role.lower():
95 "The network role ({}) extracted from the resource ID ({}) "
96 "does not match network role ({}) extracted from the "
97 "network parameter ({})"
98 ).format(rid_network_role, rid, param_network_role, param))
100 resources = base_heat.get_all_resources(os.path.split(base_path)[0])
101 networks = {rid: resource for rid, resource in resources.items()
102 if resource.get("type")
103 in {"OS::Neutron::Net",
104 "OS::ContrailV2::VirtualNetwork"}}
105 matches = (INTERNAL_NETWORK_PATTERN.match(n) for n in networks)
106 roles = {m.groupdict()["network_role"].lower() for m in matches if m}
107 if param_network_role.lower() not in roles:
109 "No internal network with a network role of {} was "
110 "found in the base modules networks: {}"
111 ).format(param_network_role, ", ".join(networks)))
113 assert not errors, ". ".join(errors)
116 @validates("R-62983")
117 def test_external_network_parameter(heat_template):
118 heat = Heat(filepath=heat_template)
120 for rid, port in heat.neutron_port_resources.items():
121 rid_match = EXTERNAL_PORT.match(rid)
123 continue # only test external ports
124 network = (port.get("properties") or {}).get("network") or {}
125 if not isinstance(network, dict) or "get_param" not in network:
127 "The external port ({}) must assign the network property "
128 "using get_param. If this port is for an internal network, "
129 "then change the resource ID format to the external format."
132 param = get_param(network)
135 "The get_param function on the network property of port ({}) "
136 "must only take a single, string parameter."
141 EXTERNAL_NAME_PATTERN.match(param)
142 or EXTERNAL_UUID_PATTERN.match(param)
146 "The network parameter ({}) on port ({}) does not match one of "
147 "{{network-role}}_net_id or {{network-role}}_net_name."
148 ).format(param, rid))
150 rid_network_role = rid_match.groupdict()["network_role"]
151 param_network_role = param_match.groupdict()["network_role"]
152 if rid_network_role.lower() != param_network_role.lower():
154 "The network role ({}) extracted from the resource ID ({}) "
155 "does not match network role ({}) extracted from the "
156 "network parameter ({})"
157 ).format(rid_network_role, rid, param_network_role, param))
159 assert not errors, ". ".join(errors)