From 5639bc494fb1d219fe7ae91fd13b610e76f59637 Mon Sep 17 00:00:00 2001 From: David Rosenfeld Date: Wed, 13 Nov 2024 15:12:57 -0500 Subject: [PATCH] Add create xml results file to validations role --- roles/validations/defaults/main.yml | 2 + .../cifmw_validations_xml_filter.py | 95 +++++++++++++++++++ roles/validations/tasks/main.yml | 28 +++++- roles/validations/tasks/run_validation.yml | 32 +++++++ 4 files changed, 155 insertions(+), 2 deletions(-) create mode 100755 roles/validations/filter_plugins/cifmw_validations_xml_filter.py create mode 100644 roles/validations/tasks/run_validation.yml diff --git a/roles/validations/defaults/main.yml b/roles/validations/defaults/main.yml index 4588d241bb..9657c2d352 100644 --- a/roles/validations/defaults/main.yml +++ b/roles/validations/defaults/main.yml @@ -45,6 +45,8 @@ cifmw_validations_namespace: "openstack" cifmw_validations_hotfixed_edpm_nova_compute_image: quay.io/podified-antelope-centos9/openstack-nova-compute:current-podified cifmw_validations_custom_nova_service: "nova-custom-ceph" +cifmw_validations_xml_status_file_dir: "{{ cifmw_validations_basedir }}/tests/validations" + # variables needed for scaledown cifmw_validations_edpm_scale_down_hostname: compute-2.ctlplane.example.com cifmw_validations_edpm_scale_down_nodename: edpm-compute-2 diff --git a/roles/validations/filter_plugins/cifmw_validations_xml_filter.py b/roles/validations/filter_plugins/cifmw_validations_xml_filter.py new file mode 100755 index 0000000000..96367f4f28 --- /dev/null +++ b/roles/validations/filter_plugins/cifmw_validations_xml_filter.py @@ -0,0 +1,95 @@ +#!/usr/bin/python3 + +__metaclass__ = type + +DOCUMENTATION = """ + name: cifmw_validations_xml_filter + short_description: Maps the internal results structure to a JUnit XML string. + description: + - Maps the internal results structure to a JUnit XML string. + options: + _input: + description: + - The internal role test results. + type: dict + required: true +""" + +EXAMPLES = """ +- name: Define data to work on in the examples below + vars: + _internal_results: + test-1: + time: 2.54512 + test-case-2: + time: 4.5450345 + error: "error message" + ansible.builtin.set_fact: + _xml_string: >- + {{ + _internal_results | cifmw_validations_xml_filter + }} +""" + +RETURN = """ + _value: + description: The translated JUnit XML string. + type: string + sample: >- + + + + + + + + + +""" + + +import xml.etree.ElementTree as ET + + +class FilterModule: + + @staticmethod + def __float_conversion(x: float) -> str: + return "{0:0.3f}".format(round(x, 3)) + + @classmethod + def __map_xml_results(cls, test_results): + + root_elm = ET.Element("testsuites") + tree = ET.ElementTree(element=root_elm) + total_time = sum( + [data["time"] for data in test_results.values() if "time" in data] + ) + ts_elm = ET.SubElement( + root_elm, + "testsuite", + attrib={ + "name": "validations", + "failures": str( + len([elem for elem in test_results.values() if "error" in elem]) + ), + "skipped": "0", + "tests": str(len(test_results)), + "errors": "0", + "time": cls.__float_conversion(total_time), + }, + ) + for name, data in test_results.items(): + attributes = {"name": name, "classname": f"validations.{name}"} + if "time" in data: + attributes["time"] = cls.__float_conversion(data["time"]) + tc_elm = ET.SubElement(ts_elm, "testcase", attrib=attributes) + if "error" in data: + ET.SubElement(tc_elm, "failure", attrib={"message": data["error"]}) + ET.indent(tree, " ") + return ET.tostring(root_elm, encoding="utf-8", xml_declaration=True) + + def filters(self): + return { + "cifmw_validations_xml_filter": self.__map_xml_results, + } diff --git a/roles/validations/tasks/main.yml b/roles/validations/tasks/main.yml index ea82763b8b..27fba72191 100644 --- a/roles/validations/tasks/main.yml +++ b/roles/validations/tasks/main.yml @@ -25,6 +25,10 @@ - artifacts - logs +- name: Initialize variables needed for generating polarion xml file + ansible.builtin.set_fact: + _cifmw_validations_results: {} + # We can execute all defined validations when cifmw_validation_run_all is defined. # Else, we will skip this and run only the explicitly defined validations from # cifmw_validations_list @@ -42,7 +46,7 @@ - name: Run all found validations ansible.builtin.include_tasks: - file: "{{ item.path }}" + file: run_validation.yml loop: "{{ found_validations.files }}" - name: Run selected validations @@ -56,5 +60,25 @@ failed_when: not validation_exists.stat.exists - name: Run validations - ansible.builtin.include_tasks: "{{ item }}" + ansible.builtin.include_tasks: + file: run_validation.yml loop: "{{ cifmw_validations_list }}" + +- name: Create validations directory + ansible.builtin.file: + path: "{{ cifmw_validations_xml_status_file_dir }}" + state: directory + mode: "0755" + +- name: Create the XML file + ansible.builtin.copy: + content: "{{ _cifmw_validations_results | cifmw_validations_xml_filter }}" + dest: "{{ cifmw_validations_xml_status_file_dir }}/validations_results.xml" + mode: "0644" + +- name: Fail job when validations fail + ansible.builtin.assert: + that: + - _cifmw_validations_results.values() | + selectattr('error', 'defined') | length == 0 + msg: "One or more validations failed" diff --git a/roles/validations/tasks/run_validation.yml b/roles/validations/tasks/run_validation.yml new file mode 100644 index 0000000000..269169089d --- /dev/null +++ b/roles/validations/tasks/run_validation.yml @@ -0,0 +1,32 @@ +--- +- name: Get validation start time + ansible.builtin.set_fact: + _cifmw_validations_run_start_time: "{{ now(fmt='%s.%f') }}" + _cifmw_validations_status: "" + +- name: Run validation and catch errors + environment: + KUBECONFIG: "{{ cifmw_openshift_kubeconfig }}" + PATH: "{{ cifmw_path }}" + block: + - name: Run a validation + ansible.builtin.include_tasks: "{{ item }}" + rescue: + - name: Flag the validation as failed + ansible.builtin.set_fact: + _cifmw_validations_status: "Validator failed task: {{ ansible_failed_task.name }}, Validator failed reason: {{ ansible_failed_result.msg}}" + +- name: Add testcase name and time to generate xml script + ansible.builtin.set_fact: + _cifmw_validations_results: >- + {{ + _cifmw_validations_results | + combine( + { + (item | basename): { + 'time': (now(fmt='%s.%f')| float - _cifmw_validations_run_start_time | float), + 'error': _cifmw_validations_status if 'failed' in _cifmw_validations_status else omit + } + } + ) + }}