[VVP] Fixing internal network check 41/78141/1
authorLovett, Trevor <trevor.lovett@att.com>
Thu, 7 Feb 2019 18:46:33 +0000 (10:46 -0800)
committerLovett, Trevor (tl2972) <tl2972@att.com>
Fri, 8 Feb 2019 18:41:46 +0000 (12:41 -0600)
The test_neutron_port_internal_network test
is intended to check that internal networks
referenced in **incremental** modules are defined
and exported in the base template.  The current
version of the test applies this check to nested
modules as well causing false violations to be flagged.

Issue-ID: VVP-163

Change-Id: I42cc81acf0cc2b6827ae8cf4e9a7faa4af91f9b4
Signed-off-by: Lovett, Trevor (tl2972) <tl2972@att.com>
ice_validator/tests/conftest.py
ice_validator/tests/fixtures/test_neutron_port_internal_network/pass/nested.yaml [new file with mode: 0644]
ice_validator/tests/fixtures/test_neutron_port_internal_network/pass/pass0.yaml
ice_validator/tests/fixtures/test_neutron_port_internal_network/pass/pass0_base.yaml
ice_validator/tests/test_neutron_port_internal_network.py
pom.xml

index 4668045..3ab1aba 100644 (file)
@@ -284,7 +284,7 @@ class TestResult:
             return self.item.funcargs["yaml_files"]
         else:
             parts = self.result.nodeid.split("[")
