From 84db7f8f65cd0ec77f09cfde365599df9890ce6c Mon Sep 17 00:00:00 2001 From: "Lovett, Trevor" Date: Tue, 27 Aug 2019 12:40:36 -0500 Subject: [PATCH] [VVP] Generated completed preload from env files User can supply an optional directory containing .env files and/or CSAR VSP which can be used to generate populated preloads in the requested format. The nested directories can be used to create sub-environments that inherit their settings from the parent directories. Optionally, values can be specified in a defaults.yaml and they will be used if that value is not defined in the .env file. This is useful if the parameter name and value will be the same in all modules. Issue-ID: VVP-278 Change-Id: Icd9846c63463537793db908be8ce5dba13c4bda3 Signed-off-by: Lovett, Trevor --- .gitignore | 1 + checks.py | 28 +- ice_validator/app_tests/preload_tests/__init__.py | 0 .../app_tests/preload_tests/preload_envs/base.env | 3 + .../preload_tests/preload_envs/defaults.yaml | 1 + .../preload_tests/preload_envs/env_one/base.env | 2 + .../preload_envs/env_one/env_one_a/base.env | 2 + .../preload_envs/env_three/defaults.yaml | 2 + .../service_Starkmultimodule243550_csar.csar | Bin 0 -> 80814 bytes .../preload_tests/preload_envs/env_two/base.env | 2 + .../preload_tests/preload_envs/incremental.env | 2 + .../app_tests/preload_tests/preload_envs/test.csar | Bin 0 -> 117854 bytes .../app_tests/preload_tests/sample_env/base.env | 39 ++ .../app_tests/preload_tests/sample_heat/base.env | 15 + .../app_tests/preload_tests/sample_heat/base.yaml | 376 ++++++++++++++++++++ .../preload_tests/sample_heat/base_volume.env | 2 + .../preload_tests/sample_heat/base_volume.yaml | 47 +++ .../preload_tests/sample_heat/incremental.env | 11 + .../preload_tests/sample_heat/incremental.yaml | 156 ++++++++ .../preload_tests/sample_heat/nested_svc.yaml | 84 +++++ .../app_tests/preload_tests/sample_heat/user.data | 0 .../app_tests/preload_tests/test_environment.py | 180 ++++++++++ .../app_tests/preload_tests/test_grapi.py | 243 +++++++++++++ .../app_tests/preload_tests/test_vnfapi.py | 195 ++++++++++ .../{test_app_config.py => test_config.py} | 147 +++++++- ice_validator/app_tests/test_data.zip | Bin 0 -> 125 bytes ice_validator/app_tests/test_helpers.py | 88 +++++ ice_validator/app_tests/vvp-config.yaml | 5 +- ice_validator/config.py | 355 +++++++++++++++++++ ice_validator/preload/__init__.py | 36 ++ ice_validator/preload/environment.py | 267 ++++++++++++++ ice_validator/preload/generator.py | 242 +++++++++++++ ice_validator/{preload.py => preload/model.py} | 231 +++--------- ice_validator/preload_grapi/grapi_generator.py | 113 +++--- ice_validator/preload_vnfapi/vnfapi_generator.py | 85 ++--- ice_validator/tests/conftest.py | 20 +- ice_validator/tests/helpers.py | 58 +++ ice_validator/tests/parametrizers.py | 4 +- .../tests/test_environment_file_parameters.py | 10 +- ice_validator/vvp-config.yaml | 1 - ice_validator/vvp.py | 393 ++++----------------- requirements.txt | 1 + 42 files changed, 2827 insertions(+), 620 deletions(-) create mode 100644 ice_validator/app_tests/preload_tests/__init__.py create mode 100644 ice_validator/app_tests/preload_tests/preload_envs/base.env create mode 100644 ice_validator/app_tests/preload_tests/preload_envs/defaults.yaml create mode 100644 ice_validator/app_tests/preload_tests/preload_envs/env_one/base.env create mode 100644 ice_validator/app_tests/preload_tests/preload_envs/env_one/env_one_a/base.env create mode 100644 ice_validator/app_tests/preload_tests/preload_envs/env_three/defaults.yaml create mode 100644 ice_validator/app_tests/preload_tests/preload_envs/env_three/service_Starkmultimodule243550_csar.csar create mode 100644 ice_validator/app_tests/preload_tests/preload_envs/env_two/base.env create mode 100644 ice_validator/app_tests/preload_tests/preload_envs/incremental.env create mode 100644 ice_validator/app_tests/preload_tests/preload_envs/test.csar create mode 100644 ice_validator/app_tests/preload_tests/sample_env/base.env create mode 100644 ice_validator/app_tests/preload_tests/sample_heat/base.env create mode 100644 ice_validator/app_tests/preload_tests/sample_heat/base.yaml create mode 100644 ice_validator/app_tests/preload_tests/sample_heat/base_volume.env create mode 100644 ice_validator/app_tests/preload_tests/sample_heat/base_volume.yaml create mode 100644 ice_validator/app_tests/preload_tests/sample_heat/incremental.env create mode 100644 ice_validator/app_tests/preload_tests/sample_heat/incremental.yaml create mode 100644 ice_validator/app_tests/preload_tests/sample_heat/nested_svc.yaml create mode 100644 ice_validator/app_tests/preload_tests/sample_heat/user.data create mode 100644 ice_validator/app_tests/preload_tests/test_environment.py create mode 100644 ice_validator/app_tests/preload_tests/test_grapi.py create mode 100644 ice_validator/app_tests/preload_tests/test_vnfapi.py rename ice_validator/app_tests/{test_app_config.py => test_config.py} (52%) create mode 100644 ice_validator/app_tests/test_data.zip create mode 100644 ice_validator/app_tests/test_helpers.py create mode 100644 ice_validator/config.py create mode 100644 ice_validator/preload/__init__.py create mode 100644 ice_validator/preload/environment.py create mode 100644 ice_validator/preload/generator.py rename ice_validator/{preload.py => preload/model.py} (70%) diff --git a/.gitignore b/.gitignore index be6137d..24c7a51 100644 --- a/.gitignore +++ b/.gitignore @@ -96,3 +96,4 @@ ENV/ .idea/ ice_validator/output/ +sample_env/grapi/* diff --git a/checks.py b/checks.py index b43d6c7..4431d26 100644 --- a/checks.py +++ b/checks.py @@ -52,6 +52,16 @@ THIS_DIR = os.path.dirname(os.path.abspath(__file__)) CURRENT_NEEDS_PATH = os.path.join(THIS_DIR, "ice_validator/heat_requirements.json") +def run_pytest(*args, msg="pytest failed"): + original_dir = os.getcwd() + try: + os.chdir(os.path.join(THIS_DIR, "ice_validator")) + if pytest.main(list(args)) != 0: + return [msg] + finally: + os.chdir(original_dir) + + class Traceability: PATH = os.path.join(THIS_DIR, "ice_validator/output/traceability.csv") @@ -145,18 +155,14 @@ def check_requirements_up_to_date(): return None +def check_app_tests_pass(): + return run_pytest("tests", "--self-test", + msg="app_tests failed. Run pytest app_tests and fix errors.") + + def check_self_test_pass(): - """ - Run pytest self-test and ensure it passes - :return: - """ - original_dir = os.getcwd() - try: - os.chdir(os.path.join(THIS_DIR, "ice_validator")) - if pytest.main(["tests", "--self-test"]) != 0: - return ["VVP self-test failed. Run pytest --self-test and fix errors."] - finally: - os.chdir(original_dir) + return run_pytest("tests", "--self-test", + msg="self-test failed. Run pytest --self-test and fix errors.") def check_testable_requirements_are_mapped(): diff --git a/ice_validator/app_tests/preload_tests/__init__.py b/ice_validator/app_tests/preload_tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ice_validator/app_tests/preload_tests/preload_envs/base.env b/ice_validator/app_tests/preload_tests/preload_envs/base.env new file mode 100644 index 0000000..9d38e4f --- /dev/null +++ b/ice_validator/app_tests/preload_tests/preload_envs/base.env @@ -0,0 +1,3 @@ +parameters: + common: "ABC" + my_ip: default diff --git a/ice_validator/app_tests/preload_tests/preload_envs/defaults.yaml b/ice_validator/app_tests/preload_tests/preload_envs/defaults.yaml new file mode 100644 index 0000000..f4b1b59 --- /dev/null +++ b/ice_validator/app_tests/preload_tests/preload_envs/defaults.yaml @@ -0,0 +1 @@ +availability_zone_0: az0 diff --git a/ice_validator/app_tests/preload_tests/preload_envs/env_one/base.env b/ice_validator/app_tests/preload_tests/preload_envs/env_one/base.env new file mode 100644 index 0000000..4135914 --- /dev/null +++ b/ice_validator/app_tests/preload_tests/preload_envs/env_one/base.env @@ -0,0 +1,2 @@ +parameters: + my_ip: 192.168.0.1 diff --git a/ice_validator/app_tests/preload_tests/preload_envs/env_one/env_one_a/base.env b/ice_validator/app_tests/preload_tests/preload_envs/env_one/env_one_a/base.env new file mode 100644 index 0000000..d799d77 --- /dev/null +++ b/ice_validator/app_tests/preload_tests/preload_envs/env_one/env_one_a/base.env @@ -0,0 +1,2 @@ +parameters: + my_ip: 192.168.0.13 diff --git a/ice_validator/app_tests/preload_tests/preload_envs/env_three/defaults.yaml b/ice_validator/app_tests/preload_tests/preload_envs/env_three/defaults.yaml new file mode 100644 index 0000000..5476931 --- /dev/null +++ b/ice_validator/app_tests/preload_tests/preload_envs/env_three/defaults.yaml @@ -0,0 +1,2 @@ +availability_zone_0: az0-b +custom_env_3: default diff --git a/ice_validator/app_tests/preload_tests/preload_envs/env_three/service_Starkmultimodule243550_csar.csar b/ice_validator/app_tests/preload_tests/preload_envs/env_three/service_Starkmultimodule243550_csar.csar new file mode 100644 index 0000000000000000000000000000000000000000..64ce5566cb1a2fc265304d76eaa740c7249f96fd GIT binary patch literal 80814 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 literal 0 HcmV?d00001 diff --git a/ice_validator/app_tests/preload_tests/preload_envs/env_two/base.env b/ice_validator/app_tests/preload_tests/preload_envs/env_two/base.env new file mode 100644 index 0000000..616e178 --- /dev/null +++ b/ice_validator/app_tests/preload_tests/preload_envs/env_two/base.env @@ -0,0 +1,2 @@ +parameters: + my_ip: 192.168.0.2 diff --git a/ice_validator/app_tests/preload_tests/preload_envs/incremental.env b/ice_validator/app_tests/preload_tests/preload_envs/incremental.env new file mode 100644 index 0000000..97db79f --- /dev/null +++ b/ice_validator/app_tests/preload_tests/preload_envs/incremental.env @@ -0,0 +1,2 @@ +parameters: + inc_property: "global" diff --git a/ice_validator/app_tests/preload_tests/preload_envs/test.csar b/ice_validator/app_tests/preload_tests/preload_envs/test.csar new file mode 100644 index 0000000000000000000000000000000000000000..d23a746e42b3e5929e0b4a37328f9f15562c39b6 GIT binary patch literal 117854 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 z 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/" in data["vnf-type"] + + +def test_base_azs(base): + az = base["input"]["preload-vf-module-topology-information"][ + "vnf-resource-assignments" + ]["availability-zones"]["availability-zone"] + assert isinstance(az, list) + assert len(az) == 2 + assert az[0] == "VALUE FOR: availability_zone_0" + + +def test_base_networks(base): + nets = base["input"]["preload-vf-module-topology-information"][ + "vnf-resource-assignments" + ]["vnf-networks"]["vnf-network"] + assert isinstance(nets, list) + assert len(nets) == 3 + oam = first(nets, lambda n: n["network-role"] == "oam") + assert oam == { + "network-role": "oam", + "network-name": "VALUE FOR: network name of oam_net_id", + "subnets-data": {"subnet-data": [{"subnet-id": "VALUE FOR: oam_subnet_id"}]}, + } + + +def test_base_vm_types(base): + vms = base["input"]["preload-vf-module-topology-information"]["vf-module-topology"][ + "vf-module-assignments" + ]["vms"]["vm"] + vm_types = {vm["vm-type"] for vm in vms} + assert vm_types == {"db", "svc", "mgmt", "lb"} + db = first(vms, lambda v: v["vm-type"] == "db") + assert db == { + "vm-type": "db", + "vm-count": 2, + "vm-names": {"vm-name": ["VALUE FOR: db_name_0", "VALUE FOR: db_name_1"]}, + "vm-networks": { + "vm-network": [ + { + "network-role": "oam", + "network-information-items": { + "network-information-item": [ + { + "ip-version": "4", + "use-dhcp": "N", + "ip-count": 2, + "network-ips": { + "network-ip": [ + "VALUE FOR: db_oam_ip_0", + "VALUE FOR: db_oam_ip_1", + ] + }, + }, + { + "ip-version": "6", + "use-dhcp": "N", + "ip-count": 0, + "network-ips": {"network-ip": []}, + }, + ] + }, + "mac-addresses": {"mac-address": []}, + "floating-ips": {"floating-ip-v4": [], "floating-ip-v6": []}, + "interface-route-prefixes": {"interface-route-prefix": []}, + }, + { + "network-role": "ha", + "network-information-items": { + "network-information-item": [ + { + "ip-version": "4", + "use-dhcp": "N", + "ip-count": 0, + "network-ips": {"network-ip": []}, + }, + { + "ip-version": "6", + "use-dhcp": "N", + "ip-count": 0, + "network-ips": {"network-ip": []}, + }, + ] + }, + "mac-addresses": {"mac-address": []}, + "floating-ips": { + "floating-ip-v4": ["VALUE FOR: db_ha_floating_ip"], + "floating-ip-v6": ["VALUE FOR: db_ha_floating_v6_ip"], + }, + "interface-route-prefixes": {"interface-route-prefix": []}, + }, + ] + }, + } + + +def test_base_general(base): + general = base["input"]["preload-vf-module-topology-information"][ + "vf-module-topology" + ]["vf-module-topology-identifier"] + assert ( + general["vf-module-type"] == "VALUE FOR: from CSAR or SDC" + ) + assert general["vf-module-name"] == "VALUE FOR: vf_module_name" + + +def test_base_parameters(base): + params = base["input"]["preload-vf-module-topology-information"][ + "vf-module-topology" + ]["vf-module-parameters"]["param"] + assert params == [ + {"name": "svc_image_name", "value": "svc_image"}, + {"name": "svc_flavor_name", "value": "svc_flavor"}, + ] + + +def test_incremental(incremental): + az = incremental["input"]["preload-vf-module-topology-information"][ + "vnf-resource-assignments" + ]["availability-zones"]["availability-zone"] + assert isinstance(az, list) + assert len(az) == 1 + assert az[0] == "VALUE FOR: availability_zone_0" + + +def test_incremental_networks(incremental): + nets = incremental["input"]["preload-vf-module-topology-information"][ + "vnf-resource-assignments" + ]["vnf-networks"]["vnf-network"] + assert isinstance(nets, list) + assert len(nets) == 1 + assert nets[0]["network-role"] == "ha" + + +def test_preload_env_population(preload): + base_path = THIS_DIR / "sample_env/preloads/grapi/base.json" + data = load_json(base_path) + azs = data["input"]["preload-vf-module-topology-information"][ + "vnf-resource-assignments" + ]["availability-zones"]["availability-zone"] + assert azs == ["az0", "az1"] diff --git a/ice_validator/app_tests/preload_tests/test_vnfapi.py b/ice_validator/app_tests/preload_tests/test_vnfapi.py new file mode 100644 index 0000000..5732335 --- /dev/null +++ b/ice_validator/app_tests/preload_tests/test_vnfapi.py @@ -0,0 +1,195 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START==================================================== +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2019 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ============LICENSE_END============================================ +import tempfile +from pathlib import Path +from shutil import rmtree + +import pytest + +from app_tests.preload_tests.test_grapi import load_json +from preload.environment import PreloadEnvironment +from preload.model import Vnf, get_heat_templates +from preload_vnfapi import VnfApiPreloadGenerator +from tests.helpers import load_yaml, first + +THIS_DIR = Path(__file__).parent +SAMPLE_HEAT_DIR = THIS_DIR / "sample_heat" + + +def load_module(base_dir, name): + path = Path(str(base_dir / "vnfapi" / name)) + assert path.exists(), "{} does not exist".format(path) + return load_yaml(str(path)) + + +@pytest.fixture(scope="session") +def session_dir(request): + # Temporary directory that gets deleted at the session + # pytest tmpdir doesn't support a non-function scoped temporary directory + session_dir = Path(tempfile.mkdtemp()) + request.addfinalizer(lambda: rmtree(session_dir)) + return session_dir + + +@pytest.fixture(scope="session") +def preload(pytestconfig, session_dir): + # Generate the preloads for testing + def fake_getoption(opt, default=None): + return [SAMPLE_HEAT_DIR.as_posix()] if opt == "template_dir" else None + + pytestconfig.getoption = fake_getoption + templates = get_heat_templates(pytestconfig) + vnf = Vnf(templates) + preload_env = PreloadEnvironment(THIS_DIR / "sample_env") + generator = VnfApiPreloadGenerator(vnf, session_dir, preload_env) + generator.generate() + return session_dir + + +@pytest.fixture(scope="session") +def base(preload): + return load_module(preload, "base.json") + + +@pytest.fixture(scope="session") +def incremental(preload): + return load_module(preload, "incremental.json") + + +def test_base_azs(base): + az = base["input"]["vnf-topology-information"]["vnf-assignments"][ + "availability-zones" + ] + assert az == [ + {"availability-zone": "VALUE FOR: availability_zone_0"}, + {"availability-zone": "VALUE FOR: availability_zone_1"}, + ] + + +def test_base_networks(base): + nets = base["input"]["vnf-topology-information"]["vnf-assignments"]["vnf-networks"] + assert nets == [ + { + "network-role": "oam", + "network-name": "VALUE FOR: network name for oam_net_id", + "subnet-id": "oam_subnet_id", + }, + {"network-role": "ha", "network-name": "VALUE FOR: network name for ha_net_id"}, + { + "network-role": "ctrl", + "network-name": "VALUE FOR: network name for ctrl_net_id", + "subnet-id": "ctrl_subnet_id", + }, + ] + + +def test_base_vm_types(base): + vms = base["input"]["vnf-topology-information"]["vnf-assignments"]["vnf-vms"] + vm_types = {vm["vm-type"] for vm in vms} + assert vm_types == {"db", "svc", "mgmt", "lb"} + db = first(vms, lambda v: v["vm-type"] == "db") + assert db == { + "vm-type": "db", + "vm-count": 2, + "vm-names": {"vm-name": ["VALUE FOR: db_name_0", "VALUE FOR: db_name_1"]}, + "vm-networks": [ + { + "network-role": "oam", + "network-role-tag": "oam", + "ip-count": 2, + "ip-count-ipv6": 0, + "floating-ip": "", + "floating-ip-v6": "", + "network-ips": [ + {"ip-address": "VALUE FOR: db_oam_ip_0"}, + {"ip-address": "VALUE FOR: db_oam_ip_1"}, + ], + "network-ips-v6": [], + "network-macs": [], + "interface-route-prefixes": [], + "use-dhcp": "N", + }, + { + "network-role": "ha", + "network-role-tag": "ha", + "ip-count": 0, + "ip-count-ipv6": 0, + "floating-ip": "VALUE FOR: db_ha_floating_ip", + "floating-ip-v6": "VALUE FOR: db_ha_floating_v6_ip", + "network-ips": [], + "network-ips-v6": [], + "network-macs": [], + "interface-route-prefixes": [], + "use-dhcp": "N", + }, + ], + } + + +def test_base_parameters(base): + params = base["input"]["vnf-topology-information"]["vnf-parameters"] + assert params == [ + {"vnf-parameter-name": "svc_image_name", "vnf-parameter-value": "svc_image"}, + {"vnf-parameter-name": "svc_flavor_name", "vnf-parameter-value": "svc_flavor"}, + ] + + +def test_incremental(incremental): + az = incremental["input"]["vnf-topology-information"]["vnf-assignments"][ + "availability-zones" + ] + assert isinstance(az, list) + assert len(az) == 1 + assert az[0] == {"availability-zone": "VALUE FOR: availability_zone_0"} + + +def test_incremental_networks(incremental): + nets = incremental["input"]["vnf-topology-information"]["vnf-assignments"][ + "vnf-networks" + ] + assert isinstance(nets, list) + assert len(nets) == 1 + assert nets[0]["network-role"] == "ha" + + +def test_preload_env_population(preload): + base_path = THIS_DIR / "sample_env/preloads/vnfapi/base.json" + data = load_json(base_path) + azs = data["input"]["vnf-topology-information"]["vnf-assignments"][ + "availability-zones" + ] + assert azs == [{"availability-zone": "az0"}, {"availability-zone": "az1"}] diff --git a/ice_validator/app_tests/test_app_config.py b/ice_validator/app_tests/test_config.py similarity index 52% rename from ice_validator/app_tests/test_app_config.py rename to ice_validator/app_tests/test_config.py index a021b53..a41cfbf 100644 --- a/ice_validator/app_tests/test_app_config.py +++ b/ice_validator/app_tests/test_config.py @@ -34,19 +34,23 @@ # limitations under the License. # # ============LICENSE_END============================================ -# -# +import uuid from io import StringIO import pytest import yaml +from config import Config, get_generator_plugin_names, to_uri import vvp + DEFAULT_CONFIG = """ +namespace: {namespace} +owner: onap-test ui: app-name: VNF Validation Tool + requirement-link-url: http://requirement.url.com categories: - name: Environment File Compliance. (Required to Onboard) category: environment_file @@ -55,14 +59,23 @@ categories: Required for ASDC onboarding, not needed for manual Openstack testing. settings: polling-freqency: 1000 - default-verbosity: Standard + env-specs: + - tests.test_environment_file_parameters.ENV_PARAMETER_SPEC +terms: + version: 1.0.0 + path: path/to/terms.txt + popup-title: Terms and Conditions + popup-link-text: View Terms and Conditions + popup-msg-text: Review and Accept the Terms """ # noinspection PyShadowingNames -@pytest.fixture(scope="module") +@pytest.fixture() def config(): - return vvp.Config(yaml.safe_load(StringIO(DEFAULT_CONFIG))) + unique = str(uuid.uuid4()) + data = DEFAULT_CONFIG.format(namespace=unique) + return Config(yaml.safe_load(StringIO(data))) def test_app_name(config): @@ -87,10 +100,6 @@ def test_get_category_when_other(config): ) -def test_default_verbosity(config): - assert config.default_verbosity(vvp.ValidatorApp.VERBOSITY_LEVELS) == "Standard (-v)" - - def test_queues(config): assert config.log_queue.empty(), "Log should start empty" config.log_file.write("Test") @@ -102,6 +111,8 @@ def test_queues(config): MISSING_CATEGORY_FIELD = """ +namespace: org.onap.test +owner: onap-test ui: app-name: VNF Validation Tool categories: @@ -116,7 +127,7 @@ settings: def test_missing_category_fields(): settings = yaml.safe_load(StringIO(MISSING_CATEGORY_FIELD)) with pytest.raises(RuntimeError) as e: - vvp.Config(settings) + Config(settings) assert "Missing: name" in str(e) @@ -140,3 +151,119 @@ def test_default_input_format(config): def test_input_formats(config): assert "Directory (Uncompressed)" in config.input_formats assert "ZIP File" in config.input_formats + + +def test_env_specs(config): + specs = config.env_specs + assert len(specs) == 1 + assert "ALL" in specs[0] + + +def test_get_generator_plugin_names(config): + names = get_generator_plugin_names() + assert "VNF-API" in names + assert "GR-API" in names + + +def test_preload_formats(config): + formats = config.preload_formats + assert all(format in formats for format in ("VNF-API", "GR-API")) + + +def test_requirement_link_http(config): + assert config.requirement_link_url == "http://requirement.url.com" + + +def test_to_uri_relative_path(): + assert to_uri("path/").startswith("file://") + assert to_uri("path/").endswith("/path") + + +def test_to_uri_relative_http(): + assert to_uri("http://url.com") == "http://url.com" + + +def test_to_uri_absolute_path(): + assert to_uri("/path/one").startswith("file:///") + assert to_uri("/path/one").endswith("/path/one") + + +def test_requirement_link_path(config): + config._config["ui"]["requirement-link-url"] = "path/to/reqs.txt" + url = config.requirement_link_url + assert url.startswith("file://") + assert "path/to/reqs.txt" in url + + +def test_terms_version(config): + assert config.terms_version == "1.0.0" + + +def test_terms_popup_title(config): + assert config.terms_popup_title == "Terms and Conditions" + + +def test_terms_popup_message(config): + assert config.terms_popup_message == "Review and Accept the Terms" + + +def test_terms_link_url_default(config): + config._config["terms"]["path"] = None + assert config.terms_link_url is None + + +def test_terms_acceptance(config): + assert not config.are_terms_accepted + config.set_terms_accepted() + assert config.are_terms_accepted + + +def test_terms_link_url_path(config): + assert config.terms_link_url.startswith("file://") + assert config.terms_link_url.endswith("/path/to/terms.txt") + + +def test_terms_link_text(config): + assert config.terms_link_text == "View Terms and Conditions" + + +def test_default_halt_on_failure(config): + assert config.default_halt_on_failure + + +def test_get_subdir_for_preload(config): + assert config.get_subdir_for_preload("VNF-API") == "vnfapi" + + +def test_default_preload_format(config): + assert config.default_preload_format in ("VNF-API", "GR-API", "Excel") + + +def test_category_description(config): + assert "Checks certain parameters" in config.get_description( + "Environment File Compliance. (Required to Onboard)" + ) + + +def test_get_category_by_name(config): + assert ( + config.get_category("Environment File Compliance. (Required to Onboard)") + == "environment_file" + ) + + +def test_cached_category_setting(config): + assert ( + config.get_category_value("Environment File Compliance. (Required to Onboard)") + == 0 + ) + + +def test_disclaimer_text(config): + assert config.disclaimer_text == "" + + +def test_requirement_link_text(config): + url_text = "Requirement URL" + config._config["ui"]["requirement-link-text"] = url_text + assert config.requirement_link_text == url_text diff --git a/ice_validator/app_tests/test_data.zip b/ice_validator/app_tests/test_data.zip new file mode 100644 index 0000000000000000000000000000000000000000..27871596c58301fe22d23037f5305ced8c5be9fa GIT binary patch literal 125 zcmWIWW@Zs#0D*uBiGBvl+_SiWY!K!E;*`XaM7@%VlH$bNf}B(Z5I4Y^kx7IBw-%r- c21W)25CsG<0|LBR*+6_oAT$Hgh9C|D0G(wMp#T5? literal 0 HcmV?d00001 diff --git a/ice_validator/app_tests/test_helpers.py b/ice_validator/app_tests/test_helpers.py new file mode 100644 index 0000000..d90374a --- /dev/null +++ b/ice_validator/app_tests/test_helpers.py @@ -0,0 +1,88 @@ +# -*- 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============================================ +from pathlib import Path + +import pytest + +from tests.helpers import check, first, unzip, remove + +THIS_DIR = Path(__file__).parent + + +def test_check_fail(): + with pytest.raises(RuntimeError, match="pre-condition failed"): + check(False, "pre-condition failed") + + +def test_check_pass(): + check(True, "pre-condition failed") + + +def test_first_found(): + result = first(range(1, 10), lambda x: x % 4 == 0) + assert result == 4 + + +def test_first_not_found(): + result = first(range(1, 3), lambda x: x % 4 == 0) + assert result is None + + +def test_first_custom_default(): + result = first(range(1, 3), lambda x: x % 4 == 0, default="not found") + assert result == "not found" + + +def test_unzip_success(tmpdir): + test_zip = THIS_DIR / "test_data.zip" + target_dir = tmpdir.join("sub-dir") + unzip(test_zip, target_dir) + assert "data.txt" in (p.basename for p in target_dir.listdir()) + + +def test_unzip_not_found(tmpdir): + test_zip = THIS_DIR / "test_data1.zip" + with pytest.raises(RuntimeError, match="not a valid zipfile"): + unzip(test_zip, tmpdir) + + +def test_remove_with_no_key(): + assert remove([1, 2, 3, 4], [3]) == [1, 2, 4] + + +def test_remove_with_key(): + assert remove(["a", "b", "c", "d"], ["A"], lambda s: s.upper()) == ["b", "c", "d"] diff --git a/ice_validator/app_tests/vvp-config.yaml b/ice_validator/app_tests/vvp-config.yaml index f80cb6f..512d82d 100644 --- a/ice_validator/app_tests/vvp-config.yaml +++ b/ice_validator/app_tests/vvp-config.yaml @@ -36,7 +36,8 @@ # ============LICENSE_END============================================ # # - +namespace: org.onap.test +owner: onap-test ui: app-name: VNF Validation Tool categories: @@ -48,3 +49,5 @@ categories: settings: polling-freqency: 1000 default-verbosity: Standard + env-specs: + - tests.test_environment_file_parameters.ENV_PARAMETER_SPEC diff --git a/ice_validator/config.py b/ice_validator/config.py new file mode 100644 index 0000000..5ac1cf5 --- /dev/null +++ b/ice_validator/config.py @@ -0,0 +1,355 @@ +import importlib +import inspect +import multiprocessing +import os +import pkgutil +import queue +from configparser import ConfigParser +from itertools import chain +from pathlib import Path +from typing import MutableMapping, Iterator, List, Optional, Dict + +import appdirs +import yaml +from cached_property import cached_property + +from version import VERSION +from preload.generator import AbstractPreloadGenerator +from tests.test_environment_file_parameters import ENV_PARAMETER_SPEC + +PATH = os.path.dirname(os.path.realpath(__file__)) +PROTOCOLS = ("http:", "https:", "file:") + + +def to_uri(path): + if any(path.startswith(p) for p in PROTOCOLS): + return path + return Path(path).absolute().as_uri() + + +class UserSettings(MutableMapping): + FILE_NAME = "UserSettings.ini" + + def __init__(self, namespace, owner): + user_config_dir = appdirs.AppDirs(namespace, owner).user_config_dir + if not os.path.exists(user_config_dir): + os.makedirs(user_config_dir, exist_ok=True) + self._settings_path = os.path.join(user_config_dir, self.FILE_NAME) + self._config = ConfigParser() + self._config.read(self._settings_path) + + def __getitem__(self, k): + return self._config["DEFAULT"][k] + + def __setitem__(self, k, v) -> None: + self._config["DEFAULT"][k] = v + + def __delitem__(self, v) -> None: + del self._config["DEFAULT"][v] + + def __len__(self) -> int: + return len(self._config["DEFAULT"]) + + def __iter__(self) -> Iterator: + return iter(self._config["DEFAULT"]) + + def save(self): + with open(self._settings_path, "w") as f: + self._config.write(f) + + +class Config: + """ + Configuration for the Validation GUI Application + + Attributes + ---------- + ``log_queue`` Queue for the ``stdout`` and ``stderr` of + the background job + ``log_file`` File-like object (write only!) that writes to + the ``log_queue`` + ``status_queue`` Job completion status of the background job is + posted here as a tuple of (bool, Exception). + The first parameter is True if the job completed + successfully, and False otherwise. If the job + failed, then an Exception will be provided as the + second element. + ``command_queue`` Used to send commands to the GUI. Currently only + used to send shutdown commands in tests. + """ + + DEFAULT_FILENAME = "vvp-config.yaml" + DEFAULT_POLLING_FREQUENCY = "1000" + + def __init__(self, config: dict = None): + """Creates instance of application configuration. + + :param config: override default configuration if provided.""" + if config: + self._config = config + else: + with open(self.DEFAULT_FILENAME, "r") as f: + self._config = yaml.safe_load(f) + self._user_settings = UserSettings( + self._config["namespace"], self._config["owner"] + ) + self._watched_variables = [] + self._validate() + + @cached_property + def manager(self): + return multiprocessing.Manager() + + @cached_property + def log_queue(self): + return self.manager.Queue() + + @cached_property + def status_queue(self): + return self.manager.Queue() + + @cached_property + def log_file(self): + return QueueWriter(self.log_queue) + + @cached_property + def command_queue(self): + return self.manager.Queue() + + def watch(self, *variables): + """Traces the variables and saves their settings for the user. The + last settings will be used where available""" + self._watched_variables = variables + for var in self._watched_variables: + var.trace_add("write", self.save_settings) + + # noinspection PyProtectedMember,PyUnusedLocal + def save_settings(self, *args): + """Save the value of all watched variables to user settings""" + for var in self._watched_variables: + self._user_settings[var._name] = str(var.get()) + self._user_settings.save() + + @property + def app_name(self) -> str: + """Name of the application (displayed in title bar)""" + app_name = self._config["ui"].get("app-name", "VNF Validation Tool") + return "{} - {}".format(app_name, VERSION) + + @property + def category_names(self) -> List[str]: + """List of validation profile names for display in the UI""" + return [category["name"] for category in self._config["categories"]] + + @property + def polling_frequency(self) -> int: + """Returns the frequency (in ms) the UI polls the queue communicating + with any background job""" + return int( + self._config["settings"].get( + "polling-frequency", self.DEFAULT_POLLING_FREQUENCY + ) + ) + + @property + def disclaimer_text(self) -> str: + return self._config["ui"].get("disclaimer-text", "") + + @property + def requirement_link_text(self) -> str: + return self._config["ui"].get("requirement-link-text", "") + + @property + def requirement_link_url(self) -> str: + path = self._config["ui"].get("requirement-link-url", "") + return to_uri(path) + + @property + def terms(self) -> dict: + return self._config.get("terms", {}) + + @property + def terms_link_url(self) -> Optional[str]: + path = self.terms.get("path") + return to_uri(path) if path else None + + @property + def terms_link_text(self): + return self.terms.get("popup-link-text") + + @property + def terms_version(self) -> Optional[str]: + return self.terms.get("version") + + @property + def terms_popup_title(self) -> Optional[str]: + return self.terms.get("popup-title") + + @property + def terms_popup_message(self) -> Optional[str]: + return self.terms.get("popup-msg-text") + + @property + def are_terms_accepted(self) -> bool: + version = "terms-{}".format(self.terms_version) + return self._user_settings.get(version, "False") == "True" + + def set_terms_accepted(self): + version = "terms-{}".format(self.terms_version) + self._user_settings[version] = "True" + self._user_settings.save() + + def get_description(self, category_name: str) -> str: + """Returns the description associated with the category name""" + return self._get_category(category_name)["description"] + + def get_category(self, category_name: str) -> str: + """Returns the category associated with the category name""" + return self._get_category(category_name).get("category", "") + + def get_category_value(self, category_name: str) -> str: + """Returns the saved value for a category name""" + return self._user_settings.get(category_name, 0) + + def _get_category(self, category_name: str) -> Dict[str, str]: + """Returns the profile definition""" + for category in self._config["categories"]: + if category["name"] == category_name: + return category + raise RuntimeError( + "Unexpected error: No category found in vvp-config.yaml " + "with a name of " + category_name + ) + + @property + def default_report_format(self): + return self._user_settings.get("report_format", "HTML") + + @property + def report_formats(self): + return ["CSV", "Excel", "HTML"] + + @property + def preload_formats(self): + excluded = self._config.get("excluded-preloads", []) + formats = (cls.format_name() for cls in get_generator_plugins()) + return [f for f in formats if f not in excluded] + + @property + def default_preload_format(self): + default = self._user_settings.get("preload_format") + if default and default in self.preload_formats: + return default + else: + return self.preload_formats[0] + + @staticmethod + def get_subdir_for_preload(preload_format): + for gen in get_generator_plugins(): + if gen.format_name() == preload_format: + return gen.output_sub_dir() + return "" + + @property + def default_input_format(self): + requested_default = self._user_settings.get("input_format") or self._config[ + "settings" + ].get("default-input-format") + if requested_default in self.input_formats: + return requested_default + else: + return self.input_formats[0] + + @property + def input_formats(self): + return ["Directory (Uncompressed)", "ZIP File"] + + @property + def default_halt_on_failure(self): + setting = self._user_settings.get("halt_on_failure", "True") + return setting.lower() == "true" + + @property + def env_specs(self): + env_specs = self._config["settings"].get("env-specs") + specs = [] + if not env_specs: + return [ENV_PARAMETER_SPEC] + for mod_path, attr in (s.rsplit(".", 1) for s in env_specs): + module = importlib.import_module(mod_path) + specs.append(getattr(module, attr)) + return specs + + def _validate(self): + """Ensures the config file is properly formatted""" + categories = self._config["categories"] + + # All profiles have required keys + expected_keys = {"name", "description"} + for category in categories: + actual_keys = set(category.keys()) + missing_keys = expected_keys.difference(actual_keys) + if missing_keys: + raise RuntimeError( + "Error in vvp-config.yaml file: " + "Required field missing in category. " + "Missing: {} " + "Categories: {}".format(",".join(missing_keys), category) + ) + + +class QueueWriter: + """``stdout`` and ``stderr`` will be written to this queue by pytest, and + pulled into the main GUI application""" + + def __init__(self, log_queue: queue.Queue): + """Writes data to the provided queue. + + :param log_queue: the queue instance to write to. + """ + self.queue = log_queue + + def write(self, data: str): + """Writes ``data`` to the queue """ + self.queue.put(data) + + # noinspection PyMethodMayBeStatic + def isatty(self) -> bool: + """Always returns ``False``""" + return False + + def flush(self): + """No operation method to satisfy file-like behavior""" + pass + + +def is_preload_generator(class_): + """ + Returns True if the class is an implementation of AbstractPreloadGenerator + """ + return ( + inspect.isclass(class_) + and not inspect.isabstract(class_) + and issubclass(class_, AbstractPreloadGenerator) + ) + + +def get_generator_plugins(): + """ + Scan the system path for modules that are preload plugins and discover + and return the classes that implement AbstractPreloadGenerator in those + modules + """ + preload_plugins = ( + importlib.import_module(name) + for finder, name, ispkg in pkgutil.iter_modules() + if name.startswith("preload_") + ) + members = chain.from_iterable( + inspect.getmembers(mod, is_preload_generator) for mod in preload_plugins + ) + return [m[1] for m in members] + + +def get_generator_plugin_names(): + return [g.format_name() for g in get_generator_plugins()] diff --git a/ice_validator/preload/__init__.py b/ice_validator/preload/__init__.py new file mode 100644 index 0000000..70f9ecb --- /dev/null +++ b/ice_validator/preload/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START==================================================== +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2019 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ============LICENSE_END============================================ diff --git a/ice_validator/preload/environment.py b/ice_validator/preload/environment.py new file mode 100644 index 0000000..c0f357a --- /dev/null +++ b/ice_validator/preload/environment.py @@ -0,0 +1,267 @@ +import re +import tempfile +from pathlib import Path + +from cached_property import cached_property + +from tests.helpers import check, first, unzip, load_yaml + +SERVICE_TEMPLATE_PATTERN = re.compile(r".*service-.*?-template.yml") +RESOURCE_TEMPLATE_PATTERN = re.compile(r".*resource-(.*?)-template.yml") + + +def yaml_files(path): + """ + Return files that are YAML (end with .yml or .yaml) + + :param path: Directory path object + :return: list of paths to YAML files + """ + return [ + p + for p in path.iterdir() + if p.is_file() and p.suffix.lower() in (".yml", ".yaml") + ] + + +class CloudServiceArchive: + """ + Wrapper to extract information from a CSAR file. + """ + + def __init__(self, csar_path): + self.csar_path = Path(csar_path) + with tempfile.TemporaryDirectory() as csar_dir: + csar_dir = Path(csar_dir) + unzip(self.csar_path, csar_dir) + self._service = self._get_service_template(csar_dir) + self._resources = self._get_vf_module_resource_templates(csar_dir) + + def get_vf_module(self, vf_module): + """ + Retrieve the VF Module definition from the CSAR for the given heat + module name (should not include the file extension - ex: base) + + :param vf_module: name of Heat module (no path or file extension) + :return: The definition of the module as a dict or None if not found + """ + groups = self._service.get("topology_template", {}).get("groups", {}) + for props in groups.values(): + module_label = props.get("properties", {}).get("vf_module_label", "") + if module_label.lower() == vf_module.lower(): + return props + return None + + def get_vf_module_model_name(self, vf_module): + """ + Retrieves the vfModuleModelName of the module or None if vf_module is not + found (see get_vf_module) + + :param vf_module: name of Heat module (no path or file extension) + :return: The value if vfModuleModelName as string or None if not found + """ + module = self.get_vf_module(vf_module) + return module.get("metadata", {}).get("vfModuleModelName") if module else None + + @property + def topology_template(self): + """ + Return dict representing the topology_template node of the service + template + """ + return self._service.get("topology_template") or {} + + @property + def groups(self): + """ + Return dict representing the groups node of the service + template + """ + return self.topology_template.get("groups") or {} + + @property + def vf_modules(self): + """ + Returns mapping of group ID to VfModule present in the service template + """ + return { + group_id: props + for group_id, props in self.groups.items() + if props.get("type") == "org.openecomp.groups.VfModule" + } + + @property + def vf_module_resource_names(self): + """ + Returns the resource names for all VfModules (these can be used + to find the resource templates as they will be part of the filename) + """ + names = ( + module.get("metadata", {}).get("vfModuleModelName") + for module in self.vf_modules.values() + ) + return [name.split(".")[0] for name in names if name] + + def get_vf_module_resource_name(self, vf_module): + """ + Retrieves the resource name of the module or None if vf_module is not + found (see get_vf_module) + + :param vf_module: name of Heat module (no path or file extension) + :return: The value if resource nae as string or None if not found + """ + vf_model_name = self.get_vf_module_model_name(vf_module) + if not vf_model_name: + return None + resource_name = vf_model_name.split(".")[0] + resource = self._resources.get(resource_name, {}) + return resource.get("metadata", {}).get("name") + + @staticmethod + def _get_definition_files(csar_dir): + """ + Returns a list of all files in the CSAR's Definitions directory + """ + def_dir = csar_dir / "Definitions" + check( + def_dir.exists(), + f"CSAR is invalid. {csar_dir.as_posix()} does not contain a " + f"Definitions directory.", + ) + return yaml_files(def_dir) + + def _get_service_template(self, csar_dir): + """ + Returns the service template as a dict. Assumes there is only one. + """ + files = map(str, self._get_definition_files(csar_dir)) + service_template = first(files, SERVICE_TEMPLATE_PATTERN.match) + return load_yaml(service_template) if service_template else {} + + def _get_vf_module_resource_templates(self, csar_dir): + """ + Returns a mapping of resource name to resource definition (as a dict) + (Only loads resource templates that correspond to VF Modules + """ + def_dir = csar_dir / "Definitions" + mapping = ( + (name, def_dir / "resource-{}-template.yml".format(name)) + for name in self.vf_module_resource_names + ) + return {name: load_yaml(path) for name, path in mapping if path.exists()} + + @property + def service_name(self): + """ + Name of the service (extracted from the service template + """ + return self._service.get("metadata", {}).get("name") + + def __repr__(self): + return f"CSAR (path={self.csar_path.name}, name={self.service_name})" + + def __str__(self): + return repr(self) + + +class PreloadEnvironment: + """ + A + """ + + def __init__(self, env_dir, parent=None): + self.base_dir = Path(env_dir) + self.parent = parent + self._modules = self._load_modules() + self._sub_env = self._load_envs() + self._defaults = self._load_defaults() + + def _load_defaults(self): + defaults = self.base_dir / "defaults.yaml" + return load_yaml(defaults) if defaults.exists() else {} + + def _load_modules(self): + files = [ + p + for p in self.base_dir.iterdir() + if p.is_file() and p.suffix.lower().endswith(".env") + ] + return {f.name.lower(): load_yaml(f).get("parameters", {}) for f in files} + + def _load_envs(self): + env_dirs = [ + p for p in self.base_dir.iterdir() if p.is_dir() and p.name != "preloads" + ] + return {d.name: PreloadEnvironment(d, self) for d in env_dirs} + + @cached_property + def csar(self): + csar_path = first(self.base_dir.iterdir(), lambda p: p.suffix == ".csar") + if csar_path: + return CloudServiceArchive(csar_path) + else: + return self.parent.csar if self.parent else None + + @property + def defaults(self): + result = {} + if self.parent: + result.update(self.parent.defaults) + result.update(self._defaults) + return result + + @property + def environments(self): + all_envs = [self] + for env in self._sub_env.values(): + all_envs.append(env) + all_envs.extend(env.environments) + return [e for e in all_envs if e.is_leaf] + + def get_module(self, name): + name = name if name.lower().endswith(".env") else f"{name}.env".lower() + if name not in self.module_names: + return {} + result = {} + parent_module = self.parent.get_module(name) if self.parent else None + module = self._modules.get(name) + for m in (parent_module, self.defaults, module): + if m: + result.update(m) + return result + + @property + def module_names(self): + parent_modules = self.parent.module_names if self.parent else set() + result = set() + result.update(self._modules.keys()) + result.update(parent_modules) + return result + + @property + def modules(self): + return {name: self.get_module(name) for name in self.module_names} + + def get_environment(self, env_name): + for name, env in self._sub_env.items(): + if name == env_name: + return env + result = env.get_environment(env_name) + if result: + return result + return None + + @property + def is_base(self): + return self.parent is None + + @property + def is_leaf(self): + return not self._sub_env + + @property + def name(self): + return self.base_dir.name + + def __repr__(self): + return f"PreloadEnvironment(name={self.name})" diff --git a/ice_validator/preload/generator.py b/ice_validator/preload/generator.py new file mode 100644 index 0000000..38a051d --- /dev/null +++ b/ice_validator/preload/generator.py @@ -0,0 +1,242 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START==================================================== +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2019 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ============LICENSE_END============================================ + +import json +import os +from abc import ABC, abstractmethod + +import yaml + + +def get_json_template(template_dir, template_name): + template_name = template_name + ".json" + with open(os.path.join(template_dir, template_name)) as f: + return json.loads(f.read()) + + +def get_or_create_template(template_dir, key, value, sequence, template_name): + """ + Search a sequence of dicts where a given key matches value. If + found, then it returns that item. If not, then it loads the + template identified by template_name, adds it ot the sequence, and + returns the template + """ + for item in sequence: + if item[key] == value: + return item + new_template = get_json_template(template_dir, template_name) + sequence.append(new_template) + return new_template + + +def yield_by_count(sequence): + """ + Iterates through sequence and yields each item according to its __count__ + attribute. If an item has a __count__ of it will be returned 3 times + before advancing to the next item in the sequence. + + :param sequence: sequence of dicts (must contain __count__) + :returns: generator of tuple key, value pairs + """ + for key, value in sequence.items(): + for i in range(value["__count__"]): + yield (key, value) + + +def replace(param): + """ + Optionally used by the preload generator to wrap items in the preload + that need to be replaced by end users + :param param: p + """ + return "VALUE FOR: {}".format(param) if param else "" + + +class AbstractPreloadGenerator(ABC): + """ + All preload generators must inherit from this class and implement the + abstract methods. + + Preload generators are automatically discovered at runtime via a plugin + architecture. The system path is scanned looking for modules with the name + preload_*, then all non-abstract classes that inherit from AbstractPreloadGenerator + are registered as preload plugins + + Attributes: + :param vnf: Instance of Vnf that contains the preload data + :param base_output_dir: Base directory to house the preloads. All preloads + must be written to a subdirectory under this directory + """ + + def __init__(self, vnf, base_output_dir, preload_env): + self.preload_env = preload_env + self.vnf = vnf + self.current_module = None + self.current_module_env = {} + self.base_output_dir = base_output_dir + self.env_cache = {} + + @classmethod + @abstractmethod + def format_name(cls): + """ + String name to identify the format (ex: VN-API, GR-API) + """ + raise NotImplementedError() + + @classmethod + @abstractmethod + def output_sub_dir(cls): + """ + String sub-directory name that will appear under ``base_output_dir`` + """ + raise NotImplementedError() + + @classmethod + @abstractmethod + def supports_output_passing(cls): + """ + Some preload methods allow automatically mapping output parameters in the + base module to the input parameter of other modules. This means these + that the incremental modules do not need these base module outputs in their + preloads. + + At this time, VNF-API does not support output parameter passing, but + GR-API does. + + If this is true, then the generator will call Vnf#filter_output_params + after the preload module for the base module has been created + """ + raise NotImplementedError() + + @abstractmethod + def generate_module(self, module, output_dir): + """ + Create the preloads and write them to ``output_dir``. This + method is responsible for generating the content of the preload and + writing the file to disk. + """ + raise NotImplementedError() + + def generate(self): + # handle the base module first + print("\nGenerating {} preloads".format(self.format_name())) + self.generate_environments(self.vnf.base_module) + if self.supports_output_passing(): + self.vnf.filter_base_outputs() + for mod in self.vnf.incremental_modules: + self.generate_environments(mod) + + def replace(self, param_name, alt_message=None, single=False): + value = self.get_param(param_name, single) + if value: + return value + return alt_message or replace(param_name) + + def generate_environments(self, module): + """ + Generate a preload for the given module in all available environments + in the ``self.preload_env``. This will invoke the abstract + generate_module once for each available environment **and** an + empty environment to create a blank template. + + :param module: module to generate for + """ + print("\nGenerating Preloads for {}".format(module)) + print("-" * 50) + print("... generating blank template") + self.current_module = module + self.current_module_env = {} + self.env_cache = {} + blank_preload_dir = self.make_preload_dir(self.base_output_dir) + self.generate_module(module, blank_preload_dir) + self.generate_preload_env(module, blank_preload_dir) + if self.preload_env: + for env in self.preload_env.environments: + output_dir = self.make_preload_dir(env.base_dir / "preloads") + print( + "... generating preload for env ({}) to {}".format( + env.name, output_dir + ) + ) + self.env_cache = {} + self.current_module = module + self.current_module_env = env.get_module(module.label) + self.generate_module(module, output_dir) + self.current_module = None + self.current_module_env = None + + def make_preload_dir(self, base_dir): + path = os.path.join(base_dir, self.output_sub_dir()) + if not os.path.exists(path): + os.makedirs(path, exist_ok=True) + return path + + def generate_preload_env(self, module, blank_preload_dir): + """ + Create a .env template suitable for completing and using for + preload generation from env files. + """ + output_dir = os.path.join(blank_preload_dir, "preload_env") + output_file = os.path.join(output_dir, "{}.env".format(module.vnf_name)) + if not os.path.exists(output_dir): + os.makedirs(output_dir, exist_ok=True) + with open(output_file, "w") as f: + yaml.dump(module.env_template, f) + + def get_param(self, param_name, single): + """ + Retrieves the value for the given param if it exists. If requesting a + single item, and the parameter is tied to a list then only one item from + the list will be returned. For each subsequent call with the same parameter + it will iterate/rotate through the values in that list. If single is False + then the full list will be returned. + + :param param_name: name of the parameter + :param single: If True returns single value from lists otherwises the full + list. This has no effect on non-list values + """ + value = self.env_cache.get(param_name) + if not value: + value = self.current_module_env.get(param_name) + if isinstance(value, list): + value.reverse() + self.env_cache[param_name] = value + if value and single and isinstance(value, list): + return value.pop() + else: + return value diff --git a/ice_validator/preload.py b/ice_validator/preload/model.py similarity index 70% rename from ice_validator/preload.py rename to ice_validator/preload/model.py index 8f3e0d5..e37c914 100644 --- a/ice_validator/preload.py +++ b/ice_validator/preload/model.py @@ -34,28 +34,28 @@ # limitations under the License. # # ============LICENSE_END============================================ -import importlib -import inspect -import json import os -import pkgutil import shutil from abc import ABC, abstractmethod -from itertools import chain -from typing import Set +from preload.generator import yield_by_count +from preload.environment import PreloadEnvironment from tests.helpers import ( get_param, get_environment_pair, prop_iterator, get_output_dir, is_base_module, + remove, ) from tests.parametrizers import parametrize_heat_templates from tests.structures import NeutronPortProcessor, Heat from tests.test_environment_file_parameters import get_preload_excluded_parameters from tests.utils import nested_dict from tests.utils.vm_types import get_vm_type_for_nova_server +from config import Config, get_generator_plugins + +CHANGE = "CHANGEME" # This is only used to fake out parametrizers @@ -84,129 +84,6 @@ def get_heat_templates(config): return heat_templates -def get_json_template(template_dir, template_name): - template_name = template_name + ".json" - with open(os.path.join(template_dir, template_name)) as f: - return json.loads(f.read()) - - -def remove(sequence, exclude, key=None): - """ - Remove a copy of sequence that items occur in exclude. - - :param sequence: sequence of objects - :param exclude: objects to excluded (must support ``in`` check) - :param key: optional function to extract key from item in sequence - :return: list of items not in the excluded - """ - key_func = key if key else lambda x: x - result = (s for s in sequence if key_func(s) not in exclude) - return set(result) if isinstance(sequence, Set) else list(result) - - -def get_or_create_template(template_dir, key, value, sequence, template_name): - """ - Search a sequence of dicts where a given key matches value. If - found, then it returns that item. If not, then it loads the - template identified by template_name, adds it ot the sequence, and - returns the template - """ - for item in sequence: - if item[key] == value: - return item - new_template = get_json_template(template_dir, template_name) - sequence.append(new_template) - return new_template - - -def replace(param): - """ - Optionally used by the preload generator to wrap items in the preload - that need to be replaced by end users - :param param: p - """ - return "VALUE FOR: {}".format(param) if param else "" - - -class AbstractPreloadGenerator(ABC): - """ - All preload generators must inherit from this class and implement the - abstract methods. - - Preload generators are automatically discovered at runtime via a plugin - architecture. The system path is scanned looking for modules with the name - preload_*, then all non-abstract classes that inherit from AbstractPreloadGenerator - are registered as preload plugins - - Attributes: - :param vnf: Instance of Vnf that contains the preload data - :param base_output_dir: Base directory to house the preloads. All preloads - must be written to a subdirectory under this directory - """ - - def __init__(self, vnf, base_output_dir): - self.vnf = vnf - self.base_output_dir = base_output_dir - os.makedirs(self.output_dir, exist_ok=True) - - @classmethod - @abstractmethod - def format_name(cls): - """ - String name to identify the format (ex: VN-API, GR-API) - """ - raise NotImplementedError() - - @classmethod - @abstractmethod - def output_sub_dir(cls): - """ - String sub-directory name that will appear under ``base_output_dir`` - """ - raise NotImplementedError() - - @classmethod - @abstractmethod - def supports_output_passing(cls): - """ - Some preload methods allow automatically mapping output parameters in the - base module to the input parameter of other modules. This means these - that the incremental modules do not need these base module outputs in their - preloads. - - At this time, VNF-API does not support output parameter passing, but - GR-API does. - - If this is true, then the generator will call Vnf#filter_output_params - after the preload module for the base module has been created - """ - raise NotImplementedError() - - @abstractmethod - def generate_module(self, module): - """ - Create the preloads and write them to ``self.output_dir``. This - method is responsible for generating the content of the preload and - writing the file to disk. - """ - raise NotImplementedError() - - @property - def output_dir(self): - return os.path.join(self.base_output_dir, self.output_sub_dir()) - - def generate(self): - # handle the base module first - print("\nGenerating {} preloads".format(self.format_name())) - self.generate_module(self.vnf.base_module) - print("... generated template for {}".format(self.vnf.base_module)) - if self.supports_output_passing(): - self.vnf.filter_base_outputs() - for mod in self.vnf.incremental_modules: - self.generate_module(mod) - print("... generated for {}".format(mod)) - - class FilterBaseOutputs(ABC): """ Invoked to remove parameters in an object that appear in the base module. @@ -364,20 +241,6 @@ class Vnf: mod.filter_output_params(self.base_output_params) -def yield_by_count(sequence): - """ - Iterates through sequence and yields each item according to its __count__ - attribute. If an item has a __count__ of it will be returned 3 times - before advancing to the next item in the sequence. - - :param sequence: sequence of dicts (must contain __count__) - :returns: generator of tuple key, value pairs - """ - for key, value in sequence.items(): - for i in range(value["__count__"]): - yield (key, value) - - def env_path(heat_path): """ Create the path to the env file for the give heat path. @@ -451,6 +314,49 @@ class VnfModule(FilterBaseOutputs): p for p in self.heat.parameters if p.startswith("availability_zone") ) + @property + def label(self): + """ + Label for the VF module that will appear in the CSAR + """ + return self.vnf_name + + @property + def env_specs(self): + """Return available Environment Spec definitions""" + return Config().env_specs + + @property + def env_template(self): + """ + Returns a a template .env file that can be completed to enable + preload generation. + """ + params = {} + params["vnf-name"] = CHANGE + params["vnf-type"] = CHANGE + params["vf-module-model-name"] = CHANGE + params["vf_module_name"] = CHANGE + for network in self.networks: + params[network.name_param] = CHANGE + for param in set(network.subnet_params): + params[param] = CHANGE + for vm in self.virtual_machine_types: + for name in set(vm.names): + params[name] = CHANGE + for ip in vm.floating_ips: + params[ip.param] = CHANGE + for ip in vm.fixed_ips: + params[ip.param] = CHANGE + excluded = get_preload_excluded_parameters( + self.template_file, persistent_only=True + ) + for name, value in self.parameters.items(): + if name in excluded: + continue + params[name] = value + return {"parameters": params} + @property def preload_parameters(self): """ @@ -505,11 +411,18 @@ def create_preloads(config, exitstatus): preload_dir = os.path.join(get_output_dir(config), "preloads") if os.path.exists(preload_dir): shutil.rmtree(preload_dir) + env_directory = config.getoption("env_dir") + preload_env = PreloadEnvironment(env_directory) if env_directory else None + plugins = get_generator_plugins() + available_formats = [p.format_name() for p in plugins] + selected_formats = config.getoption("preload_formats") or available_formats heat_templates = get_heat_templates(config) vnf = None - for gen_class in get_generator_plugins(): + for plugin_class in plugins: + if plugin_class.format_name() not in selected_formats: + continue vnf = Vnf(heat_templates) - generator = gen_class(vnf, preload_dir) + generator = plugin_class(vnf, preload_dir, preload_env) generator.generate() if vnf and vnf.uses_contrail: print( @@ -522,31 +435,3 @@ def create_preloads(config, exitstatus): "\nWARNING: Heat violations detected. Preload templates may be\n" "incomplete." ) - - -def is_preload_generator(class_): - """ - Returns True if the class is an implementation of AbstractPreloadGenerator - """ - return ( - inspect.isclass(class_) - and not inspect.isabstract(class_) - and issubclass(class_, AbstractPreloadGenerator) - ) - - -def get_generator_plugins(): - """ - Scan the system path for modules that are preload plugins and discover - and return the classes that implement AbstractPreloadGenerator in those - modules - """ - preload_plugins = ( - importlib.import_module(name) - for finder, name, ispkg in pkgutil.iter_modules() - if name.startswith("preload_") - ) - members = chain.from_iterable( - inspect.getmembers(mod, is_preload_generator) for mod in preload_plugins - ) - return [m[1] for m in members] diff --git a/ice_validator/preload_grapi/grapi_generator.py b/ice_validator/preload_grapi/grapi_generator.py index bc338c3..2060cb4 100644 --- a/ice_validator/preload_grapi/grapi_generator.py +++ b/ice_validator/preload_grapi/grapi_generator.py @@ -37,11 +37,10 @@ import json import os -from preload import ( - AbstractPreloadGenerator, - get_or_create_template, +from preload.generator import ( get_json_template, - replace, + get_or_create_template, + AbstractPreloadGenerator, ) THIS_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -58,30 +57,6 @@ def get_or_create_network_template(network, vm_networks): ) -def add_fixed_ips(network_template, fixed_ips, uses_dhcp): - items = network_template["network-information-items"]["network-information-item"] - ipv4s = next(item for item in items if item["ip-version"] == "4") - ipv6s = next(item for item in items if item["ip-version"] == "6") - if uses_dhcp: - ipv4s["use-dhcp"] = "Y" - ipv6s["use-dhcp"] = "Y" - for ip in fixed_ips: - target = ipv4s if ip.ip_version == 4 else ipv6s - ips = target["network-ips"]["network-ip"] - if ip.param not in ips: - ips.append(replace(ip.param)) - target["ip-count"] += 1 - - -def add_floating_ips(network_template, floating_ips): - for ip in floating_ips: - key = "floating-ip-v4" if ip.ip_version == 4 else "floating-ip-v6" - ips = network_template["floating-ips"][key] - value = replace(ip.param) - if value not in ips: - ips.append(value) - - class GrApiPreloadGenerator(AbstractPreloadGenerator): @classmethod def supports_output_passing(cls): @@ -95,14 +70,38 @@ class GrApiPreloadGenerator(AbstractPreloadGenerator): def output_sub_dir(cls): return "grapi" - def generate_module(self, vnf_module): + def generate_module(self, vnf_module, output_dir): template = get_json_template(DATA_DIR, "preload_template") self._populate(template, vnf_module) vnf_name = vnf_module.vnf_name - outfile = "{}/{}.json".format(self.output_dir, vnf_name) + outfile = "{}/{}.json".format(output_dir, vnf_name) with open(outfile, "w") as f: json.dump(template, f, indent=4) + def add_floating_ips(self, network_template, floating_ips): + for ip in floating_ips: + key = "floating-ip-v4" if ip.ip_version == 4 else "floating-ip-v6" + ips = network_template["floating-ips"][key] + value = self.replace(ip.param, single=True) + if value not in ips: + ips.append(value) + + def add_fixed_ips(self, network_template, fixed_ips, uses_dhcp): + items = network_template["network-information-items"][ + "network-information-item" + ] + ipv4s = next(item for item in items if item["ip-version"] == "4") + ipv6s = next(item for item in items if item["ip-version"] == "6") + if uses_dhcp: + ipv4s["use-dhcp"] = "Y" + ipv6s["use-dhcp"] = "Y" + for ip in fixed_ips: + target = ipv4s if ip.ip_version == 4 else ipv6s + ips = target["network-ips"]["network-ip"] + if ip.param not in ips: + ips.append(self.replace(ip.param, single=True)) + target["ip-count"] += 1 + def _populate(self, preload, vnf_module): self._add_vnf_metadata(preload) self._add_vms(preload, vnf_module) @@ -110,8 +109,7 @@ class GrApiPreloadGenerator(AbstractPreloadGenerator): self._add_parameters(preload, vnf_module) self._add_vnf_networks(preload, vnf_module) - @staticmethod - def _add_vms(preload, vnf_module): + def _add_vms(self, preload, vnf_module): vms = preload["input"]["preload-vf-module-topology-information"][ "vf-module-topology" ]["vf-module-assignments"]["vms"]["vm"] @@ -119,58 +117,67 @@ class GrApiPreloadGenerator(AbstractPreloadGenerator): vm_template = get_json_template(DATA_DIR, "vm") vms.append(vm_template) vm_template["vm-type"] = vm.vm_type - vm_template["vm-names"]["vm-name"].extend(map(replace, vm.names)) + for name in vm.names: + value = self.replace(name, single=True) + vm_template["vm-names"]["vm-name"].append(value) vm_template["vm-count"] = vm.vm_count vm_networks = vm_template["vm-networks"]["vm-network"] for port in vm.ports: role = port.network.network_role network_template = get_or_create_network_template(role, vm_networks) network_template["network-role"] = role - add_fixed_ips(network_template, port.fixed_ips, port.uses_dhcp) - add_floating_ips(network_template, port.floating_ips) + self.add_fixed_ips(network_template, port.fixed_ips, port.uses_dhcp) + self.add_floating_ips(network_template, port.floating_ips) - @staticmethod - def _add_availability_zones(preload, vnf_module): + def _add_availability_zones(self, preload, vnf_module): zones = preload["input"]["preload-vf-module-topology-information"][ "vnf-resource-assignments" ]["availability-zones"]["availability-zone"] - zones.extend(map(replace, vnf_module.availability_zones)) + for zone in vnf_module.availability_zones: + value = self.replace(zone, single=True) + zones.append(value) - @staticmethod - def _add_parameters(preload, vnf_module): + def _add_parameters(self, preload, vnf_module): params = [ - {"name": key, "value": value} + {"name": key, "value": self.replace(key, value)} for key, value in vnf_module.preload_parameters.items() ] preload["input"]["preload-vf-module-topology-information"][ "vf-module-topology" ]["vf-module-parameters"]["param"].extend(params) - @staticmethod - def _add_vnf_networks(preload, vnf_module): + def _add_vnf_networks(self, preload, vnf_module): networks = preload["input"]["preload-vf-module-topology-information"][ "vnf-resource-assignments" ]["vnf-networks"]["vnf-network"] for network in vnf_module.networks: network_data = { "network-role": network.network_role, - "network-name": replace("network name of {}".format(network.name_param)), + "network-name": self.replace( + network.name_param, + "VALUE FOR: network name of {}".format(network.name_param), + ), } if network.subnet_params: network_data["subnets-data"] = {"subnet-data": []} subnet_data = network_data["subnets-data"]["subnet-data"] for subnet_param in network.subnet_params: - subnet_data.append({"subnet-id": replace(subnet_param)}) + subnet_data.append( + {"subnet-id": self.replace(subnet_param, single=True)} + ) networks.append(network_data) - @staticmethod - def _add_vnf_metadata(preload): + def _add_vnf_metadata(self, preload): topology = preload["input"]["preload-vf-module-topology-information"] vnf_meta = topology["vnf-topology-identifier-structure"] - vnf_meta["vnf-name"] = replace("vnf_name") - vnf_meta["vnf-type"] = replace("Concatenation of " - "/ " - "MUST MATCH SDC") + vnf_meta["vnf-name"] = self.replace("vnf_name") + vnf_meta["vnf-type"] = self.replace( + "vnf-type", + "VALUE FOR: Concatenation of /" + " MUST MATCH SDC", + ) module_meta = topology["vf-module-topology"]["vf-module-topology-identifier"] - module_meta["vf-module-name"] = replace("vf_module_name") - module_meta["vf-module-type"] = replace(" from CSAR or SDC") + module_meta["vf-module-name"] = self.replace("vf_module_name") + module_meta["vf-module-type"] = self.replace( + "vf-module-model-name", "VALUE FOR: from CSAR or SDC" + ) diff --git a/ice_validator/preload_vnfapi/vnfapi_generator.py b/ice_validator/preload_vnfapi/vnfapi_generator.py index bf4c61c..517c789 100644 --- a/ice_validator/preload_vnfapi/vnfapi_generator.py +++ b/ice_validator/preload_vnfapi/vnfapi_generator.py @@ -40,37 +40,16 @@ import json import os -from preload import ( - AbstractPreloadGenerator, +from preload.generator import ( get_json_template, get_or_create_template, - replace, + AbstractPreloadGenerator, ) THIS_DIR = os.path.dirname(os.path.abspath(__file__)) DATA_DIR = os.path.join(THIS_DIR, "vnfapi_data") -def add_fixed_ips(network_template, port): - for ip in port.fixed_ips: - if ip.ip_version == 4: - network_template["network-ips"].append({"ip-address": replace(ip.param)}) - network_template["ip-count"] += 1 - else: - network_template["network-ips-v6"].append({"ip-address": replace(ip.param)}) - network_template["ip-count-ipv6"] += 1 - - -def add_floating_ips(network_template, network): - # only one floating IP is really supported, in the preload model - # so for now we'll just use the last one. We might revisit this - # and if multiple floating params exist, then come up with an - # approach to pick just one - for ip in network.floating_ips: - key = "floating-ip" if ip.ip_version == 4 else "floating-ip-v6" - network_template[key] = replace(ip.param) - - def get_or_create_network_template(network_role, vm_networks): """ If the network role already exists in vm_networks, then @@ -94,37 +73,58 @@ class VnfApiPreloadGenerator(AbstractPreloadGenerator): def output_sub_dir(cls): return "vnfapi" - def generate_module(self, vnf_module): + def generate_module(self, vnf_module, output_dir): preload = get_json_template(DATA_DIR, "preload_template") self._populate(preload, vnf_module) - outfile = "{}/{}.json".format(self.output_dir, vnf_module.vnf_name) + outfile = "{}/{}.json".format(output_dir, vnf_module.vnf_name) with open(outfile, "w") as f: json.dump(preload, f, indent=4) + def add_floating_ips(self, network_template, network): + # only one floating IP is really supported, in the preload model + # so for now we'll just use the last one. We might revisit this + # and if multiple floating params exist, then come up with an + # approach to pick just one + for ip in network.floating_ips: + key = "floating-ip" if ip.ip_version == 4 else "floating-ip-v6" + network_template[key] = self.replace(ip.param, single=True) + + def add_fixed_ips(self, network_template, port): + for ip in port.fixed_ips: + if ip.ip_version == 4: + network_template["network-ips"].append( + {"ip-address": self.replace(ip.param, single=True)} + ) + network_template["ip-count"] += 1 + else: + network_template["network-ips-v6"].append( + {"ip-address": self.replace(ip.param, single=True)} + ) + network_template["ip-count-ipv6"] += 1 + def _populate(self, preload, vnf_module): self._add_availability_zones(preload, vnf_module) self._add_vnf_networks(preload, vnf_module) self._add_vms(preload, vnf_module) self._add_parameters(preload, vnf_module) - @staticmethod - def _add_availability_zones(preload, vnf_module): + def _add_availability_zones(self, preload, vnf_module): zones = preload["input"]["vnf-topology-information"]["vnf-assignments"][ "availability-zones" ] for zone in vnf_module.availability_zones: - zones.append({"availability-zone": replace(zone)}) + zones.append({"availability-zone": self.replace(zone, single=True)}) - @staticmethod - def _add_vnf_networks(preload, vnf_module): + def _add_vnf_networks(self, preload, vnf_module): networks = preload["input"]["vnf-topology-information"]["vnf-assignments"][ "vnf-networks" ] for network in vnf_module.networks: network_data = { "network-role": network.network_role, - "network-name": replace( - "network name for {}".format(network.name_param) + "network-name": self.replace( + network.name_param, + "VALUE FOR: network name for {}".format(network.name_param), ), } for subnet in network.subnet_params: @@ -132,8 +132,7 @@ class VnfApiPreloadGenerator(AbstractPreloadGenerator): network_data[key] = subnet networks.append(network_data) - @staticmethod - def _add_vms(preload, vnf_module): + def _add_vms(self, preload, vnf_module): vm_list = preload["input"]["vnf-topology-information"]["vnf-assignments"][ "vnf-vms" ] @@ -141,7 +140,9 @@ class VnfApiPreloadGenerator(AbstractPreloadGenerator): vm_template = get_json_template(DATA_DIR, "vm") vm_template["vm-type"] = vm.vm_type vm_template["vm-count"] = vm.vm_count - vm_template["vm-names"]["vm-name"].extend(map(replace, vm.names)) + for name in vm.names: + value = self.replace(name, single=True) + vm_template["vm-names"]["vm-name"].append(value) vm_list.append(vm_template) vm_networks = vm_template["vm-networks"] for port in vm.ports: @@ -150,11 +151,15 @@ class VnfApiPreloadGenerator(AbstractPreloadGenerator): network_template["network-role"] = role network_template["network-role-tag"] = role network_template["use-dhcp"] = "Y" if port.uses_dhcp else "N" - add_fixed_ips(network_template, port) - add_floating_ips(network_template, port) + self.add_fixed_ips(network_template, port) + self.add_floating_ips(network_template, port) - @staticmethod - def _add_parameters(preload, vnf_module): + def _add_parameters(self, preload, vnf_module): params = preload["input"]["vnf-topology-information"]["vnf-parameters"] for key, value in vnf_module.preload_parameters.items(): - params.append({"vnf-parameter-name": key, "vnf-parameter-value": value}) + params.append( + { + "vnf-parameter-name": key, + "vnf-parameter-value": self.replace(key, value), + } + ) diff --git a/ice_validator/tests/conftest.py b/ice_validator/tests/conftest.py index 2507753..9868067 100644 --- a/ice_validator/tests/conftest.py +++ b/ice_validator/tests/conftest.py @@ -44,7 +44,8 @@ import os import re import time -from preload import create_preloads +from preload.model import create_preloads +from config import get_generator_plugin_names from tests.helpers import get_output_dir try: @@ -828,6 +829,23 @@ def pytest_addoption(parser): help="optional category of test to execute", ) + parser.addoption( + "--env-directory", + dest="env_dir", + action="store", + help="optional directory of .env files for preload generation" + ) + + parser.addoption( + "--preload-format", + dest="preload_formats", + action="append", + help=( + "Preload format to create (multiple allowed). If not provided " + "then all available formats will be created: {}" + ).format(", ".join(get_generator_plugin_names())) + ) + def pytest_configure(config): """ diff --git a/ice_validator/tests/helpers.py b/ice_validator/tests/helpers.py index ff82c71..94effed 100644 --- a/ice_validator/tests/helpers.py +++ b/ice_validator/tests/helpers.py @@ -42,7 +42,9 @@ import os import re +import zipfile from collections import defaultdict +from typing import Set from boltons import funcutils from tests import cached_yaml as yaml @@ -356,3 +358,59 @@ def get_output_dir(config): if not os.path.exists(output_dir): os.makedirs(output_dir, exist_ok=True) return output_dir + + +def first(seq, predicate, default=None): + """ + Return the first item in sequence that satisfies the callable, predicate, or + returns the default if not found. + + :param seq: iterable sequence of objects + :param predicate: callable that accepts one item from the sequence + :param default: value to return if not found (default is None) + :return: default value if no item satisfies the predicate + """ + return next((i for i in seq if predicate(i)), default) + + +def check(predicate, message): + """ + Raise a RuntimeError with the provided message if predicate is False. + + Example: + check(path.is_file(), "{} must be a file".format(path.as_posix())) + + :param predicate: boolean condition + :param message: message + """ + if not predicate: + raise RuntimeError(message) + + +def unzip(zip_path, target_dir): + """ + Extracts a Zip archive located at zip_path to target_dir (which will be + created if it already exists) + + :param zip_path: path to valid zip file + :param target_dir: directory to unzip zip_path + """ + check(zipfile.is_zipfile(zip_path), "{} is not a valid zipfile or does not exist".format(zip_path)) + archive = zipfile.ZipFile(zip_path) + if not os.path.exists(target_dir): + os.makedirs(target_dir, exist_ok=True) + archive.extractall(path=target_dir) + + +def remove(sequence, exclude, key=None): + """ + Remove a copy of sequence that items occur in exclude. + + :param sequence: sequence of objects + :param exclude: objects to excluded (must support ``in`` check) + :param key: optional function to extract key from item in sequence + :return: list of items not in the excluded + """ + key_func = key if key else lambda x: x + result = (s for s in sequence if key_func(s) not in exclude) + return set(result) if isinstance(sequence, Set) else list(result) diff --git a/ice_validator/tests/parametrizers.py b/ice_validator/tests/parametrizers.py index 35cf6e0..763296c 100644 --- a/ice_validator/tests/parametrizers.py +++ b/ice_validator/tests/parametrizers.py @@ -58,7 +58,7 @@ def get_template_dir(metafunc): 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", None) is None: return path.join( path.dirname(metafunc.module.__file__), "fixtures", @@ -155,7 +155,7 @@ def get_filenames_lists( """ extensions = [".yaml", ".yml", ".env"] if extensions is None else extensions filenames_lists = [] - if metafunc.config.getoption("self_test"): + if metafunc.config.getoption("self_test", None): filenames_lists.append( list_template_dir( metafunc, extensions, exclude_nested, template_type, ["pass"] diff --git a/ice_validator/tests/test_environment_file_parameters.py b/ice_validator/tests/test_environment_file_parameters.py index ff57c35..69485bc 100644 --- a/ice_validator/tests/test_environment_file_parameters.py +++ b/ice_validator/tests/test_environment_file_parameters.py @@ -218,16 +218,22 @@ def run_test_parameter(yaml_file, resource_type, *prop, **kwargs): assert not invalid_parameters, "\n".join(invalid_parameters) -def get_preload_excluded_parameters(yaml_file): +def get_preload_excluded_parameters(yaml_file, persistent_only=False, env_spec=None): """ Returns set of all parameters that should not be included in the preload's vnf parameters/tag-values section. + + if persistent_only only parameters that are marked as persistent will + be excluded """ + env_spec = env_spec or ENV_PARAMETER_SPEC results = [] - for resource_type, specs in ENV_PARAMETER_SPEC.items(): + for resource_type, specs in env_spec.items(): # apply to all resources if not in the format of an OpenStack resource all_resources = "::" not in resource_type for spec in specs: + if persistent_only and not spec.get("persistent"): + continue results.extend(get_template_parameters(yaml_file, resource_type, spec, all_resources)) return {item["param"] for item in results} diff --git a/ice_validator/vvp-config.yaml b/ice_validator/vvp-config.yaml index 5754b92..4681ff4 100644 --- a/ice_validator/vvp-config.yaml +++ b/ice_validator/vvp-config.yaml @@ -54,4 +54,3 @@ categories: heat template-validate from the command line. settings: polling-freqency: 1000 - default-verbosity: Standard diff --git a/ice_validator/vvp.py b/ice_validator/vvp.py index b8e2e84..cc2c66f 100644 --- a/ice_validator/vvp.py +++ b/ice_validator/vvp.py @@ -46,11 +46,12 @@ To make an executable for windows execute the ``make_exe.bat`` to generate the NOTE: This script does require Python 3.6+ """ -import appdirs + import os +import traceback + import pytest import version -import yaml import contextlib import multiprocessing import queue @@ -60,8 +61,6 @@ import zipfile import platform import subprocess # nosec -from collections import MutableMapping -from configparser import ConfigParser from multiprocessing import Queue from pathlib import Path from shutil import rmtree @@ -102,9 +101,9 @@ from tkinter import ( NORMAL, ) from tkinter.scrolledtext import ScrolledText -from typing import Optional, List, Dict, TextIO, Callable, Iterator +from typing import Optional, TextIO, Callable -import preload +from config import Config VERSION = version.VERSION PATH = os.path.dirname(os.path.realpath(__file__)) @@ -213,40 +212,16 @@ class HyperlinkManager: return -class QueueWriter: - """``stdout`` and ``stderr`` will be written to this queue by pytest, and - pulled into the main GUI application""" - - def __init__(self, log_queue: queue.Queue): - """Writes data to the provided queue. - - :param log_queue: the queue instance to write to. - """ - self.queue = log_queue - - def write(self, data: str): - """Writes ``data`` to the queue """ - self.queue.put(data) - - # noinspection PyMethodMayBeStatic - def isatty(self) -> bool: - """Always returns ``False``""" - return False - - def flush(self): - """No operation method to satisfy file-like behavior""" - pass - - def run_pytest( template_dir: str, log: TextIO, result_queue: Queue, categories: Optional[list], - verbosity: str, report_format: str, halt_on_failure: bool, template_source: str, + env_dir: str, + preload_format: list, ): """Runs pytest using the given ``profile`` in a background process. All ``stdout`` and ``stderr`` are redirected to ``log``. The result of the job @@ -261,9 +236,6 @@ def run_pytest( will collect and execute all tests that are decorated with any of the passed categories, as well as tests not decorated with a category. - :param verbosity: Flag to be passed to pytest to control verbosity. - Options are '' (empty string), '-v' (verbose), - '-vv' (more verbose). :param report_format: Determines the style of report written. Options are csv, html, or excel :param halt_on_failure: Determines if validation will halt when basic failures @@ -271,6 +243,9 @@ def run_pytest( prevent a large number of errors from flooding the report. :param template_source: The path or name of the template to show on the report + :param env_dir: Optional directory of env files that can be used + to generate populated preload templates + :param preload_format: Selected preload format """ out_path = "{}/{}".format(PATH, OUT_DIR) if os.path.exists(out_path): @@ -280,283 +255,23 @@ def run_pytest( args = [ "--ignore=app_tests", "--capture=sys", - verbosity, "--template-directory={}".format(template_dir), "--report-format={}".format(report_format), "--template-source={}".format(template_source), ] + if env_dir: + args.append("--env-directory={}".format(env_dir)) if categories: for category in categories: args.extend(("--category", category)) if not halt_on_failure: args.append("--continue-on-failure") + if preload_format: + args.append("--preload-format={}".format(preload_format)) pytest.main(args=args) result_queue.put((True, None)) - except Exception as e: - result_queue.put((False, e)) - - -class UserSettings(MutableMapping): - FILE_NAME = "UserSettings.ini" - - def __init__(self, namespace, owner): - user_config_dir = appdirs.AppDirs(namespace, owner).user_config_dir - if not os.path.exists(user_config_dir): - os.makedirs(user_config_dir, exist_ok=True) - self._settings_path = os.path.join(user_config_dir, self.FILE_NAME) - self._config = ConfigParser() - self._config.read(self._settings_path) - - def __getitem__(self, k): - return self._config["DEFAULT"][k] - - def __setitem__(self, k, v) -> None: - self._config["DEFAULT"][k] = v - - def __delitem__(self, v) -> None: - del self._config["DEFAULT"][v] - - def __len__(self) -> int: - return len(self._config["DEFAULT"]) - - def __iter__(self) -> Iterator: - return iter(self._config["DEFAULT"]) - - def save(self): - with open(self._settings_path, "w") as f: - self._config.write(f) - - -class Config: - """ - Configuration for the Validation GUI Application - - Attributes - ---------- - ``log_queue`` Queue for the ``stdout`` and ``stderr` of - the background job - ``log_file`` File-like object (write only!) that writes to - the ``log_queue`` - ``status_queue`` Job completion status of the background job is - posted here as a tuple of (bool, Exception). - The first parameter is True if the job completed - successfully, and False otherwise. If the job - failed, then an Exception will be provided as the - second element. - ``command_queue`` Used to send commands to the GUI. Currently only - used to send shutdown commands in tests. - """ - - DEFAULT_FILENAME = "vvp-config.yaml" - DEFAULT_POLLING_FREQUENCY = "1000" - - def __init__(self, config: dict = None): - """Creates instance of application configuration. - - :param config: override default configuration if provided.""" - if config: - self._config = config - else: - with open(self.DEFAULT_FILENAME, "r") as f: - self._config = yaml.safe_load(f) - self._user_settings = UserSettings( - self._config["namespace"], self._config["owner"] - ) - self._watched_variables = [] - self._validate() - self._manager = multiprocessing.Manager() - self.log_queue = self._manager.Queue() - self.status_queue = self._manager.Queue() - self.log_file = QueueWriter(self.log_queue) - self.command_queue = self._manager.Queue() - - def watch(self, *variables): - """Traces the variables and saves their settings for the user. The - last settings will be used where available""" - self._watched_variables = variables - for var in self._watched_variables: - var.trace_add("write", self.save_settings) - - # noinspection PyProtectedMember,PyUnusedLocal - def save_settings(self, *args): - """Save the value of all watched variables to user settings""" - for var in self._watched_variables: - self._user_settings[var._name] = str(var.get()) - self._user_settings.save() - - @property - def app_name(self) -> str: - """Name of the application (displayed in title bar)""" - app_name = self._config["ui"].get("app-name", "VNF Validation Tool") - return "{} - {}".format(app_name, VERSION) - - @property - def category_names(self) -> List[str]: - """List of validation profile names for display in the UI""" - return [category["name"] for category in self._config["categories"]] - - @property - def polling_frequency(self) -> int: - """Returns the frequency (in ms) the UI polls the queue communicating - with any background job""" - return int( - self._config["settings"].get( - "polling-frequency", self.DEFAULT_POLLING_FREQUENCY - ) - ) - - @property - def disclaimer_text(self) -> str: - return self._config["ui"].get("disclaimer-text", "") - - @property - def requirement_link_text(self) -> str: - return self._config["ui"].get("requirement-link-text", "") - - @property - def requirement_link_url(self) -> str: - path = self._config["ui"].get("requirement-link-url", "") - if not path.startswith("http"): - path = "file://{}".format(os.path.join(PATH, path)) - return path - - @property - def terms(self) -> dict: - return self._config.get("terms", {}) - - @property - def terms_link_url(self) -> Optional[str]: - return self.terms.get("path") - - @property - def terms_link_text(self): - return self.terms.get("popup-link-text") - - @property - def terms_version(self) -> Optional[str]: - return self.terms.get("version") - - @property - def terms_popup_title(self) -> Optional[str]: - return self.terms.get("popup-title") - - @property - def terms_popup_message(self) -> Optional[str]: - return self.terms.get("popup-msg-text") - - @property - def are_terms_accepted(self) -> bool: - version = "terms-{}".format(self.terms_version) - return self._user_settings.get(version, "False") == "True" - - def set_terms_accepted(self): - version = "terms-{}".format(self.terms_version) - self._user_settings[version] = "True" - self._user_settings.save() - - def default_verbosity(self, levels: Dict[str, str]) -> str: - requested_level = self._user_settings.get("verbosity") or self._config[ - "settings" - ].get("default-verbosity", "Standard") - keys = [key for key in levels] - for key in levels: - if key.lower().startswith(requested_level.lower()): - return key - raise RuntimeError( - "Invalid default-verbosity level {}. Valid " - "values are {}".format(requested_level, ", ".join(keys)) - ) - - def get_description(self, category_name: str) -> str: - """Returns the description associated with the category name""" - return self._get_category(category_name)["description"] - - def get_category(self, category_name: str) -> str: - """Returns the category associated with the category name""" - return self._get_category(category_name).get("category", "") - - def get_category_value(self, category_name: str) -> str: - """Returns the saved value for a category name""" - return self._user_settings.get(category_name, 0) - - def _get_category(self, category_name: str) -> Dict[str, str]: - """Returns the profile definition""" - for category in self._config["categories"]: - if category["name"] == category_name: - return category - raise RuntimeError( - "Unexpected error: No category found in vvp-config.yaml " - "with a name of " + category_name - ) - - @property - def default_report_format(self): - return self._user_settings.get("report_format", "HTML") - - @property - def report_formats(self): - return ["CSV", "Excel", "HTML"] - - @property - def preload_formats(self): - excluded = self._config.get("excluded-preloads", []) - formats = (cls.format_name() for cls in preload.get_generator_plugins()) - return [f for f in formats if f not in excluded] - - @property - def default_preload_format(self): - default = self._user_settings.get("preload_format") - if default and default in self.preload_formats: - return default - else: - return self.preload_formats[0] - - @staticmethod - def get_subdir_for_preload(preload_format): - for gen in preload.get_generator_plugins(): - if gen.format_name() == preload_format: - return gen.output_sub_dir() - return "" - - @property - def default_input_format(self): - requested_default = self._user_settings.get("input_format") or self._config[ - "settings" - ].get("default-input-format") - if requested_default in self.input_formats: - return requested_default - else: - return self.input_formats[0] - - @property - def input_formats(self): - return ["Directory (Uncompressed)", "ZIP File"] - - @property - def default_halt_on_failure(self): - setting = self._user_settings.get("halt_on_failure", "True") - return setting.lower() == "true" - - def _validate(self): - """Ensures the config file is properly formatted""" - categories = self._config["categories"] - - # All profiles have required keys - expected_keys = {"name", "description"} - for category in categories: - actual_keys = set(category.keys()) - missing_keys = expected_keys.difference(actual_keys) - if missing_keys: - raise RuntimeError( - "Error in vvp-config.yaml file: " - "Required field missing in category. " - "Missing: {} " - "Categories: {}".format(",".join(missing_keys), category) - ) - - -def validate(): - return True + except Exception: + result_queue.put((False, traceback.format_exc())) class Dialog(Toplevel): @@ -610,9 +325,6 @@ class Dialog(Toplevel): # noinspection PyUnusedLocal def ok(self, event=None): - if not validate(): - self.initial_focus.focus_set() # put focus back - return self.withdraw() self.update_idletasks() self.apply() @@ -656,8 +368,6 @@ class TermsAndConditionsDialog(Dialog): class ValidatorApp: - VERBOSITY_LEVELS = {"Less": "", "Standard (-v)": "-v", "More (-vv)": "-vv"} - def __init__(self, config: Config = None): """Constructs the GUI element of the Validation Tool""" self.task = None @@ -684,7 +394,7 @@ class ValidatorApp: ) actions = Frame(control_panel) control_panel.add(actions) - control_panel.paneconfigure(actions, minsize=250) + control_panel.paneconfigure(actions, minsize=350) if self.config.disclaimer_text or self.config.requirement_link_text: self.footer = self.create_footer(parent_frame) @@ -713,16 +423,6 @@ class ValidatorApp: settings_frame = LabelFrame(actions, text="Settings") settings_row = 1 settings_frame.grid(row=3, column=1, columnspan=3, pady=10, sticky="we") - verbosity_label = Label(settings_frame, text="Verbosity:") - verbosity_label.grid(row=settings_row, column=1, sticky=W) - self.verbosity = StringVar(self._root, name="verbosity") - self.verbosity.set(self.config.default_verbosity(self.VERBOSITY_LEVELS)) - verbosity_menu = OptionMenu( - settings_frame, self.verbosity, *tuple(self.VERBOSITY_LEVELS.keys()) - ) - verbosity_menu.config(width=25) - verbosity_menu.grid(row=settings_row, column=2, columnspan=3, sticky=E, pady=5) - settings_row += 1 if self.config.preload_formats: preload_format_label = Label(settings_frame, text="Preload Template:") @@ -766,12 +466,35 @@ class ValidatorApp: self.halt_on_failure = BooleanVar(self._root, name="halt_on_failure") self.halt_on_failure.set(self.config.default_halt_on_failure) - halt_on_failure_label = Label(settings_frame, text="Halt on Basic Failures:") - halt_on_failure_label.grid(row=settings_row, column=1, sticky=E, pady=5) + halt_on_failure_label = Label( + settings_frame, text="Halt on Basic Failures:", anchor=W, justify=LEFT + ) + halt_on_failure_label.grid(row=settings_row, column=1, sticky=W, pady=5) halt_checkbox = Checkbutton( settings_frame, offvalue=False, onvalue=True, variable=self.halt_on_failure ) halt_checkbox.grid(row=settings_row, column=2, columnspan=2, sticky=W, pady=5) + settings_row += 1 + + self.create_preloads = BooleanVar(self._root, name="create_preloads") + self.create_preloads.set(0) + create_preloads_label = Label( + settings_frame, + text="Create Preload from Env Files:", + anchor=W, + justify=LEFT, + ) + create_preloads_label.grid(row=settings_row, column=1, sticky=W, pady=5) + create_preloads_checkbox = Checkbutton( + settings_frame, + offvalue=False, + onvalue=True, + variable=self.create_preloads, + command=self.set_env_dir_state, + ) + create_preloads_checkbox.grid( + row=settings_row, column=2, columnspan=2, sticky=W, pady=5 + ) directory_label = Label(actions, text="Template Location:") directory_label.grid(row=4, column=1, pady=5, sticky=W) @@ -781,10 +504,20 @@ class ValidatorApp: directory_browse = Button(actions, text="...", command=self.ask_template_source) directory_browse.grid(row=4, column=3, pady=5, sticky=W) + env_dir_label = Label(actions, text="Env Files:") + env_dir_label.grid(row=5, column=1, pady=5, sticky=W) + self.env_dir = StringVar(self._root, name="env_dir") + self.env_dir_entry = Entry( + actions, width=40, textvariable=self.env_dir, state=DISABLED + ) + self.env_dir_entry.grid(row=5, column=2, pady=5, sticky=W) + env_dir_browse = Button(actions, text="...", command=self.ask_env_dir_source) + env_dir_browse.grid(row=5, column=3, pady=5, sticky=W) + validate_button = Button( - actions, text="Validate Templates", command=self.validate + actions, text="Process Templates", command=self.validate ) - validate_button.grid(row=5, column=1, columnspan=2, pady=5) + validate_button.grid(row=6, column=1, columnspan=2, pady=5) self.result_panel = Frame(actions) # We'll add these labels now, and then make them visible when the run completes @@ -796,12 +529,12 @@ class ValidatorApp: self.result_label.bind("", self.open_report) self.preload_label = Label( - self.result_panel, text="View Preloads", fg="blue", cursor="hand2" + self.result_panel, text="View Preload Templates", fg="blue", cursor="hand2" ) self.underline(self.preload_label) self.preload_label.bind("", self.open_preloads) - self.result_panel.grid(row=6, column=1, columnspan=2) + self.result_panel.grid(row=7, column=1, columnspan=2) control_panel.pack(fill=BOTH, expand=1) main_window.add(control_panel) @@ -827,7 +560,6 @@ class ValidatorApp: self.config.watch( *self.categories, - self.verbosity, self.input_format, self.report_format, self.halt_on_failure, @@ -871,6 +603,10 @@ class ValidatorApp: footer.pack(fill=BOTH, expand=True) return footer + def set_env_dir_state(self): + state = NORMAL if self.create_preloads.get() else DISABLED + self.env_dir_entry.config(state=state) + def ask_template_source(self): if self.input_format.get() == "ZIP File": template_source = filedialog.askopenfilename( @@ -881,6 +617,9 @@ class ValidatorApp: template_source = filedialog.askdirectory() self.template_source.set(template_source) + def ask_env_dir_source(self): + self.env_dir.set(filedialog.askdirectory()) + def validate(self): """Run the pytest validations in a background process""" if not self.delete_prior_report(): @@ -904,10 +643,11 @@ class ValidatorApp: self.config.log_file, self.config.status_queue, self.categories_list(), - self.VERBOSITY_LEVELS[self.verbosity.get()], self.report_format.get().lower(), self.halt_on_failure.get(), self.template_source.get(), + self.env_dir.get(), + self.preload_format.get(), ), ) self.task.daemon = True @@ -1001,7 +741,8 @@ class ValidatorApp: # noinspection PyUnusedLocal def open_report(self, event): """Open the report in the user's default browser""" - webbrowser.open_new("file://{}".format(self.report_file_path)) + path = Path(self.report_file_path).absolute().resolve().as_uri() + webbrowser.open_new(path) def open_preloads(self, event): """Open the report in the user's default browser""" diff --git a/requirements.txt b/requirements.txt index 1f2247d..a0d292d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -49,3 +49,4 @@ six==1.12.0 pyinstaller mock openstack-heat +cached-property>=1.5,<1.6 -- 2.16.6