[VVP] Allow multi-line error messages on error reports
[vvp/validation-scripts.git] / ice_validator / tests / test_environment_file_parameters.py
index 031f898..010edab 100644 (file)
@@ -2,7 +2,7 @@
 # ============LICENSE_START====================================================
 # org.onap.vvp/validation-scripts
 # ===================================================================
-# Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+# Copyright © 2019 AT&T Intellectual Property. All rights reserved.
 # ===================================================================
 #
 # Unless otherwise specified, all software contained herein is licensed
 #
 # ============LICENSE_END============================================
 #
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
 #
-
 """ environment file structure
 """
+import os
+from collections import Iterable
+
+from tests.structures import Heat
+from tests.utils import nested_dict
+from .helpers import (
+    validates,
+    categories,
+    get_environment_pair,
+    find_environment_file,
+    get_param,
+)
 import re
 import pytest
-
-from .helpers import validates, get_environment_pair
-
+from tests import cached_yaml as yaml
 
 VERSION = "1.0.0"
 
@@ -96,6 +104,7 @@ resource_id:
             - nested_prop_1: { get_param: [parameter_2, {index}] }
         prop2:  # this is a dict of dicts
             nested_prop_0: { get_param: parameter_1 }
+        prop3: { get_param: [parameter_3, 0]}
 """
 
 
@@ -109,7 +118,6 @@ def check_resource_parameter(
     exclude_resource="",
     exclude_parameter="",
 ):
-
     if not environment_pair:
         pytest.skip("No heat/env pair could be identified")
 
@@ -120,7 +128,6 @@ def check_resource_parameter(
         pytest.skip("No parameters specified in the environment file")
 
     invalid_parameters = []
-
     if template_file:
         for resource, resource_prop in template_file.get("resources", {}).items():
 
@@ -141,10 +148,8 @@ def check_resource_parameter(
 
                 if not resource_parameter:
                     continue
-
                 if isinstance(resource_parameter, list) and nested_prop:
                     for param in resource_parameter:
-
                         nested_param = param.get(nested_prop)
                         if not nested_param:
                             continue
@@ -169,7 +174,6 @@ def check_resource_parameter(
                             invalid_parameters.append(pattern)
 
                 elif isinstance(resource_parameter, dict):
-
                     if nested_prop and nested_prop in resource_parameter:
                         resource_parameter = resource_parameter.get(nested_prop)
 
@@ -177,6 +181,9 @@ def check_resource_parameter(
                     if not pattern:
                         continue
 
+                    if isinstance(pattern, list):
+                        pattern = pattern[0]
+
                     if check_param_in_env_file(
                         environment_pair,
                         pattern,
@@ -190,498 +197,321 @@ def check_resource_parameter(
     return set(invalid_parameters)
 
 
-@validates("R-91125")
-def test_nova_server_image_parameter_exists_in_environment_file(heat_template):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = "image"
-    DESIRED = True
-    resource_type = "OS::Nova::Server"
-
-    invalid_parameters = check_resource_parameter(
-        environment_pair, prop, DESIRED, resource_type
-    )
+def run_check_resource_parameter(
+    yaml_file, prop, DESIRED, resource_type, check_resource=True, **kwargs
+):
+    filepath, filename = os.path.split(yaml_file)
+    environment_pair = get_environment_pair(yaml_file)
 
-    assert not invalid_parameters, (
-        "OS::Nova::Server {} parameters not"
-        " found in {} environment file {}".format(
-            prop, environment_pair.get("name"), invalid_parameters
+    if not environment_pair:
+        # this is a nested file
+
+        if not check_resource:
+            # dont check env for nested files
+            # This will be tested separately for parent template
+            pytest.skip("This test doesn't apply to nested files")
+
+        environment_pair = find_environment_file(yaml_file)
+        if environment_pair:
+            with open(yaml_file, "r") as f:
+                yml = yaml.load(f)
+            environment_pair["yyml"] = yml
+        else:
+            pytest.skip("unable to determine environment file for nested yaml file")
+
+    if check_resource:
+        invalid_parameters = check_resource_parameter(
+            environment_pair, prop, DESIRED, resource_type, **kwargs
         )
-    )
-
-
-@validates("R-69431")
-def test_nova_server_flavor_parameter_exists_in_environment_file(heat_template):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
+    else:
+        invalid_parameters = check_param_in_env_file(environment_pair, prop, DESIRED)
 
-    prop = "flavor"
-    DESIRED = True
-    resource_type = "OS::Nova::Server"
+    if kwargs.get("resource_type_inverse"):
+        resource_type = "non-{}".format(resource_type)
 
-    invalid_parameters = check_resource_parameter(
-        environment_pair, prop, DESIRED, resource_type
+    params = (
+        ": {}".format(", ".join(invalid_parameters))
+        if isinstance(invalid_parameters, Iterable)
+        else ""
     )
 
     assert not invalid_parameters, (
-        "OS::Nova::Server {} parameters not"
-        " found in {} environment file {}".format(
-            prop, environment_pair.get("name"), invalid_parameters
+        "{} {} parameters in template {}{}"
+        " found in {} environment file{}".format(
+            resource_type,
+            prop,
+            filename,
+            " not" if DESIRED else "",
+            environment_pair.get("name"),
+            params,
         )
     )
 
 
-@validates("R-22838")
-def test_nova_server_name_parameter_doesnt_exist_in_environment_file(heat_template):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
+@validates("R-91125")
+def test_nova_server_image_parameter_exists_in_environment_file(yaml_file):
+    run_check_resource_parameter(yaml_file, "image", True, "OS::Nova::Server")
 
-    environment_pair = get_environment_pair(heat_template)
 
-    prop = "name"
-    DESIRED = False
-    resource_type = "OS::Nova::Server"
+@validates("R-69431")
+def test_nova_server_flavor_parameter_exists_in_environment_file(yaml_file):
+    run_check_resource_parameter(yaml_file, "flavor", True, "OS::Nova::Server")
 
-    invalid_parameters = check_resource_parameter(
-        environment_pair, prop, DESIRED, resource_type
-    )
 
-    assert not invalid_parameters, (
-        "OS::Nova::Server {} parameters"
-        " found in {} environment file {}".format(
-            prop, environment_pair.get("name"), invalid_parameters
-        )
-    )
+@categories("environment_file")
+@validates("R-22838")
+def test_nova_server_name_parameter_doesnt_exist_in_environment_file(yaml_file):
+    run_check_resource_parameter(yaml_file, "name", False, "OS::Nova::Server")
 
 
+@categories("environment_file")
 @validates("R-59568")
-def test_nova_server_az_parameter_doesnt_exist_in_environment_file(heat_template):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = "availability_zone"
-    DESIRED = False
-    resource_type = "OS::Nova::Server"
-
-    invalid_parameters = check_resource_parameter(
-        environment_pair, prop, DESIRED, resource_type
-    )
-
-    assert not invalid_parameters, (
-        "OS::Nova::Server {} parameters"
-        " found in {} environment file {}".format(
-            prop, environment_pair.get("name"), invalid_parameters
-        )
+def test_nova_server_az_parameter_doesnt_exist_in_environment_file(yaml_file):
+    run_check_resource_parameter(
+        yaml_file, "availability_zone", False, "OS::Nova::Server"
     )
 
 
+@categories("environment_file")
 @validates("R-20856")
-def test_nova_server_vnf_id_parameter_doesnt_exist_in_environment_file(heat_template):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = "vnf_id"
-    DESIRED = False
-
-    invalid_parameters = check_param_in_env_file(environment_pair, prop, DESIRED)
-
-    assert not invalid_parameters, (
-        "{} parameters"
-        " found in {} environment file {}".format(
-            prop, environment_pair.get("name"), invalid_parameters
-        )
-    )
+def test_nova_server_vnf_id_parameter_doesnt_exist_in_environment_file(yaml_file):
+    run_check_resource_parameter(yaml_file, "vnf_id", False, "", check_resource=False)
 
 
+@categories("environment_file")
 @validates("R-72871")
-def test_nova_server_vf_module_id_parameter_doesnt_exist_in_environment_file(
-    heat_template
-):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = "vf_module_id"
-    DESIRED = False
-
-    invalid_parameters = check_param_in_env_file(environment_pair, prop, DESIRED)
-
-    assert not invalid_parameters, (
-        "{} parameters"
-        " found in {} environment file {}".format(
-            prop, environment_pair.get("name"), invalid_parameters
-        )
+def test_nova_server_vf_module_id_parameter_doesnt_exist_in_environment_file(yaml_file):
+    run_check_resource_parameter(
+        yaml_file, "vf_module_id", False, "", check_resource=False
     )
 
 
+@categories("environment_file")
 @validates("R-37039")
 def test_nova_server_vf_module_index_parameter_doesnt_exist_in_environment_file(
-    heat_template
+    yaml_file
 ):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = "vf_module_index"
-    DESIRED = False
-
-    invalid_parameters = check_param_in_env_file(environment_pair, prop, DESIRED)
-
-    assert not invalid_parameters, (
-        "{} parameters"
-        " found in {} environment file {}".format(
-            prop, environment_pair.get("name"), invalid_parameters
-        )
+    run_check_resource_parameter(
+        yaml_file, "vf_module_index", False, "", check_resource=False
     )
 
 
+@categories("environment_file")
 @validates("R-36542")
-def test_nova_server_vnf_name_parameter_doesnt_exist_in_environment_file(heat_template):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = "vnf_name"
-    DESIRED = False
-
-    invalid_parameters = check_param_in_env_file(environment_pair, prop, DESIRED)
-
-    assert not invalid_parameters, (
-        "{} parameters"
-        " found in {} environment file {}".format(
-            prop, environment_pair.get("name"), invalid_parameters
-        )
-    )
+def test_nova_server_vnf_name_parameter_doesnt_exist_in_environment_file(yaml_file):
+    run_check_resource_parameter(yaml_file, "vnf_name", False, "", check_resource=False)
 
 
+@categories("environment_file")
 @validates("R-80374")
 def test_nova_server_vf_module_name_parameter_doesnt_exist_in_environment_file(
-    heat_template
+    yaml_file
 ):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = "vf_module_name"
-    DESIRED = False
-
-    invalid_parameters = check_param_in_env_file(environment_pair, prop, DESIRED)
-
-    assert not invalid_parameters, (
-        "{} parameters"
-        " found in {} environment file {}".format(
-            prop, environment_pair.get("name"), invalid_parameters
-        )
+    run_check_resource_parameter(
+        yaml_file, "vf_module_name", False, "", check_resource=False
     )
 
 
+@categories("environment_file")
 @validates("R-02691")
 def test_nova_server_workload_context_parameter_doesnt_exist_in_environment_file(
-    heat_template
+    yaml_file
 ):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = "workload_context"
-    DESIRED = False
-
-    invalid_parameters = check_param_in_env_file(environment_pair, prop, DESIRED)
-
-    assert not invalid_parameters, (
-        "{} parameters"
-        " found in {} environment file {}".format(
-            prop, environment_pair.get("name"), invalid_parameters
-        )
+    run_check_resource_parameter(
+        yaml_file, "workload_context", False, "", check_resource=False
     )
 
 
+@categories("environment_file")
 @validates("R-13194")
 def test_nova_server_environment_context_parameter_doesnt_exist_in_environment_file(
-    heat_template
+    yaml_file
 ):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = "environment_context"
-    DESIRED = False
-
-    invalid_parameters = check_param_in_env_file(environment_pair, prop, DESIRED)
-
-    assert not invalid_parameters, (
-        "{} parameters"
-        " found in {} environment file {}".format(
-            prop, environment_pair.get("name"), invalid_parameters
-        )
+    run_check_resource_parameter(
+        yaml_file, "environment_context", False, "", check_resource=False
     )
 
 
+@categories("environment_file")
 @validates("R-29872")
-def test_nova_server_network_parameter_doesnt_exist_in_environment_file(heat_template):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = "networks"
-    nested_prop = "network"
-    DESIRED = False
-    resource_type = "OS::Nova::Server"
-
-    invalid_parameters = check_resource_parameter(
-        environment_pair, prop, DESIRED, resource_type, nested_prop=nested_prop
-    )
-
-    assert not invalid_parameters, (
-        "{} {} parameters"
-        " found in {} environment file {}".format(
-            resource_type, nested_prop, environment_pair.get("name"), invalid_parameters
-        )
-    )
+def test_neutron_port_network_parameter_doesnt_exist_in_environment_file(yaml_file):
+    run_check_resource_parameter(yaml_file, "network", False, "OS::Neutron::Port")
 
 
+@categories("environment_file")
 @validates("R-39841", "R-87123", "R-62590", "R-98905", "R-93030", "R-62590")
 def test_neutron_port_external_fixedips_ipaddress_parameter_doesnt_exist_in_environment_file(
-    heat_template
+    yaml_file
 ):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = "fixed_ips"
-    nested_prop = "ip_address"
-    DESIRED = False
-    resource_type = "OS::Neutron::Port"
-    exclude_parameter = re.compile(r"^(.+?)_int_(.+?)$")
-
-    invalid_parameters = check_resource_parameter(
-        environment_pair,
-        prop,
-        DESIRED,
-        resource_type,
-        nested_prop=nested_prop,
-        exclude_parameter=exclude_parameter,
-    )
-
-    assert not invalid_parameters, (
-        "{} {} external parameters"
-        " found in {} environment file {}".format(
-            resource_type, nested_prop, environment_pair.get("name"), invalid_parameters
-        )
+    run_check_resource_parameter(
+        yaml_file,
+        "fixed_ips",
+        False,
+        "OS::Neutron::Port",
+        nested_prop="ip_address",
+        exclude_parameter=re.compile(r"^(.+?)_int_(.+?)$"),
     )
 
 
 @validates("R-28795", "R-97201", "R-93496", "R-90206", "R-98569", "R-93496")
 def test_neutron_port_internal_fixedips_ipaddress_parameter_exists_in_environment_file(
-    heat_template
+    yaml_file
 ):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = "fixed_ips"
-    nested_prop = "ip_address"
-    DESIRED = True
-    resource_type = "OS::Neutron::Port"
-    exclude_parameter = re.compile(r"^((?!_int_).)*$")
-
-    invalid_parameters = check_resource_parameter(
-        environment_pair,
-        prop,
-        DESIRED,
-        resource_type,
-        nested_prop=nested_prop,
-        exclude_parameter=exclude_parameter,
-    )
-
-    assert not invalid_parameters, (
-        "{} {} internal parameters"
-        " not found in {} environment file {}".format(
-            resource_type, nested_prop, environment_pair.get("name"), invalid_parameters
-        )
+    run_check_resource_parameter(
+        yaml_file,
+        "fixed_ips",
+        True,
+        "OS::Neutron::Port",
+        nested_prop="ip_address",
+        exclude_parameter=re.compile(r"^((?!_int_).)*$"),
     )
 
 
+@categories("environment_file")
 @validates("R-83677", "R-80829", "R-69634", "R-22288")
 def test_neutron_port_fixedips_subnet_parameter_doesnt_exist_in_environment_file(
-    heat_template
+    yaml_file
 ):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = "fixed_ips"
-    nested_prop = "subnet"
-    DESIRED = False
-    resource_type = "OS::Neutron::Port"
-
-    invalid_parameters = check_resource_parameter(
-        environment_pair, prop, DESIRED, resource_type, nested_prop=nested_prop
-    )
-
-    assert not invalid_parameters, (
-        "{} {} parameters"
-        " found in {} environment file {}".format(
-            resource_type, nested_prop, environment_pair.get("name"), invalid_parameters
-        )
+    run_check_resource_parameter(
+        yaml_file, "fixed_ips", False, "OS::Neutron::Port", nested_prop="subnet"
     )
 
 
+@categories("environment_file")
 @validates("R-83412", "R-83418")
-def test_neutron_port_aap_ip_parameter_doesnt_exist_in_environment_file(heat_template):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = "allowed_address_pairs"
-    nested_prop = "ip_address"
-    DESIRED = False
-    resource_type = "OS::Neutron::Port"
-
-    invalid_parameters = check_resource_parameter(
-        environment_pair, prop, DESIRED, resource_type, nested_prop=nested_prop
-    )
-
-    assert not invalid_parameters, (
-        "{} {} parameters"
-        " found in {} environment file {}".format(
-            resource_type, nested_prop, environment_pair.get("name"), invalid_parameters
-        )
+def test_neutron_port_external_aap_ip_parameter_doesnt_exist_in_environment_file(
+    yaml_file
+):
+    run_check_resource_parameter(
+        yaml_file,
+        "allowed_address_pairs",
+        False,
+        "OS::Neutron::Port",
+        nested_prop="ip_address",
+        exclude_parameter=re.compile(r"^(.+?)_int_(.+?)$"),
     )
 
 
+@categories("environment_file")
 @validates("R-99812")
-def test_non_nova_server_name_parameter_doesnt_exist_in_environment_file(heat_template):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = "name"
-    DESIRED = False
-    resource_type = "OS::Nova::Server"
-
-    invalid_parameters = check_resource_parameter(
-        environment_pair, prop, DESIRED, resource_type, resource_type_inverse=True
-    )
-
-    assert not invalid_parameters, (
-        "non-{} {} parameters"
-        " found in {} environment file {}".format(
-            resource_type, prop, environment_pair.get("name"), invalid_parameters
-        )
+def test_non_nova_server_name_parameter_doesnt_exist_in_environment_file(yaml_file):
+    run_check_resource_parameter(
+        yaml_file, "name", False, "OS::Nova::Server", resource_type_inverse=True
     )
 
 
+@categories("environment_file")
 @validates("R-92193")
-def test_network_fqdn_parameter_doesnt_exist_in_environment_file(heat_template):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = r"^(.+?)_net_fqdn$"
-    DESIRED = False
-
-    invalid_parameters = check_param_in_env_file(environment_pair, prop, DESIRED)
-
-    assert not invalid_parameters, (
-        "{} parameters"
-        " found in {} environment file {}".format(
-            prop, environment_pair.get("name"), invalid_parameters
-        )
+def test_network_fqdn_parameter_doesnt_exist_in_environment_file(yaml_file):
+    run_check_resource_parameter(
+        yaml_file, r"^(.+?)_net_fqdn$", False, "", check_resource=False
     )
 
 
+@categories("environment_file")
 @validates("R-76682")
-def test_contrail_route_prefixes_parameter_doesnt_exist_in_environment_file(
-    heat_template
-):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = "interface_route_table_routes"
-    nested_prop = "interface_route_table_routes_route"
-    DESIRED = False
-    resource_type = "OS::ContrailV2::InterfaceRouteTable"
-
-    invalid_parameters = check_resource_parameter(
-        environment_pair, prop, DESIRED, resource_type, nested_prop=nested_prop
-    )
-
-    assert not invalid_parameters, (
-        "{} {} parameters"
-        " found in {} environment file {}".format(
-            resource_type, nested_prop, environment_pair.get("name"), invalid_parameters
-        )
+def test_contrail_route_prefixes_parameter_doesnt_exist_in_environment_file(yaml_file):
+    run_check_resource_parameter(
+        yaml_file,
+        "interface_route_table_routes",
+        False,
+        "OS::ContrailV2::InterfaceRouteTable",
+        nested_prop="interface_route_table_routes_route",
     )
 
 
 @validates("R-50011")
-def test_heat_rg_count_parameter_exists_in_environment_file(heat_template):
-
-    if pytest.config.getoption("validation_profile") == "heat_only":
-        pytest.skip("skipping test because validation profile is heat only")
-
-    environment_pair = get_environment_pair(heat_template)
-
-    prop = "count"
-    DESIRED = True
-    resource_type = "OS::Heat::ResourceGroup"
-    exclude_resource = re.compile(r"^(.+?)_subint_(.+?)_port_(.+?)_subinterfaces$")
-
-    invalid_parameters = check_resource_parameter(
-        environment_pair,
-        prop,
-        DESIRED,
-        resource_type,
-        exclude_resource=exclude_resource,
-    )
-
-    assert not invalid_parameters, (
-        "{} {} parameters not"
-        " found in {} environment file {}".format(
-            resource_type, prop, environment_pair.get("name"), invalid_parameters
+def test_heat_rg_count_parameter_exists_in_environment_file(yaml_file):
+    run_check_resource_parameter(
+        yaml_file,
+        "count",
+        True,
+        "OS::Heat::ResourceGroup",
+        exclude_resource=re.compile(r"^(.+?)_subint_(.+?)_port_(.+?)_subinterfaces$"),
+    )
+
+
+@categories("environment_file")
+@validates("R-100020", "R-100040", "R-100060", "R-100080", "R-100170")
+def test_contrail_external_instance_ip_does_not_exist_in_environment_file(yaml_file):
+    run_check_resource_parameter(
+        yaml_file,
+        "instance_ip_address",
+        False,
+        "OS::ContrailV2::InstanceIp",
+        exclude_resource=re.compile(r"^.*_int_.*$"),  # exclude internal IPs
+    )
+
+
+@validates("R-100100", "R-100120", "R-100140", "R-100160", "R-100180")
+def test_contrail_internal_instance_ip_does_exist_in_environment_file(yaml_file):
+    run_check_resource_parameter(
+        yaml_file,
+        "instance_ip_address",
+        True,
+        "OS::ContrailV2::InstanceIp",
+        exclude_resource=re.compile(r"(?!.*_int_.*)"),  # exclude external IPs
+    )
+
+
+@categories("environment_file")
+@validates("R-100210", "R-100230", "R-100250", "R-100270")
+def test_contrail_subnet_uuid_does_not_exist_in_environment_file(yaml_file):
+    run_check_resource_parameter(
+        yaml_file, "subnet_uuid", False, "OS::ContrailV2::InstanceIp"
+    )
+
+
+@categories("environment_file")
+@validates("R-100320", "R-100340")
+def test_contrail_vmi_aap_does_not_exist_in_environment_file(yaml_file):
+    # This test needs to check a more complex structure.  Rather than try to force
+    # that into the existing run_check_resource_parameter logic we'll just check it
+    # directly
+    pairs = get_environment_pair(yaml_file)
+    if not pairs:
+        pytest.skip("No matching env file found")
+    heat = Heat(filepath=yaml_file)
+    env_parameters = pairs["eyml"].get("parameters") or {}
+    vmis = heat.get_resource_by_type("OS::ContrailV2::VirtualMachineInterface")
+    external_vmis = {rid: data for rid, data in vmis.items() if "_int_" not in rid}
+    invalid_params = []
+    for r_id, vmi in external_vmis.items():
+        aap_value = nested_dict.get(
+            vmi,
+            "properties",
+            "virtual_machine_interface_allowed_address_pairs",
+            "virtual_machine_interface_allowed_address_pairs_allowed_address_pair",
         )
-    )
+        if not aap_value or not isinstance(aap_value, list):
+            # Skip if aap not used or is not a list.
+            continue
+        for pair_ip in aap_value:
+            if not isinstance(pair_ip, dict):
+                continue  # Invalid Heat will be detected by another test
+            settings = (
+                pair_ip.get(
+                    "virtual_machine_interface_allowed_address"
+                    "_pairs_allowed_address_pair_ip"
+                )
+                or {}
+            )
+            if isinstance(settings, dict):
+                ip_prefix = (
+                    settings.get(
+                        "virtual_machine_interface_allowed_address"
+                        "_pairs_allowed_address_pair_ip_ip_prefix"
+                    )
+                    or {}
+                )
+                ip_prefix_param = get_param(ip_prefix)
+                if ip_prefix_param and ip_prefix_param in env_parameters:
+                    invalid_params.append(ip_prefix_param)
+
+    msg = (
+        "OS::ContrailV2::VirtualMachineInterface "
+        "virtual_machine_interface_allowed_address_pairs"
+        "_allowed_address_pair_ip_ip_prefix "
+        "parameters found in environment file {}: {}"
+    ).format(pairs.get("name"), ", ".join(invalid_params))
+    assert not invalid_params, msg