2 # ============LICENSE_START====================================================
3 # org.onap.vvp/validation-scripts
4 # ===================================================================
5 # Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6 # ===================================================================
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
13 # http://www.apache.org/licenses/LICENSE-2.0
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.
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
28 # https://creativecommons.org/licenses/by/4.0/
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.
36 # ============LICENSE_END============================================
42 {vm-type}_{vm-type_index}_{network-role}_port_{port-index}:
43 type: OS::Neutron::Port
45 network: { get_param: ...}
46 fixed_ips: [ { "ipaddress": { get_param: ... } } ]
47 binding:vnic_type: direct #only SR-IOV ports, not OVS ports
49 vlan_filter: { get_param: ... }, #all NC ports
50 public_vlans: { get_param: ... }, #all NC ports
51 private_vlans: { get_param: ... },#all NC ports
52 guest_vlans: { get_param: ... }, #SR-IOV Trunk Port only
53 vlan_mirror: { get_param: ... }, #SRIOV Trunk Port
54 # Receiving Mirrored Traffic only
55 ATT_FABRIC_CONFIGURATION_REQUIRED: true #all NC ports
58 port_type: SR-IOV_Trunk #SR-IOV Trunk Port
59 port_type: SR-IOV_Non_Trunk #SR-IOV Non Trunk Port
60 port_type: OVS #OVS Port
61 port_type: SR-IOV_Mirrored_Trunk #SR-IOV Trunk Port
62 # Receiving Mirrored Traffic
70 from .structures import Heat
71 from .helpers import validates
75 RE_BASE = re.compile(r"(^base$)|(^base_)|(_base_)|(_base$)") # search pattern
76 RE_NEUTRON_PORT_RID = re.compile( # match pattern
78 r"_(?P<vm_type_index>\d+)"
79 r"_(?P<network_role>.+)"
81 r"(?P<port_index>\d+)"
84 RE_INTERNAL_NETWORK_PARAM = re.compile( # match pattern
85 r"int_(?P<network_role>.+)_net_(?P<value_type>id|name)$"
87 RE_INTERNAL_NETWORK_RID = re.compile( # match pattern
88 r"int_(?P<network_role>.+)_network$"
92 def get_base_template_filepath(yaml_files):
93 """Return first filepath to match RE_BASE
95 for filepath in yaml_files:
96 basename, __ = os.path.splitext(os.path.basename(filepath))
97 if RE_BASE.search(basename) and basename.find("volume") == -1:
102 def get_internal_network(yaml_files):
103 """Return the base template's Heat istance.
105 base_template_filepath = get_base_template_filepath(yaml_files)
106 if base_template_filepath is None:
107 pytest.skip("No base template found")
108 base_template = Heat(filepath=base_template_filepath)
109 for r in base_template.resources.values():
110 # if base_template.nested_get(r, 'type') == 'OS::Neutron::Net':
116 def get_neutron_ports(heat):
117 """Return dict of resource_id: resource, whose type is
122 for rid, resource in heat.resources.items()
123 if heat.nested_get(resource, "type") == "OS::Neutron::Port"
127 # pylint: disable=invalid-name
130 @validates("R-86182", "R-22688")
131 def test_neutron_port_internal_network(yaml_files):
133 When the VNF's Heat Orchestration Template's Resource
134 ``OS::Neutron::Port`` is attaching to an internal network,
135 and the internal network is created in a
136 different Heat Orchestration Template than the ``OS::Neutron::Port``,
137 the ``network`` parameter name **MUST**
139 * follow the naming convention ``int_{network-role}_net_id``
141 network UUID value is used to reference the network
142 * follow the naming convention ``int_{network-role}_net_name`` if the
143 OpenStack network name in is used to reference the network.
145 where ``{network-role}`` is the network-role of the internal network and
146 a ``get_param`` **MUST** be used as the intrinsic function.
148 In Requirement R-86182, the internal network is created in the VNF's
149 Base Module (Heat Orchestration Template) and the parameter name is
150 declared in the Base Module's ``outputs`` section.
151 When the parameter's value uses a "get_param" function, its name
152 must end in "_name", and when it uses a "get_resource" function,
153 its name must end in "_id".
155 The output parameter name will be declared as a parameter in the
156 ``parameters`` section of the incremental module.
158 internal_network = get_internal_network(yaml_files)
159 if not internal_network:
160 pytest.skip("internal_network template not found")
162 if not internal_network.outputs:
163 pytest.skip('internal_network template has no "outputs"')
165 for filepath in yaml_files:
166 if filepath != internal_network.filepath:
167 validate_neutron_port(filepath, internal_network)
170 def validate_neutron_port(filepath, internal_network):
171 """validate the neutron port
173 heat = Heat(filepath=filepath)
174 if not heat.resources:
176 neutron_ports = get_neutron_ports(heat)
177 if not neutron_ports:
180 for rid, resource in neutron_ports.items():
181 if not heat.parameters:
182 bad[rid] = 'missing "parameters"'
184 network = heat.nested_get(resource, "properties", "network", "get_param")
186 bad[rid] = 'missing "network.get_param"'
188 if not network.startswith("int_"):
189 continue # not an internal network port
190 error = validate_param(heat, network, internal_network)
195 "Bad OS::Neutron::Port: %s"
196 % (", ".join("%s: %s" % (rid, error) for rid, error in bad.items()))
200 def validate_param(heat, network, internal_network):
201 """Ensure network (the parameter name) is defined in the base
202 template, and has the correct value function. Ensure its
203 network-role is found in the base template in some
204 OS::Neutron::Net resource.
205 Return error message string, or None if no no errors.
207 match = RE_INTERNAL_NETWORK_PARAM.match(network)
209 return 'network.get_param "%s" does not match "%s"' % (
211 RE_INTERNAL_NETWORK_PARAM.pattern,
213 if heat.nested_get(heat.parameters, network) is None:
214 return "missing parameters.%s" % network
215 output = heat.nested_get(internal_network.outputs, network)
217 return 'network.get_param "%s"' " not found in base template outputs" % network
218 param_dict = match.groupdict()
219 expect = {"name": "get_param", "id": "get_resource"}[param_dict["value_type"]]
220 value = heat.nested_get(output, "value")
221 if heat.nested_get(value, expect) is None:
223 'network.get_param "%s" implies its base template'
224 ' output value function should be "%s" dict not "%s"'
225 % (network, expect, value)
227 network_role = param_dict["network_role"]
228 for rid, resource in internal_network.resources.items():
230 heat.nested_get(resource, "type") == "OS::Neutron::Net"
231 or heat.nested_get(resource, "type") == "OS::ContrailV2::VirtualNetwork"
233 match = RE_INTERNAL_NETWORK_RID.match(rid)
234 if match and match.groupdict()["network_role"] == network_role:
237 "OS::Neutron::Net with network-role"
238 ' "%s" not found in base template."' % network_role