diff options
Diffstat (limited to 'ice_validator/tests')
35 files changed, 983 insertions, 2041 deletions
diff --git a/ice_validator/tests/test_env_files_provided.py b/ice_validator/tests/cached_yaml.py index d784d20..99fbd2c 100644 --- a/ice_validator/tests/test_env_files_provided.py +++ b/ice_validator/tests/cached_yaml.py @@ -34,19 +34,27 @@ # limitations under the License. # # ============LICENSE_END============================================ -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. -# -''' -test_env_files_provided -''' +import os + +import yaml + +YAML_CACHE = {} +resolver = yaml.resolver +YAMLError = yaml.YAMLError +constructor = yaml.constructor + -VERSION = '1.0.0' +def add_constructor(tag, constructor): + yaml.add_constructor( + tag, + constructor, + ) -def test_env_files_provided(env_files): - ''' - Make sure environment files have been provided - ''' - assert len(env_files) > 0, '"*.env" not found' +def load(fp): + """Provides cached loading of yaml files""" + abs_path = os.path.abspath(fp.name) + if abs_path not in YAML_CACHE: + YAML_CACHE[abs_path] = yaml.safe_load(fp) + return YAML_CACHE[abs_path] diff --git a/ice_validator/tests/conftest.py b/ice_validator/tests/conftest.py index f5f25a3..a08b99a 100644 --- a/ice_validator/tests/conftest.py +++ b/ice_validator/tests/conftest.py @@ -45,8 +45,10 @@ import os import sys import time import requests +import traceback import docutils.core +import jinja2 import pytest from more_itertools import partition from six import string_types @@ -55,9 +57,7 @@ import xlsxwriter __path__ = [os.path.dirname(os.path.abspath(__file__))] resolution_steps_file = "resolution_steps.json" -requirements_file = "requirements.json" - -FAILURE_DATA = {} +heat_requirements_file = "heat_requirements.json" report_columns = [ ("Input File", "file"), @@ -69,6 +69,12 @@ report_columns = [ ] report = collections.OrderedDict(report_columns) +COLLECTION_FAILURES = [] +COLLECTION_FAILURE_WARNING = """WARNING: The following unexpected errors occurred +while preparing to validate the the input files. Some validations may not have been +executed. Please refer these issue to the VNF Validation Tool team. +""" + def extract_error_msg(rep): try: @@ -92,6 +98,11 @@ def pytest_runtest_makereport(item, call): if rep.outcome == "failed": if not os.path.exists(output_dir): os.mkdir(output_dir) + if os.path.exists("{}/failures".format(output_dir)): + with open("{}/failures".format(output_dir), "r") as o: + jdata = json.loads(o.read()) + else: + jdata = {} if hasattr(item.function, "requirement_ids"): requirement_ids = item.function.requirement_ids @@ -113,17 +124,31 @@ def pytest_runtest_makereport(item, call): else: resolved_pair = rep.nodeid.split("[")[1][:-1] - FAILURE_DATA[len(FAILURE_DATA)] = { + markers = set(m.name for m in item.iter_markers()) + base_test = "base" in markers + + msg = extract_error_msg(rep) + if base_test: + msg = "!!Base Test Failure!! Halting test suite execution...\n{}".format( + msg + ) + + jdata[len(jdata)] = { "file": resolved_pair, "vnfrqts": requirement_ids, "test": item.function.__name__, "test_file": item.function.__module__.split(".")[-1], "raw_output": str(rep.longrepr), - "message": extract_error_msg(rep), + "message": msg, } with open("{}/failures".format(output_dir), "w") as f: - json.dump(FAILURE_DATA, f, indent=4) + json.dump(jdata, f, indent=4) + + if not item.config.option.continue_on_failure and base_test: + pytest.exit( + "{}\n{}\n{}".format(msg, resolved_pair, item.function.__name__) + ) def make_timestamp(): @@ -144,13 +169,31 @@ def pytest_sessionfinish(session, exitstatus): ) -def pytest_runtest_setup(item): - profile = item.session.config.option.validation_profile - markers = set(m.name for m in item.iter_markers()) - if not profile and markers and "xfail" not in markers: - pytest.skip("No validation profile selected. Skipping tests with marks.") - if profile and markers and profile not in markers and "xfail" not in markers: - pytest.skip("Doesn't match selection validation profile") +def pytest_collection_modifyitems(session, config, items): + allowed_marks = ["xfail", "base"] + profile = config.option.validation_profile + + for item in items: + markers = set(m.name for m in item.iter_markers()) + if not profile and markers and set(markers).isdisjoint(allowed_marks): + item.add_marker( + pytest.mark.skip( + reason="No validation profile selected. Skipping tests with marks." + ) + ) + if ( + profile + and markers + and profile not in markers + and set(markers).isdisjoint(allowed_marks) + ): + item.add_marker( + pytest.mark.skip(reason="Doesn't match selection validation profile") + ) + + items.sort( + key=lambda item: 0 if "base" in set(m.name for m in item.iter_markers()) else 1 + ) def make_href(path): @@ -158,10 +201,10 @@ def make_href(path): links = [] for p in paths: abs_path = os.path.abspath(p) - filename = os.path.split(abs_path)[1] + name = abs_path if os.path.isdir(abs_path) else os.path.split(abs_path)[1] links.append( - "<a href='file://{abs_path}' target='_blank'>{filename}</a>".format( - abs_path=abs_path, filename=filename + "<a href='file://{abs_path}' target='_blank'>{name}</a>".format( + abs_path=abs_path, name=name ) ) return "<br/>".join(links) @@ -184,7 +227,7 @@ def generate_report(outpath, template_path, profile_name, output_format): with open(resolution_steps, "r") as f: rdata = json.loads(f.read()) - heat_requirements = "{}/../{}".format(__path__[0], requirements_file) + heat_requirements = "{}/../{}".format(__path__[0], heat_requirements_file) if os.path.exists(heat_requirements): with open(heat_requirements, "r") as f: hdata = json.loads(f.read()) @@ -203,6 +246,7 @@ def generate_report(outpath, template_path, profile_name, output_format): # mapping resolution steps to module and test name for k, v in faildata.items(): + # resolution_step = "" faildata[k]["resolution_steps"] = "" for rs in rdata: if v["test_file"] == rs["module"] and v["test"] == rs["function"]: @@ -221,21 +265,33 @@ def generate_report(outpath, template_path, profile_name, output_format): def generate_csv_report(output_dir, profile_name, template_path, faildata): - rows = [] - rows.append(["Validation Failures"]) + rows = [["Validation Failures"]] headers = [ ("Profile Selected:", profile_name), ("Report Generated At:", make_timestamp()), ("Directory Validated:", template_path), ("Checksum:", hash_directory(template_path)), - ("Total Errors:", len(faildata)), + ("Total Errors:", len(faildata) + len(COLLECTION_FAILURES)), ] - rows.append([]) for header in headers: rows.append(header) rows.append([]) + if COLLECTION_FAILURES: + rows.append([COLLECTION_FAILURE_WARNING]) + rows.append(["Validation File", "Test", "Fixtures", "Error"]) + for failure in COLLECTION_FAILURES: + rows.append( + [ + failure["module"], + failure["test"], + ";".join(failure["fixtures"]), + failure["error"], + ] + ) + rows.append([]) + # table header rows.append([col for col, _ in report_columns]) @@ -274,7 +330,7 @@ def generate_excel_report(output_dir, profile_name, template_path, faildata): ("Report Generated At:", make_timestamp()), ("Directory Validated:", template_path), ("Checksum:", hash_directory(template_path)), - ("Total Errors:", len(faildata)), + ("Total Errors:", len(faildata) + len(COLLECTION_FAILURES)), ] for row, (header, value) in enumerate(headers, start=2): worksheet.write(row, 0, header, bold) @@ -283,13 +339,26 @@ def generate_excel_report(output_dir, profile_name, template_path, faildata): worksheet.set_column(0, len(headers) - 1, 40) worksheet.set_column(len(headers), len(headers), 80) + if COLLECTION_FAILURES: + collection_failures_start = 2 + len(headers) + 2 + worksheet.write(collection_failures_start, 0, COLLECTION_FAILURE_WARNING, bold) + collection_failure_headers = ["Validation File", "Test", "Fixtures", "Error"] + for col_num, col_name in enumerate(collection_failure_headers): + worksheet.write(collection_failures_start + 1, col_num, col_name, bold) + for row, data in enumerate(COLLECTION_FAILURES, collection_failures_start + 2): + worksheet.write(row, 0, data["module"]) + worksheet.write(row, 1, data["test"]) + worksheet.write(row, 2, ",".join(data["fixtures"])) + worksheet.write(row, 3, data["error"], code) + # table header - start_error_table_row = 2 + len(headers) + 2 + start_error_table_row = 2 + len(headers) + len(COLLECTION_FAILURES) + 4 + worksheet.write(start_error_table_row, 0, "Validation Failures", bold) for col_num, (col_name, _) in enumerate(report_columns): - worksheet.write(start_error_table_row, col_num, col_name, bold) + worksheet.write(start_error_table_row + 1, col_num, col_name, bold) # table content - for row, data in enumerate(faildata.values(), start=start_error_table_row + 1): + for row, data in enumerate(faildata.values(), start=start_error_table_row + 2): for col, key in enumerate(report.values()): if key == "file": paths = ( @@ -306,70 +375,35 @@ def generate_excel_report(output_dir, profile_name, template_path, faildata): def generate_html_report(outpath, profile_name, template_path, faildata): - with open("{}/report.html".format(outpath), "w") as of: - body_begin = """ - <style type="text/css"> - h1, li {{ - font-family:Arial, sans-serif; - }} - .tg {{border-collapse:collapse;border-spacing:0;}} - .tg td{{font-family:Arial, sans-serif;font-size:8px;padding:10px 5px; - border-style:solid;border-width:1px;overflow:hidden;word-break:normal; - border-color:black;}} - .tg th{{font-family:Arial, sans-serif;font-size:14px;font-weight:normal; - padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden; - word-break:normal;border-color:black;}} - .tg .tg-rwj1{{font-size:10px;font-family:Arial, Helvetica, - sans-serif !important;;border-color:inherit;vertical-align:top}}</style> - <h1>Validation Failures</h1> - <ul> - <li><b>Profile Selected: </b> <tt>{profile}</tt></li> - <li><b>Report Generated At:</b> <tt>{timestamp}</tt></li> - <li><b>Directory Validated:</b> <tt>{template_dir}</tt></li> - <li><b>Checksum:</b> <tt>{checksum}</tt></li> - <li><b>Total Errors:</b> {num_failures}</li> - </ul> - """.format( - profile=profile_name, - timestamp=make_timestamp(), + failures = [] + for data in faildata.values(): + failures.append( + { + "file_links": make_href(data["file"]), + "test_id": data["test_file"], + "error_message": data["message"], + "raw_output": data["raw_output"], + "requirements": docutils.core.publish_parts( + writer_name="html", source=data["req_description"] + )["body"], + "resolution_steps": data["resolution_steps"], + } + ) + pkg_dir = os.path.split(__file__)[0] + j2_template_path = os.path.join(pkg_dir, "report.html.jinja2") + with open(j2_template_path, "r") as f: + report_template = jinja2.Template(f.read()) + contents = report_template.render( + num_failures=len(failures) + len(COLLECTION_FAILURES), + profile_name=profile_name, + template_dir=make_href(template_path), checksum=hash_directory(template_path), - template_dir=template_path, - num_failures=len(faildata), + timestamp=make_timestamp(), + failures=failures, + collection_failures=COLLECTION_FAILURES, ) - of.write(body_begin) - - if len(faildata) == 0: - of.write("<p>Success! No validation failures detected.</p>") - return - - table_begin = '<table class="tg">' - of.write(table_begin) - - # table headers - of.write("<tr>") - for k, v in report.items(): - of.write('<th class="tg-rwj1">{}</th>'.format(k)) - of.write("</tr>") - - # table content - for k, v in faildata.items(): - of.write("<tr>") - for rk, rv in report.items(): - if rv == "file": - value = make_href(v[rv]) - elif rv == "raw_output": - value = "<pre>{}</pre>".format(v[rv]) - elif rv == "req_description": - parts = docutils.core.publish_parts( - writer_name="html", source=v[rv] - ) - value = parts["body"] - else: - value = v[rv].replace("\n", "<br />") - of.write(" <td>{}</td>".format(value)) - of.write("</tr>") - - of.write("</table>") + with open(os.path.join(outpath, "report.html"), "w") as f: + f.write(contents) def pytest_addoption(parser): @@ -411,6 +445,13 @@ def pytest_addoption(parser): help="Format of output report (html, csv, excel)", ) + parser.addoption( + "--continue-on-failure", + dest="continue_on_failure", + action="store_true", + help="Continue validation even when structural errors exist in input files", + ) + def pytest_configure(config): """ @@ -420,9 +461,9 @@ def pytest_configure(config): if config.getoption("template_dir") and config.getoption("self_test"): raise Exception('"--template-dir", and "--self-test"' " are mutually exclusive") if not ( - config.getoption("template_dir") or - config.getoption("self_test") or - config.getoption("help") + config.getoption("template_dir") + or config.getoption("self_test") + or config.getoption("help") ): raise Exception('One of "--template-dir" or' ' "--self-test" must be specified') @@ -435,90 +476,107 @@ def pytest_generate_tests(metafunc): is not specified on the CLI, the fixtures associated with this test name. """ - if "filename" in metafunc.fixturenames: - from .parametrizers import parametrize_filename - parametrize_filename(metafunc) + # noinspection PyBroadException + try: + if "filename" in metafunc.fixturenames: + from .parametrizers import parametrize_filename + + parametrize_filename(metafunc) - if "filenames" in metafunc.fixturenames: - from .parametrizers import parametrize_filenames + if "filenames" in metafunc.fixturenames: + from .parametrizers import parametrize_filenames - parametrize_filenames(metafunc) + parametrize_filenames(metafunc) - if "template_dir" in metafunc.fixturenames: - from .parametrizers import parametrize_template_dir + if "template_dir" in metafunc.fixturenames: + from .parametrizers import parametrize_template_dir - parametrize_template_dir(metafunc) + parametrize_template_dir(metafunc) - if "environment_pair" in metafunc.fixturenames: - from .parametrizers import parametrize_environment_pair + if "environment_pair" in metafunc.fixturenames: + from .parametrizers import parametrize_environment_pair - parametrize_environment_pair(metafunc) + parametrize_environment_pair(metafunc) - if "heat_volume_pair" in metafunc.fixturenames: - from .parametrizers import parametrize_heat_volume_pair + if "heat_volume_pair" in metafunc.fixturenames: + from .parametrizers import parametrize_heat_volume_pair - parametrize_heat_volume_pair(metafunc) + parametrize_heat_volume_pair(metafunc) - if "yaml_files" in metafunc.fixturenames: - from .parametrizers import parametrize_yaml_files + if "yaml_files" in metafunc.fixturenames: + from .parametrizers import parametrize_yaml_files - parametrize_yaml_files(metafunc) + parametrize_yaml_files(metafunc) - if "env_files" in metafunc.fixturenames: - from .parametrizers import parametrize_environment_files + if "env_files" in metafunc.fixturenames: + from .parametrizers import parametrize_environment_files - parametrize_environment_files(metafunc) + parametrize_environment_files(metafunc) - if "yaml_file" in metafunc.fixturenames: - from .parametrizers import parametrize_yaml_file + if "yaml_file" in metafunc.fixturenames: + from .parametrizers import parametrize_yaml_file - parametrize_yaml_file(metafunc) + parametrize_yaml_file(metafunc) - if "env_file" in metafunc.fixturenames: - from .parametrizers import parametrize_environment_file + if "env_file" in metafunc.fixturenames: + from .parametrizers import parametrize_environment_file - parametrize_environment_file(metafunc) + parametrize_environment_file(metafunc) - if "parsed_yaml_file" in metafunc.fixturenames: - from .parametrizers import parametrize_parsed_yaml_file + if "parsed_yaml_file" in metafunc.fixturenames: + from .parametrizers import parametrize_parsed_yaml_file - parametrize_parsed_yaml_file(metafunc) + parametrize_parsed_yaml_file(metafunc) - if "parsed_environment_file" in metafunc.fixturenames: - from .parametrizers import parametrize_parsed_environment_file + if "parsed_environment_file" in metafunc.fixturenames: + from .parametrizers import parametrize_parsed_environment_file - parametrize_parsed_environment_file(metafunc) + parametrize_parsed_environment_file(metafunc) - if "heat_template" in metafunc.fixturenames: - from .parametrizers import parametrize_heat_template + if "heat_template" in metafunc.fixturenames: + from .parametrizers import parametrize_heat_template - parametrize_heat_template(metafunc) + parametrize_heat_template(metafunc) - if "heat_templates" in metafunc.fixturenames: - from .parametrizers import parametrize_heat_templates + if "heat_templates" in metafunc.fixturenames: + from .parametrizers import parametrize_heat_templates - parametrize_heat_templates(metafunc) + parametrize_heat_templates(metafunc) - if "volume_template" in metafunc.fixturenames: - from .parametrizers import parametrize_volume_template + if "volume_template" in metafunc.fixturenames: + from .parametrizers import parametrize_volume_template - parametrize_volume_template(metafunc) + parametrize_volume_template(metafunc) - if "volume_templates" in metafunc.fixturenames: - from .parametrizers import parametrize_volume_templates + if "volume_templates" in metafunc.fixturenames: + from .parametrizers import parametrize_volume_templates - parametrize_volume_templates(metafunc) + parametrize_volume_templates(metafunc) - if "template" in metafunc.fixturenames: - from .parametrizers import parametrize_template + if "template" in metafunc.fixturenames: + from .parametrizers import parametrize_template - parametrize_template(metafunc) + parametrize_template(metafunc) - if "templates" in metafunc.fixturenames: - from .parametrizers import parametrize_templates + if "templates" in metafunc.fixturenames: + from .parametrizers import parametrize_templates - parametrize_templates(metafunc) + parametrize_templates(metafunc) + except Exception as e: + # If an error occurs in the collection phase, then it won't be logged as a + # normal test failure. This means that failures could occur, but not + # be seen on the report resulting in a false positive success message. These + # errors will be stored and reported separately on the report + COLLECTION_FAILURES.append( + { + "module": metafunc.module.__name__, + "test": metafunc.function.__name__, + "fixtures": metafunc.fixturenames, + "error": traceback.format_exc(), + } + ) + raise e def hash_directory(path): @@ -538,7 +596,7 @@ def load_current_requirements(): r = requests.get(url) with open('requirements.json', 'wb') as needs: needs.write(r.content) - path = "requirements.json" + path = "heat_requirements.json" if not os.path.exists(path): return {} with io.open(path, encoding="utf8", mode="r") as f: @@ -562,6 +620,7 @@ def unicode_writerow(writer, row): def pytest_report_collectionfinish(config, startdir, items): + """Generates a simple traceability report to output/traceability.csv""" traceability_path = os.path.join(__path__[0], "../output/traceability.csv") output_dir = os.path.split(traceability_path)[0] diff --git a/ice_validator/tests/fixtures/test_allowed_address_pairs_include_vm_type_network_role/pass/valid_template.yaml b/ice_validator/tests/fixtures/test_allowed_address_pairs_include_vm_type_network_role/pass/valid_template.yaml index 8b291bd..e219ab4 100644 --- a/ice_validator/tests/fixtures/test_allowed_address_pairs_include_vm_type_network_role/pass/valid_template.yaml +++ b/ice_validator/tests/fixtures/test_allowed_address_pairs_include_vm_type_network_role/pass/valid_template.yaml @@ -97,11 +97,11 @@ parameters: description: db_int_intranet_v6_ip_0 db_int_intranet_ips: - type: string + type: comma_delimited_list description: db_int_intranet_ips db_int_intranet_v6_ips: - type: string + type: comma_delimited_list description: db_int_intranet_v6_ips db_int_intranet_floating_ip: @@ -121,11 +121,11 @@ parameters: description: lb_extnet_v6_ip_0 lb_extnet_ips: - type: string + type: comma_delimited_list description: lb_extnet_ips lb_extnet_v6_ips: - type: string + type: comma_delimited_list description: lb_extnet_v6_ips lb_extnet_floating_ip: diff --git a/ice_validator/tests/fixtures/test_fixed_ips_include_vm_type_network_role/pass/valid_template.yaml b/ice_validator/tests/fixtures/test_fixed_ips_include_vm_type_network_role/pass/valid_template.yaml index cbd60b5..8bb53ac 100644 --- a/ice_validator/tests/fixtures/test_fixed_ips_include_vm_type_network_role/pass/valid_template.yaml +++ b/ice_validator/tests/fixtures/test_fixed_ips_include_vm_type_network_role/pass/valid_template.yaml @@ -97,11 +97,11 @@ parameters: description: db_int_intranet_v6_ip_0 db_int_intranet_ips: - type: string + type: comma_delimited_list description: db_int_intranet_ips db_int_intranet_v6_ips: - type: string + type: comma_delimited_list description: db_int_intranet_v6_ips lb_extnet_ip_0: @@ -117,11 +117,11 @@ parameters: description: lb_extnet_v6_ip_0 lb_extnet_ips: - type: string + type: comma_delimited_list description: lb_extnet_ips lb_extnet_v6_ips: - type: string + type: comma_delimited_list description: lb_extnet_v6_ips resources: diff --git a/ice_validator/tests/helpers.py b/ice_validator/tests/helpers.py index 5e4f3d3..502d3aa 100644 --- a/ice_validator/tests/helpers.py +++ b/ice_validator/tests/helpers.py @@ -41,8 +41,9 @@ """Helpers """ +import os from boltons import funcutils -import yaml +from tests import cached_yaml as yaml VERSION = '1.1.0' @@ -106,3 +107,20 @@ def validates(*requirement_ids): return wrapper decorator.requirement_ids = requirement_ids return decorator + + +def get_environment_pair(heat_template): + """Returns a yaml/env pair given a yaml file""" + base_dir, filename = os.path.split(heat_template) + basename = os.path.splitext(filename)[0] + env_template = os.path.join(base_dir, "{}.env".format(basename)) + if os.path.exists(env_template): + with open(heat_template, "r") as fh: + yyml = yaml.load(fh) + with open(env_template, "r") as fh: + eyml = yaml.load(fh) + + environment_pair = {"name": basename, "yyml": yyml, "eyml": eyml} + return environment_pair + + return None diff --git a/ice_validator/tests/parametrizers.py b/ice_validator/tests/parametrizers.py index 511c39a..641d53a 100644 --- a/ice_validator/tests/parametrizers.py +++ b/ice_validator/tests/parametrizers.py @@ -43,37 +43,35 @@ from os import path, listdir import re -import yaml +from tests import cached_yaml as yaml import pytest from .helpers import get_parsed_yml_for_yaml_files, check_basename_ending from .utils.nested_files import get_list_of_nested_files -VERSION = "1.0.0" - +VERSION = '1.0.0' # pylint: disable=invalid-name def get_template_dir(metafunc): - """ + ''' returns template_dir, either as its passed in on CLI or, during --self-test, the directory whos name matches the current tests module name - """ - if metafunc.config.getoption("template_dir") is None: + ''' + if metafunc.config.getoption('template_dir') is None: return path.join( path.dirname(path.realpath(__file__)), - "fixtures", - metafunc.function.__module__.split(".")[-1], - ) + 'fixtures', + metafunc.function.__module__.split('.')[-1]) else: - return metafunc.config.getoption("template_dir")[0] + return metafunc.config.getoption('template_dir')[0] def get_nested_files(filenames): - """ + ''' returns all the nested files for a set of filenames - """ + ''' nested_files = [] for filename in filenames: try: @@ -81,319 +79,334 @@ def get_nested_files(filenames): yml = yaml.load(fh) if "resources" not in yml: continue - nested_files.extend( - get_list_of_nested_files(yml["resources"], path.dirname(filename)) - ) + nested_files.extend(get_list_of_nested_files( + yml["resources"], + path.dirname(filename))) except yaml.YAMLError as e: print(e) # pylint: disable=superfluous-parens continue return nested_files -def list_filenames_in_template_dir( - metafunc, extensions, template_type="", sub_dirs=None -): - """ +def list_filenames_in_template_dir(metafunc, + extensions, + template_type='', + sub_dirs=None): + ''' returns the filenames in a template_dir, either as its passed in on CLI or, during --self-test, the directory whos name matches the current tests module name - """ + ''' sub_dirs = [] if sub_dirs is None else sub_dirs template_dir = get_template_dir(metafunc) filenames = [] - if metafunc.config.getoption("self_test"): - filenames = [ - path.join(template_dir, s, f) - for s in sub_dirs - for f in listdir(path.join(template_dir, s)) - if (path.isfile(path.join(template_dir, s, f)) and - path.splitext(f)[-1] in extensions and - check_basename_ending(template_type, path.splitext(f)[0])) - ] + if metafunc.config.getoption('self_test'): + filenames = [path.join(template_dir, s, f) + for s in sub_dirs + for f in listdir(path.join(template_dir, s)) + if path.isfile(path.join(template_dir, s, f)) + and path.splitext(f)[-1] in extensions + and check_basename_ending(template_type, + path.splitext(f)[0])] else: - filenames = [ - path.join(template_dir, f) - for f in listdir(template_dir) - if (path.isfile(path.join(template_dir, f)) and - path.splitext(f)[-1] in extensions and - check_basename_ending(template_type, path.splitext(f)[0])) - ] + filenames = [path.join(template_dir, f) + for f in listdir(template_dir) + if path.isfile(path.join(template_dir, f)) + and path.splitext(f)[-1] in extensions + and check_basename_ending(template_type, + path.splitext(f)[0])] return filenames -def list_template_dir( - metafunc, extensions, exclude_nested=True, template_type="", sub_dirs=None -): - """ +def list_template_dir(metafunc, + extensions, + exclude_nested=True, + template_type='', + sub_dirs=None): + ''' returns the filenames excluding the nested files for a template_dir, either as its passed in on CLI or, during --self-test, the directory whos name matches the current tests module name - """ + ''' sub_dirs = [] if sub_dirs is None else sub_dirs filenames = [] nested_files = [] - filenames = list_filenames_in_template_dir( - metafunc, extensions, template_type, sub_dirs - ) + filenames = list_filenames_in_template_dir(metafunc, + extensions, + template_type, + sub_dirs) if exclude_nested: nested_files = get_nested_files(filenames) return list(set(filenames) - set(nested_files)) -def get_filenames_list( - metafunc, extensions=None, exclude_nested=False, template_type="" -): - """ +def get_filenames_list(metafunc, + extensions=None, + exclude_nested=False, + template_type=''): + ''' returns the filename fixtures for the template dir, either as by how its passed in on CLI or, during --self-test, the directory whos name matches the current tests module name - """ - extensions = [".yaml", ".yml", ".env"] if extensions is None else extensions - if metafunc.config.getoption("self_test"): - filenames_list = list_template_dir( - metafunc, extensions, exclude_nested, template_type, ["pass"] - ) - filenames_list += [ - pytest.mark.xfail(f, strict=True) - for f in list_template_dir( - metafunc, extensions, exclude_nested, template_type, ["fail"] - ) - ] + ''' + extensions = [".yaml", + ".yml", + ".env"] if extensions is None else extensions + if metafunc.config.getoption('self_test'): + filenames_list = list_template_dir(metafunc, + extensions, + exclude_nested, + template_type, + ['pass']) + filenames_list += [pytest.mark.xfail(f, strict=True) + for f in list_template_dir(metafunc, + extensions, + exclude_nested, + template_type, + ['fail'])] else: - filenames_list = list_template_dir( - metafunc, extensions, exclude_nested, template_type - ) + filenames_list = list_template_dir(metafunc, + extensions, + exclude_nested, + template_type) return filenames_list -def get_filenames_lists( - metafunc, extensions=None, exclude_nested=False, template_type="" -): - """ +def get_filenames_lists(metafunc, + extensions=None, + exclude_nested=False, + template_type=''): + ''' returns the list of files in the template dir, either as by how its passed in on CLI or, during --self-test, the directory whos name matches the current tests module name - """ - extensions = [".yaml", ".yml", ".env"] if extensions is None else extensions + ''' + extensions = [".yaml", + ".yml", + ".env"] if extensions is None else extensions filenames_lists = [] - if metafunc.config.getoption("self_test"): - filenames_lists.append( - list_template_dir( - metafunc, extensions, exclude_nested, template_type, ["pass"] - ) - ) - filenames_lists.append( - pytest.mark.xfail( - list_template_dir( - metafunc, extensions, exclude_nested, template_type, ["fail"] - ), - strict=True, - ) - ) + if metafunc.config.getoption('self_test'): + filenames_lists.append(list_template_dir(metafunc, + extensions, + exclude_nested, + template_type, + ['pass'])) + filenames_lists.append(pytest.mark.xfail( + list_template_dir(metafunc, + extensions, + exclude_nested, + template_type, + ['fail']), strict=True)) else: - filenames_lists.append( - list_template_dir(metafunc, extensions, exclude_nested, template_type) - ) + filenames_lists.append(list_template_dir(metafunc, + extensions, + exclude_nested, + template_type)) return filenames_lists -def get_parsed_yaml_files( - metafunc, extensions, exclude_nested=True, template_type="", sections=None -): - """ +def get_parsed_yaml_files(metafunc, + extensions, + exclude_nested=True, + template_type='', + sections=None): + ''' returns the list of parsed yaml files in the specified template dir, either as by how its passed in on CLI or, during --self-test, the directory whos name matches the current tests module name - """ + ''' sections = [] if sections is None else sections extensions = [".yaml", ".yml"] - if metafunc.config.getoption("self_test"): - yaml_files = list_template_dir( - metafunc, extensions, exclude_nested, template_type, ["pass"] - ) - parsed_yml_list = get_parsed_yml_for_yaml_files(yaml_files, sections) - - yaml_files = list_template_dir( - metafunc, extensions, exclude_nested, template_type, ["fail"] - ) - parsed_yml_list = get_parsed_yml_for_yaml_files(yaml_files, sections) - parsed_yml_list += [ - pytest.mark.xfail(parsed_yml, strict=True) for parsed_yml in parsed_yml_list - ] + if metafunc.config.getoption('self_test'): + yaml_files = list_template_dir(metafunc, extensions, exclude_nested, + template_type, ['pass']) + parsed_yml_list = get_parsed_yml_for_yaml_files(yaml_files, + sections) + + yaml_files = list_template_dir(metafunc, extensions, exclude_nested, + template_type, ['fail']) + parsed_yml_list = get_parsed_yml_for_yaml_files(yaml_files, + sections) + parsed_yml_list += [pytest.mark.xfail(parsed_yml, strict=True) + for parsed_yml in parsed_yml_list] else: yaml_files = list_template_dir(metafunc, extensions) - parsed_yml_list = get_parsed_yml_for_yaml_files(yaml_files, sections) + parsed_yml_list = get_parsed_yml_for_yaml_files(yaml_files, + sections) return parsed_yml_list def parametrize_filenames(metafunc): - """ + ''' This param runs tests all files in the template dir - """ + ''' filenames = get_filenames_lists(metafunc) - metafunc.parametrize("filenames", filenames) + metafunc.parametrize('filenames', filenames) def parametrize_filename(metafunc): - """ + ''' This param runs tests once for every file in the template dir - """ + ''' filenames = get_filenames_list(metafunc) - metafunc.parametrize("filename", filenames) + metafunc.parametrize('filename', filenames) def parametrize_yaml_files(metafunc): - """ + ''' This param runs tests for the yaml files in the template dir - """ - yaml_files = get_filenames_lists(metafunc, [".yaml", ".yml"], False) + ''' + yaml_files = get_filenames_lists(metafunc, ['.yaml', '.yml'], False) metafunc.parametrize("yaml_files", yaml_files) def parametrize_yaml_file(metafunc): - """ + ''' This param runs tests for every yaml file in the template dir - """ - yaml_files = get_filenames_list(metafunc, [".yaml", ".yml"], False) - metafunc.parametrize("yaml_file", yaml_files) + ''' + yaml_files = get_filenames_list(metafunc, ['.yaml', '.yml'], False) + metafunc.parametrize('yaml_file', yaml_files) def parametrize_templates(metafunc): - """ + ''' This param runs tests for the template in the template dir - """ - templates = get_filenames_lists(metafunc, [".yaml", ".yml"], True) + ''' + templates = get_filenames_lists(metafunc, ['.yaml', '.yml'], True) metafunc.parametrize("templates", templates) def parametrize_template(metafunc): - """ + ''' This param runs tests for every template in the template dir - """ - templates = get_filenames_list(metafunc, [".yaml", ".yml"], True) - metafunc.parametrize("template", templates) + ''' + templates = get_filenames_list(metafunc, ['.yaml', '.yml'], True) + metafunc.parametrize('template', templates) def parametrize_parsed_yaml_file(metafunc): - """ + ''' This param runs tests for a parsed version of each yaml file in the template dir - """ - parsed_yaml_files = get_parsed_yaml_files(metafunc, [".yaml", ".yml"], False) - metafunc.parametrize("parsed_yaml_file", parsed_yaml_files) + ''' + parsed_yaml_files = get_parsed_yaml_files(metafunc, ['.yaml', '.yml'], + False) + metafunc.parametrize('parsed_yaml_file', parsed_yaml_files) def parametrize_heat_templates(metafunc): - """ + ''' This param runs tests for all heat templates in the template dir - """ - heat_templates = get_filenames_lists(metafunc, [".yaml", ".yml"], True, "heat") - metafunc.parametrize("heat_templates", heat_templates) + ''' + heat_templates = get_filenames_lists(metafunc, ['.yaml', '.yml'], + True, 'heat') + metafunc.parametrize('heat_templates', heat_templates) def parametrize_heat_template(metafunc): - """ + ''' This param runs tests for every heat template in the template dir - """ - heat_templates = get_filenames_list(metafunc, [".yaml", ".yml"], True, "heat") - metafunc.parametrize("heat_template", heat_templates) + ''' + heat_templates = get_filenames_list(metafunc, ['.yaml', '.yml'], + True, 'heat') + metafunc.parametrize('heat_template', heat_templates) def parametrize_volume_templates(metafunc): - """ + ''' This param runs tests for all volume templates in the template dir - """ - volume_templates = get_filenames_lists(metafunc, [".yaml", ".yml"], True, "volume") - metafunc.parametrize("volume_templates", volume_templates) + ''' + volume_templates = get_filenames_lists(metafunc, ['.yaml', '.yml'], + True, 'volume') + metafunc.parametrize('volume_templates', volume_templates) def parametrize_volume_template(metafunc): - """ + ''' This param runs tests for every volume template in the template dir - """ - volume_templates = get_filenames_list(metafunc, [".yaml", ".yml"], True, "volume") - metafunc.parametrize("volume_template", volume_templates) + ''' + volume_templates = get_filenames_list(metafunc, ['.yaml', '.yml'], + True, 'volume') + metafunc.parametrize('volume_template', volume_templates) def parametrize_environment_files(metafunc): - """ + ''' This param runs tests for all environment files in the template dir - """ - env_files = get_filenames_lists(metafunc, [".env"]) - metafunc.parametrize("env_files", env_files) + ''' + env_files = get_filenames_lists(metafunc, ['.env']) + metafunc.parametrize('env_files', env_files) def parametrize_environment_file(metafunc): - """ + ''' This param runs tests for every environment file in the template dir - """ - env_files = get_filenames_list(metafunc, [".env"]) - metafunc.parametrize("env_file", env_files) + ''' + env_files = get_filenames_list(metafunc, ['.env']) + metafunc.parametrize('env_file', env_files) def parametrize_parsed_environment_file(metafunc): - """ + ''' This param runs tests for every parsed environment file in the template dir - """ - parsed_env_files = get_parsed_yaml_files(metafunc, [".env"]) - metafunc.parametrize("parsed_env_file", parsed_env_files) + ''' + parsed_env_files = get_parsed_yaml_files(metafunc, ['.env']) + metafunc.parametrize('parsed_env_file', parsed_env_files) def parametrize_template_dir(metafunc): - """ + ''' This param passes a the template_dir as passed in on CLI or, during --self-test, passes in the sub directories of template_dir/pass/ and template_dir/fail template_dir = get_template_dir(metafunc) - """ + ''' template_dir = get_template_dir(metafunc) - if metafunc.config.getoption("self_test"): - dirs = [ - path.join(template_dir, s, t) - for s in ["pass"] - for t in listdir(path.join(template_dir, s)) - if path.isdir(path.join(template_dir, s, t)) - ] - - dirs += [ - pytest.mark.xfail(path.join(template_dir, s, t)) - for s in ["fail"] - for t in listdir(path.join(template_dir, s)) - if path.isdir(path.join(template_dir, s, t)) - ] + if metafunc.config.getoption('self_test'): + dirs = [path.join(template_dir, s, t) + for s in ['pass'] + for t in listdir(path.join(template_dir, s)) + if path.isdir(path.join(template_dir, s, t))] + + dirs += [pytest.mark.xfail(path.join(template_dir, s, t)) + for s in ['fail'] + for t in listdir(path.join(template_dir, s)) + if path.isdir(path.join(template_dir, s, t))] else: dirs = [template_dir] - metafunc.parametrize("template_dir", dirs) + metafunc.parametrize('template_dir', dirs) -def parametrize_environment_pair(metafunc, template_type=""): - """ +def parametrize_environment_pair(metafunc, template_type=''): + ''' Define a list of pairs of parsed yaml from the heat templates and environment files - """ + ''' pairs = [] - if metafunc.config.getoption("self_test"): - sub_dirs = ["pass", "fail"] - env_files = list_template_dir(metafunc, [".env"], True, template_type, sub_dirs) - yaml_files = list_template_dir( - metafunc, [".yaml", ".yml"], True, template_type, sub_dirs - ) + if metafunc.config.getoption('self_test'): + sub_dirs = ['pass', 'fail'] + env_files = list_template_dir(metafunc, ['.env'], True, + template_type, sub_dirs) + yaml_files = list_template_dir(metafunc, ['.yaml', '.yml'], True, + template_type, sub_dirs) else: - env_files = list_template_dir(metafunc, [".env"], True, template_type) - yaml_files = list_template_dir(metafunc, [".yaml", ".yml"], True, template_type) + env_files = list_template_dir(metafunc, ['.env'], True, + template_type) + yaml_files = list_template_dir(metafunc, ['.yaml', '.yml'], + True, template_type) for filename in env_files: basename = path.splitext(filename)[0] - if basename + ".yml" in yaml_files: - yfilename = basename + ".yml" + if basename + '.yml' in yaml_files: + yfilename = basename + '.yml' else: - yfilename = basename + ".yaml" + yfilename = basename + '.yaml' try: with open(filename) as fh: @@ -401,44 +414,44 @@ def parametrize_environment_pair(metafunc, template_type=""): with open(yfilename) as fh: yyml = yaml.load(fh) - if "fail" in filename: - pairs.append( - pytest.mark.xfail( - {"name": basename, "yyml": yyml, "eyml": eyml}, strict=True - ) - ) + if 'fail' in filename: + pairs.append(pytest.mark.xfail({"name": basename, + "yyml": yyml, + "eyml": eyml}, + strict=True)) else: pairs.append({"name": basename, "yyml": yyml, "eyml": eyml}) except yaml.YAMLError as e: print(e) # pylint: disable=superfluous-parens - metafunc.parametrize("environment_pair", pairs) + metafunc.parametrize('environment_pair', pairs) def parametrize_heat_volume_pair(metafunc): - """ + ''' Define a list of pairs of parsed yaml from the a heat and volume template - """ + ''' pairs = [] - if metafunc.config.getoption("self_test"): - sub_dirs = ["pass", "fail"] - volume_files = list_template_dir( - metafunc, [".yaml", ".yml"], True, "volume", sub_dirs - ) - yaml_files = list_template_dir(metafunc, [".yaml", ".yml"], True, "", sub_dirs) + if metafunc.config.getoption('self_test'): + sub_dirs = ['pass', 'fail'] + volume_files = list_template_dir(metafunc, ['.yaml', '.yml'], + True, 'volume', sub_dirs) + yaml_files = list_template_dir(metafunc, ['.yaml', '.yml'], + True, '', sub_dirs) else: - volume_files = list_template_dir(metafunc, [".yaml", ".yml"], True, "volume") - yaml_files = list_template_dir(metafunc, [".yaml", ".yml"], True) + volume_files = list_template_dir(metafunc, ['.yaml', '.yml'], + True, 'volume') + yaml_files = list_template_dir(metafunc, ['.yaml', '.yml'], True) - pattern = re.compile(r"\_volume$") + pattern = re.compile(r'\_volume$') for vfilename in volume_files: - basename = pattern.sub("", path.splitext(vfilename)[0]) - if basename + ".yml" in yaml_files: - yfilename = basename + ".yml" + basename = pattern.sub('', path.splitext(vfilename)[0]) + if basename + '.yml' in yaml_files: + yfilename = basename + '.yml' else: - yfilename = basename + ".yaml" + yfilename = basename + '.yaml' try: with open(vfilename) as fh: @@ -446,16 +459,15 @@ def parametrize_heat_volume_pair(metafunc): with open(yfilename) as fh: yyml = yaml.load(fh) - if "fail" in vfilename: - pairs.append( - pytest.mark.xfail( - {"name": basename, "yyml": yyml, "vyml": vyml}, strict=True - ) - ) + if 'fail' in vfilename: + pairs.append(pytest.mark.xfail({"name": basename, + "yyml": yyml, + "vyml": vyml}, + strict=True)) else: pairs.append({"name": basename, "yyml": yyml, "vyml": vyml}) except yaml.YAMLError as e: print(e) # pylint: disable=superfluous-parens - metafunc.parametrize("heat_volume_pair", pairs) + metafunc.parametrize('heat_volume_pair', pairs) diff --git a/ice_validator/tests/report.html.jinja2 b/ice_validator/tests/report.html.jinja2 new file mode 100644 index 0000000..5bc59e5 --- /dev/null +++ b/ice_validator/tests/report.html.jinja2 @@ -0,0 +1,220 @@ +{## ============LICENSE_START=======================================================#} +{## org.onap.vvp/validation-scripts#} +{## ===================================================================#} +{## Copyright © 2018 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============================================#} +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta http-equiv="x-ua-compatible" content="ie=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"/> + <title>HEAT Validation Report</title> + <script src="https://code.jquery.com/jquery-3.3.1.js"></script> + <link rel="stylesheet" type="text/css" + href="https://cdn.datatables.net/v/zf/jszip-2.5.0/dt-1.10.18/b-1.5.2/b-flash-1.5.2/b-html5-1.5.2/b-print-1.5.2/fh-3.1.4/datatables.min.css"/> + <link rel="stylesheet" type="text/css" + href="https://cdn.datatables.net/buttons/1.5.2/css/buttons.dataTables.min.css"/> + <link rel="stylesheet" + href="https://cdn.jsdelivr.net/npm/foundation-sites@6.5.0-rc.2/dist/css/foundation.min.css" + integrity="sha256-iJQ8dZac/jUYHxiEnZJsyVpKcdq2sQvdA7t02QFmp30= sha384-SplqNBo/0ZlvSdwrP/riIPDozO5ck8+yIm++KVqyMAC53S6m3BaV+2OLpi7ULOOh sha512-ho6hK4sAWdCeqopNZWNy1d9Ok2hzfTLQLcGSr8ZlRzDzh6tNHkVoqSl6wgLsqls3yazwiG9H9dBCtSfPuiLRCQ==" + crossorigin="anonymous"> + + <script type="text/javascript" + src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.36/pdfmake.min.js"></script> + <script type="text/javascript" + src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.36/vfs_fonts.js"></script> + <script type="text/javascript" + src="https://cdn.datatables.net/v/zf/jszip-2.5.0/dt-1.10.18/b-1.5.2/b-flash-1.5.2/b-html5-1.5.2/b-print-1.5.2/fh-3.1.4/datatables.min.js"></script> + <script src="https://cdn.jsdelivr.net/npm/foundation-sites@6.5.0-rc.2/dist/js/foundation.min.js" + integrity="sha256-G6jsRyH1fxbsvFIXSCuwYmI1aIDYBa28xscrvmYjJy0= sha384-vtoG68NvPc9azmFJr447vvY8qgdyA4FdaJ5/bqvzIM4eAdZfO0iyRRF8l2AAscYI sha512-43seCcNrHA0BQgrtyajB9sp8yOdv5c8QdYvgjP7zJ7v+dmzAcxYDQ2gupb9aztsNWBq1COIp/3NHYkQs4l/dkg==" + crossorigin="anonymous"></script> +</head> +<style> + #collection_failures { + table-layout: fixed; + } + .fileNames { + width: 25%; + word-wrap: break-word; + word-break: break-all; + white-space: normal; + } + .fixtures { + width: 10%; + } + .errorColumn { + width: 40%; + } + .fileLinks, .errorMessage { + word-wrap: break-word; + word-break: break-all; + white-space: normal; + } + .details { + white-space: nowrap; + } + + .codeCell { + width: 100%; + overflow: auto; + } +</style> +<body> +<div class="grid-container fluid"> + + <div class="callout {{ "alert" if failures or collection_failures else "success" }}"> + <h1>Validation Report</h1> + <ul> + <li><b>Profile:</b> {{ profile_name }}</li> + <li><b>Directory Validated:</b> {{ template_dir }}</li> + <li><b>Checksum:</b> {{ checksum }}</li> + <li><b>Generated:</b> {{ timestamp }}</li> + <li><b>Total Failures:</b> {{ num_failures }}</li> + </ul> + </div> + {% if collection_failures %} + <div class="callout alert"> + <h2>WARNING: Errors Occurred During Validation Setup</h2> + <p> + The following unexpected errors occurred while preparing to validate + the the input files. Some validations may not have been executed. + Please refer these issue to the VNF Validation Tool team. + </p> + </div> + <table id="collection_failures"> + <thead> + <tr> + <th class="fileNames">Validation File</th> + <th class="fileNames">Test</th> + <th class="fixtures">Fixtures</th> + <th class="errorColumnn">Error</th> + </tr> + </thead> + <tbody> + {% for failure in collection_failures %} + <tr> + <td class="fileNames">{{ failure.module }}</td> + <td class="fileNames">{{ failure.test }}</td> + <td class="fixtures">{{ failure.fixtures }}</td> + <td class="errorColumn"> + <div class="codeCell"> + <pre class="code">{{ failure.error }}</pre> + </div> + </td> + </tr> + {% endfor %} + </tbody> + </table> + {% endif %} + + {% if failures %} + <h2>Validation Failures</h2> + <table id="failures"> + <thead> + <tr> + <th>File(s)</th> + <th>Error Message</th> + <th>Requirement(s)</th> + <th>Resolution Steps</th> + </tr> + </thead> + <tbody> + {% for failure in failures %} + <tr> + <td> + {{ failure.file_links }} + </td> + <td> + <p><b>Failed:</b> {{ failure.test_id }}</p> + <p>{{ failure.error_message }} + <a class="details" href="#" + data-open="raw-output-{{ loop.index }}">Full Details</a> + </p> + </td> + <td> + {{ failure.requirements }} + </td> + <td> + {{ failure.resolution_steps }} + </td> + </tr> + {% endfor %} + </tbody> + {% for failure in failures %} + <div id="raw-output-{{ loop.index }}" class="large reveal" data-reveal + aria-labelledby="modalTitle" + aria-hidden="true" role="dialog"> + <h2 id="modalTitle">{{ failure.test_id }}</h2> + <pre class="code"> + {{ failure.raw_output }} + </pre> + <button class="close-button" data-close aria-label="Close modal" + type="button"> + <span aria-hidden="true">×</span> + </button> + <a class="close-reveal-modal" aria-label="Close">×</a> + </div> + {% endfor %} + </table> + {% else %} + <h2>No validation errors found.</h2> + {% endif %} + <script lang="javascript"> + $(document).foundation(); + $(document).ready(function () { + $('#failures').DataTable({ + dom: 'Bfrtip', + buttons: [ + 'csv', + 'excel', + { + extend: 'pdfHtml5', + orientation: 'landscape', + }, + 'print' + ], + fixedHeader: true, + pageLength: 50, + columns: [ + {width: "25%", className: "fileLinks"}, + {width: "25%", className: "errorMessage"}, + {width: "25%", className: "requirements"}, + {width: "25%", className: "resolutionSteps"}, + ] + }); + }); + </script> +</div> +</body> +</html>
\ No newline at end of file diff --git a/ice_validator/tests/structures.py b/ice_validator/tests/structures.py index d10c5ea..8d66220 100644 --- a/ice_validator/tests/structures.py +++ b/ice_validator/tests/structures.py @@ -38,16 +38,16 @@ # ECOMP is a trademark and service mark of AT&T Intellectual Property. # -"""structures -""" +'''structures +''' import os -import yaml +from tests import cached_yaml as yaml from .utils import nested_dict -VERSION = "1.4.0" +VERSION = '1.4.0' class Heat(object): @@ -55,7 +55,6 @@ class Heat(object): filepath - absolute path to template file. envpath - absolute path to environmnt file. """ - def __init__(self, filepath=None, envpath=None): self.filepath = None self.basename = None @@ -82,13 +81,13 @@ class Heat(object): self.dirname = os.path.dirname(self.filepath) with open(self.filepath) as fi: self.yml = yaml.load(fi) - self.heat_template_version = self.yml.get("heat_template_version", None) - self.description = self.yml.get("description", "") - self.parameter_groups = self.yml.get("parameter_groups", {}) - self.parameters = self.yml.get("parameters", {}) - self.resources = self.yml.get("resources", {}) - self.outputs = self.yml.get("outputs", {}) - self.conditions = self.yml.get("conditions", {}) + self.heat_template_version = self.yml.get('heat_template_version', None) + self.description = self.yml.get('description', '') + self.parameter_groups = self.yml.get('parameter_groups', {}) + self.parameters = self.yml.get('parameters', {}) + self.resources = self.yml.get('resources', {}) + self.outputs = self.yml.get('outputs', {}) + self.conditions = self.yml.get('conditions', {}) def load_env(self, envpath): """Load the Environment template given a envpath. @@ -105,21 +104,22 @@ class Heat(object): class Env(Heat): """An Environment file """ - pass class Resource(object): """A Resource """ - def __init__(self, resource_id=None, resource=None): - self.resource_id = resource_id or "" + self.resource_id = resource_id or '' self.resource = resource or {} @staticmethod def get_index_var(resource): """Return the index_var for this resource. """ - index_var = nested_dict.get(resource, "properties", "index_var") or "index" + index_var = nested_dict.get(resource, + 'properties', + 'index_var') or 'index' return index_var + diff --git a/ice_validator/tests/test_all_referenced_resources_exists.py b/ice_validator/tests/test_all_referenced_resources_exists.py deleted file mode 100644 index e77f4f8..0000000 --- a/ice_validator/tests/test_all_referenced_resources_exists.py +++ /dev/null @@ -1,76 +0,0 @@ -# -*- coding: utf8 -*- -# ============LICENSE_START==================================================== -# org.onap.vvp/validation-scripts -# =================================================================== -# Copyright © 2017 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============================================ -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. -# - -"""test_all_referenced_resources_exists -""" - -import pytest -import yaml - -from .utils.nested_iterables import find_all_get_resource_in_yml - -VERSION = "1.0.0" - -# pylint: disable=invalid-name - - -def test_all_referenced_resources_exists(yaml_file): - """ - Check that all resources referenced by get_resource - actually exists in all yaml files - """ - with open(yaml_file) as fh: - yml = yaml.load(fh) - - # skip if resources are not defined - if "resources" not in yml: - pytest.skip("No resources specified in the yaml file") - - resource_ids = yml["resources"].keys() - referenced_resource_ids = find_all_get_resource_in_yml(yml) - - missing_referenced_resources = set() - for referenced_resource_id in referenced_resource_ids: - if referenced_resource_id not in resource_ids: - missing_referenced_resources.add(referenced_resource_id) - - assert not missing_referenced_resources, "missing referenced resources %s" % list( - missing_referenced_resources - ) diff --git a/ice_validator/tests/test_allowed_address_pair_format.py b/ice_validator/tests/test_allowed_address_pair_format.py deleted file mode 100644 index 816f486..0000000 --- a/ice_validator/tests/test_allowed_address_pair_format.py +++ /dev/null @@ -1,177 +0,0 @@ -# -*- coding: utf8 -*- -# ============LICENSE_START==================================================== -# org.onap.vvp/validation-scripts -# =================================================================== -# Copyright © 2017 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============================================ -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. -# - -""" -test_allowed_address_pairs_format -""" - -import re - -import pytest -import yaml - -from .utils.network_roles import get_network_role_from_port, property_uses_get_resource - -VERSION = "1.0.0" - -# pylint: disable=invalid-name - - -def test_allowed_address_pairs_format(heat_template): - """ - Make sure all allowed_address_pairs properties follow the allowed - naming conventions - """ - allowed_formats = [ - [ - "allowed_address_pairs", - "string", - "internal", - re.compile(r"(.+?)_int_(.+?)_floating_v6_ip"), - ], - [ - "allowed_address_pairs", - "string", - "internal", - re.compile(r"(.+?)_int_(.+?)_floating_ip"), - ], - [ - "allowed_address_pairs", - "string", - "external", - re.compile(r"(.+?)_floating_v6_ip"), - ], - [ - "allowed_address_pairs", - "string", - "external", - re.compile(r"(.+?)_floating_ip"), - ], - [ - "allowed_address_pairs", - "string", - "internal", - re.compile(r"(.+?)_int_(.+?)_v6_ip_\d+"), - ], - [ - "allowed_address_pairs", - "string", - "internal", - re.compile(r"(.+?)_int_(.+?)_ip_\d+"), - ], - ["allowed_address_pairs", "string", "external", re.compile(r"(.+?)_v6_ip_\d+")], - ["allowed_address_pairs", "string", "external", re.compile(r"(.+?)_ip_\d+")], - [ - "allowed_address_pairs", - "comma_delimited_list", - "internal", - re.compile(r"(.+?)_int_(.+?)_v6_ips"), - ], - [ - "allowed_address_pairs", - "comma_delimited_list", - "internal", - re.compile(r"(.+?)_int_(.+?)_ips"), - ], - [ - "allowed_address_pairs", - "comma_delimited_list", - "external", - re.compile(r"(.+?)_v6_ips"), - ], - [ - "allowed_address_pairs", - "comma_delimited_list", - "external", - re.compile(r"(.+?)_ips"), - ], - ] - - with open(heat_template) as fh: - yml = yaml.load(fh) - - # skip if resources are not defined - if "resources" not in yml: - pytest.skip("No resources specified in the heat template") - - # check both valid and invalid patterns to catch edge cases - invalid_allowed_address_pairs = [] - - for v1 in yml["resources"].values(): - if ( - not isinstance(v1, dict) or - "properties" not in v1 or - v1.get("type") != "OS::Neutron::Port" or - property_uses_get_resource(v1, "network") - ): - continue - network_role = get_network_role_from_port(v1) - - v2 = v1["properties"].get("allowed_address_pairs", {}) - for v3 in v2: - if "ip_address" not in v3 or "get_param" not in v3["ip_address"]: - continue - - param = v3["ip_address"]["get_param"] - if isinstance(param, list): - param = param[0] - - for v4 in allowed_formats: - # check if pattern matches - m = v4[3].match(param) - if m: - if ( - v4[2] == "internal" and - len(m.groups()) > 1 and - m.group(2) == network_role - ): - break - elif ( - v4[2] == "external" - and len(m.groups()) > 0 - and m.group(1).endswith("_" + network_role) - ): - break - else: - invalid_allowed_address_pairs.append(param) - - assert not set( - invalid_allowed_address_pairs - ), "invalid_allowed_address_pairs %s" % list(set(invalid_allowed_address_pairs)) diff --git a/ice_validator/tests/test_allowed_address_pairs_include_vm_type_network_role.py b/ice_validator/tests/test_allowed_address_pairs_include_vm_type_network_role.py index 7ce9c43..0d4bbdf 100644 --- a/ice_validator/tests/test_allowed_address_pairs_include_vm_type_network_role.py +++ b/ice_validator/tests/test_allowed_address_pairs_include_vm_type_network_role.py @@ -38,28 +38,30 @@ # ECOMP is a trademark and service mark of AT&T Intellectual Property. # -""" +''' test_allowed_address_pairs_include_vm_type_network_role -""" +''' import pytest -import yaml +from tests import cached_yaml as yaml from .helpers import validates from .utils.ports import get_invalid_ip_addresses -VERSION = "1.0.0" +VERSION = '1.0.0' # pylint: disable=invalid-name -@validates("R-41492", "R-35735") +@validates('R-41492', + 'R-35735', + 'R-98748') def test_allowed_address_pairs_include_vm_type_network_role(heat_template): - """ + ''' Check that all allowed_address_pairs include the {vm_type} of the nova server it is associated to and also contains the {network_role} of the network it is associated with - """ + ''' with open(heat_template) as fh: yml = yaml.load(fh) @@ -67,10 +69,14 @@ def test_allowed_address_pairs_include_vm_type_network_role(heat_template): if "resources" not in yml: pytest.skip("No resources specified in the heat template") - invalid_ip_addresses = get_invalid_ip_addresses( - yml["resources"], "allowed_address_pairs" - ) + if "parameters" not in yml: + pytest.skip("No parameters specified in the heat template") + + invalid_ip_addresses = get_invalid_ip_addresses(yml['resources'], + "allowed_address_pairs", + yml["parameters"]) + + assert not set(invalid_ip_addresses), ( + 'invalid ip addresses allowed address pairs %s' % list( + set(invalid_ip_addresses))) - assert not set( - invalid_ip_addresses - ), "invalid ip addresses allowed address pairs %s" % list(set(invalid_ip_addresses)) diff --git a/ice_validator/tests/test_availability_zone_get_param.py b/ice_validator/tests/test_availability_zone_get_param.py deleted file mode 100644 index 456f74b..0000000 --- a/ice_validator/tests/test_availability_zone_get_param.py +++ /dev/null @@ -1,73 +0,0 @@ -# -*- coding: utf8 -*- -# ============LICENSE_START======================================================= -# org.onap.vvp/validation-scripts -# =================================================================== -# Copyright © 2018 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============================================ -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. -# - -import pytest -import yaml - - -def test_availability_zone_naming_use_get_param(heat_template): - ''' - Make sure all availability zones only use get_param - ''' - - with open(heat_template) as fh: - yml = yaml.load(fh) - - # skip if resources are not defined - if "resources" not in yml: - pytest.skip("No resources specified in the heat template") - - invalid_availability_zones = [] - - for v1 in yml["resources"].values(): - if not isinstance(v1, dict): - continue - if "properties" not in v1: - continue - if "type" not in v1: - continue - if v1["type"] != "OS::Nova::Server": - continue - - for k2, v2 in v1["properties"].items(): - if k2 == 'availability_zone' and not v2["get_param"]: - invalid_availability_zones.append(v2["get_param"]) - - assert not set(invalid_availability_zones) diff --git a/ice_validator/tests/test_base_template_outputs_consumed.py b/ice_validator/tests/test_base_template_outputs_consumed.py deleted file mode 100644 index 5d5bcd9..0000000 --- a/ice_validator/tests/test_base_template_outputs_consumed.py +++ /dev/null @@ -1,100 +0,0 @@ -# -*- coding: utf8 -*- -# ============LICENSE_START==================================================== -# org.onap.vvp/validation-scripts -# =================================================================== -# Copyright © 2017 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============================================ -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. -# - -""" -test_base_template_outputs_consumed -""" - -from os import path, sep - -import pytest -import yaml - -from .helpers import validates - -VERSION = "1.0.0" - -# pylint: disable=invalid-name - - -@validates("R-52753") -def test_base_template_outputs_consumed(heat_templates): - """ - Check that all outputs in the base template is consumed - by another template. The exception is the predefined output - parameters. - """ - base_template = "" - base_template_yml = "" - for heat_template in heat_templates: - with open(heat_template) as fh: - yml = yaml.load(fh) - basename = path.splitext(heat_template)[0].rsplit(sep, 1)[1] - if ( - basename.endswith("_base") or - basename.startswith("base_") or - basename.find("_base_") > 0 - ): - base_template = heat_template - base_template_yml = yml - - # get the base template outputs - if "outputs" not in base_template_yml: - pytest.skip("No outputs specified in the base template") - - predefined_outputs = ["oam_management_v4_address", "oam_management_v6_address"] - base_outputs = set(base_template_yml["outputs"]) - set(predefined_outputs) - - # get all add-on templates - addon_heat_templates = set(heat_templates) - set([base_template]) - - # get all parameters from add-on templates - non_base_parameters = [] - for addon_heat_template in addon_heat_templates: - with open(addon_heat_template) as fh: - yml = yaml.load(fh) - if "parameters" not in yml: - continue - parameters = yml["parameters"].keys() - non_base_parameters.extend(parameters) - - assert base_outputs <= set(non_base_parameters), "unconsumed outputs %s" % list( - base_outputs - set(non_base_parameters) - ) diff --git a/ice_validator/tests/test_fixed_ips_format.py b/ice_validator/tests/test_fixed_ips_format.py deleted file mode 100644 index 77a515e..0000000 --- a/ice_validator/tests/test_fixed_ips_format.py +++ /dev/null @@ -1,123 +0,0 @@ -# -*- coding: utf8 -*- -# ============LICENSE_START======================================================= -# org.onap.vvp/validation-scripts -# =================================================================== -# Copyright © 2018 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============================================ -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. -# - -import pytest -import yaml -from .utils.ports import is_reserved_port -from .utils.network_roles import get_network_role_from_port, property_uses_get_resource -import re - - -def test_fixed_ips_format(heat_template): - ''' - Make sure all fixed_ips properties follow the allowed - naming conventions - ''' - formats = [ - ["fixed_ips", "string", "internal", - re.compile(r'(.+?)_int_(.+?)_ip_\d+')], - ["fixed_ips", "string", "internal", - re.compile(r'(.+?)_int_(.+?)_v6_ip_\d+')], - ["fixed_ips", "string", "external", - re.compile(r'(.+?)_ip_\d+')], - ["fixed_ips", "string", "external", - re.compile(r'(.+?)_v6_ip_\d+')], - ["fixed_ips", "comma_delimited_list", "internal", - re.compile(r'(.+?)_int_(.+?)_ips')], - ["fixed_ips", "comma_delimited_list", "internal", - re.compile(r'(.+?)_int_(.+?)_v6_ips')], - ["fixed_ips", "comma_delimited_list", "external", - re.compile(r'(.+?)_ips')], - ["fixed_ips", "comma_delimited_list", "external", - re.compile(r'(.+?)_v6_ips')], - ] - - with open(heat_template) as fh: - yml = yaml.load(fh) - - # skip if resources are not defined - if "resources" not in yml: - pytest.skip("No resources specified in the heat template") - - invalid_fixed_ips = [] - for k1, v1 in yml["resources"].items(): - if not isinstance(v1, dict): - continue - if "properties" not in v1: - continue - if v1.get("type") != "OS::Neutron::Port": - continue - if is_reserved_port(k1): - continue - if property_uses_get_resource(v1, "network"): - continue - network_role = get_network_role_from_port(v1) - - for k2, v2 in v1["properties"].items(): - if k2 != "fixed_ips": - continue - for v3 in v2: - if "ip_address" not in v3: - continue - if "get_param" not in v3["ip_address"]: - continue - - valid_fixed_ip = False - for v4 in formats: - param = v3["ip_address"]["get_param"] - if isinstance(param, list): - param = param[0] - m = v4[3].match(param) - if m: - if v4[2] == "internal" and\ - len(m.groups()) > 1 and\ - m.group(2) == network_role: - valid_fixed_ip = True - break - elif v4[2] == "external" and\ - len(m.groups()) > 0 and\ - m.group(1).endswith("_" + network_role): - valid_fixed_ip = True - break - - if not valid_fixed_ip: - invalid_fixed_ips.append(param) - - assert not set(invalid_fixed_ips) diff --git a/ice_validator/tests/test_fixed_ips_format_use_get_param.py b/ice_validator/tests/test_fixed_ips_format_use_get_param.py deleted file mode 100644 index df57c01..0000000 --- a/ice_validator/tests/test_fixed_ips_format_use_get_param.py +++ /dev/null @@ -1,81 +0,0 @@ -# -*- coding: utf8 -*- -# ============LICENSE_START======================================================= -# org.onap.vvp/validation-scripts -# =================================================================== -# Copyright © 2018 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============================================ -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. -# - -import pytest -import yaml -from .utils.ports import is_reserved_port - - -def test_fixed_ips_format_use_get_parm(heat_template): - """ - Make sure all fixed_ips properties only use get_param - """ - with open(heat_template) as fh: - yml = yaml.load(fh) - - # skip if resources are not defined - if "resources" not in yml: - pytest.skip("No resources specified in the heat template") - - invalid_fixed_ips = [] - for k, v in yml["resources"].items(): - if not isinstance(v, dict): - continue - if "properties" not in v: - continue - if v.get("type") != "OS::Neutron::Port": - continue - if is_reserved_port(k): - continue - - valid_fixed_ip = True - for k2, v2 in v["properties"].items(): - if k2 != "fixed_ips": - continue - for v3 in v2: - if "ip_address" not in v3: - continue - if "get_param" not in v3["ip_address"]: - valid_fixed_ip = False - - if not valid_fixed_ip: - invalid_fixed_ips.append(k) - - assert not set(invalid_fixed_ips) diff --git a/ice_validator/tests/test_fixed_ips_include_vm_type_network_role.py b/ice_validator/tests/test_fixed_ips_include_vm_type_network_role.py index 53f6453..428b72e 100644 --- a/ice_validator/tests/test_fixed_ips_include_vm_type_network_role.py +++ b/ice_validator/tests/test_fixed_ips_include_vm_type_network_role.py @@ -2,7 +2,7 @@ # ============LICENSE_START======================================================= # org.onap.vvp/validation-scripts # =================================================================== -# Copyright © 2018 AT&T Intellectual Property. All rights reserved. +# Copyright © 2017 AT&T Intellectual Property. All rights reserved. # =================================================================== # # Unless otherwise specified, all software contained herein is licensed @@ -37,10 +37,11 @@ # # ECOMP is a trademark and service mark of AT&T Intellectual Property. # -from .helpers import validates import pytest -import yaml +from tests import cached_yaml as yaml + +from .helpers import validates from .utils.ports import get_invalid_ip_addresses @@ -51,7 +52,8 @@ from .utils.ports import get_invalid_ip_addresses 'R-78380', 'R-23503', 'R-71577', - 'R-04697') + 'R-04697', + 'R-34037') def test_fixed_ips_include_vm_type_network_role(heat_template): ''' Check that all fixed_ips ip addresses include the {vm_type} of the @@ -65,7 +67,11 @@ def test_fixed_ips_include_vm_type_network_role(heat_template): if "resources" not in yml: pytest.skip("No resources specified in the heat template") + if "parameters" not in yml: + pytest.skip("No parameters specified in the heat template") + invalid_ip_addresses = get_invalid_ip_addresses(yml['resources'], - "fixed_ips") + "fixed_ips", + yml["parameters"]) assert not set(invalid_ip_addresses) diff --git a/ice_validator/tests/test_heat_template_and_env_file_extension.py b/ice_validator/tests/test_heat_template_and_env_file_extension.py deleted file mode 100644 index de83745..0000000 --- a/ice_validator/tests/test_heat_template_and_env_file_extension.py +++ /dev/null @@ -1,82 +0,0 @@ -# -*- coding: utf8 -*- -# ============LICENSE_START======================================================= -# org.onap.vvp/validation-scripts -# =================================================================== -# Copyright © 2018 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============================================ -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. -# - -import yaml -import pytest - - -def test_heat_template_file_extension(yaml_file): - ''' - Check that all heat templates are in fact heat - templates - ''' - with open(yaml_file) as fh: - yml = yaml.load(fh) - - # skip if parameters are not defined - if "parameters" not in yml: - pytest.skip("No parameters specified in the heat template") - - invalid_params = [] - for k, v in yml["parameters"].items(): - if not isinstance(v, dict): - invalid_params.append(k) - - assert not set(invalid_params) - - -def test_environment_file_extension(env_file): - ''' - Check that all environments files are in fact environment - files - ''' - with open(env_file) as fh: - yml = yaml.load(fh) - - # skip if parameters are not defined - if "parameters" not in yml: - pytest.skip("No parameters specified in the environment file") - - invalid_params = [] - for k, v in yml["parameters"].items(): - if isinstance(v, dict): - invalid_params.append(k) - - assert not set(invalid_params) diff --git a/ice_validator/tests/test_heat_templates_provided.py b/ice_validator/tests/test_heat_templates_provided.py deleted file mode 100644 index 168dbe0..0000000 --- a/ice_validator/tests/test_heat_templates_provided.py +++ /dev/null @@ -1,46 +0,0 @@ -# -*- coding: utf8 -*- -# ============LICENSE_START======================================================= -# org.onap.vvp/validation-scripts -# =================================================================== -# Copyright © 2018 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============================================ -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. -# - - -def test_heat_templates_provided(heat_templates): - ''' - Make sure heat templates have been provided - ''' - assert len(heat_templates) > 0 diff --git a/ice_validator/tests/test_nested_templates.py b/ice_validator/tests/test_nested_templates.py deleted file mode 100644 index c9caa19..0000000 --- a/ice_validator/tests/test_nested_templates.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf8 -*- -# ============LICENSE_START======================================================= -# org.onap.vvp/validation-scripts -# =================================================================== -# Copyright © 2018 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============================================ -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. -# - -from .helpers import validates -import yaml -from os import path -from .utils.nested_files import get_list_of_nested_files - - -@validates("R-70276") -def test_all_nested_templates_provided(yaml_files): - """ - Check that all templates marked as volume templates are - in fact volume templates - """ - nested_yaml_files = [] - - for yaml_file in yaml_files: - with open(yaml_file) as fh: - yml = yaml.load(fh) - if "resources" not in yml: - continue - nested_yaml_files.extend( - get_list_of_nested_files(yml["resources"], path.dirname(yaml_file)) - ) - - # detect all provided nested files - provided_nested_yaml_files = [ - f1 for f1 in nested_yaml_files for f2 in yaml_files if f1 in f2 - ] - - assert set(provided_nested_yaml_files) == set(nested_yaml_files) diff --git a/ice_validator/tests/test_nested_templates_invalid_nesting.py b/ice_validator/tests/test_nested_templates_invalid_nesting.py deleted file mode 100644 index edd5ca3..0000000 --- a/ice_validator/tests/test_nested_templates_invalid_nesting.py +++ /dev/null @@ -1,63 +0,0 @@ -# -*- coding: utf8 -*- -# ============LICENSE_START======================================================= -# org.onap.vvp/validation-scripts -# =================================================================== -# Copyright © 2018 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============================================ -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. -# - -import yaml -from os import path -from .utils.nested_files import check_for_invalid_nesting - - -def test_valid_nesting(yaml_files): - ''' - Check that the nesting is following the proper format and - that all nested files exists and are parsable - ''' - invalid_nesting = [] - - for yaml_file in yaml_files: - with open(yaml_file) as fh: - yml = yaml.load(fh) - if "resources" not in yml: - continue - invalid_nesting.extend(check_for_invalid_nesting( - yml["resources"], - yaml_file, - path.dirname(yaml_file))) - - assert not set(invalid_nesting) diff --git a/ice_validator/tests/test_no_unused_parameters_between_env_and_templates.py b/ice_validator/tests/test_no_unused_parameters_between_env_and_templates.py index 781f35d..81cfc9a 100644 --- a/ice_validator/tests/test_no_unused_parameters_between_env_and_templates.py +++ b/ice_validator/tests/test_no_unused_parameters_between_env_and_templates.py @@ -37,13 +37,39 @@ # # ECOMP is a trademark and service mark of AT&T Intellectual Property. # -from .helpers import validates +import os +import pytest +from .helpers import validates, get_environment_pair + +@pytest.mark.heat_only @validates('R-90279', 'R-01896', 'R-26124') -def test_no_unused_parameters_between_env_and_templates(environment_pair): - ''' +def test_no_unused_parameters_between_env_and_templates(heat_template): + """ Check all defined parameters are used in the appropiate Heat template. - ''' - assert (set(environment_pair["eyml"]['parameters']) == - set(environment_pair["yyml"]['parameters'])) + """ + environment_pair = get_environment_pair(heat_template) + if not environment_pair: + pytest.skip("No heat/env pair could be identified") + + env_parameters = set(environment_pair["eyml"]["parameters"].keys()) + template_parameters = set(environment_pair["yyml"]["parameters"].keys()) + + extra_in_template = template_parameters.difference(env_parameters) + extra_in_env = env_parameters.difference(template_parameters) + + msg = "Mismatched parameters detected for the template and environment pair " \ + "with basename ({basename}). " + if extra_in_env: + msg += "The following parameters exist in the env file, but not the " \ + "template: {extra_in_env}. " + if extra_in_template: + msg += "The following parameters exist in the template file, but not the " \ + "environment file: {extra_in_template}" + + assert not (extra_in_template or extra_in_env), msg.format( + basename=os.path.split(environment_pair["name"])[-1], + extra_in_env=", ".join(extra_in_env), + extra_in_template=", ".join(extra_in_template) + ) diff --git a/ice_validator/tests/test_nova_servers_correct_parameter_types.py b/ice_validator/tests/test_nova_servers_correct_parameter_types.py deleted file mode 100644 index 8364d38..0000000 --- a/ice_validator/tests/test_nova_servers_correct_parameter_types.py +++ /dev/null @@ -1,114 +0,0 @@ -# -*- coding: utf8 -*- -# ============LICENSE_START======================================================= -# org.onap.vvp/validation-scripts -# =================================================================== -# Copyright © 2018 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============================================ -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. -# - -from .helpers import validates - -import pytest -import yaml -import re - - -@validates('R-71152', 'R-50436') -def test_nova_servers_correct_parameter_types(heat_template): - ''' - Make sure all nova servers have properly assigned types for the parameters - used for their name, image and flavor - ''' - key_values = ["name", "flavor", "image"] - key_value_formats = [ - ["name", "string", - re.compile(r'(.+?)_name_\d+')], - ["name", "comma_delimited_list", - re.compile(r'(.+?)_names')], - ["flavor", "string", - re.compile(r'(.+?)_flavor_name')], - ["image", "string", - re.compile(r'(.+?)_image_name')], - ] - - with open(heat_template) as fh: - yml = yaml.load(fh) - - # skip if parameters are not defined - if "parameters" not in yml: - pytest.skip("No parameters specified in the heat template") - - # skip if resources are not defined - if "resources" not in yml: - pytest.skip("No resources specified in the heat template") - - parameters = yml["parameters"] - - invalid_nova_servers = [] - - for k1, v1 in yml["resources"].items(): - if not isinstance(v1, dict): - continue - if "properties" not in v1: - continue - if v1.get("type") != "OS::Nova::Server": - continue - - valid_nova_server = True - for k2, v2 in v1["properties"].items(): - if k2 not in key_values: - continue - formats = [v for v in key_value_formats if v[0] == k2] - for v3 in formats: - if "get_param" not in v2: - continue - - param = v2["get_param"] - if isinstance(param, list): - param = param[0] - - m = v3[2].match(param) - if m and m.group(1): - if parameters[param]: - param_spec = parameters[param] - if not param_spec["type"]: - valid_nova_server = False - elif param_spec["type"] != v3[1]: - valid_nova_server = False - - if not valid_nova_server: - invalid_nova_servers.append(k1) - - assert not set(invalid_nova_servers) diff --git a/ice_validator/tests/test_parse_yaml.py b/ice_validator/tests/test_parse_yaml.py deleted file mode 100644 index 637e93d..0000000 --- a/ice_validator/tests/test_parse_yaml.py +++ /dev/null @@ -1,62 +0,0 @@ -# -*- coding: utf8 -*- -# ============LICENSE_START======================================================= -# org.onap.vvp/validation-scripts -# =================================================================== -# Copyright © 2018 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============================================ -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. -# - -from os import path -from .helpers import validates -import pytest -import yaml - - -@validates('R-95303') -def test_parse_yaml(filename): - ''' - Read in each .yaml or .env file. If it is successfully parsed as yaml, save - contents, else add filename to list of bad yaml files. Log the result of - parse attempt. - ''' - if path.splitext(filename)[-1] in [".yml", ".yaml", ".env"]: - try: - yaml.load(open(filename, 'r')) - assert True - except Exception: - assert False - else: - pytest.skip("The file does not have any of the extensions .yml,\ - .yaml, or .env") diff --git a/ice_validator/tests/test_referenced_and_defined_parameters_match.py b/ice_validator/tests/test_referenced_and_defined_parameters_match.py deleted file mode 100644 index 918d396..0000000 --- a/ice_validator/tests/test_referenced_and_defined_parameters_match.py +++ /dev/null @@ -1,54 +0,0 @@ -# -*- coding: utf8 -*- -# ============LICENSE_START======================================================= -# org.onap.vvp/validation-scripts -# =================================================================== -# Copyright © 2018 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============================================ -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. -# -import yaml -from .helpers import validates -from .utils.nested_iterables import find_all_get_param_in_yml - - -@validates('R-23664') -def test_referenced_and_defined_parameters_match(yaml_file): - ''' - Check that all referenced parameters are actually defined - as parameters - ''' - with open(yaml_file) as fh: - yml = yaml.load(fh) - resource_params = find_all_get_param_in_yml(yml) - assert set(yml['parameters'].keys()) == set(resource_params) diff --git a/ice_validator/tests/test_required_parameters_specified_in_env_files.py b/ice_validator/tests/test_required_parameters_specified_in_env_files.py index 15894c3..63b890c 100644 --- a/ice_validator/tests/test_required_parameters_specified_in_env_files.py +++ b/ice_validator/tests/test_required_parameters_specified_in_env_files.py @@ -2,7 +2,7 @@ # ============LICENSE_START======================================================= # org.onap.vvp/validation-scripts # =================================================================== -# Copyright © 2018 AT&T Intellectual Property. All rights reserved. +# Copyright © 2017 AT&T Intellectual Property. All rights reserved. # =================================================================== # # Unless otherwise specified, all software contained herein is licensed @@ -40,13 +40,20 @@ import pytest +from .helpers import get_environment_pair -def test_required_parameters_provided_in_env_file(environment_pair): + +@pytest.mark.heat_only +def test_required_parameters_provided_in_env_file(heat_template): ''' Make sure all required parameters are specified properly in the environment file if a server is defined in the corresponding heat template ''' + environment_pair = get_environment_pair(heat_template) + if not environment_pair: + pytest.skip("No heat/env pair could be identified") + required_parameters = ["vnf_id", "vf_module_id", "vnf_name"] if "resources" not in environment_pair["yyml"]: diff --git a/ice_validator/tests/test_reserve_port_fixed_ips_format.py b/ice_validator/tests/test_reserve_port_fixed_ips_format.py deleted file mode 100644 index 5b28d06..0000000 --- a/ice_validator/tests/test_reserve_port_fixed_ips_format.py +++ /dev/null @@ -1,118 +0,0 @@ -# -*- coding: utf8 -*- -# ============LICENSE_START======================================================= -# org.onap.vvp/validation-scripts -# =================================================================== -# Copyright © 2017 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============================================ -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. -# - -import pytest -import yaml -from .utils.network_roles import get_network_role_from_port -from .utils.ports import is_reserved_port -import re - - -def test_reserve_port_fixed_ips_format(heat_template): - ''' - Make sure all fixed_ips properties for a reserved port - follow the allowed naming conventions - ''' - allowed_formats = [ - ["fixed_ips", "string", "internal", - re.compile(r'(.+?)_int_(.+?)_floating_v6_ip')], - ["fixed_ips", "string", "internal", - re.compile(r'(.+?)_int_(.+?)_floating_ip')], - ["fixed_ips", "string", "external", - re.compile(r'(.+?)_floating_v6_ip')], - ["fixed_ips", "string", "external", - re.compile(r'(.+?)_floating_ip')], - ] - - with open(heat_template) as fh: - yml = yaml.load(fh) - - # skip if resources are not defined - if "resources" not in yml: - pytest.skip("No resources specified in the heat template") - - # check both valid and invalid patterns to catch edge cases - invalid_fixed_ips = [] - - for k1, v1 in yml["resources"].items(): - if not isinstance(v1, dict): - continue - if "properties" not in v1: - continue - if v1.get("type") != "OS::Neutron::Port": - continue - if not is_reserved_port(k1): - continue - - network_role = get_network_role_from_port(v1) - - for k2, v2 in v1["properties"].items(): - if k2 != "fixed_ips": - continue - for v3 in v2: - if "ip_address" not in v3: - continue - if "get_param" not in v3["ip_address"]: - continue - - valid_fixed_ip = False - for v4 in allowed_formats: - param = v3["ip_address"]["get_param"] - if isinstance(param, list): - param = param[0] - - # check if pattern matches - m = v4[3].match(param) - if m: - if v4[2] == "internal" and\ - len(m.groups()) > 1 and\ - m.group(2) == network_role: - valid_fixed_ip = True - break - elif v4[2] == "external" and\ - len(m.groups()) > 0 and\ - m.group(1).endswith("_" + network_role): - valid_fixed_ip = True - break - - if not valid_fixed_ip: - invalid_fixed_ips.append(param) - - assert not set(invalid_fixed_ips) diff --git a/ice_validator/tests/test_reserve_port_fixed_ips_has_base_outputs.py b/ice_validator/tests/test_reserve_port_fixed_ips_has_base_outputs.py deleted file mode 100644 index 7245ba8..0000000 --- a/ice_validator/tests/test_reserve_port_fixed_ips_has_base_outputs.py +++ /dev/null @@ -1,114 +0,0 @@ -# -*- coding: utf8 -*- -# ============LICENSE_START======================================================= -# org.onap.vvp/validation-scripts -# =================================================================== -# Copyright © 2017 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============================================ -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. -# - -import pytest -from os import path, sep -import yaml -from .utils.ports import is_reserved_port - - -def test_reserve_port_fixed_ips_has_base_outputs(heat_template): - ''' - Make sure all fixed ips specified in reserved ports are - also exported as outputs in the same base template - ''' - basename = path.splitext(heat_template)[0].rsplit(sep, 1)[1] - if not (basename.endswith("_base") or - basename.startswith("base_") or - basename.find("_base_") > 0): - pytest.skip("Skipping as it is not a base template") - - # parse the yml - with open(heat_template) as fh: - yml = yaml.load(fh) - - # get the outputs - try: - outputs = yml["outputs"] - except (TypeError, KeyError): - outputs = {} - - # skip if resources are not defined - if "resources" not in yml: - pytest.skip("No resources specified in the heat template") - - invalid_fixed_ips = [] - for k1, v1 in yml["resources"].items(): - if not isinstance(v1, dict): - continue - if "properties" not in v1: - continue - if v1.get("type") != "OS::Neutron::Port": - continue - if not is_reserved_port(k1): - continue - - for k2, v2 in v1["properties"].items(): - if k2 != "fixed_ips": - continue - for v3 in v2: - if "ip_address" not in v3: - continue - if "get_param" not in v3["ip_address"]: - continue - - param = v3["ip_address"]["get_param"] - - # construct the expected output param - if 'v6' in param: - output_param = param.replace('floating_v6_ip', 'v6_vip') - else: - output_param = param.replace('floating_ip', 'vip') - - # check the output is constructed correctly - try: - output_vip = outputs[output_param] - if not output_vip: - invalid_fixed_ips.append(param) - else: - # make sure the value is set properly using the - # original param value - output_value_param = output_vip["value"]["get_param"] - if output_value_param != param: - invalid_fixed_ips.append(param) - except (TypeError, KeyError): - invalid_fixed_ips.append(param) - - assert not set(invalid_fixed_ips) diff --git a/ice_validator/tests/test_reserve_port_only_in_base_template.py b/ice_validator/tests/test_reserve_port_only_in_base_template.py deleted file mode 100644 index 52cbec2..0000000 --- a/ice_validator/tests/test_reserve_port_only_in_base_template.py +++ /dev/null @@ -1,78 +0,0 @@ -# -*- coding: utf8 -*- -# ============LICENSE_START======================================================= -# org.onap.vvp/validation-scripts -# =================================================================== -# Copyright © 2017 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============================================ -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. -# - -import pytest -from os import path, sep -import yaml -from .utils.ports import is_reserved_port - - -def test_reserve_port_only_in_base_template(heat_template): - ''' - Make sure reserved ports are only specified in the base template - ''' - basename = path.splitext(heat_template)[0].rsplit(sep, 1)[1] - if (basename.endswith("_base") or - basename.startswith("base_") or - basename.find("_base_") > 0): - pytest.skip("A base template may or may not have reserved ports") - - # parse the yaml - with open(heat_template) as fh: - yml = yaml.load(fh) - - # skip if resources are not defined - if "resources" not in yml: - pytest.skip("No resources specified in the heat template") - - has_reserved_ports = False - for k1, v1 in yml["resources"].items(): - if not isinstance(v1, dict): - continue - if "properties" not in v1: - continue - if v1.get("type") != "OS::Neutron::Port": - continue - if not is_reserved_port(k1): - continue - - has_reserved_ports = True - - assert not has_reserved_ports diff --git a/ice_validator/tests/test_volume_templates_outputs_resources.py b/ice_validator/tests/test_volume_templates_outputs_resources.py index 90096e3..d3cec1e 100644 --- a/ice_validator/tests/test_volume_templates_outputs_resources.py +++ b/ice_validator/tests/test_volume_templates_outputs_resources.py @@ -2,7 +2,7 @@ # ============LICENSE_START======================================================= # org.onap.vvp/validation-scripts # =================================================================== -# Copyright © 2018 AT&T Intellectual Property. All rights reserved. +# Copyright © 2017 AT&T Intellectual Property. All rights reserved. # =================================================================== # # Unless otherwise specified, all software contained herein is licensed @@ -38,7 +38,7 @@ # ECOMP is a trademark and service mark of AT&T Intellectual Property. # -import yaml +from tests import cached_yaml as yaml import pytest from .utils.nested_iterables import find_all_get_resource_in_yml diff --git a/ice_validator/tests/utils/nested_dict.py b/ice_validator/tests/utils/nested_dict.py index 9bc3e99..24f7e5e 100644 --- a/ice_validator/tests/utils/nested_dict.py +++ b/ice_validator/tests/utils/nested_dict.py @@ -62,3 +62,4 @@ def is_dict_has_key(obj, key): '''return True/False `obj` is a dict and has `key` ''' return isinstance(obj, dict) and key in obj + diff --git a/ice_validator/tests/utils/nested_files.py b/ice_validator/tests/utils/nested_files.py index 02f733d..c551646 100644 --- a/ice_validator/tests/utils/nested_files.py +++ b/ice_validator/tests/utils/nested_files.py @@ -43,17 +43,17 @@ from os import path import re -import yaml +from tests import cached_yaml as yaml -VERSION = "1.0.2" +VERSION = '1.0.2' def get_list_of_nested_files(yml, dirpath): - """ + ''' return a list of all nested files - """ + ''' - if not hasattr(yml, "items"): + if not hasattr(yml, 'items'): return [] nested_files = [] @@ -63,34 +63,41 @@ def get_list_of_nested_files(yml, dirpath): t = v["type"] if t.endswith(".yml") or t.endswith(".yaml"): filepath = path.join(dirpath, t) - with open(filepath) as fh: - t_yml = yaml.load(fh) - nested_files.append(filepath) - nested_files.extend(get_list_of_nested_files(t_yml, dirpath)) + if path.exists(filepath): + with open(filepath) as fh: + t_yml = yaml.load(fh) + nested_files.append(filepath) + nested_files.extend(get_list_of_nested_files(t_yml, dirpath)) elif t == "OS::Heat::ResourceGroup": - rdt = v.get("properties", {}).get("resource_def", {}).get("type", None) + rdt = (v.get("properties", {}) + .get("resource_def", {}) + .get("type", None)) if rdt and (rdt.endswith(".yml") or rdt.endswith(".yaml")): filepath = path.join(dirpath, rdt) - with open(filepath) as fh: - rdt_yml = yaml.load(fh) - nested_files.append(filepath) - nested_files.extend(get_list_of_nested_files(rdt_yml, dirpath)) + if path.exists(filepath): + with open(filepath) as fh: + rdt_yml = yaml.load(fh) + nested_files.append(filepath) + nested_files.extend( + get_list_of_nested_files(rdt_yml, dirpath)) if isinstance(v, dict): - nested_files.extend(get_list_of_nested_files(v, dirpath)) + nested_files.extend( + get_list_of_nested_files(v, dirpath)) elif isinstance(v, list): for d in v: - nested_files.extend(get_list_of_nested_files(d, dirpath)) + nested_files.extend( + get_list_of_nested_files(d, dirpath)) return nested_files def check_for_invalid_nesting(yml, yaml_file, dirpath): - """ + ''' return a list of all nested files - """ - if not hasattr(yml, "items"): + ''' + if not hasattr(yml, 'items'): return [] invalid_nesting = [] - p = re.compile("^[A-z]*::[A-z]*::[A-z]*$") + p = re.compile('^[A-z]*::[A-z]*::[A-z]*$') for v in yml.values(): if isinstance(v, dict) and "type" in v: @@ -102,9 +109,7 @@ def check_for_invalid_nesting(yml, yaml_file, dirpath): if not isinstance(rd, dict) or "type" not in rd: invalid_nesting.append(yaml_file) continue - elif not p.match(rd["type"]) and not ( - rd["type"].endswith(".yml") or rd["type"].endswith(".yaml") - ): + elif not p.match(rd["type"]): filepath = path.join(dirpath, rd["type"]) else: continue @@ -115,11 +120,21 @@ def check_for_invalid_nesting(yml, yaml_file, dirpath): yml = yaml.load(fh) except yaml.YAMLError as e: invalid_nesting.append(filepath) - print(e) # pylint: disable=superfluous-parens - invalid_nesting.extend(check_for_invalid_nesting(yml, filepath, dirpath)) + print(e) # pylint: disable=superfluous-parens + invalid_nesting.extend(check_for_invalid_nesting( + yml, + filepath, + dirpath)) if isinstance(v, dict): - invalid_nesting.extend(check_for_invalid_nesting(v, yaml_file, dirpath)) + invalid_nesting.extend(check_for_invalid_nesting( + v, + yaml_file, + dirpath)) elif isinstance(v, list): for d in v: - invalid_nesting.extend(check_for_invalid_nesting(d, yaml_file, dirpath)) + invalid_nesting.extend(check_for_invalid_nesting( + d, + yaml_file, + dirpath)) return invalid_nesting + diff --git a/ice_validator/tests/utils/network_roles.py b/ice_validator/tests/utils/network_roles.py index d4b2cce..bed3a5a 100644 --- a/ice_validator/tests/utils/network_roles.py +++ b/ice_validator/tests/utils/network_roles.py @@ -43,31 +43,36 @@ import socket def get_network_role_from_port(resource): - """ + ''' get the network role from a neutron port resource - """ + ''' if not isinstance(resource, dict): return None - if "type" not in resource: + if 'type' not in resource: return None - if resource["type"] != "OS::Neutron::Port": + if resource['type'] != 'OS::Neutron::Port': return None - if "properties" not in resource: + if 'properties' not in resource: return None formats = [ - ["network", "string", "internal", re.compile(r"int_(.+?)_net_id")], - ["network", "string", "internal", re.compile(r"int_(.+?)_net_name")], - ["network", "string", "external", re.compile(r"(.+?)_net_id")], - ["network", "string", "external", re.compile(r"(.+?)_net_name")], - ] + ["network", "string", "internal", + re.compile(r'int_(.+?)_net_id')], + ["network", "string", "internal", + re.compile(r'int_(.+?)_net_name')], + ["network", "string", "external", + re.compile(r'(.+?)_net_id')], + ["network", "string", "external", + re.compile(r'(.+?)_net_name')]] for k1, v1 in resource["properties"].items(): - if k1 != "network": + if k1 != 'network': continue # get the network id or name - network = v1.get("get_param") or v1.get("get_resource") + network = ( + v1.get('get_param') or + v1.get('get_resource')) if not network: continue @@ -79,28 +84,41 @@ def get_network_role_from_port(resource): return None +def get_network_roles(resources): + network_roles = [] + for v in resources.values(): + nr = get_network_role_from_port(v) + if nr: + network_roles.append(nr) + + return set(network_roles) + + def get_network_type_from_port(resource): - """ + ''' get the network type from a neutron port resource - """ + ''' if not isinstance(resource, dict): return None - if "type" not in resource: + if 'type' not in resource: return None - if resource["type"] != "OS::Neutron::Port": + if resource['type'] != 'OS::Neutron::Port': return None - if "properties" not in resource: + if 'properties' not in resource: return None formats = [ - ["network", "string", "internal", re.compile(r"int_(.+?)_net_id")], - ["network", "string", "internal", re.compile(r"int_(.+?)_net_name")], - ["network", "string", "external", re.compile(r"(.+?)_net_id")], - ["network", "string", "external", re.compile(r"(.+?)_net_name")], - ] + ["network", "string", "internal", + re.compile(r'int_(.+?)_net_id')], + ["network", "string", "internal", + re.compile(r'int_(.+?)_net_name')], + ["network", "string", "external", + re.compile(r'(.+?)_net_id')], + ["network", "string", "external", + re.compile(r'(.+?)_net_name')]] for k1, v1 in resource["properties"].items(): - if k1 != "network": + if k1 != 'network': continue if "get_param" not in v1: continue @@ -112,22 +130,22 @@ def get_network_type_from_port(resource): return None -def is_valid_ip_address(ip_address, ip_type="ipv4"): - """ +def is_valid_ip_address(ip_address, ip_type='ipv4'): + ''' check if an ip address is valid - """ - if ip_type == "ipv4": + ''' + if ip_type == 'ipv4': return is_valid_ipv4_address(ip_address) - elif ip_type == "ipv6": + elif ip_type == 'ipv6': return is_valid_ipv6_address(ip_address) return False def is_valid_ipv4_address(ip_address): - """ + ''' check if an ip address of the type ipv4 is valid - """ + ''' try: socket.inet_pton(socket.AF_INET, ip_address) except AttributeError: @@ -135,17 +153,17 @@ def is_valid_ipv4_address(ip_address): socket.inet_aton(ip_address) except (OSError, socket.error): return False - return ip_address.count(".") == 3 + return ip_address.count('.') == 3 except (OSError, socket.error): return False return True def is_valid_ipv6_address(ip_address): - """ + ''' check if an ip address of the type ipv6 is valid - """ + ''' try: socket.inet_pton(socket.AF_INET6, ip_address) except (OSError, socket.error): @@ -154,13 +172,13 @@ def is_valid_ipv6_address(ip_address): def property_uses_get_resource(resource, property_name): - """ + ''' returns true if a port's network property uses the get_resource function - """ + ''' if not isinstance(resource, dict): return False - if "properties" not in resource: + if 'properties' not in resource: return False for k1, v1 in resource["properties"].items(): if k1 != property_name: diff --git a/ice_validator/tests/utils/ports.py b/ice_validator/tests/utils/ports.py index e479201..a2ae8a9 100644 --- a/ice_validator/tests/utils/ports.py +++ b/ice_validator/tests/utils/ports.py @@ -43,125 +43,89 @@ from .vm_types import get_vm_type_for_nova_server import re -def is_valid_ip_address(ip_address, vm_type, network_role, port_property): - """ +def is_valid_ip_address(ip_address, vm_type, network_role, port_property, parameter_type): + ''' Check the ip_address to make sure it is properly formatted and also contains {vm_type} and {network_role} - """ + ''' allowed_formats = [ - [ - "allowed_address_pairs", - "string", - "internal", - re.compile(r"(.+?)_int_(.+?)_floating_v6_ip"), - ], - [ - "allowed_address_pairs", - "string", - "internal", - re.compile(r"(.+?)_int_(.+?)_floating_ip"), - ], - [ - "allowed_address_pairs", - "string", - "external", - re.compile(r"(.+?)_floating_v6_ip"), - ], - [ - "allowed_address_pairs", - "string", - "external", - re.compile(r"(.+?)_floating_ip"), - ], - [ - "allowed_address_pairs", - "string", - "internal", - re.compile(r"(.+?)_int_(.+?)_v6_ip_\d+"), - ], - [ - "allowed_address_pairs", - "string", - "internal", - re.compile(r"(.+?)_int_(.+?)_ip_\d+"), - ], - ["allowed_address_pairs", "string", "external", re.compile(r"(.+?)_v6_ip_\d+")], - ["allowed_address_pairs", "string", "external", re.compile(r"(.+?)_ip_\d+")], - [ - "allowed_address_pairs", - "comma_delimited_list", - "internal", - re.compile(r"(.+?)_int_(.+?)_v6_ips"), - ], - [ - "allowed_address_pairs", - "comma_delimited_list", - "internal", - re.compile(r"(.+?)_int_(.+?)_ips"), - ], - [ - "allowed_address_pairs", - "comma_delimited_list", - "external", - re.compile(r"(.+?)_v6_ips"), - ], - [ - "allowed_address_pairs", - "comma_delimited_list", - "external", - re.compile(r"(.+?)_ips"), - ], - ["fixed_ips", "string", "internal", re.compile(r"(.+?)_int_(.+?)_v6_ip_\d+")], - ["fixed_ips", "string", "internal", re.compile(r"(.+?)_int_(.+?)_ip_\d+")], - ["fixed_ips", "string", "external", re.compile(r"(.+?)_v6_ip_\d+")], - ["fixed_ips", "string", "external", re.compile(r"(.+?)_ip_\d+")], - [ - "fixed_ips", - "comma_delimited_list", - "internal", - re.compile(r"(.+?)_int_(.+?)_v6_ips"), - ], - [ - "fixed_ips", - "comma_delimited_list", - "internal", - re.compile(r"(.+?)_int_(.+?)_ips"), - ], - ["fixed_ips", "comma_delimited_list", "external", re.compile(r"(.+?)_v6_ips")], - ["fixed_ips", "comma_delimited_list", "external", re.compile(r"(.+?)_ips")], - ] + ["allowed_address_pairs", "string", "internal", + re.compile(r'(.+?)_int_(.+?)_floating_v6_ip')], + ["allowed_address_pairs", "string", "internal", + re.compile(r'(.+?)_int_(.+?)_floating_ip')], + ["allowed_address_pairs", "string", "external", + re.compile(r'(.+?)_floating_v6_ip')], + ["allowed_address_pairs", "string", "external", + re.compile(r'(.+?)_floating_ip')], + ["allowed_address_pairs", "string", "internal", + re.compile(r'(.+?)_int_(.+?)_v6_ip_\d+')], + ["allowed_address_pairs", "string", "internal", + re.compile(r'(.+?)_int_(.+?)_ip_\d+')], + ["allowed_address_pairs", "string", "external", + re.compile(r'(.+?)_v6_ip_\d+')], + ["allowed_address_pairs", "string", "external", + re.compile(r'(.+?)_ip_\d+')], + ["allowed_address_pairs", "comma_delimited_list", + "internal", re.compile(r'(.+?)_int_(.+?)_v6_ips')], + ["allowed_address_pairs", "comma_delimited_list", + "internal", re.compile(r'(.+?)_int_(.+?)_ips')], + ["allowed_address_pairs", "comma_delimited_list", + "external", re.compile(r'(.+?)_v6_ips')], + ["allowed_address_pairs", "comma_delimited_list", + "external", re.compile(r'(.+?)_ips')], + ["fixed_ips", "string", "internal", + re.compile(r'(.+?)_int_(.+?)_v6_ip_\d+')], + ["fixed_ips", "string", "internal", + re.compile(r'(.+?)_int_(.+?)_ip_\d+')], + ["fixed_ips", "string", "external", + re.compile(r'(.+?)_v6_ip_\d+')], + ["fixed_ips", "string", "external", + re.compile(r'(.+?)_ip_\d+')], + ["fixed_ips", "comma_delimited_list", "internal", + re.compile(r'(.+?)_int_(.+?)_v6_ips')], + ["fixed_ips", "comma_delimited_list", "internal", + re.compile(r'(.+?)_int_(.+?)_ips')], + ["fixed_ips", "comma_delimited_list", "external", + re.compile(r'(.+?)_v6_ips')], + ["fixed_ips", "comma_delimited_list", "external", + re.compile(r'(.+?)_ips')]] for v3 in allowed_formats: + if v3[1] != parameter_type: + continue if v3[0] != port_property: continue # check if pattern matches m = v3[3].match(ip_address) if m: - if v3[2] == "internal" and len(m.groups()) > 1: - return m.group(1) == vm_type and m.group(2) == network_role - elif v3[2] == "external" and len(m.groups()) > 0: + if (v3[2] == "internal" and + len(m.groups()) > 1): + return m.group(1) == vm_type and\ + m.group(2) == network_role + elif (v3[2] == "external" and + len(m.groups()) > 0): return m.group(1) == vm_type + "_" + network_role return False -def get_invalid_ip_addresses(resources, port_property): - """ +def get_invalid_ip_addresses(resources, port_property, parameters): + ''' Get a list of valid ip addresses for a heat resources section - """ + ''' invalid_ip_addresses = [] for k, v in resources.items(): if not isinstance(v, dict): continue - if "type" not in v: + if 'type' not in v: continue - if v["type"] not in "OS::Nova::Server": + if v['type'] not in 'OS::Nova::Server': continue - if "properties" not in v: + if 'properties' not in v: continue - if "networks" not in v["properties"]: + if 'networks' not in v['properties']: continue port_resource = None @@ -171,16 +135,16 @@ def get_invalid_ip_addresses(resources, port_property): continue # get all ports associated with the nova server - properties = v["properties"] - for network in properties["networks"]: + properties = v['properties'] + for network in properties['networks']: for k3, v3 in network.items(): - if k3 != "port": + if k3 != 'port': continue if not isinstance(v3, dict): continue - if "get_resource" in v3: - port_id = v3["get_resource"] + if 'get_resource' in v3: + port_id = v3['get_resource'] if not resources[port_id]: continue port_resource = resources[port_id] @@ -199,15 +163,23 @@ def get_invalid_ip_addresses(resources, port_property): continue if "get_param" not in v2["ip_address"]: continue - ip_address = v2["ip_address"]["get_param"] if isinstance(ip_address, list): ip_address = ip_address[0] - valid_ip_address = is_valid_ip_address( - ip_address, vm_type, network_role, port_property - ) + if ip_address not in parameters: + continue + + parameter_type = parameters[ip_address].get("type") + if not parameter_type: + continue + + valid_ip_address = is_valid_ip_address(ip_address, + vm_type, + network_role, + port_property, + parameter_type) if not valid_ip_address: invalid_ip_addresses.append(ip_address) @@ -216,14 +188,15 @@ def get_invalid_ip_addresses(resources, port_property): def is_reserved_port(port_id): - """ + ''' Checks to see if the resource id for a port follows the reserve port concept - """ + ''' formats = [ - ["port_id", re.compile(r"reserve_port_(.+?)_floating_ip_\d+")], - ["port_id", re.compile(r"reserve_port_(.+?)_floating_v6_ip_\d+")], - ] + ["port_id", + re.compile(r'reserve_port_(.+?)_floating_ip_\d+')], + ["port_id", + re.compile(r'reserve_port_(.+?)_floating_v6_ip_\d+')]] for f in formats: m = f[1].match(port_id.lower()) if m and m.group(1): diff --git a/ice_validator/tests/utils/vm_types.py b/ice_validator/tests/utils/vm_types.py index 78006b9..6802666 100644 --- a/ice_validator/tests/utils/vm_types.py +++ b/ice_validator/tests/utils/vm_types.py @@ -42,33 +42,36 @@ import re def get_vm_types_for_resource(resource): - """ + ''' Get all unique vm_types for a resource Notes: - Returns set([]) if the resource is not formatted properly, the passed resource is not a nova server - If more than one vm_type is detected all vm_types will be returned - """ + ''' if not isinstance(resource, dict): return set() - if "type" not in resource: + if 'type' not in resource: return set() - if resource["type"] != "OS::Nova::Server": + if resource['type'] != 'OS::Nova::Server': return set() - if "properties" not in resource: + if 'properties' not in resource: return set() key_values = ["name", "flavor", "image"] key_value_formats = [ - ["name", "string", re.compile(r"(.+?)_name_\d+")], - ["name", "comma_delimited_list", re.compile(r"(.+?)_names")], - ["flavor", "string", re.compile(r"(.+?)_flavor_name")], - ["image", "string", re.compile(r"(.+?)_image_name")], - ] + ["name", "string", + re.compile(r'(.+?)_name_\d+')], + ["name", "comma_delimited_list", + re.compile(r'(.+?)_names')], + ["flavor", "string", + re.compile(r'(.+?)_flavor_name')], + ["image", "string", + re.compile(r'(.+?)_image_name')]] vm_types = [] - for k2, v2 in resource["properties"].items(): + for k2, v2 in resource['properties'].items(): if k2 not in key_values: continue if "get_param" not in v2: @@ -86,12 +89,12 @@ def get_vm_types_for_resource(resource): def get_vm_type_for_nova_server(resource): - """ + ''' Get the vm_type for a resource Note: Returns None if not exactly one vm_type is detected, if the resource is not formatted properly, or the passed resource is not a nova server - """ + ''' vm_types = get_vm_types_for_resource(resource) # if more than one vm_type was identified, return None @@ -102,10 +105,10 @@ def get_vm_type_for_nova_server(resource): def get_vm_types(resources): - """ + ''' Get all vm_types for a list of heat resources, do note that some of the values retrieved may be invalid - """ + ''' vm_types = [] for v in resources.values(): vm_types.extend(list(get_vm_types_for_resource(v))) diff --git a/ice_validator/tests/utils/volumes.py b/ice_validator/tests/utils/volumes.py index c64c0ee..40731bf 100644 --- a/ice_validator/tests/utils/volumes.py +++ b/ice_validator/tests/utils/volumes.py @@ -42,7 +42,7 @@ """ from os import path -import yaml +from tests import cached_yaml as yaml VERSION = '1.0.0' |