Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions roles/test_operator/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ cifmw_test_operator_storage_class: "{{ cifmw_test_operator_storage_class_prefix
cifmw_test_operator_delete_logs_pod: false
cifmw_test_operator_privileged: true
cifmw_test_operator_selinux_level: "s0:c478,c978"
cifmw_test_operator_generate_stackviz: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[blocking] Missing a new entry in roles/test_operator/README.md describing this var

cifmw_test_operator_crs_path: "{{ cifmw_basedir }}/artifacts/test-operator-crs"
cifmw_test_operator_log_pod_definition:
apiVersion: v1
Expand Down
120 changes: 120 additions & 0 deletions roles/test_operator/tasks/STACKVIZ_INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Stackviz Integration for Tempest Results
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[blocking] Don't put this markdown file in the tasks directory. Additionally, I think a lot of the information in this file is unnecessary/irrelevant. Maybe include a bit about stackviz in roles/test_operator/README.md instead?


## Overview
This integration automatically generates HTML visualization reports from Tempest test results using Stackviz. The reports are generated automatically after each Tempest run when using the test_operator role.

## Files Modified/Created

### New Files
- `tasks/generate-stackviz.yml` - Main task file that handles stackviz HTML generation

### Modified Files
- `tasks/run-test-operator-job.yml` - Added include statement to call stackviz generation after log collection
- `defaults/main.yml` - Added `cifmw_test_operator_generate_stackviz` configuration variable

## How It Works

1. **After tempest completes**, logs are collected from the test-operator PVCs
2. **The script searches** for `tempest_results.subunit.gz` in the artifacts directory
3. **Decompresses** the gzipped subunit file
4. **Runs stackviz-export** to convert subunit data to interactive HTML
5. **Stores the HTML report** in `<artifacts_basedir>/stackviz/html/index.html`

## Configuration

### Enable/Disable Stackviz Generation
```yaml
# In your scenario file or playbook variables
cifmw_test_operator_generate_stackviz: true # Default: true
```

Set to `false` to disable stackviz generation.

## Output Location

After a successful tempest run, the stackviz HTML report will be available at:
```
~/ci-framework-data/tests/test_operator/stackviz/html/index.html
```

You can open this file directly in a web browser:
```bash
firefox ~/ci-framework-data/tests/test_operator/stackviz/html/index.html
```

Or via file:// URL in your browser.

## Dependencies

The role will automatically install required dependencies:
- `python3-pip` (system package)
- `stackviz` (Python package via pip)

## Integration with CI/CD Artifacts

To publish the stackviz HTML to your CI/CD system, you can add a task after the test_operator role runs:

### Example: Publishing to Jenkins/Zuul Artifacts
```yaml
- name: Upload stackviz HTML to artifacts storage
ansible.builtin.copy:
src: "{{ cifmw_test_operator_artifacts_basedir }}/stackviz/"
dest: "{{ zuul.executor.log_root }}/stackviz/"
remote_src: true
when: stackviz_html_path is defined
```

### Example: Publishing to S3/Swift
```yaml
- name: Upload stackviz to object storage
amazon.aws.s3_sync:
bucket: "{{ ci_artifacts_bucket }}"
file_root: "{{ cifmw_test_operator_artifacts_basedir }}/stackviz/html"
key_prefix: "tempest-results/{{ zuul.build }}/stackviz"
when: stackviz_html_path is defined
```

## Troubleshooting

### Subunit file not found
If you see the warning "No tempest_results.subunit.gz file found", check:
1. Tempest actually ran and completed
2. Logs were successfully collected from the PVCs
3. The expected file path: `{{ cifmw_test_operator_artifacts_basedir }}/**/tempest_results.subunit.gz`

### Stackviz export failed
Check the ansible output for the error message. Common issues:
- Corrupted subunit file
- Incompatible stackviz version
- Insufficient disk space

### Installation failures
If stackviz installation fails:
```bash
# Manually install stackviz
sudo pip3 install stackviz

# Or use a specific version
sudo pip3 install stackviz==0.28
```

## Example Usage

```yaml
- name: Run tempest tests with stackviz
hosts: controller
vars:
cifmw_test_operator_generate_stackviz: true
cifmw_test_operator_artifacts_basedir: "/home/zuul/tempest-artifacts"
roles:
- test_operator
```

## Future Enhancements

Potential improvements for this integration:
1. Add support for comparing multiple test runs
2. Integration with ReportPortal for centralized test reporting
3. Automatic upload to centralized artifact storage
4. Email notifications with stackviz report links
5. Support for other test frameworks (tobiko, etc.)
142 changes: 142 additions & 0 deletions roles/test_operator/tasks/generate-stackviz.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
---
# Copyright Red Hat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file 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.

