From 0e416e09414951200d809cb07376d67a94043e3b Mon Sep 17 00:00:00 2001 From: lkuchlan Date: Sun, 14 Jun 2026 12:03:43 +0300 Subject: [PATCH] [rally] Add Rally role MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new `rally` Ansible role that runs OpenStack Rally benchmarks inside a podman container (quay.io/airshipit/xrally-openstack:3.0.0). The role auto-discovers OpenStack credentials from the cluster's KeystoneAPI resource and appends the deployment CA to the trust bundle so SSL verification works without any manual configuration. The role supports two execution modes: * Single run (default): `cifmw_rally_runs: []` — uses the top-level `cifmw_rally_*` variables, artifacts stored in `cifmw_rally_artifacts_basedir/`. * Multi-run: set `cifmw_rally_runs` to a list of dicts, each with a `name` key and optional `cifmw_rally_*` overrides. Artifacts for each run are namespaced under `cifmw_rally_artifacts_basedir//`. This is modelled after `cifmw_test_operator_stages` and replaces the former test_operator-based wiring (Rally is not a test-operator CRD). A `hooks/playbooks/rally_run.yaml` entry point is provided so jobs can invoke Rally via the ci-framework hooks system, e.g.: post_tests_90_rally_run: type: playbook source: rally_run.yaml Assisted-By: Claude Sonnet 4.6 Signed-off-by: lkuchlan --- .ansible-lint | 1 + docs/dictionary/en-custom.txt | 2 + .../playbooks/rally_cinder_prerequisites.yaml | 56 ++++++ .../playbooks/rally_manila_prerequisites.yaml | 15 ++ hooks/playbooks/rally_run.yaml | 11 ++ roles/rally/OWNERS | 7 + roles/rally/README.md | 81 +++++++++ roles/rally/defaults/main.yml | 36 ++++ roles/rally/meta/main.yml | 29 +++ roles/rally/molecule/default/converge.yml | 24 +++ roles/rally/molecule/default/molecule.yml | 9 + roles/rally/molecule/default/prepare.yml | 28 +++ roles/rally/tasks/create-deployment.yml | 55 ++++++ roles/rally/tasks/main.yml | 64 +++++++ roles/rally/tasks/run_once.yml | 171 ++++++++++++++++++ roles/rally/templates/rally-run.sh.j2 | 15 ++ roles/rally/vars/main.yml | 18 ++ zuul.d/molecule.yaml | 11 ++ zuul.d/projects.yaml | 1 + 19 files changed, 634 insertions(+) create mode 100644 hooks/playbooks/rally_cinder_prerequisites.yaml create mode 100644 hooks/playbooks/rally_manila_prerequisites.yaml create mode 100644 hooks/playbooks/rally_run.yaml create mode 100644 roles/rally/OWNERS create mode 100644 roles/rally/README.md create mode 100644 roles/rally/defaults/main.yml create mode 100644 roles/rally/meta/main.yml create mode 100644 roles/rally/molecule/default/converge.yml create mode 100644 roles/rally/molecule/default/molecule.yml create mode 100644 roles/rally/molecule/default/prepare.yml create mode 100644 roles/rally/tasks/create-deployment.yml create mode 100644 roles/rally/tasks/main.yml create mode 100644 roles/rally/tasks/run_once.yml create mode 100644 roles/rally/templates/rally-run.sh.j2 create mode 100644 roles/rally/vars/main.yml diff --git a/.ansible-lint b/.ansible-lint index 8ffdeaeb1..4bcb5cd12 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -18,6 +18,7 @@ exclude_paths: - roles/ci_gen_kustomize_values/molecule/default/converge.yml # invalid due to calls to "lookup('file')" - roles/ci_gen_kustomize_values/molecule/default/prepare.yml # import_playbook - roles/reproducer/files/cifmw-bootstrap.yml # invalid due to calls to "lookup('file')" + - roles/rally/files/ # Rally task files, not Ansible YAML - roles/kustomize_deploy/molecule/flexible_loop/files/networking-environment-definition.yml # Generated - roles/kustomize_deploy/molecule/flexible_loop/prepare.yml # import_playbook - roles/*/molecule/*/side_effect.yml # syntax-check[empty-playbook] https://github.com/ansible/molecule/issues/3617 diff --git a/docs/dictionary/en-custom.txt b/docs/dictionary/en-custom.txt index 0d8732d56..cd4d708f5 100644 --- a/docs/dictionary/en-custom.txt +++ b/docs/dictionary/en-custom.txt @@ -77,6 +77,7 @@ baseimg baseurl bashrc bd +benchmarking bgp blockquote bmaas @@ -326,6 +327,7 @@ kerberos keycloak keypair keyring +keystoneapi keystoneauth keystoneauth1 keytab diff --git a/hooks/playbooks/rally_cinder_prerequisites.yaml b/hooks/playbooks/rally_cinder_prerequisites.yaml new file mode 100644 index 000000000..7b3b0083f --- /dev/null +++ b/hooks/playbooks/rally_cinder_prerequisites.yaml @@ -0,0 +1,56 @@ +--- +- name: Ensure Cinder Rally prerequisites exist + hosts: "{{ cifmw_target_hook_host | default('localhost') }}" + gather_facts: false + environment: + KUBECONFIG: "{{ cifmw_openshift_kubeconfig }}" + PATH: "{{ cifmw_path }}" + tasks: + - name: Create m1.tiny flavor if missing + ansible.builtin.shell: | + set -xe -o pipefail + oc -n {{ namespace }} rsh openstackclient \ + openstack flavor show m1.tiny &>/dev/null || \ + oc -n {{ namespace }} rsh openstackclient \ + openstack flavor create m1.tiny \ + --ram 512 --disk 1 --vcpus 1 --public + + - name: Create lvmdriver-1 volume type if missing + ansible.builtin.shell: | + set -xe -o pipefail + oc -n {{ namespace }} rsh openstackclient \ + openstack volume type show lvmdriver-1 &>/dev/null || \ + oc -n {{ namespace }} rsh openstackclient \ + openstack volume type create lvmdriver-1 --public + + - name: Check if cirros image already exists + ansible.builtin.shell: | + set -e -o pipefail + oc -n {{ namespace }} rsh openstackclient \ + openstack image list -f value -c Name | grep -q '^cirros' + register: _cirros_image_exists + failed_when: false + + - name: Download cirros image to hypervisor + when: _cirros_image_exists.rc != 0 + ansible.builtin.get_url: + url: "https://github.com/cirros-dev/cirros/releases/download/0.6.2/cirros-0.6.2-x86_64-disk.img" + dest: "/tmp/cirros-0.6.2-x86_64-disk.img" + mode: "0644" + + - name: Copy cirros image to openstackclient pod + when: _cirros_image_exists.rc != 0 + ansible.builtin.command: >- + oc cp /tmp/cirros-0.6.2-x86_64-disk.img + {{ namespace }}/openstackclient:/home/cloud-admin/cirros-0.6.2-x86_64-disk.img + + - name: Upload cirros image for Rally if missing + when: _cirros_image_exists.rc != 0 + ansible.builtin.shell: | + set -xe -o pipefail + oc -n {{ namespace }} rsh openstackclient \ + openstack image create cirros-0.6.2-x86_64-disk \ + --disk-format qcow2 \ + --container-format bare \ + --file /home/cloud-admin/cirros-0.6.2-x86_64-disk.img \ + --public diff --git a/hooks/playbooks/rally_manila_prerequisites.yaml b/hooks/playbooks/rally_manila_prerequisites.yaml new file mode 100644 index 000000000..c2d847410 --- /dev/null +++ b/hooks/playbooks/rally_manila_prerequisites.yaml @@ -0,0 +1,15 @@ +--- +- name: Ensure Manila Rally prerequisites exist + hosts: "{{ cifmw_target_hook_host | default('localhost') }}" + gather_facts: false + environment: + KUBECONFIG: "{{ cifmw_openshift_kubeconfig }}" + PATH: "{{ cifmw_path }}" + tasks: + - name: Create dhss_true share type if missing + ansible.builtin.shell: | + set -xe -o pipefail + oc -n {{ namespace }} rsh openstackclient \ + openstack share type show dhss_true &>/dev/null || \ + oc -n {{ namespace }} rsh openstackclient \ + openstack share type create dhss_true False diff --git a/hooks/playbooks/rally_run.yaml b/hooks/playbooks/rally_run.yaml new file mode 100644 index 000000000..b3b39a36b --- /dev/null +++ b/hooks/playbooks/rally_run.yaml @@ -0,0 +1,11 @@ +--- +- name: Run Rally benchmarks + hosts: "{{ cifmw_target_hook_host | default('localhost') }}" + gather_facts: false + environment: + KUBECONFIG: "{{ cifmw_openshift_kubeconfig }}" + PATH: "{{ cifmw_path }}" + tasks: + - name: Run rally role + ansible.builtin.include_role: + name: rally diff --git a/roles/rally/OWNERS b/roles/rally/OWNERS new file mode 100644 index 000000000..b7883e567 --- /dev/null +++ b/roles/rally/OWNERS @@ -0,0 +1,7 @@ +# See the OWNERS docs at https://www.kubernetes.dev/docs/guide/owners/ + +approvers: + - ciops-team + +reviewers: + - ciops-team diff --git a/roles/rally/README.md b/roles/rally/README.md new file mode 100644 index 000000000..8e22e08b4 --- /dev/null +++ b/roles/rally/README.md @@ -0,0 +1,81 @@ +# rally +Role to setup and run Rally benchmarking tests against an OpenStack deployment. + +Rally is run inside the `quay.io/airshipit/xrally-openstack` container via podman. +OpenStack credentials are discovered automatically from the Kubernetes KeystoneAPI resource, +or can be supplied directly via `cifmw_rally_os_*` variables. + +## Prerequisites +- `oc` CLI available and pointing to the target OpenStack cluster (used for credential and CA discovery). +- `cifmw_openstack_namespace` must be set to the OpenStack namespace (typically `openstack`). + +## Privilege escalation +become - Required to install podman and fix artifact directory ownership after the container run. + +## Parameters + +* `cifmw_rally_artifacts_basedir`: (String) Directory where all Rally artifacts are stored. + Default: `{{ cifmw_basedir }}/tests/rally` +* `cifmw_rally_registry`: (String) Container registry. Default: `quay.io` +* `cifmw_rally_namespace`: (String) Registry namespace. Default: `airshipit` +* `cifmw_rally_container`: (String) Container image name. Default: `xrally-openstack` +* `cifmw_rally_image`: (String) Full image reference. Composed from registry/namespace/container. +* `cifmw_rally_image_tag`: (String) Container image tag. Default: `3.0.0` +* `cifmw_rally_dry_run`: (Boolean) Skip actual container execution. Default: `false` +* `cifmw_rally_remove_container`: (Boolean) Remove container after run. Default: `true` +* `cifmw_rally_fail_on_task_failure`: (Boolean) Fail the play if Rally exits non-zero. Default: `true` +* `cifmw_rally_deployment_name`: (String) Rally deployment name. Default: `cifmw` +* `cifmw_rally_concurrency`: (Integer) Concurrency passed to the Rally task via `--task-args`. Default: `1` +* `cifmw_rally_task_file`: (String) Absolute path to a custom Rally task YAML on the host. + Mutually exclusive with `cifmw_rally_openstack_tests`. Default: `""` +* `cifmw_rally_task_extra_args`: (String) Extra arguments passed verbatim to `rally task start`. Default: `""` +* `cifmw_rally_dns_servers`: (List) DNS servers used inside the Rally container. Default: `["192.168.122.10"]` +* `cifmw_rally_os_auth_url`: (String) OpenStack auth URL. Auto-discovered from KeystoneAPI if unset. +* `cifmw_rally_os_username`: (String) OpenStack username. Default: `admin` +* `cifmw_rally_os_password`: (String) OpenStack password. Auto-discovered from Kubernetes secret if unset. +* `cifmw_rally_os_project_name`: (String) OpenStack project. Default: `admin` +* `cifmw_rally_os_user_domain_name`: (String) User domain. Default: `Default` +* `cifmw_rally_os_project_domain_name`: (String) Project domain. Default: `Default` +* `cifmw_rally_os_region_name`: (String) OpenStack region. Default: `regionOne` +* `cifmw_rally_runs`: (List) List of run definitions for sequential multi-run mode. + When empty (default), the role runs once using the top-level `cifmw_rally_*` variables. + When non-empty, the role loops over the list; each entry may override any `cifmw_rally_*` + variable for that run and **must** include a `name` key used to namespace artifacts. + Default: `[]` + +## Standalone usage (single run) + +```yaml +- hosts: hypervisor + roles: + - role: rally + vars: + cifmw_rally_openstack_tests: "cinder" + cifmw_rally_concurrency: 2 + cifmw_rally_fail_on_task_failure: false +``` + +## Usage via hook (multiple runs) + +Add a `post_tests_*` hook in your job vars and define `cifmw_rally_runs`: + +```yaml +post_tests_90_rally_run: + type: playbook + source: rally_run.yaml +cifmw_rally_runs: + - name: cinder + cifmw_rally_openstack_tests: "cinder" + cifmw_rally_concurrency: 1 + - name: nova + cifmw_rally_openstack_tests: "nova" + cifmw_rally_concurrency: 2 + cifmw_rally_fail_on_task_failure: false +``` + +Each run stores its artifacts under `cifmw_rally_artifacts_basedir//`. + +## Custom task files + +To use a custom Rally task file, set `cifmw_rally_task_file` to the absolute path of the +YAML on the hypervisor host (pre-stage it via an earlier hook if needed). diff --git a/roles/rally/defaults/main.yml b/roles/rally/defaults/main.yml new file mode 100644 index 000000000..eeba69fb5 --- /dev/null +++ b/roles/rally/defaults/main.yml @@ -0,0 +1,36 @@ +--- +# 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. + + +# All variables intended for modification should be placed in this file. +# All variables within this role should have a prefix of "cifmw_rally" +cifmw_rally_artifacts_basedir: "{{ cifmw_basedir }}/tests/rally" +cifmw_rally_registry: "quay.io" +cifmw_rally_namespace: "airshipit" +cifmw_rally_container: "xrally-openstack" +cifmw_rally_image: "{{ cifmw_rally_registry }}/{{ cifmw_rally_namespace }}/{{ cifmw_rally_container }}" +cifmw_rally_image_tag: "3.0.0" +cifmw_rally_dry_run: false +cifmw_rally_remove_container: true +cifmw_rally_fail_on_task_failure: true +cifmw_rally_deployment_name: "cifmw" +cifmw_rally_concurrency: 1 +cifmw_rally_task_file: "" +cifmw_rally_openstack_tests: "" +cifmw_rally_task_extra_args: "" +cifmw_rally_dns_servers: + - "192.168.122.10" +cifmw_rally_runs: [] diff --git a/roles/rally/meta/main.yml b/roles/rally/meta/main.yml new file mode 100644 index 000000000..0891072bf --- /dev/null +++ b/roles/rally/meta/main.yml @@ -0,0 +1,29 @@ +--- +# 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. + + +galaxy_info: + author: CI Framework + description: CI Framework Role -- rally + company: Red Hat + license: Apache-2.0 + min_ansible_version: "2.14" + namespace: cifmw + galaxy_tags: + - cifmw + - rally + +dependencies: [] diff --git a/roles/rally/molecule/default/converge.yml b/roles/rally/molecule/default/converge.yml new file mode 100644 index 000000000..cc07f8c22 --- /dev/null +++ b/roles/rally/molecule/default/converge.yml @@ -0,0 +1,24 @@ +--- +# 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: Converge + hosts: all + vars: + cifmw_rally_dry_run: true + cifmw_rally_openstack_tests: "cinder" + roles: + - role: "rally" diff --git a/roles/rally/molecule/default/molecule.yml b/roles/rally/molecule/default/molecule.yml new file mode 100644 index 000000000..8162360ec --- /dev/null +++ b/roles/rally/molecule/default/molecule.yml @@ -0,0 +1,9 @@ +--- +# Mainly used to override the defaults set in .config/molecule/ +# By default, it uses the "config_podman.yml" - in CI, it will use +# "config_local.yml". +log: true + +provisioner: + name: ansible + log: true diff --git a/roles/rally/molecule/default/prepare.yml b/roles/rally/molecule/default/prepare.yml new file mode 100644 index 000000000..0c6d52693 --- /dev/null +++ b/roles/rally/molecule/default/prepare.yml @@ -0,0 +1,28 @@ +--- +# 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: Prepare + hosts: all + vars: + cifmw_basedir: "{{ ansible_user_dir }}/ci-framework-data" + cifmw_install_yamls_tasks_out: "{{ ansible_user_dir }}/zuul-jobs/roles/install_yamls_makes/tasks" + cifmw_install_yamls_defaults: + NAMESPACE: openstack + roles: + - role: test_deps + - role: ci_setup + - role: install_yamls diff --git a/roles/rally/tasks/create-deployment.yml b/roles/rally/tasks/create-deployment.yml new file mode 100644 index 000000000..5db4d2930 --- /dev/null +++ b/roles/rally/tasks/create-deployment.yml @@ -0,0 +1,55 @@ +--- +# 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: Discover credentials from Kubernetes when not overridden + when: cifmw_rally_os_auth_url is not defined + block: + - name: Get keystone data + register: _cifmw_rally_keystone_data + environment: + KUBECONFIG: "{{ cifmw_openshift_kubeconfig }}" + PATH: "{{ cifmw_path }}" + ansible.builtin.command: + cmd: "oc get keystoneapi keystone -n {{ cifmw_openstack_namespace }} -o json" + + - name: Set keystone vars + vars: + _keystone_json: "{{ _cifmw_rally_keystone_data.stdout | from_json }}" + ansible.builtin.set_fact: + _cifmw_rally_keystone_secret_name: "{{ _keystone_json['spec']['secret'] }}" + _cifmw_rally_keystone_passwd_select: "{{ _keystone_json['spec']['passwordSelectors']['admin'] }}" + _cifmw_rally_keystone_api: "{{ _keystone_json['status']['apiEndpoints']['public'] }}" + + - name: Get credentials data + register: _cifmw_rally_os_password_data + environment: + KUBECONFIG: "{{ cifmw_openshift_kubeconfig }}" + PATH: "{{ cifmw_path }}" + ansible.builtin.command: + cmd: >- + oc get secret {{ _cifmw_rally_keystone_secret_name }} + -n {{ cifmw_openstack_namespace }} -o json + + - name: Set OpenStack credentials facts + vars: + _pw_json: "{{ _cifmw_rally_os_password_data.stdout | from_json }}" + ansible.builtin.set_fact: + cifmw_rally_os_auth_url: "{{ _cifmw_rally_keystone_api }}" + cifmw_rally_os_password: >- + {{ + _pw_json['data'][_cifmw_rally_keystone_passwd_select] | + ansible.builtin.b64decode + }} diff --git a/roles/rally/tasks/main.yml b/roles/rally/tasks/main.yml new file mode 100644 index 000000000..ea243cc04 --- /dev/null +++ b/roles/rally/tasks/main.yml @@ -0,0 +1,64 @@ +--- +# 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: Ensure podman is installed + become: true + ansible.builtin.package: + name: podman + state: present + +- name: Create rally base artifacts directory + ansible.builtin.file: + path: "{{ cifmw_rally_artifacts_basedir }}" + state: directory + mode: "0755" + +- name: Create rally deployment configuration + ansible.builtin.include_tasks: create-deployment.yml + when: not cifmw_rally_dry_run | bool + +- name: Get OpenStack public CA secret + ansible.builtin.command: + cmd: >- + oc get secret rootca-public + -n {{ cifmw_openstack_namespace }} + -o json + register: _cifmw_rally_rootca_secret + failed_when: false + when: not cifmw_rally_dry_run | bool + +- name: Ensure we have rally container image + register: _cifmw_rally_fetch_img + containers.podman.podman_image: + name: "{{ cifmw_rally_image }}:{{ cifmw_rally_image_tag }}" + retries: 5 + delay: 5 + until: _cifmw_rally_fetch_img is success + +- name: Run rally (single run) + when: cifmw_rally_runs | length == 0 + ansible.builtin.include_tasks: run_once.yml + vars: + _cifmw_rally_run_name: "" + _cifmw_rally_run_vars: {} + +- name: Run rally (multiple runs) + when: cifmw_rally_runs | length > 0 + ansible.builtin.include_tasks: run_once.yml + vars: + _cifmw_rally_run_name: "{{ item.name }}" + _cifmw_rally_run_vars: "{{ item }}" + loop: "{{ cifmw_rally_runs }}" diff --git a/roles/rally/tasks/run_once.yml b/roles/rally/tasks/run_once.yml new file mode 100644 index 000000000..791733adf --- /dev/null +++ b/roles/rally/tasks/run_once.yml @@ -0,0 +1,171 @@ +--- +# 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: Set effective variables for this run + ansible.builtin.set_fact: + _cifmw_rally_effective_basedir: >- + {{ (cifmw_rally_artifacts_basedir ~ '/' ~ _cifmw_rally_run_name) + if _cifmw_rally_run_name | length > 0 + else cifmw_rally_artifacts_basedir }} + _cifmw_rally_effective_openstack_tests: >- + {{ _cifmw_rally_run_vars.cifmw_rally_openstack_tests + | default(cifmw_rally_openstack_tests) }} + _cifmw_rally_effective_task_file: >- + {{ _cifmw_rally_run_vars.cifmw_rally_task_file + | default(cifmw_rally_task_file) }} + _cifmw_rally_effective_concurrency: >- + {{ _cifmw_rally_run_vars.cifmw_rally_concurrency + | default(cifmw_rally_concurrency) }} + _cifmw_rally_effective_fail_on_task_failure: >- + {{ _cifmw_rally_run_vars.cifmw_rally_fail_on_task_failure + | default(cifmw_rally_fail_on_task_failure) }} + _cifmw_rally_effective_task_extra_args: >- + {{ _cifmw_rally_run_vars.cifmw_rally_task_extra_args + | default(cifmw_rally_task_extra_args) }} + _cifmw_rally_effective_container_name: >- + {{ 'rally-' ~ _cifmw_rally_run_name + if _cifmw_rally_run_name | length > 0 + else 'rally' }} + +- name: Assert that a workload source is specified for this run + ansible.builtin.assert: + that: + - >- + _cifmw_rally_effective_task_file | length > 0 or + _cifmw_rally_effective_openstack_tests | length > 0 + fail_msg: >- + Either cifmw_rally_task_file (absolute path to a custom YAML on the host) + or cifmw_rally_openstack_tests (name of a bundled workload, e.g. 'cinder', + 'nova') must be set for run + '{{ _cifmw_rally_run_name | default("default") }}'. + +- name: Create per-run directories + ansible.builtin.file: + path: "{{ item.path }}" + state: directory + mode: "{{ item.mode }}" + loop: + - {path: "{{ _cifmw_rally_effective_basedir }}", mode: "0755"} + - {path: "{{ _cifmw_rally_effective_basedir }}/tasks", mode: "0755"} + - {path: "{{ _cifmw_rally_effective_basedir }}/reports", mode: "0777"} + +- name: Copy custom task file to run tasks directory + when: _cifmw_rally_effective_task_file | length > 0 + ansible.builtin.copy: + src: "{{ _cifmw_rally_effective_task_file }}" + dest: >- + {{ _cifmw_rally_effective_basedir }}/tasks/{{ + _cifmw_rally_effective_task_file | basename }} + mode: "0644" + remote_src: true + +- name: Copy CA bundle to run artifacts directory + ansible.builtin.copy: + src: "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" + dest: "{{ _cifmw_rally_effective_basedir }}/tls-ca-bundle.pem" + mode: "0644" + owner: "{{ ansible_user | default(lookup('env', 'USER')) }}" + group: "{{ ansible_user | default(lookup('env', 'USER')) }}" + remote_src: true + force: true + when: not cifmw_rally_dry_run | bool + +- name: Append OpenStack public CA to tls-ca-bundle + when: + - not cifmw_rally_dry_run | bool + - _cifmw_rally_rootca_secret.rc is defined + - _cifmw_rally_rootca_secret.rc == 0 + vars: + _rootca_pem: >- + {{ (_cifmw_rally_rootca_secret.stdout | from_json).data['ca.crt'] | b64decode }} + ansible.builtin.shell: + cmd: >- + cat >> {{ _cifmw_rally_effective_basedir }}/tls-ca-bundle.pem + stdin: "{{ _rootca_pem }}" + +- name: Create rally run script + ansible.builtin.template: + src: rally-run.sh.j2 + dest: "{{ _cifmw_rally_effective_basedir }}/tasks/rally-run.sh" + mode: "0755" + vars: + cifmw_rally_task_file: "{{ _cifmw_rally_effective_task_file }}" + cifmw_rally_openstack_tests: "{{ _cifmw_rally_effective_openstack_tests }}" + cifmw_rally_concurrency: "{{ _cifmw_rally_effective_concurrency }}" + cifmw_rally_task_extra_args: "{{ _cifmw_rally_effective_task_extra_args }}" + when: not cifmw_rally_dry_run | bool + +- name: Run rally + ignore_errors: true + containers.podman.podman_container: + name: "{{ _cifmw_rally_effective_container_name }}" + image: "{{ cifmw_rally_image }}:{{ cifmw_rally_image_tag }}" + state: started + auto_remove: "{{ cifmw_rally_remove_container }}" + network: host + dns: "{{ cifmw_rally_dns_servers }}" + volume: + - "{{ _cifmw_rally_effective_basedir }}/tasks:{{ _cifmw_rally_container_tasks_dir }}:Z" + - "{{ _cifmw_rally_effective_basedir }}/reports:{{ _cifmw_rally_container_reports_dir }}:Z" + - "{{ _cifmw_rally_effective_basedir }}/tls-ca-bundle.pem:\ + /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem:Z" + detach: false + entrypoint: "/bin/bash" + command: "{{ _cifmw_rally_container_tasks_dir }}/rally-run.sh" + env: + OS_AUTH_URL: "{{ cifmw_rally_os_auth_url | default('') }}" + OS_USERNAME: "{{ cifmw_rally_os_username | default('admin') }}" + OS_PASSWORD: "{{ cifmw_rally_os_password | default('') }}" + OS_PROJECT_NAME: "{{ cifmw_rally_os_project_name | default('admin') }}" + OS_USER_DOMAIN_NAME: "{{ cifmw_rally_os_user_domain_name | default('Default') }}" + OS_PROJECT_DOMAIN_NAME: "{{ cifmw_rally_os_project_domain_name | default('Default') }}" + OS_REGION_NAME: "{{ cifmw_rally_os_region_name | default('regionOne') }}" + OS_IDENTITY_API_VERSION: "3" + OS_CACERT: "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" + REQUESTS_CA_BUNDLE: "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" + when: not cifmw_rally_dry_run | bool + register: _cifmw_rally_run_output + +- name: Restore ownership of run artifacts directory + become: true + ansible.builtin.file: + path: "{{ _cifmw_rally_effective_basedir }}" + state: directory + recurse: true + owner: "{{ ansible_user | default(lookup('env', 'USER')) }}" + group: "{{ ansible_user | default(lookup('env', 'USER')) }}" + when: not cifmw_rally_dry_run | bool + +- name: Save logs from podman + when: not cifmw_rally_dry_run | bool + ansible.builtin.copy: + mode: "0644" + dest: "{{ _cifmw_rally_effective_basedir }}/podman_rally.log" + content: | + "{{ _cifmw_rally_run_output.stdout }}" + +- name: Fail if podman container did not succeed + when: + - not cifmw_rally_dry_run | bool + - _cifmw_rally_effective_fail_on_task_failure | bool + ansible.builtin.assert: + that: + - "_cifmw_rally_run_output.failed == false" + fail_msg: >- + Rally task execution failed for run + '{{ _cifmw_rally_run_name | default("default") }}'. + Check logs at {{ _cifmw_rally_effective_basedir }}/podman_rally.log + and reports at {{ _cifmw_rally_effective_basedir }}/reports/ diff --git a/roles/rally/templates/rally-run.sh.j2 b/roles/rally/templates/rally-run.sh.j2 new file mode 100644 index 000000000..0212893da --- /dev/null +++ b/roles/rally/templates/rally-run.sh.j2 @@ -0,0 +1,15 @@ +#!/bin/bash +set -e +rally db ensure +rally deployment create --fromenv --name {{ cifmw_rally_deployment_name }} +{% if cifmw_rally_task_file | length > 0 %} +rally task start {{ _cifmw_rally_container_tasks_dir }}/{{ cifmw_rally_task_file | basename }} \ +{% else %} +rally task start /rally/xrally_openstack/rally-jobs/{{ cifmw_rally_openstack_tests }}.yaml \ +{% endif %} + --task-args '{"concurrency": {{ cifmw_rally_concurrency }}}' \ + {{ cifmw_rally_task_extra_args }} || RALLY_TASK_RC=$? +rally task report --out {{ _cifmw_rally_container_reports_dir }}/rally-report.html || true +rally task report --json \ + --out {{ _cifmw_rally_container_reports_dir }}/rally-report.json || true +exit ${RALLY_TASK_RC:-0} diff --git a/roles/rally/vars/main.yml b/roles/rally/vars/main.yml new file mode 100644 index 000000000..1c8c0b9d6 --- /dev/null +++ b/roles/rally/vars/main.yml @@ -0,0 +1,18 @@ +--- +# 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. + +_cifmw_rally_container_tasks_dir: "/var/lib/rally/tasks" +_cifmw_rally_container_reports_dir: "/var/lib/rally/reports" diff --git a/zuul.d/molecule.yaml b/zuul.d/molecule.yaml index 84a9fee42..ff6ddce04 100644 --- a/zuul.d/molecule.yaml +++ b/zuul.d/molecule.yaml @@ -672,6 +672,17 @@ parent: cifmw-molecule-base vars: TEST_RUN: radvd +- job: + files: + - ^common-requirements.txt + - ^test-requirements.txt + - ^roles/rally/.* + - ^ci/playbooks/molecule.* + - ^.config/molecule/.* + name: cifmw-molecule-rally + parent: cifmw-molecule-base + vars: + TEST_RUN: rally - job: files: - ^common-requirements.txt diff --git a/zuul.d/projects.yaml b/zuul.d/projects.yaml index f7f8ef64f..a2e3f28ef 100644 --- a/zuul.d/projects.yaml +++ b/zuul.d/projects.yaml @@ -93,6 +93,7 @@ - cifmw-molecule-podman - cifmw-molecule-polarion - cifmw-molecule-radvd + - cifmw-molecule-rally - cifmw-molecule-recognize_ssh_keypair - cifmw-molecule-registry_deploy - cifmw-molecule-repo_setup