Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6e3b687
Fix idempotence on comparing capabilities for podman
ednxzu Nov 29, 2025
a47fd77
Fix podman idempotence on comparing container dimensions
ednxzu Oct 29, 2025
7a6cd24
Fix idempotence on podman volume comparison
ednxzu Oct 30, 2025
645d204
ovn-db: set RELAY_ID per iterated relay container
bbezak Feb 16, 2026
2d4f30e
Merge "ovn-db: set RELAY_ID per iterated relay container" into stable…
Mar 11, 2026
c2d1255
ovn: make iterated relay checks idempotent
bbezak Feb 10, 2026
b7821be
skyline: add TLSv1.2 and TLSv1.3 support for HTTPS upstream endpoints
berendt Jun 30, 2025
f39083b
Fix inventory file for cyborg control services
ednxzu Nov 8, 2024
db6da6a
Bind cAdvisor exporter to listen only internally
oneswig Mar 17, 2026
f5b5e56
nova-cell: parallelize libvirt version check
bbezak Mar 17, 2026
7adca80
Merge "skyline: add TLSv1.2 and TLSv1.3 support for HTTPS upstream en…
Mar 20, 2026
e2fad9e
Merge "nova-cell: parallelize libvirt version check" into stable/2025.1
Mar 20, 2026
063e03e
Merge "Bind cAdvisor exporter to listen only internally" into stable/…
Mar 20, 2026
6797ad1
Merge "Fix idempotence on comparing capabilities for podman" into sta…
Mar 25, 2026
a6fd763
Merge "Fix podman idempotence on comparing container dimensions" into…
Mar 25, 2026
02aa0fa
Merge "Fix idempotence on podman volume comparison" into stable/2025.1
Mar 25, 2026
d113cda
Properly stop iterated services
jayjahns Sep 29, 2025
c84ae0f
[2025.1 only] Support ProxySQL 3.0.x on 2025.1
seunghun1ee Jan 26, 2026
440ba05
Run cinder-backup with ipc_mode: host
chembervint Mar 26, 2026
0468811
Merge "[2025.1 only] Support ProxySQL 3.0.x on 2025.1" into stable/20…
Mar 31, 2026
156262e
Merge "Run cinder-backup with ipc_mode: host" into stable/2025.1
Apr 2, 2026
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
4 changes: 4 additions & 0 deletions ansible/group_vars/all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,10 @@ prometheus_blackbox_exporter_port: "9115"
prometheus_instance_label:

proxysql_admin_port: "6032"
# Integer variable to set ProxySQL version. Valid options are 2 and 3
# When it's set to 2 (Default), ProxySQL 2.7.x is deployed.
# When it's set to 3, ProxySQL 3.0.x is used.
proxysql_version: 2

rabbitmq_port: "{{ '5671' if rabbitmq_enable_tls | bool else '5672' }}"
rabbitmq_management_port: "15672"
Expand Down
1 change: 0 additions & 1 deletion ansible/inventory/all-in-one
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ control

[cyborg:children]
control
compute

[tacker:children]
control
Expand Down
1 change: 0 additions & 1 deletion ansible/inventory/multinode
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ control

[cyborg:children]
control
compute

[gnocchi:children]
control
Expand Down
182 changes: 136 additions & 46 deletions ansible/module_utils/kolla_podman_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ def parse_volumes(self, volumes, mounts, filtered_volumes):
)
if src == 'devpts':
mount_item = dict(
source=src,
target=dest,
type='devpts'
)
Expand Down Expand Up @@ -306,6 +307,42 @@ def compare_container(self):
self.changed = True
return self.changed

def compare_cap_add(self, container_info):
new_cap_add = self.params.get('cap_add', list()).copy()

new_cap_add = [
'CAP_' + cap.upper()
if not cap.upper().startswith('CAP_')
else cap.upper()
for cap in new_cap_add
]

try:
current_cap_add = (
container_info['HostConfig'].get('CapAdd', None) or []
)
except (KeyError, TypeError):
current_cap_add = []

current_cap_add = [cap.upper() for cap in current_cap_add]

privileged = container_info['HostConfig'].get('Privileged', False)
if not privileged:
# NOTE(blanson): prepare_container_args() always adds AUDIT_WRITE
# for non-privileged containers. Also works around Podman <4.4 bug
# where AUDIT_WRITE doesn't appear in inspect. Since capabilities
# can't be modified post-creation, this won't mask real drift.
if 'CAP_AUDIT_WRITE' not in new_cap_add:
new_cap_add.append('CAP_AUDIT_WRITE')

if 'CAP_AUDIT_WRITE' not in current_cap_add:
current_cap_add.append('CAP_AUDIT_WRITE')

