X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=ice_validator%2Ftests%2Fstructures.py;h=aaed8d10cdb557d83668d09a346b6386d954c784;hb=ded5c74ea07eb1541587de1042444fa6b590ddde;hp=e81f3a1c0645d2c99c86626bea4e936c5ab8c2dc;hpb=7a7bc66879f2efd93be2ebbd959176a10b07f74f;p=vvp%2Fvalidation-scripts.git diff --git a/ice_validator/tests/structures.py b/ice_validator/tests/structures.py index e81f3a1..aaed8d1 100644 --- a/ice_validator/tests/structures.py +++ b/ice_validator/tests/structures.py @@ -36,8 +36,6 @@ # ============LICENSE_END============================================ # # -"""structures -""" import collections import inspect import os @@ -45,7 +43,7 @@ import re import sys from tests import cached_yaml as yaml -from tests.helpers import load_yaml +from tests.helpers import load_yaml, get_param from .utils import nested_dict VERSION = "4.2.0" @@ -85,13 +83,19 @@ class HeatProcessor(object): # regex parses the proper resource id format. @staticmethod - def get_param_value(value): + def get_param_value(value, withIndex=False): """Return get_param value of `value` """ if isinstance(value, dict) and len(value) == 1: v = value.get("get_param") if isinstance(v, list) and v: - v = v[0] + if withIndex and len(v) > 1: + idx = v[1] + if isinstance(idx, dict): + idx = idx.get("get_param", idx) + v = "{}{}".format(v[0], idx) + else: + v = v[0] else: v = None return v @@ -161,7 +165,7 @@ class HeatProcessor(object): # are replaced in the template in arbitrary order. name = template for key, value in params.items(): - param = cls.get_param_value(value) + param = cls.get_param_value(value, withIndex=True) if param is None: return None name = name.replace(key, str(param)) @@ -223,7 +227,7 @@ class ContrailV2NetworkFlavorBaseProcessor(HeatProcessor): network_flavor_external = "external" network_flavor_internal = "internal" - network_flavor_subint = "subint" + network_flavor_subint = "subinterface" @classmethod def get_network_flavor(cls, resource): @@ -246,15 +250,19 @@ class ContrailV2NetworkFlavorBaseProcessor(HeatProcessor): network_flavor = cls.network_flavor_internal else: p = param.get("get_param") - if isinstance(p, str): - if "_int_" in p or p.startswith("int_"): - network_flavor = cls.network_flavor_internal - elif "_subint_" in p: - network_flavor = cls.network_flavor_subint - else: - network_flavor = cls.network_flavor_external + network_flavor = cls.get_network_format(p) return network_flavor + @classmethod + def get_network_format(cls, param): + if isinstance(param, str): + if "_int_" in param or param.startswith("int_"): + return cls.network_flavor_internal + elif "_subint_" in param: + return cls.network_flavor_subint + else: + return cls.network_flavor_external + class ContrailV2InstanceIpProcessor(ContrailV2NetworkFlavorBaseProcessor): """ ContrailV2 InstanceIp @@ -264,7 +272,7 @@ class ContrailV2InstanceIpProcessor(ContrailV2NetworkFlavorBaseProcessor): re_rids = collections.OrderedDict( [ ( - "int_ip", + "internal", _get_regex( r"(?P.+)" r"_(?P\d+)" @@ -272,27 +280,14 @@ class ContrailV2InstanceIpProcessor(ContrailV2NetworkFlavorBaseProcessor): r"_(?P.+)" r"_vmi" r"_(?P\d+)" + r"(_v6)?" r"_IP" r"_(?P\d+)" r"$" ), ), ( - "int_v6_ip", - _get_regex( - r"(?P.+)" - r"_(?P\d+)" - r"_int" - r"_(?P.+)" - r"_vmi" - r"_(?P\d+)" - r"_v6_IP" - r"_(?P\d+)" - r"$" - ), - ), - ( - "subint_ip", + "subinterface", _get_regex( r"(?P.+)" r"_(?P\d+)" @@ -300,51 +295,26 @@ class ContrailV2InstanceIpProcessor(ContrailV2NetworkFlavorBaseProcessor): r"_(?P.+)" r"_vmi" r"_(?P\d+)" + r"(_v6)?" r"_IP" r"_(?P\d+)" r"$" ), ), ( - "subint_v6_ip", - _get_regex( - r"(?P.+)" - r"_(?P\d+)" - r"_subint" - r"_(?P.+)" - r"_vmi" - r"_(?P\d+)" - r"_v6_IP" - r"_(?P\d+)" - r"$" - ), - ), - ( - "ip", + "external", _get_regex( r"(?P.+)" r"_(?P\d+)" r"_(?P.+)" r"_vmi" r"_(?P\d+)" + r"(_v6)?" r"_IP" r"_(?P\d+)" r"$" ), ), - ( - "v6_ip", - _get_regex( - r"(?P.+)" - r"_(?P\d+)" - r"_(?P.+)" - r"_vmi" - r"_(?P\d+)" - r"_v6_IP" - r"_(?P\d+)" - r"$" - ), - ), ] ) @@ -406,7 +376,7 @@ class ContrailV2VirtualMachineInterfaceProcessor(ContrailV2NetworkFlavorBaseProc re_rids = collections.OrderedDict( [ ( - "vmi_internal", + "internal", _get_regex( r"(?P.+)" r"_(?P\d+)" @@ -418,7 +388,7 @@ class ContrailV2VirtualMachineInterfaceProcessor(ContrailV2NetworkFlavorBaseProc ), ), ( - "vmi_subint", + "subinterface", _get_regex( r"(?P.+)" r"_(?P\d+)" @@ -430,7 +400,7 @@ class ContrailV2VirtualMachineInterfaceProcessor(ContrailV2NetworkFlavorBaseProc ), ), ( - "vmi_external", + "external", _get_regex( r"(?P.+)" r"_(?P\d+)" @@ -498,7 +468,7 @@ class NeutronPortProcessor(HeatProcessor): re_rids = collections.OrderedDict( [ ( - "internal_port", + "internal", _get_regex( r"(?P.+)" r"_(?P\d+)" @@ -509,7 +479,7 @@ class NeutronPortProcessor(HeatProcessor): ), ), ( - "port", + "external", _get_regex( r"(?P.+)" r"_(?P\d+)" @@ -518,26 +488,6 @@ class NeutronPortProcessor(HeatProcessor): r"$" ), ), - ( - "floating_ip", - _get_regex( - r"reserve_port" - r"_(?P.+)" - r"_(?P.+)" - r"_floating_ip_(?P\d+)" - r"$" - ), - ), - ( - "floating_v6_ip", - _get_regex( - r"reserve_port" - r"_(?P.+)" - r"_(?P.+)" - r"_floating_v6_ip_(?P\d+)" - r"$" - ), - ), ] ) @@ -546,11 +496,14 @@ class NeutronPortProcessor(HeatProcessor): """Returns True/False as `resource` is/not An OS::Nova:Port with the property binding:vnic_type """ - return nested_dict.get( - resource, "type" - ) == cls.resource_type and "binding:vnic_type" in nested_dict.get( - resource, "properties", default={} - ) + resource_properties = nested_dict.get(resource, "properties", default={}) + if ( + nested_dict.get(resource, "type") == cls.resource_type + and resource_properties.get("binding:vnic_type", "") == "direct" + ): + return True + + return False class NovaServerProcessor(HeatProcessor): @@ -608,7 +561,7 @@ class NovaServerProcessor(HeatProcessor): d = dict( flavor=cls.get_flavor(resource), image=cls.get_image(resource), - networks=cls.get_network(resource), + network_role=cls.get_network(resource), ) if all(d.values()): vm_class.update(d) @@ -650,6 +603,10 @@ class Heat(object): self.load_env(envpath) self.heat_processors = self.get_heat_processors() + @property + def is_heat(self): + return "heat_template_version" in self.yml + @property def contrail_resources(self): """This attribute is a dict of Contrail resources. @@ -658,19 +615,28 @@ class Heat(object): resource_type=ContrailV2VirtualMachineInterfaceProcessor.resource_type ) - def get_all_resources(self, base_dir): + def get_all_resources(self, base_dir=None, count=1): """ - Like ``resources``, - but this returns all the resources definitions + Like ``resources``, but this returns all the resources definitions defined in the template, resource groups, and nested YAML files. + + A special variable will be added to all resource properties (__count__). + This will normally be 1, but if the resource is generated by a + ResourceGroup **and** an env file is present, then the count will be + the value from the env file (assuming this follows standard VNF Heat + Guidelines) """ + base_dir = base_dir or self.dirname resources = {} for r_id, r_data in self.resources.items(): + r_data["__count__"] = count resources[r_id] = r_data resource = Resource(r_id, r_data) if resource.is_nested(): + nested_count = resource.get_count(self.env) nested = Heat(os.path.join(base_dir, resource.get_nested_filename())) - resources.update(nested.get_all_resources(base_dir)) + nested_resources = nested.get_all_resources(count=nested_count) + resources.update(nested_resources) return resources @staticmethod @@ -680,13 +646,14 @@ class Heat(object): """ return _HEAT_PROCESSORS - def get_resource_by_type(self, resource_type): + def get_resource_by_type(self, resource_type, all_resources=False): """Return dict of resources whose type is `resource_type`. key is resource_id, value is resource. """ + resources = self.get_all_resources() if all_resources else self.resources return { rid: resource - for rid, resource in self.resources.items() + for rid, resource in resources.items() if self.nested_get(resource, "type") == resource_type } @@ -763,6 +730,31 @@ class Heat(object): re.search("(^(%(x)s)_)|(_(%(x)s)_)|(_(%(x)s)$)" % dict(x=part), name) ) + def iter_nested_heat(self): + """ + Returns an iterable of tuples (int, heat) where the first parameter is the + depth of the nested file and the second item is an instance of Heat + """ + + def walk_nested(heat, level=1): + resources = [Resource(r_id, data) for r_id, data in heat.resources.items()] + for resource in resources: + if resource.is_nested(): + nested_path = os.path.join( + self.dirname, resource.get_nested_filename() + ) + nested_heat = Heat(nested_path) + yield level, nested_heat + yield from walk_nested(nested_heat, level + 1) + + yield from walk_nested(self) + + def __str__(self): + return "Heat({})".format(self.filepath) + + def __repr__(self): + return str(self) + class Env(Heat): """An Environment file @@ -817,6 +809,24 @@ class Resource(object): else: return self.properties + def get_count(self, env): + if self.resource_type == "OS::Heat::ResourceGroup": + if not env: + return 1 + env_params = env.parameters + count_param = get_param(self.properties["count"]) + count_value = env_params.get(count_param) if count_param else 1 + try: + return int(count_value) + except (ValueError, TypeError): + print( + ( + "WARNING: Invalid value for count parameter {}. Expected " + "an integer, but got {}. Defaulting to 1" + ).format(count_param, count_value) + ) + return 1 + @property def depends_on(self): """ @@ -845,6 +855,12 @@ class Resource(object): else: return {} + def __str__(self): + return "Resource(id={}, type={})".format(self.resource_id, self.resource_type) + + def __repr__(self): + return str(self) + def get_all_resources(yaml_files): """Return a dict, resource id: resource