From 2e4979ee3968d97c30770c43a11e7699481656a6 Mon Sep 17 00:00:00 2001 From: "stark, steven" Date: Mon, 18 Feb 2019 16:22:52 -0800 Subject: [PATCH] [VVP-171] R-84123 and R-76160 test update R-84123 and R-76160 test_neutron_port_internal_fixed_ips should only be checking incremental modules Change-Id: I7ef3cd4aa2b01273c2592f0b17751c5cb79c002b Issue-ID: VVP-171 Signed-off-by: stark, steven --- .../fail/fail0.yaml | 4 +- .../fail/fail1.yaml | 10 +- .../pass/pass0.yaml | 0 .../pass/pass0_base.yaml | 0 ice_validator/tests/helpers.py | 28 +++ ...ps.py => test_neutron_port_fixed_ips_subnet.py} | 199 +++++++++------------ .../tests/test_neutron_port_internal_network.py | 49 +++-- 7 files changed, 138 insertions(+), 152 deletions(-) rename ice_validator/tests/fixtures/{test_neutron_port_fixed_ips => test_neutron_port_fixed_ips_subnet}/fail/fail0.yaml (96%) rename ice_validator/tests/fixtures/{test_neutron_port_fixed_ips => test_neutron_port_fixed_ips_subnet}/fail/fail1.yaml (93%) rename ice_validator/tests/fixtures/{test_neutron_port_fixed_ips => test_neutron_port_fixed_ips_subnet}/pass/pass0.yaml (100%) rename ice_validator/tests/fixtures/{test_neutron_port_fixed_ips => test_neutron_port_fixed_ips_subnet}/pass/pass0_base.yaml (100%) rename ice_validator/tests/{test_neutron_port_fixed_ips.py => test_neutron_port_fixed_ips_subnet.py} (58%) diff --git a/ice_validator/tests/fixtures/test_neutron_port_fixed_ips/fail/fail0.yaml b/ice_validator/tests/fixtures/test_neutron_port_fixed_ips_subnet/fail/fail0.yaml similarity index 96% rename from ice_validator/tests/fixtures/test_neutron_port_fixed_ips/fail/fail0.yaml rename to ice_validator/tests/fixtures/test_neutron_port_fixed_ips_subnet/fail/fail0.yaml index 8ca5cec..901072f 100644 --- a/ice_validator/tests/fixtures/test_neutron_port_fixed_ips/fail/fail0.yaml +++ b/ice_validator/tests/fixtures/test_neutron_port_fixed_ips_subnet/fail/fail0.yaml @@ -50,7 +50,7 @@ parameters: vm_typeX_bialy_guest_vlans: type: comma_delimited_list subnet_param: - type: string + type: comma_delimited_list subnet_id_param: type: comma_delimited_list @@ -63,7 +63,6 @@ resources: fixed_ips: - ip_address: { get_param: lb_1_int_intranet_floating_ip } subnet: { get_param: subnet_param } - subnet_id: { get_param: subnet_id_param } binding:vnic_type: direct value_specs: vlan_filter: {get_param: vm_typeX_bialy_vlan_filter} @@ -82,7 +81,6 @@ resources: fixed_ips: - ip_address: { get_param: lb_2_extnet_floating_v6_ip } subnet: { get_param: subnet_param } - subnet_id: { get_param: subnet_id_param } binding:vnic_type: direct value_specs: vlan_filter: {get_param: vm_typeX_bialy_vlan_filter} diff --git a/ice_validator/tests/fixtures/test_neutron_port_fixed_ips/fail/fail1.yaml b/ice_validator/tests/fixtures/test_neutron_port_fixed_ips_subnet/fail/fail1.yaml similarity index 93% rename from ice_validator/tests/fixtures/test_neutron_port_fixed_ips/fail/fail1.yaml rename to ice_validator/tests/fixtures/test_neutron_port_fixed_ips_subnet/fail/fail1.yaml index 58c75bb..60f9874 100644 --- a/ice_validator/tests/fixtures/test_neutron_port_fixed_ips/fail/fail1.yaml +++ b/ice_validator/tests/fixtures/test_neutron_port_fixed_ips_subnet/fail/fail1.yaml @@ -49,8 +49,8 @@ parameters: type: comma_delimited_list vm_typeX_bialy_guest_vlans: type: comma_delimited_list - bialy_subnet: - type: string + bialy_susbnet: + type: comma_delimited_list bialy_subnet_id: type: string @@ -62,8 +62,7 @@ resources: network: { get_param: int_intranet_net_name } fixed_ips: - ip_address: { get_param: lb_1_int_intranet_floating_ip } - subnet: { get_param: bialy_subnet } - subnet_id: { get_param: bialy_subnet_id } + subnet: { get_param: bialy_susbnet } binding:vnic_type: direct value_specs: vlan_filter: {get_param: vm_typeX_bialy_vlan_filter} @@ -81,8 +80,7 @@ resources: network: { get_param: extnet_net_id } fixed_ips: - ip_address: { get_param: lb_2_extnet_floating_v6_ip } - subnet: { get_param: bialy_subnet } - subnet_id: { get_param: bialy_subnet_id } + subnet: { get_param: bialy_susbnet } binding:vnic_type: direct value_specs: vlan_filter: {get_param: vm_typeX_bialy_vlan_filter} diff --git a/ice_validator/tests/fixtures/test_neutron_port_fixed_ips/pass/pass0.yaml b/ice_validator/tests/fixtures/test_neutron_port_fixed_ips_subnet/pass/pass0.yaml similarity index 100% rename from ice_validator/tests/fixtures/test_neutron_port_fixed_ips/pass/pass0.yaml rename to ice_validator/tests/fixtures/test_neutron_port_fixed_ips_subnet/pass/pass0.yaml diff --git a/ice_validator/tests/fixtures/test_neutron_port_fixed_ips/pass/pass0_base.yaml b/ice_validator/tests/fixtures/test_neutron_port_fixed_ips_subnet/pass/pass0_base.yaml similarity index 100% rename from ice_validator/tests/fixtures/test_neutron_port_fixed_ips/pass/pass0_base.yaml rename to ice_validator/tests/fixtures/test_neutron_port_fixed_ips_subnet/pass/pass0_base.yaml diff --git a/ice_validator/tests/helpers.py b/ice_validator/tests/helpers.py index 69190d8..12a7a12 100644 --- a/ice_validator/tests/helpers.py +++ b/ice_validator/tests/helpers.py @@ -41,6 +41,7 @@ """ import os +import re from collections import defaultdict from boltons import funcutils @@ -259,3 +260,30 @@ def check_indices(pattern, values, value_type): ).format(value_type, prefix, indices) ) return invalid_params + + +RE_BASE = re.compile(r"(^base$)|(^base_)|(_base_)|(_base$)") + + +def get_base_template_from_yaml_files(yaml_files): + """Return first filepath to match RE_BASE + """ + for filepath in yaml_files: + basename = get_base_template_from_yaml_file(filepath) + if basename: + return basename + return None + + +def get_base_template_from_yaml_file(yaml_file): + (dirname, filename) = os.path.split(yaml_file) + files = os.listdir(dirname) + for file in files: + basename, __ = os.path.splitext(os.path.basename(file)) + if ( + (__ == ".yaml" or __ == ".yml") + and RE_BASE.search(basename) + and basename.find("volume") == -1 + ): + return os.path.join(dirname, "{}{}".format(basename, __)) + return None diff --git a/ice_validator/tests/test_neutron_port_fixed_ips.py b/ice_validator/tests/test_neutron_port_fixed_ips_subnet.py similarity index 58% rename from ice_validator/tests/test_neutron_port_fixed_ips.py rename to ice_validator/tests/test_neutron_port_fixed_ips_subnet.py index 6e2d562..7b9bf3b 100644 --- a/ice_validator/tests/test_neutron_port_fixed_ips.py +++ b/ice_validator/tests/test_neutron_port_fixed_ips_subnet.py @@ -61,55 +61,40 @@ resources: port_type: SR-IOV_Mirrored_Trunk #SR-IOV Trunk Port # Receiving Mirrored Traffic """ - -import os -import os.path import re import pytest from .structures import Heat -from .helpers import validates +from .helpers import validates, get_base_template_from_yaml_file VERSION = "1.3.0" RE_BASE = re.compile(r"(^base$)|(^base_)|(_base_)|(_base$)") # search pattern -RE_EXTERNAL_PARAM_SUBNET_ID = re.compile( # match pattern +RE_EXTERNAL_PARAM_SUBNET = re.compile( # match pattern r"(?P.+)(_v6)?_subnet_id$" ) -RE_EXTERNAL_PARAM_SUBNET = RE_EXTERNAL_PARAM_SUBNET_ID -# RE_EXTERNAL_PARAM_SUBNET = re.compile( # match pattern -# r'(?P.+)(_v6)?_subnet$') -RE_INTERNAL_PARAM_SUBNET_ID = re.compile( # match pattern +RE_INTERNAL_PARAM_SUBNET = re.compile( # match pattern r"int_(?P.+)(_v6)?_subnet_id$" ) -RE_INTERNAL_PARAM_SUBNET = RE_INTERNAL_PARAM_SUBNET_ID -# RE_INTERNAL_PARAM_SUBNET = re.compile( # match pattern -# r'int_(?P.+)(_v6)?_subnet$') -def get_network(base_template_filepath): +def get_base(base_template_filepath): """Return the base template's Heat instance. """ if base_template_filepath is None: pytest.skip("No base template found") base_template = Heat(filepath=base_template_filepath) - for r in base_template.resources.values(): - if ( - base_template.nested_get(r, "type") == "OS::Neutron::Net" - or base_template.nested_get(r, "type") == "OS::ContrailV2::VirtualNetwork" - ): - return base_template - return None + return base_template -def run_test(heat_template, validate): +def run_test(heat_template, validate, validator=None): """call validate for each fixed_ips """ heat = Heat(filepath=heat_template) - base_template = get_base_template(heat_template) + base_template = get_base_template_from_yaml_file(heat_template) if not heat.resources: pytest.skip("No resources found") @@ -125,163 +110,109 @@ def run_test(heat_template, validate): if not isinstance(fixed_ips, list): bad[rid] = "properties.fixed_ips must be a list." continue - if not heat.parameters: - bad[rid] = "fixed_ips requires parameters" - continue for fixed_ip in fixed_ips: - error = validate(heat, fixed_ip, base_template) + error = validate(heat, fixed_ip, base_template, validator) if error: bad[rid] = error break if bad: # raise RuntimeError( raise AssertionError( - "Bad OS::Neutron::Port: %s" - % (", ".join("%s: %s" % (rid, error) for rid, error in bad.items())) + "%s" + % (", ".join("%s: %s" % (rid, error) for rid, error in bad.items())) ) -def validate_external_fixed_ip(heat, fixed_ip, base_template): - """ensure fixed_ip subnet and subnet_id for external network +def validate_external_fixed_ip_subnet(heat, fixed_ip, base_template, validator): + """ensure fixed_ip subnet for external network match the pattern. Returns error message string or None. """ subnet = heat.nested_get(fixed_ip, "subnet", "get_param") - subnet_id = heat.nested_get(fixed_ip, "subnet_id", "get_param") - if subnet and subnet_id: - error = 'fixed_ip %s has both "subnet" and "subnet_id"' % (fixed_ip) - elif subnet: - error = validate_external_subnet(subnet) - elif subnet_id: - error = validate_external_subnet_id(subnet_id) + if subnet: + error = validator(subnet, RE_EXTERNAL_PARAM_SUBNET) else: error = None return error -def validate_external_subnet(subnet): +def validate_external_subnet_parameter_format(subnet, regx): """ensure subnet matches template. Returns error message string or None. """ - if ( - subnet - and not subnet.startswith("int_") - and RE_EXTERNAL_PARAM_SUBNET.match(subnet) is None - ): - return 'fixed_ip subnet parameter "%s" does not match "%s"' % ( - subnet, - RE_EXTERNAL_PARAM_SUBNET.pattern, + if subnet and not subnet.startswith("int_") and regx.match(subnet) is None: + return ( + 'fixed_ip subnet parameter "%s" does not match ' + "{network-role}_subnet_id or {network-role}_v6_subnet_id" % (subnet) ) return None -def validate_external_subnet_id(subnet_id): - """ensure subnet_id matches template. - Returns error message string or None. - """ - if ( - subnet_id - and not subnet_id.startswith("int_") - and RE_EXTERNAL_PARAM_SUBNET_ID.match(subnet_id) is None - ): - return 'fixed_ip subnet_id parameter "%s" does not match "%s"' % ( - subnet_id, - RE_EXTERNAL_PARAM_SUBNET_ID.pattern, - ) - return None - - -def validate_internal_fixed_ip(heat, fixed_ip, base_template): - """ensure fixed_ip subnet and subnet_id for internal network +def validate_internal_fixed_ip_subnet(heat, fixed_ip, base_template, validator): + """ensure fixed_ip subnet for internal network match the pattern. Returns error message string or None. """ - base_module = get_network(base_template) + base_module = get_base(base_template) subnet = heat.nested_get(fixed_ip, "subnet", "get_param") - subnet_id = heat.nested_get(fixed_ip, "subnet_id", "get_param") - if subnet and subnet_id: - error = 'fixed_ip %s has both "subnet" and "subnet_id"' % (fixed_ip) - elif subnet: - error = validate_internal_subnet(heat, base_module, subnet) - elif subnet_id: - error = validate_internal_subnet_id(heat, base_module, subnet_id) + if subnet: + error = validator(heat, base_module, subnet, RE_INTERNAL_PARAM_SUBNET) else: error = None return error -def validate_internal_subnet(heat, base_module, subnet): +def validate_internal_subnet_parameter_format(heat, base_module, subnet, regx): """ensure if subnet matches template then its parameter exists. Returns error message string or None. """ - if ( - subnet - and subnet.startswith("int_") - and RE_INTERNAL_PARAM_SUBNET.match(subnet) - and heat.nested_get(base_module.outputs, subnet) is None - ): - return 'fixed_ip subnet parameter "%s" not in base outputs"' % (subnet) + if subnet and subnet.startswith("int_") and regx.match(subnet) is None: + return ( + 'fixed_ip subnet parameter "%s" does not match ' + "int_{network-role}_subnet_id or int_{network-role}_v6_subnet_id" % (subnet) + ) return None -def validate_internal_subnet_id(heat, base_module, subnet_id): - """ensure if subnet_id matches template then its parameter exists. +def validate_internal_subnet_exists_in_base_output(heat, base_module, subnet, regx): + """ensure if subnet matches template then its parameter exists. Returns error message string or None. """ if ( - subnet_id - and subnet_id.startswith("int_") - and RE_INTERNAL_PARAM_SUBNET_ID.match(subnet_id) - and heat.nested_get(base_module.outputs, subnet_id) is None + subnet + and subnet.startswith("int_") + and regx.match(subnet) + and heat.nested_get(base_module.outputs, subnet) is None ): - return 'fixed_ip subnet_id parameter "%s" not in base outputs"' % (subnet_id) + return 'fixed_ip subnet(_id) parameter "%s" not in base outputs"' % (subnet) return None -def validate_fixed_ip(heat, fixed_ip, base_template): +def validate_fixed_ip_subnet(heat, fixed_ip, base_template, validator): """ensure fixed_ip has proper parameters Returns error message string or None. """ subnet = heat.nested_get(fixed_ip, "subnet", "get_param") - subnet_id = heat.nested_get(fixed_ip, "subnet_id", "get_param") - if subnet and subnet_id: - error = 'fixed_ip %s has both "subnet" and "subnet_id"' % (fixed_ip) - elif subnet and heat.nested_get(heat.parameters, subnet, "type") != "string": + if subnet and heat.nested_get(heat.parameters, subnet, "type") != "string": error = 'subnet parameter "%s" must be type "string"' % subnet - elif subnet_id and heat.nested_get(heat.parameters, subnet_id, "type") != "string": - error = 'subnet_id parameter "%s" must be type "string"' % subnet_id else: error = None return error -def get_base_template(heat_template): - (dirname, filename) = os.path.split(heat_template) - files = os.listdir(dirname) - for file in files: - basename, __ = os.path.splitext(os.path.basename(file)) - if ( - __ == ".yaml" - and basename.find("base") != -1 - and basename.find("volume") == -1 - ): - return os.path.join(dirname, "{}{}".format(basename, __)) - return None - - @validates("R-38236") -def test_neutron_port_fixed_ips(yaml_file): +def test_neutron_port_fixed_ips_subnet(yaml_file): """ The VNF's Heat Orchestration Template's resource ``OS::Neutron::Port`` property ``fixed_ips`` map property ``subnet``/``subnet_id`` parameter **MUST** be declared type ``string``. """ - run_test(yaml_file, validate_fixed_ip) + run_test(yaml_file, validate_fixed_ip_subnet) @validates("R-62802", "R-15287") -def test_neutron_port_external_fixed_ips(yaml_file): +def test_neutron_port_external_fixed_ips_subnet(yaml_file): """ When the VNF's Heat Orchestration Template's resource ``OS::Neutron::Port`` is attaching @@ -296,12 +227,46 @@ def test_neutron_port_external_fixed_ips(yaml_file): and the external network IPv6 subnet is to be specified * ``{network-role}_v6_subnet_id`` """ - run_test(yaml_file, validate_external_fixed_ip) + run_test( + yaml_file, + validate_external_fixed_ip_subnet, + validate_external_subnet_parameter_format, + ) + + +@validates("R-84123", "R-76160") +def test_neutron_port_internal_fixed_ips_subnet(yaml_file): + """ + When + + * the VNF's Heat Orchestration Template's + resource ``OS::Neutron::Port`` in an Incremental Module is attaching + to an internal network + that is created in the Base Module, AND + * an IPv4 address is being cloud assigned by OpenStack's DHCP Service AND + * the internal network IPv4 subnet is to be specified + using the property ``fixed_ips`` map property ``subnet``/``subnet_id``, + + the parameter **MUST** follow the naming convention + + * ``int_{network-role}_subnet_id`` + an IPv6 address is being cloud assigned by OpenStack's DHCP Service AND + * ``int_{network-role}_v6_subnet_id`` + + """ + run_test( + yaml_file, + validate_internal_fixed_ip_subnet, + validate_internal_subnet_parameter_format, + ) @validates("R-84123", "R-76160") -def test_neutron_port_internal_fixed_ips(yaml_file): +def test_neutron_port_internal_fixed_ips_subnet_in_base(heat_template): """ + Only check parent incremental modules, because nested file parameter + name may have been changed. + When * the VNF's Heat Orchestration Template's @@ -318,5 +283,11 @@ def test_neutron_port_internal_fixed_ips(yaml_file): an IPv6 address is being cloud assigned by OpenStack's DHCP Service AND * ``int_{network-role}_v6_subnet_id`` + Note that the parameter MUST be defined as an output parameter in + the base module. """ - run_test(yaml_file, validate_internal_fixed_ip) + run_test( + heat_template, + validate_internal_fixed_ip_subnet, + validate_internal_subnet_exists_in_base_output, + ) diff --git a/ice_validator/tests/test_neutron_port_internal_network.py b/ice_validator/tests/test_neutron_port_internal_network.py index 00a3a93..e90f87c 100644 --- a/ice_validator/tests/test_neutron_port_internal_network.py +++ b/ice_validator/tests/test_neutron_port_internal_network.py @@ -36,39 +36,26 @@ # ============LICENSE_END============================================ # -import os.path -import re - from tests.parametrizers import get_nested_files from tests.utils.network_roles import get_network_type_from_port from .structures import Heat -from .helpers import validates, load_yaml - - -RE_BASE = re.compile(r"(^base$)|(^base_)|(_base_)|(_base$)") - - -def get_base_template_filepath(yaml_files): - """Return first filepath to match RE_BASE - """ - for filepath in yaml_files: - basename, __ = os.path.splitext(os.path.basename(filepath)) - if RE_BASE.search(basename) and basename.find("volume") == -1: - return filepath - return None +from .helpers import validates, load_yaml, get_base_template_from_yaml_files @validates("R-22688") def test_neutron_port_internal_network_v2(yaml_files): - base_path = get_base_template_filepath(yaml_files) + base_path = get_base_template_from_yaml_files(yaml_files) nested_template_paths = get_nested_files(yaml_files) errors = [] for yaml_file in yaml_files: if yaml_file == base_path or yaml_file in nested_template_paths: continue # Only applies to incremental modules heat = Heat(filepath=yaml_file) - internal_ports = {r_id: p for r_id, p in heat.neutron_port_resources.items() - if get_network_type_from_port(p) == "internal"} + internal_ports = { + r_id: p + for r_id, p in heat.neutron_port_resources.items() + if get_network_type_from_port(p) == "internal" + } for r_id, port in internal_ports.items(): props = port.get("properties") or {} network_value = props.get("network") or {} @@ -80,15 +67,19 @@ def test_neutron_port_internal_network_v2(yaml_files): base_heat = load_yaml(base_path) base_outputs = base_heat.get("outputs") or {} if not param.endswith("_net_id"): - errors.append(( - "Internal network {} is attached to port {}, but the " - "network must be attached via UUID of the network not " - "the name (ex: int_{{network-role}}_net_id)." - ).format(param, r_id)) + errors.append( + ( + "Internal network {} is attached to port {}, but the " + "network must be attached via UUID of the network not " + "the name (ex: int_{{network-role}}_net_id)." + ).format(param, r_id) + ) if param not in base_outputs: - errors.append(( - "Internal network {} is attached to port {}, but network " - "is not defined as an output in the base module ({})." - ).format(param, r_id, base_path)) + errors.append( + ( + "Internal network {} is attached to port {}, but network " + "is not defined as an output in the base module ({})." + ).format(param, r_id, base_path) + ) assert not errors, " ".join(errors) -- 2.16.6