- name: Generate stackviz HTML from tempest results
when:
- not cifmw_test_operator_dry_run | bool
- cifmw_test_operator_generate_stackviz | default(true) | bool
- run_test_fw == 'tempest'
block:
- name: Find tempest_results.subunit.gz file in collected logs
ansible.builtin.find:
paths: "{{ cifmw_test_operator_artifacts_basedir }}"
patterns: "tempest_results.subunit.gz"
file_type: file
recurse: true
register: subunit_files

- name: Process stackviz generation
when: subunit_files.matched > 0
block:
- name: Set subunit file path
ansible.builtin.set_fact:
tempest_subunit_gz: "{{ subunit_files.files[0].path }}"

- name: Display subunit file location
ansible.builtin.debug:
msg: "Found tempest subunit file at: {{ tempest_subunit_gz }}"

- name: Create stackviz output directory
ansible.builtin.file:
path: "{{ cifmw_test_operator_artifacts_basedir }}/stackviz"
state: directory
mode: "0755"

- name: Ensure pip is available
ansible.builtin.package:
name: python3-pip
state: present
become: true

- name: Install stackviz via pip
ansible.builtin.pip:
name: stackviz
state: present
executable: pip3
become: true

- name: Decompress subunit file
ansible.builtin.command:
cmd: >
gunzip -c {{ tempest_subunit_gz }}
chdir: "{{ cifmw_test_operator_artifacts_basedir }}/stackviz"
register: decompressed_subunit
changed_when: false

- name: Write decompressed subunit to file
ansible.builtin.copy:
content: "{{ decompressed_subunit.stdout }}"
dest: "{{ cifmw_test_operator_artifacts_basedir }}/stackviz/tempest_results.subunit"
mode: "0644"

- name: Check if decompressed subunit file exists
ansible.builtin.stat:
path: "{{ cifmw_test_operator_artifacts_basedir }}/stackviz/tempest_results.subunit"
register: subunit_file

- name: Generate stackviz HTML from subunit file
when: subunit_file.stat.exists
block:
- name: Run stackviz-export
ansible.builtin.command:
cmd: >
stackviz-export
{{ cifmw_test_operator_artifacts_basedir }}/stackviz/tempest_results.subunit
{{ cifmw_test_operator_artifacts_basedir }}/stackviz/html
chdir: "{{ cifmw_test_operator_artifacts_basedir }}/stackviz"
register: stackviz_export
failed_when: false

- name: Display stackviz export status
ansible.builtin.debug:
msg: |
Stackviz export completed with return code: {{ stackviz_export.rc }}
{% if stackviz_export.rc == 0 %}
HTML report available at: {{ cifmw_test_operator_artifacts_basedir }}/stackviz/html/index.html
{% else %}
Stackviz export failed. Check logs for details.
Output: {{ stackviz_export.stdout }}
Error: {{ stackviz_export.stderr }}
{% endif %}

- name: Set stackviz HTML path fact
when: stackviz_export.rc == 0
ansible.builtin.set_fact:
stackviz_html_path: "{{ cifmw_test_operator_artifacts_basedir }}/stackviz/html/index.html"

- name: Verify stackviz HTML was generated
when: stackviz_export.rc == 0
ansible.builtin.stat:
path: "{{ stackviz_html_path }}"
register: stackviz_html

- name: Display success message
when:
- stackviz_export.rc == 0
- stackviz_html.stat.exists
ansible.builtin.debug:
msg: |
========================================
Stackviz HTML Report Generated Successfully!
========================================
Report available in artifacts: stackviz/html/index.html
========================================

- name: Warn if decompressed subunit file was not created
when: not subunit_file.stat.exists
ansible.builtin.debug:
msg: |
WARNING: Decompressed subunit file was not created.
This may indicate an issue with decompressing {{ tempest_subunit_gz }}.
Skipping stackviz generation.

- name: Warn if no subunit file found
when: subunit_files.matched == 0
ansible.builtin.debug:
msg: |
WARNING: No tempest_results.subunit.gz file found in tempest logs.
Stackviz HTML generation skipped.
Expected location: {{ cifmw_test_operator_artifacts_basedir }}/**/tempest_results.subunit.gz
3 changes: 3 additions & 0 deletions roles/test_operator/tasks/run-test-operator-job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@
- not testpod_timed_out
ansible.builtin.include_tasks: collect-logs.yaml

- name: Generate stackviz HTML report from tempest results
ansible.builtin.include_tasks: generate-stackviz.yml

- name: Get list of all pods
kubernetes.core.k8s_info:
kubeconfig: "{{ cifmw_openshift_kubeconfig }}"
Expand Down
Loading