if set(new_cap_add).symmetric_difference(set(current_cap_add)):
return True

return False

def compare_pid_mode(self, container_info):
new_pid_mode = self.params.get('pid_mode') or self.params.get('pid')
current_pid_mode = container_info['HostConfig'].get('PidMode')
Expand Down Expand Up @@ -349,50 +386,65 @@ def check_slash(string):
else:
return string

raw_volumes, binds = self.generate_volumes()
raw_vols, current_binds = self.generate_volumes(
container_info['HostConfig'].get('Binds'))

current_vols = [check_slash(vol) for vol in raw_vols if vol]
volumes = [check_slash(vol) for vol in raw_volumes if vol]
# NOTE(blanson): Podman automatically appends default flags
# such as rprivate, nosuid, nodev, rbind to all mounts.
# For special paths like /proc, /run, /sys, and /var/run,
# noexec is also added by default. We remove these defaults
# because they do not reflect a meaningful difference
# between the requested and current container configuration.
# Additionally, if neither 'ro' nor 'rw' is specified,
# we implicitly assume 'rw' (Podman's default behavior).
def normalize_mode(path, mode):
default_flags = {'rprivate', 'nosuid', 'nodev', 'rbind'}
special_paths_noexec = {'/proc', '/run', '/sys', '/var/run'}

flags = set(mode.split(',')) if mode else set()
flags -= default_flags

if any(path.startswith(p) for p in special_paths_noexec):
flags.discard('noexec')
if not (flags & {'ro', 'rw'}):
flags.add('rw')
return flags

# NOTE(blanson): Convert a binds dict into a list of
# (src, dst, normalized_flags) tuples. Normalization ignores
# default Podman flags and noexec for special paths to allow
# consistent comparison.
def build_bind_list(binds_dict):
lst = []
for src, info in (binds_dict or {}).items():
src_path = check_slash(src)
dst_path = check_slash(info['bind'])
mode_flags = normalize_mode(
dst_path,
info['mode'],
)
lst.append((src_path, dst_path, mode_flags))
return lst

if not volumes:
volumes = list()
if not current_vols:
current_vols = list()
if not current_binds:
current_binds = list()
binds_input = container_info['HostConfig'].get('Binds')
raw_volumes, binds = self.generate_volumes()
raw_vols, current_binds = (
[], {}
) if not binds_input else self.generate_volumes(binds_input)

volumes.sort()
current_vols.sort()
volumes = [check_slash(v) for v in raw_volumes or [] if v]
current_vols = [check_slash(v) for v in raw_vols or [] if v]

if set(volumes).symmetric_difference(set(current_vols)):
if set(volumes) != set(current_vols):
return True

new_binds = list()
new_current_binds = list()
if binds:
for k, v in binds.items():
k = check_slash(k)
v['bind'] = check_slash(v['bind'])
new_binds.append(
"{}:{}:{}".format(k, v['bind'], v['mode']))

if current_binds:
for k, v in current_binds.items():
k = check_slash(k)
v['bind'] = check_slash(v['bind'])
if 'ro' in v['mode']:
v['mode'] = 'ro'
else:
v['mode'] = 'rw'
new_current_binds.append(
"{}:{}:{}".format(k, v['bind'], v['mode'][0:2]))

new_binds.sort()
new_current_binds.sort()
req_bind_list = [
(src, dst, frozenset(flags))
for src, dst, flags in build_bind_list(binds)
]
cur_bind_list = [
(src, dst, frozenset(flags))
for src, dst, flags in build_bind_list(current_binds)
]

if set(new_binds).symmetric_difference(set(new_current_binds)):
if set(req_bind_list) != set(cur_bind_list):
return True

def compare_dimensions(self, container_info):
Expand All @@ -416,15 +468,53 @@ def compare_dimensions(self, container_info):
failed=True, msg=repr("Unsupported dimensions"),
unsupported_dimensions=unsupported)
current_dimensions = container_info['HostConfig']

# NOTE(blanson): We normalize ulimits names because the podman api
# returns them as RLIMIT_<UPPER_STRING>
def normalize_ulimit_name(name):
name = name.upper()
if not name.startswith('RLIMIT_'):
return 'RLIMIT_' + name
return name

for key1, key2 in dimension_map.items():
# NOTE(mgoddard): If a resource has been explicitly requested,
# check for a match. Otherwise, ensure it is set to the default.
if key1 in new_dimensions:
if key1 == 'ulimits':
if self.compare_ulimits(new_dimensions[key1],
current_dimensions[key2]):
return True
elif new_dimensions[key1] != current_dimensions[key2]:
if key1 == 'ulimits':
current_ulimits = current_dimensions.get(key2, [])

