Add openstack_user_config verification playbook as healthcheck

The playbook is designed to test basic sanity of resulting
openstack_user_config and to ensure to weird issues are not introduced
with some of latest changes.
Eventually, this playbook is handy in a CI pipeline, to prevent faulty
config to be published and promoted to a region.

We also are to use the playbook in a molecule test to ensure we're
having sane examples in docs

Depends-On: https://review.opendev.org/c/openstack/openstack-ansible/+/945025
Change-Id: I50bb8b05cdf0ed8fa486f5b5d65405b3afa84bae
This commit is contained in:
Dmitriy Rabotyagov
2025-01-11 18:29:01 +01:00
parent 3bd48b7c5a
commit 9ba71e0b6a
6 changed files with 297 additions and 1 deletions

View File

@@ -0,0 +1,29 @@
---
# Copyright 2025, Cleura AB
#
# 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.
molecule_packages:
debian:
- git
- python3-dev
- python3-packaging
- python3-pip
- python3-setuptools
- python3-venv
redhat:
- git-core
- python3-devel
- python3-packaging
- python3-pip
- python3-setuptools

View File

@@ -0,0 +1,34 @@
---
# Copyright 2025, Cleura AB
#
# 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.
openstack_test_service_groups_length: 3
openstack_test_service_groups:
- cinder_api
- galera_all
- glance_api
- keystone_all
- neutron_server
- nova_api_os_compute
- placement_all
- rabbitmq_all
openstack_test_mutualy_exclusive_groups:
- ['nova_api_os_compute', 'nova_compute']
- ['neutron_server', 'neutron_ovn_northd']
- ['neutron_server', 'neutron_ovn_controller']
- ['neutron_ovn_gateway', 'nova_compute']
openstack_test_collocated_groups:
- ['nova_compute', 'neutron_ovn_controller']

View File

@@ -0,0 +1,33 @@
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: "integrated-${MOLECULE_SCENARIO_NAME}"
image: "${DOCKER_REGISTRY:-quay.io/gotmax23}/${DOCKER_IMAGE_TAG:-debian-systemd:bookworm}"
command: ${DOCKER_COMMAND:-""}
pre_build_image: true
privileged: true
systemd: true
groups:
- deploy_host
provisioner:
name: ansible
lint:
name: ansible-lint
config_options:
defaults:
inject_facts_as_vars: false
inventory:
links:
group_vars: ./group_vars/
playbooks:
prepare: prepare.yml
converge: ../../playbooks/healthcheck/user_config.yml
scenario:
name: user_config

View File

@@ -0,0 +1,61 @@
---
# Copyright 2025, Cleura AB
#
# 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: Simulating valid deploy host
hosts: deploy_host
vars:
testing_branch: "{{ lookup('ansible.builtin.env', 'TEST_BRANCH', default='master') }}"
tasks:
- name: Create required folders
ansible.builtin.file:
path: /etc/openstack_deploy
state: directory
mode: "0755"
- name: Update apt cache
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
when:
- ansible_facts['os_family'] | lower == 'debian'
- name: Install required python3 packages
ansible.builtin.package:
name: "{{ molecule_packages[ansible_facts['os_family'] | lower] }}"
state: present
- name: Download sample openstack_user_config
ansible.builtin.get_url:
url: https://opendev.org/openstack/openstack-ansible/raw/branch/{{ testing_branch }}/etc/openstack_deploy/openstack_user_config.yml.prod.example
dest: /etc/openstack_deploy/openstack_user_config.yml
mode: "0644"
register: download_config
until: download_config is success
retries: 5
delay: 2
- name: Install openstack-ansible as python package
ansible.builtin.pip:
name: "git+https://opendev.org/openstack/openstack-ansible@{{ testing_branch }}#egg=openstack-ansible"
extra_args: >-
-c{{ lookup('ansible.builtin.env', 'TOX_CONSTRAINTS_FILE', default='https://releases.openstack.org/constraints/upper/master') }}
-rhttps://opendev.org/openstack/openstack-ansible/raw/branch/{{ testing_branch }}/requirements.txt
-e
virtualenv: "/opt/ansible-runtime"
virtualenv_command: "python3 -m venv"
register: install_osa
until: install_osa is success
retries: 5
delay: 2

View File