-            return "" if len(parts) == 1 else parts[1][:-1]
+            return [""] if len(parts) == 1 else [parts[1][:-1]]
 
     def _get_error_message(self):
         """
@@ -1041,9 +1041,9 @@ def build_rst_json(reqs):
                 else:
                     # Creates links in RST format to requirements and test cases
                     if values["test_case"]:
-                        val_list = re.findall(r'(?<=\.).*', values["test_case"])
-                        val = TEST_SCRIPT_SITE + val_list[0] + ".py"
-                        rst_value = ("`" + val_list[0] + " <" + val + ">`_")
+                        mod = values["test_case"].split(".")[-1]
+                        val = TEST_SCRIPT_SITE + mod + ".py"
+                        rst_value = ("`" + mod + " <" + val + ">`_")
                         title = "`" + values["id"] + " <" + VNFRQTS_ID_URL + values["docname"].replace(" ", "%20") + ".html#" + values["id"] + ">`_"
                         data[key].update({'full_title': title, 'test_case': rst_value})
                     else:
diff --git a/ice_validator/tests/fixtures/test_neutron_port_internal_network/pass/nested.yaml b/ice_validator/tests/fixtures/test_neutron_port_internal_network/pass/nested.yaml
new file mode 100644 (file)
index 0000000..882a757
--- /dev/null
@@ -0,0 +1,49 @@
+# -*- coding: utf8 -*-
+# ============LICENSE_START====================================================
+# org.onap.vvp/validation-scripts
+# ===================================================================
+# Copyright © 2019 AT&T Intellectual Property. All rights reserved.
+# ===================================================================
+#
+# Unless otherwise specified, all software contained herein is licensed
+# under the Apache License, Version 2.0 (the "License");
+# you may not use this software except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#             http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+#
+# Unless otherwise specified, all documentation contained herein is licensed
+# under the Creative Commons License, Attribution 4.0 Intl. (the "License");
+# you may not use this documentation except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#             https://creativecommons.org/licenses/by/4.0/
+#
+# Unless required by applicable law or agreed to in writing, documentation
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# ============LICENSE_END============================================
+
+parameters:
+
+  int_special_net_id:
+    type: string
+
+resources:
+
+  vm_typeX_0_intranet_port_3:
+    type: OS::Neutron::Port
+    properties:
+      network: { get_param: int_special_net_id }
+
index 2309104..6983f7c 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
 # limitations under the License.
 #
 # ============LICENSE_END============================================
-#
-#
-# VERSION: '1.0.0'
 
----
 parameters:
 
   vm_typeX_bialy_vlan_filter:
@@ -53,16 +49,15 @@ parameters:
     type: string
   subnet_id_param:
     type: string
-  int_extnet_net_id:
-    type: string
-  int_intranet_net_name:
+  int_intranet_net_id:
     type: string
+
 resources:
 
   vm_typeX_0_intranet_port_2:
     type: OS::Neutron::Port
     properties:
-      network: { get_param: int_intranet_net_name }
+      network: { get_param: int_intranet_net_id }
       fixed_ips:
         - ip_address: { get_param: lb_1_int_intranet_floating_ip }
           subnet: { get_param: subnet_param }
@@ -81,7 +76,7 @@ resources:
   vm_typeX_3_extnet_port_2:
     type: OS::Neutron::Port
     properties:
-      network: { get_param: int_extnet_net_id }
+      network: { get_param: int_intranet_net_id }
       fixed_ips:
         - ip_address: { get_param: lb_2_extnet_floating_v6_ip }
           subnet: { get_param: subnet_param }
@@ -97,3 +92,14 @@ resources:
     metadata:
         port_type: SR-IOV_Mirrored_Trunk
 
+    int_special_network:
+      type: OS::Neutron::Net
+
+    vm_typeX_3_extnet_port_3:
+      type: nested.yaml
+      properties:
+        int_special_net_id: { get_resource: int_special_network }
+
+
+
+
index 797396c..248f780 100644 (file)
 #
 # VERSION: '1.0.0'
 
----
-outputs:
 
-  int_extnet_net_id:
-    value: { get_resource: int_extnet_net_id }
+parameters:
   int_intranet_net_name:
-    value: { get_param: int_intranet_net_name }
-
+    type: string
 
 resources:
 
   int_intranet_network:
     type: OS::Neutron::Net
-    properties:
-      name: { get_param: int_intranet_net_name }
 
-  int_extnet_network:
-    type: OS::Neutron::Net
+outputs:
 
-parameters:
-  int_intranet_net_name:
-    type: string
+  int_intranet_net_id:
+    value: { get_resource: int_intranet_network }
index d25c415..00a3a93 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============================================
 #
-#
-
-"""
-resources:
-{vm-type}_{vm-type_index}_{network-role}_port_{port-index}:
-  type: OS::Neutron::Port
-  properties:
-    network: { get_param: ...}
-    fixed_ips: [ { "ipaddress": { get_param: ... } } ]
-    binding:vnic_type: direct           #only SR-IOV ports, not OVS ports
-    value_specs: {
-      vlan_filter: { get_param: ... },  #all NC ports
-      public_vlans: { get_param: ... }, #all NC ports
-      private_vlans: { get_param: ... },#all NC ports
-      guest_vlans: { get_param: ... },  #SR-IOV Trunk Port only
-      vlan_mirror: { get_param: ... },  #SRIOV Trunk Port
-                                        # Receiving Mirrored Traffic only
-     ATT_FABRIC_CONFIGURATION_REQUIRED: true #all NC ports
-    }
-  metadata:
-    port_type: SR-IOV_Trunk             #SR-IOV Trunk Port
-    port_type: SR-IOV_Non_Trunk         #SR-IOV Non Trunk Port
-    port_type: OVS                      #OVS Port
-    port_type: SR-IOV_Mirrored_Trunk    #SR-IOV Trunk Port
-                                        # Receiving Mirrored Traffic
-"""
 
 import os.path
 import re
 
-import pytest
-
+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
+from .helpers import validates, load_yaml
 
-VERSION = "1.1.0"
 
-RE_BASE = re.compile(r"(^base$)|(^base_)|(_base_)|(_base$)")  # search pattern
-RE_NEUTRON_PORT_RID = re.compile(  # match pattern
-    r"(?P<vm_type>.+)"
-    r"_(?P<vm_type_index>\d+)"
-    r"_(?P<network_role>.+)"
-    r"_port_"
-    r"(?P<port_index>\d+)"
-    r"$"
-)
-RE_INTERNAL_NETWORK_PARAM = re.compile(  # match pattern
-    r"int_(?P<network_role>.+)_net_(?P<value_type>id|name)$"
-)
-RE_INTERNAL_NETWORK_RID = re.compile(  # match pattern
-    r"int_(?P<network_role>.+)_network$"
-)
+RE_BASE = re.compile(r"(^base$)|(^base_)|(_base_)|(_base$)")
 
 
 def get_base_template_filepath(yaml_files):
@@ -99,141 +58,37 @@ def get_base_template_filepath(yaml_files):
     return None
 
 
-def get_internal_network(yaml_files):
-    """Return the base template's Heat istance.
-    """
-    base_template_filepath = get_base_template_filepath(yaml_files)
-    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':
-        return base_template
-
-    return None
-
-
-def get_neutron_ports(heat):
-    """Return dict of resource_id: resource, whose type is
-    OS::Neutron::Port.
-    """
-    return {
-        rid: resource
-        for rid, resource in heat.resources.items()
-        if heat.nested_get(resource, "type") == "OS::Neutron::Port"
-    }
-
-
-# pylint: disable=invalid-name
-
-
-@validates("R-86182", "R-22688")
-def test_neutron_port_internal_network(yaml_files):
-    """
-    When the VNF's Heat Orchestration Template's Resource
-    ``OS::Neutron::Port`` is attaching to an internal network,
-    and the internal network is created in a
-    different Heat Orchestration Template than the ``OS::Neutron::Port``,
-    the ``network`` parameter name **MUST**
-
-      * follow the naming convention ``int_{network-role}_net_id``
-        if the Neutron
-        network UUID value is used to reference the network
-      * follow the naming convention ``int_{network-role}_net_name`` if the
-        OpenStack network name in is used to reference the network.
-
-    where ``{network-role}`` is the network-role of the internal network and
-    a ``get_param`` **MUST** be used as the intrinsic function.
-
-    In Requirement R-86182, the internal network is created in the VNF's
-    Base Module (Heat Orchestration Template) and the parameter name is
-    declared in the Base Module's ``outputs`` section.
-    When the parameter's value uses a "get_param" function, its name
-    must end in "_name", and when it uses a "get_resource" function,
-    its name must end in "_id".
-
-    The output parameter name will be declared as a parameter in the
-    ``parameters`` section of the incremental module.
-    """
-    internal_network = get_internal_network(yaml_files)
-    if not internal_network:
-        pytest.skip("internal_network template not found")
-
-    if not internal_network.outputs:
-        pytest.skip('internal_network template has no "outputs"')
-
-    for filepath in yaml_files:
-        if filepath != internal_network.filepath:
-            validate_neutron_port(filepath, internal_network)
-
-
-def validate_neutron_port(filepath, internal_network):
-    """validate the neutron port
-    """
-    heat = Heat(filepath=filepath)
-    if not heat.resources:
-        return
-    neutron_ports = get_neutron_ports(heat)
-    if not neutron_ports:
-        return
-    bad = {}
-    for rid, resource in neutron_ports.items():
-        if not heat.parameters:
-            bad[rid] = 'missing "parameters"'
-            continue
-        network = heat.nested_get(resource, "properties", "network", "get_param")
-        if network is None:
-            bad[rid] = 'missing "network.get_param"'
-            continue
-        if not network.startswith("int_"):
-            continue  # not an internal network port
-        error = validate_param(heat, network, internal_network)
-        if error:
-            bad[rid] = error
-    if bad:
-        raise RuntimeError(
-            "Bad OS::Neutron::Port: %s"
-            % (", ".join("%s: %s" % (rid, error) for rid, error in bad.items()))
-        )
-
-
-def validate_param(heat, network, internal_network):
-    """Ensure network (the parameter name) is defined in the base
-    template, and has the correct value function.  Ensure its
-    network-role is found in the base template in some
-    OS::Neutron::Net resource.
-    Return error message string, or None if no no errors.
-    """
-    match = RE_INTERNAL_NETWORK_PARAM.match(network)
-    if not match:
-        return 'network.get_param "%s" does not match "%s"' % (
-            network,
-            RE_INTERNAL_NETWORK_PARAM.pattern,
-        )
-    if heat.nested_get(heat.parameters, network) is None:
-        return "missing parameters.%s" % network
-    output = heat.nested_get(internal_network.outputs, network)
-    if not output:
-        return 'network.get_param "%s"' " not found in base template outputs" % network
-    param_dict = match.groupdict()
-    expect = {"name": "get_param", "id": "get_resource"}[param_dict["value_type"]]
-    value = heat.nested_get(output, "value")
-    if heat.nested_get(value, expect) is None:
-        return (
-            'network.get_param "%s" implies its base template'
-            ' output value function should be "%s" dict not "%s"'
-            % (network, expect, value)
-        )
-    network_role = param_dict["network_role"]
-    for rid, resource in internal_network.resources.items():
-        if (
-            heat.nested_get(resource, "type") == "OS::Neutron::Net"
-            or heat.nested_get(resource, "type") == "OS::ContrailV2::VirtualNetwork"
-        ):
-            match = RE_INTERNAL_NETWORK_RID.match(rid)
-            if match and match.groupdict()["network_role"] == network_role:
-                return None
-    return (
-        "OS::Neutron::Net with network-role"
-        ' "%s" not found in base template."' % network_role
-    )
+@validates("R-22688")
+def test_neutron_port_internal_network_v2(yaml_files):
+    base_path = get_base_template_filepath(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"}
+        for r_id, port in internal_ports.items():
+            props = port.get("properties") or {}
+            network_value = props.get("network") or {}
+            if not isinstance(network_value, dict):
+                continue
+            if "get_param" not in network_value:
+                continue  # Not connecting to network outside the template
+            param = network_value.get("get_param")
+            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))
+            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))
+
+    assert not errors, " ".join(errors)
diff --git a/pom.xml b/pom.xml
index f09fa42..b4362e6 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -56,7 +56,7 @@
             <configuration>
                 <nexusUrl>${onap.nexus.url}</nexusUrl>
                 <stagingProfileId>176c31dfe190a</stagingProfileId>
-                <serverId>onap-staging</serverId>
+                <serverId>ecomp-staging</serverId>
             </configuration>
         </plugin>
         <plugin>
   </build>
   <distributionManagement>
       <site>
-          <id>onap-site</id>
+          <id>ecomp-site</id>
           <url>dav:${onap.nexus.url}${sitePath}</url>
       </site>
   </distributionManagement>
   <repositories>
     <repository>
-        <id>onap-releases</id>
+        <id>ecomp-releases</id>
           <name>Release Repository</name>
           <url>${onap.nexus.url}/content/repositories/releases/</url>
     </repository>
     <repository>
-          <id>onap-snapshots</id>
+          <id>ecomp-snapshots</id>
           <name>Snapshot Repository</name>
           <url>${onap.nexus.url}/content/repositories/snapshots/</url>
     </repository>
     <repository>
-        <id>onap-staging</id>
+        <id>ecomp-staging</id>
         <name>Staging Repository</name>
         <url>${onap.nexus.url}/content/repositories/staging/</url>
     </repository>