# NOTE(blanson): We strip podman default ulimits
# because they are not settable by users anyways
# and break idempotency.
filtered_current_ulimits = [
u for u in current_ulimits
if u.get('Name') not in ('RLIMIT_NOFILE', 'RLIMIT_NPROC')
]

desired_ulimits = new_dimensions.get('ulimits', {})

desired_ulimits = {
normalize_ulimit_name(name): limits
for name, limits in desired_ulimits.items()
if normalize_ulimit_name(name) not in (
'RLIMIT_NOFILE', 'RLIMIT_NPROC')
}

normalized_current = [
{
'Name': normalize_ulimit_name(u['Name']),
'Soft': u.get('Soft'),
'Hard': u.get('Hard')
}
for u in filtered_current_ulimits
]

if self.compare_ulimits(
desired_ulimits,
normalized_current
):
return True

elif key1 in new_dimensions:
if new_dimensions[key1] != current_dimensions.get(key2):
return True
elif current_dimensions[key2]:
# The default values of all (except ulimits) currently
Expand Down
1 change: 1 addition & 0 deletions ansible/roles/cinder/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ cinder_services:
enabled: "{{ enable_cinder_backup | bool }}"
image: "{{ cinder_backup_image_full }}"
privileged: True
ipc_mode: "host"
volumes: "{{ cinder_backup_default_volumes + cinder_backup_extra_volumes + lookup('vars', 'run_default_volumes_' + kolla_container_engine) }}"
dimensions: "{{ cinder_backup_dimensions }}"
healthcheck: "{{ cinder_backup_healthcheck }}"
Expand Down
3 changes: 2 additions & 1 deletion ansible/roles/cinder/handlers/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
name: "{{ service.container_name }}"
image: "{{ service.image }}"
privileged: "{{ service.privileged | default(False) }}"
ipc_mode: "{{ service.ipc_mode | default('') }}"
ipc_mode: "{{ service.ipc_mode | default(omit) }}"
tmpfs: "{{ service.tmpfs | default(omit) }}"
volumes: "{{ service.volumes | reject('equalto', '') | list }}"
dimensions: "{{ service.dimensions }}"
Expand All @@ -55,6 +55,7 @@
name: "{{ service.container_name }}"
image: "{{ service.image }}"
privileged: "{{ service.privileged | default(False) }}"
ipc_mode: "{{ service.ipc_mode | default(omit) }}"
volumes: "{{ service.volumes | reject('equalto', '') | list }}"
dimensions: "{{ service.dimensions }}"
healthcheck: "{{ service.healthcheck | default(omit) }}"
Expand Down
4 changes: 3 additions & 1 deletion ansible/roles/loadbalancer/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ haproxy_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_
haproxy_tag: "{{ openstack_tag }}"
haproxy_image_full: "{{ haproxy_image }}:{{ haproxy_tag }}"

proxysql_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ docker_image_name_prefix }}proxysql"
proxysql_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ docker_image_name_prefix }}proxysql{{ proxysql_version_suffix }}"
proxysql_tag: "{{ openstack_tag }}"
proxysql_image_full: "{{ proxysql_image }}:{{ proxysql_tag }}"

Expand Down Expand Up @@ -233,3 +233,5 @@ mariadb_singlenode: "{{ mariadb_shards_info.shards.values() | map(attribute='hos
mariadb_shun_on_failures: "{{ '10' if mariadb_singlenode else '' }}"
mariadb_connect_retries_delay: "{{ '1000' if mariadb_singlenode else '' }}"
mariadb_connect_retries_on_failure: "{{ '20' if mariadb_singlenode else '' }}"

proxysql_version_suffix: "{{ '-3' if proxysql_version | int == 3 else '' }}"
27 changes: 8 additions & 19 deletions ansible/roles/nova-cell/tasks/version-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,32 +40,25 @@
name:
- "{{ service.container_name }}"
register: container_facts_per_host
run_once: true
delegate_to: "{{ item }}"
loop: "{{ groups[service.group] }}"
loop_control:
label: "{{ item }}"
when: inventory_hostname in groups[service.group]

- name: Get current Libvirt version
any_errors_fatal: true
become: true
command: "{{ kolla_container_engine }} exec {{ service.container_name }} libvirtd --version"
register: libvirt_version_current_results
changed_when: false
run_once: true
delegate_to: "{{ item.item }}"
loop: "{{ container_facts_per_host.results }}"
loop_control:
label: "{{ item.item }}"
when:
- item.containers[service.container_name] is defined
- item.containers[service.container_name].State.Running
- container_facts_per_host is not skipped
- container_facts_per_host.containers[service.container_name] is defined
- (hostvars[groups[service.group] | first].service_image_info.images | default([]) | length) > 0
- item.containers[service.container_name].Image
- container_facts_per_host.containers[service.container_name].Image
!= hostvars[groups[service.group] | first].service_image_info.images[0].Id