@@ -0,0 +1,137 @@
---
# Copyright 2025, Cleura AB
#
# 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.
# Playbook to ensure that resulting inventory is generated according to
# deployer intentions.
# Sample variables to control test behaviour of the playbook:
#
# openstack_test_user_config_dir: /etc/openstack_deploy
# openstack_test_service_groups_length: 3
# openstack_test_service_groups:
# - galera_all
# - glance_api
# - keystone_all
# - neutron_server
# - nova_api_os_compute
# - placement_all
# - rabbitmq_all
#
# openstack_test_mutualy_exclusive_groups:
# - ['nova_api_os_compute', 'nova_compute']
# - ['neutron_server', 'neutron_ovn_northd']
# - ['neutron_server', 'neutron_ovn_controller']
# - ['neutron_ovn_gateway', 'nova_compute']
#
# openstack_test_collocated_groups:
# - ['nova_compute', 'neutron_ovn_controller']
- name: Testing resulting inventory from provided openstack_user_config
hosts: "{{ ('deploy_host' in groups) | ternary('deploy_host', 'localhost') }}"
tasks:
- name: Verify dynamic_inventory is not failing
ansible.builtin.command: >-
/opt/ansible-runtime/bin/openstack-ansible-inventory --config {{ openstack_test_user_config_dir | default('/etc/openstack_deploy') }}
changed_when: false
register: dynamic_inventory
- name: Register OSA inventory content variable
ansible.builtin.set_fact:
osa_inventory: "{{ dynamic_inventory.stdout | from_json }}"
- name: Get all real hosts out of children
ansible.builtin.set_fact:
service_hosts: |
{% set hosts = {} %}
{% for service in openstack_test_service_groups | default([]) %}
{% if osa_inventory[service]['hosts'] | length > 0 %}
{% set _ = hosts.update({service: osa_inventory[service]['hosts']}) %}
{% else %}
{% set _ = hosts.update({service: []}) %}
{% for group in osa_inventory[service]['children'] %}
{% set _ = hosts[service].extend(osa_inventory[group]['hosts']) %}
{% endfor %}
{% endif %}
{% endfor %}
{{ hosts }}
- name: Ensure that services are not running on bare metal
ansible.builtin.assert:
that:
- >-
'is_metal' not in osa_inventory['_meta']['hostvars'][item]['properties'] or
not osa_inventory['_meta']['hostvars'][item]['properties']['is_metal'] | bool
- osa_inventory['_meta']['hostvars'][item]['container_tech'] == 'lxc'
with_items: "{{ service_hosts.values() | flatten }}"
tags:
- skip_ansible_lint
- name: Ensure that each group has hosts {{ openstack_test_service_groups_length | default(3) }}
ansible.builtin.assert:
that:
- item.value | length == openstack_test_service_groups_length | default(3)
with_dict: "{{ service_hosts }}"
- name: Ensure that containers are split over different physical hosts
vars:
physical_hosts: |
{% set p_hosts = [] %}
{% for service in item.value %}
{% set _ = p_hosts.append(osa_inventory['_meta']['hostvars'][service]['physical_host']) %}
{% endfor %}
{{ p_hosts }}
ansible.builtin.assert:
that:
- physical_hosts | unique | length == openstack_test_service_groups_length | default(3)
with_dict: "{{ service_hosts }}"
- name: Verify that openstack_test_mutualy_exclusive_groups are respected
vars:
exclusive_hosts: |
{% set hosts = {} %}
{% for service in openstack_test_mutualy_exclusive_groups | default([]) | flatten | unique %}
{% if osa_inventory[service]['hosts'] | length > 0 %}
{% set _ = hosts.update({service: osa_inventory[service]['hosts']}) %}
{% else %}
{% set _ = hosts.update({service: []}) %}
{% for group in osa_inventory[service]['children'] %}
{% set _ = hosts[service].extend(osa_inventory[group]['hosts']) %}
{% endfor %}
{% endif %}
{% endfor %}
{{ hosts }}
ansible.builtin.assert:
that:
- (exclusive_hosts[item[0]] | select('in', exclusive_hosts[item[1]])) | length == 0
loop: "{{ openstack_test_mutualy_exclusive_groups | default([]) }}"
- name: Verify that second element of openstack_test_collocated_groups contain all hosts from first element
vars:
collocated_hosts: |
{% set hosts = {} %}
{% for service in openstack_test_collocated_groups | default([]) | flatten | unique %}
{% if osa_inventory[service]['hosts'] | length > 0 %}
{% set _ = hosts.update({service: osa_inventory[service]['hosts']}) %}
{% else %}
{% set _ = hosts.update({service: []}) %}
{% for group in osa_inventory[service]['children'] %}
{% set _ = hosts[service].extend(osa_inventory[group]['hosts']) %}
{% endfor %}
{% endif %}
{% endfor %}
{{ hosts }}
ansible.builtin.assert:
that:
- (collocated_hosts[item[0]] | select('in', collocated_hosts[item[1]])) == collocated_hosts[item[0]]
loop: "{{ openstack_test_collocated_groups | default([]) }}"

View File

@@ -65,10 +65,12 @@ deps =
-r{env:OSA_TEST_REQUIREMENTS_FILE:https://opendev.org/openstack/openstack-ansible/raw/branch/{env:TEST_BRANCH:master}/test-requirements.txt}
commands =
molecule test
molecule test -s default
molecule test -s user_config
passenv =
{[testenv]passenv}
DOCKER_REGISTRY
DOCKER_IMAGE_TAG
DOCKER_COMMAND
TEST_BRANCH