- name: Check that the new Libvirt version is >= current
any_errors_fatal: true
vars:
current_version: "{{ item.stdout | regex_search('[0-9]+\\.[0-9]+\\.[0-9]+') }}"
current_version: "{{ libvirt_version_current_results.stdout | regex_search('[0-9]+\\.[0-9]+\\.[0-9]+') }}"
new_version: "{{ hostvars[groups[service.group] | first].libvirt_new_version }}"
assert:
that: "{{ new_version is version(current_version, '>=', strict=true) }}"
Expand All @@ -75,11 +68,7 @@
that you want to do this, please skip the tag `nova-libvirt-version-check`.
success_msg: >
Libvirt version check successful: target {{ new_version }} >= current {{ current_version }}.
run_once: true
loop: "{{ libvirt_version_current_results.results }}"
loop_control:
label: "{{ item.item }}"
when: item.stdout is defined
when: libvirt_version_current_results is not skipped

tags: nova-libvirt-version-check
when: enable_nova_libvirt_container | bool and (groups[service.group] | length) > 0
Expand Down
2 changes: 1 addition & 1 deletion ansible/roles/ovn-db/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ ovn_db_services:
group: ovn-sb-db-relay
enabled: "{{ enable_ovn_sb_db_relay | bool }}"
environment:
RELAY_ID: "{{ ovn_sb_db_relay_group_id | default('1') }}"
RELAY_ID: "{{ item | default(ovn_sb_db_relay_group_id | default('1')) | string }}"
image: "{{ ovn_sb_db_relay_image_full }}"
iterate: true
iterate_var: "{{ ovn_sb_db_relay_count | int }}"
Expand Down
2 changes: 1 addition & 1 deletion ansible/roles/ovn-db/templates/ovn-sb-db-relay.json.j2
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"source": "{{ container_config_directory }}/ovsdb-relay.json",
"dest": "/etc/ovn/ovsdb-relay.json",
"owner": "openvswitch",
"owner": "root",
"perm": "0600"
}
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"command": "/opt/cadvisor --port={{ prometheus_cadvisor_port }} --log_dir=/var/log/kolla/prometheus{% if prometheus_cadvisor_cmdline_extras %} {{ prometheus_cadvisor_cmdline_extras }}{% endif %}",
"command": "/opt/cadvisor --port={{ prometheus_cadvisor_port }} --log_dir=/var/log/kolla/prometheus --listen_ip {{ 'api' | kolla_address(inventory_hostname) }}{% if prometheus_cadvisor_cmdline_extras %} {{ prometheus_cadvisor_cmdline_extras }}{% endif %}",
"config_files": [
{% if kolla_copy_ca_into_containers | bool %}
{
Expand Down
7 changes: 3 additions & 4 deletions ansible/roles/service-check-containers/tasks/iterated.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
- name: "{{ kolla_role_name | default(project_name) }} | Check containers with iteration"
become: true
vars:
service: "{{ outer_item.value }}"
service: "{{ lookup('vars', (kolla_role_name | default(project_name)) + '_services')[service_name] }}"
kolla_container:
action: "compare_container"
common_options: "{{ docker_common_options }}"
name: "{{ service.container_name }}"
name: "{{ service.container_name }}_{{ item }}"
image: "{{ service.image | default(omit) }}"
volumes: "{{ service.volumes | default(omit) }}"
dimensions: "{{ service.dimensions | default(omit) }}"
Expand All @@ -22,8 +22,7 @@
labels: "{{ service.labels | default(omit) }}"
command: "{{ service.command | default(omit) }}"
cgroupns_mode: "{{ service.cgroupns_mode | default(omit) }}"
loop:
- "{{ range(1,(service.iterate_var | int) + 1) | list }}"
loop: "{{ range(1, (iterate_count | int) + 1) | list }}"
register: container_check

# NOTE(yoctozepto): Must be a separate task because one cannot see the whole
Expand Down
5 changes: 3 additions & 2 deletions ansible/roles/service-check-containers/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@

- name: Include tasks
vars:
service: "{{ outer_item.value }}"
service_name: "{{ outer_item.key }}"
iterate_count: "{{ outer_item.value.iterate_var | int }}"
include_tasks: iterated.yml
loop: "{{ lookup('vars', (kolla_role_name | default(project_name)) + '_services') | select_services_enabled_and_mapped_to_host | dict2items }}"
loop_control:
loop_var: outer_item
when: (service.iterate | default(False)) | bool
when: (outer_item.value.iterate | default(False)) | bool
Loading
Loading