Skip to content

Commit 0c40482

Browse files
authored
Merge pull request #870 from stackhpc/upstream/master-2026-05-11
Synchronise master with upstream
2 parents 0af686e + 627ed9b commit 0c40482

25 files changed

Lines changed: 327 additions & 57 deletions

File tree

ansible/group_vars/all/rabbitmq.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ enable_rabbitmq: "{{ 'yes' if om_rpc_transport == 'rabbit' or om_notify_transpor
55
# RabbitMQ options
66
####################
77
rabbitmq_user: "openstack"
8-
rabbitmq_monitoring_user: ""
8+
rabbitmq_monitoring_user: "openstack-monitoring"
99
# Whether to enable TLS encryption for RabbitMQ client-server communication.
1010
rabbitmq_enable_tls: false
1111
# CA certificate bundle in RabbitMQ container.

ansible/module_utils/kolla_container_worker.py

Lines changed: 175 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ def __init__(self, module):
2828
self.changed = False
2929
# Use this to store arguments to pass to exit_json().
3030
self.result = {}
31+
# Populated by compare_config() when config differs, so diff_config()
32+
# can surface the output without running the exec a second time.
33+
self._config_diff = None
3134

3235
self.systemd = SystemdWorker(self.params)
3336

@@ -57,36 +60,182 @@ def check_container(self):
5760

5861
def compare_container(self):
5962
container = self.check_container()
60-
if (not container or
61-
self.check_container_differs() or
62-
self.compare_config() or
63-
self.systemd.check_unit_change()):
63+
failures = []
64+
65+
if not container:
66+
failures.append({'name': 'container_missing',
67+
'current': None,
68+
'desired': self.params.get('name')})
69+
else:
70+
container_info = self.get_container_info()
71+
failures.extend(self.check_container_differs(container_info))
72+
if self.compare_config():
73+
failures.append({'name': 'config',
74+
'current': self._config_diff,
75+
'desired': 'up_to_date'})
76+
if self.systemd.check_unit_change():
77+
failures.append({'name': 'systemd_unit',
78+
'current': 'out_of_date',
79+
'desired': 'up_to_date'})
80+
81+
if failures:
6482
self.changed = True
83+
self.result['comparison_failures'] = failures
84+
6585
return self.changed
6686

67-
def check_container_differs(self):
68-
container_info = self.get_container_info()
87+
def check_container_differs(self, container_info=None):
88+
"""Return a list of dicts describing differences from the desired state
89+
90+
Each entry is a dict with keys:
91+
- ``name``: the comparison that failed (e.g. ``'image'``)
92+
- ``current``: the value found on the running container
93+
- ``desired``: the value requested by the module parameters
94+
95+
An empty list means no differences were found.
96+
"""
97+
if container_info is None:
98+
container_info = self.get_container_info()
6999
if not container_info:
70-
return True
100+
return [{'name': 'container_info_missing',
101+
'current': None, 'desired': None}]
102+
103+
checks = [
104+
'cap_add',
105+
'security_opt',
106+
'image',
107+
'ipc_mode',
108+
'labels',
109+
'privileged',
110+
'pid_mode',
111+
'cgroupns_mode',
112+
'tmpfs',
113+
'volumes',
114+
'volumes_from',
115+
'environment',
116+
'container_state',
117+
'dimensions',
118+
'command',
119+
'healthcheck',
120+
]
121+
122+
failures = []
123+
for name in checks:
124+
if getattr(self, 'compare_' + name)(container_info):
125+
diff = getattr(self, 'diff_' + name)(container_info)
126+
failures.append({'name': name,
127+
'current': diff[0],
128+
'desired': diff[1]})
129+
return failures
130+
131+
# ------------------------------------------------------------------
132+
# diff_* methods: return (current, desired) for each comparison.
133+
# These are only called when the corresponding compare_* has already
134+
# returned True, so they can focus purely on extracting values.
135+
# ------------------------------------------------------------------
136+
137+
def diff_ipc_mode(self, container_info):
138+
current = container_info['HostConfig'].get('IpcMode') or None
139+
return current, self.params.get('ipc_mode')
140+
141+
def diff_cap_add(self, container_info):
142+
try:
143+
current = container_info['HostConfig'].get('CapAdd') or []
144+
except (KeyError, TypeError):
145+
current = []
146+
return sorted(current), sorted(self.params.get('cap_add', []))
71147

72-
return (
73-
self.compare_cap_add(container_info) or
74-
self.compare_security_opt(container_info) or
75-
self.compare_image(container_info) or
76-
self.compare_ipc_mode(container_info) or
77-
self.compare_labels(container_info) or
78-
self.compare_privileged(container_info) or
79-
self.compare_pid_mode(container_info) or
80-
self.compare_cgroupns_mode(container_info) or
81-
self.compare_tmpfs(container_info) or
82-
self.compare_volumes(container_info) or
83-
self.compare_volumes_from(container_info) or
84-
self.compare_environment(container_info) or
85-
self.compare_container_state(container_info) or
86-
self.compare_dimensions(container_info) or
87-
self.compare_command(container_info) or
88-
self.compare_healthcheck(container_info)
89-
)
148+
def diff_security_opt(self, container_info):
149+
try:
150+
current = container_info['HostConfig'].get('SecurityOpt') or []
151+
except (KeyError, TypeError):
152+
current = []
153+
return sorted(current), sorted(self.params.get('security_opt', []))
154+
155+
def diff_image(self, container_info):
156+
current = (container_info.get('Image') or
157+
container_info.get('Config', {}).get('Image'))
158+
return current, self.params.get('image')
159+
160+
def diff_labels(self, container_info):
161+
current = container_info['Config'].get('Labels', {})
162+
desired = self.params.get('labels')
163+
return current, desired
164+
165+
def diff_privileged(self, container_info):
166+
current = container_info['HostConfig'].get('Privileged')
167+
return current, self.params.get('privileged')
168+
169+
def diff_pid_mode(self, container_info):
170+
current = container_info['HostConfig'].get('PidMode') or None
171+
desired = self.params.get('pid_mode')
172+
return current, desired
173+
174+
def diff_cgroupns_mode(self, container_info):
175+
current = (container_info['HostConfig'].get('CgroupnsMode') or
176+
container_info['HostConfig'].get('CgroupMode') or 'host')
177+
return current, self.params.get('cgroupns_mode')
178+
179+
def diff_tmpfs(self, container_info):
180+
current = list(container_info['HostConfig'].get('Tmpfs') or [])
181+
desired = list(self.generate_tmpfs() or [])
182+
return sorted(current), sorted(desired)
183+
184+
def diff_volumes(self, container_info):
185+
volumes, binds = self.generate_volumes()
186+
current_vols = list(container_info['Config'].get('Volumes') or [])
187+
current_binds = list(container_info['HostConfig'].get('Binds') or [])
188+
desired_vols = list(volumes or [])
189+
desired_binds = []
190+
if binds:
191+
for k, v in binds.items():
192+
desired_binds.append('{}:{}:{}'.format(k,
193+
v['bind'],
194+
v['mode']))
195+
return {'volumes': current_vols, 'binds': sorted(current_binds)}, \
196+
{'volumes': desired_vols, 'binds': sorted(desired_binds)}
197+
198+
def diff_volumes_from(self, container_info):
199+
current = list(container_info['HostConfig'].get('VolumesFrom') or [])
200+
desired = list(self.params.get('volumes_from') or [])
201+
return sorted(current), sorted(desired)
202+
203+
def diff_environment(self, container_info):
204+
current_env = {}
205+
for kv in container_info['Config'].get('Env', []):
206+
k, v = kv.split('=', 1)
207+
current_env[k] = v
208+
desired_env = self.params.get('environment', {})
209+
# Only show keys that are actually different
210+
return sorted(current_env), sorted(desired_env)
211+
212+
def diff_container_state(self, container_info):
213+
current = container_info['State'].get('Status')
214+
desired = self.params.get('state')
215+
return current, desired
216+
217+
def diff_dimensions(self, container_info):
218+
new_dimensions = self.params.get('dimensions')
219+
current_dimensions = container_info['HostConfig']
220+
current = {k2: current_dimensions.get(k2)
221+
for k1, k2 in self.dimension_map.items()
222+
if k1 in new_dimensions}
223+
desired = {self.dimension_map[k]: v
224+
for k, v in new_dimensions.items()
225+
if k in self.dimension_map}
226+
return current, desired
227+
228+
def diff_command(self, container_info):
229+
current = '{} {}'.format(
230+
container_info.get('Path', ''),
231+
' '.join(container_info.get('Args', []))
232+
).strip()
233+
return current, self.params.get('command')
234+
235+
def diff_healthcheck(self, container_info):
236+
current = container_info['Config'].get('Healthcheck')
237+
desired = self.params.get('healthcheck')
238+
return current, desired
90239

91240
def compare_ipc_mode(self, container_info):
92241
new_ipc_mode = self.params.get('ipc_mode')
@@ -172,7 +321,7 @@ def compare_labels(self, container_info):
172321
if k in new_labels:
173322
if v != new_labels[k]:
174323
return True
175-
else:
324+
elif k in current_labels:
176325
del current_labels[k]
177326

178327
if new_labels != current_labels:

ansible/module_utils/kolla_docker_worker.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ def compare_config(self):
158158
# in the mean time) - assume config is stale = return True.
159159
# Else, propagate the server error back.
160160
if e.is_client_error():
161+
self._config_diff = 'container unavailable for config check'
161162
return True
162163
else:
163164
raise
@@ -169,11 +170,14 @@ def compare_config(self):
169170
if exec_inspect['ExitCode'] == 0:
170171
return False
171172
elif exec_inspect['ExitCode'] == 1:
173+
self._config_diff = (output.decode('utf-8') if
174+
isinstance(output, bytes) else output)
172175
return True
173176
elif exec_inspect['ExitCode'] == 137:
174177
# NOTE(yoctozepto): This is Docker's command exit due to container
175178
# exit. It means the container is unstable so we are better off
176179
# marking it as requiring a restart due to config update.
180+
self._config_diff = 'container exited abruptly during config check'
177181
return True
178182
else:
179183
raise Exception('Failed to compare container configuration: '

ansible/module_utils/kolla_podman_worker.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -298,15 +298,6 @@ def get_container_info(self):
298298

299299
return container.attrs
300300

301-
def compare_container(self):
302-
container = self.check_container()
303-
if (not container or
304-
self.check_container_differs() or
305-
self.compare_config() or
306-
self.systemd.check_unit_change()):
307-
self.changed = True
308-
return self.changed
309-
310301
def compare_cap_add(self, container_info):
311302
new_cap_add = self.params.get('cap_add', list()).copy()
312303

@@ -526,6 +517,7 @@ def compare_config(self):
526517
container = self.pc.containers.get(self.params['name'])
527518
container.reload()
528519
if container.status != 'running':
520+
self._config_diff = 'container not running during config check'
529521
return True
530522

531523
rc, raw_output = container.exec_run(COMPARE_CONFIG_CMD,
@@ -535,6 +527,7 @@ def compare_config(self):
535527
# expect that config is stale so we return True and recreate container
536528
except APIError as e:
537529
if e.is_client_error():
530+
self._config_diff = 'container unavailable for config check'
538531
return True
539532
else:
540533
raise
@@ -545,6 +538,8 @@ def compare_config(self):
545538
if rc == 0:
546539
return False
547540
elif rc == 1:
541+
self._config_diff = (raw_output.decode('utf-8') if
542+
isinstance(raw_output, bytes) else raw_output)
548543
return True
549544
else:
550545
raise Exception('Failed to compare container configuration: '

ansible/roles/ironic/defaults/main.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ ironic_services:
5454
# NOTE(mgoddard): This container is always enabled, since may be used by
5555
# the direct deploy driver.
5656
enabled: true
57-
image: "{{ ironic_pxe_image_full }}"
57+
image: "{{ ironic_http_image_full }}"
5858
volumes: "{{ ironic_http_default_volumes + ironic_http_extra_volumes }}"
5959
dimensions: "{{ ironic_http_dimensions }}"
6060
healthcheck: "{{ ironic_http_healthcheck }}"
@@ -146,6 +146,10 @@ ironic_prometheus_exporter_image: "{{ docker_image_url }}ironic-prometheus-expor
146146
ironic_prometheus_exporter_tag: "{{ ironic_tag }}"
147147
ironic_prometheus_exporter_image_full: "{{ ironic_prometheus_exporter_image }}:{{ ironic_prometheus_exporter_tag }}"
148148

149+
ironic_http_image: "{{ docker_image_url }}httpd"
150+
ironic_http_tag: "{{ ironic_tag }}"
151+
ironic_http_image_full: "{{ ironic_http_image }}:{{ ironic_http_tag }}"
152+
149153
ironic_api_dimensions: "{{ default_container_dimensions }}"
150154
ironic_conductor_dimensions: "{{ default_container_dimensions }}"
151155
ironic_tftp_dimensions: "{{ default_container_dimensions }}"

ansible/roles/keystone/templates/keystone.conf.j2

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ transport_url = {{ rpc_transport_url }}
55

66
# NOTE(elemoine) log_dir alone does not work for Keystone
77
log_file = /var/log/kolla/keystone/keystone.log
8-
use_stderr = true
98

109
[oslo_middleware]
1110
enable_proxy_headers_parsing = true

ansible/roles/letsencrypt/defaults/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ letsencrypt_lego_image: "{{ docker_image_url }}letsencrypt-lego"
2525
letsencrypt_lego_tag: "{{ letsencrypt_tag }}"
2626
letsencrypt_lego_image_full: "{{ letsencrypt_lego_image }}:{{ letsencrypt_lego_tag }}"
2727

28-
letsencrypt_webserver_image: "{{ docker_image_url }}letsencrypt-webserver"
28+
letsencrypt_webserver_image: "{{ docker_image_url }}httpd"
2929
letsencrypt_webserver_tag: "{{ letsencrypt_tag }}"
3030
letsencrypt_webserver_image_full: "{{ letsencrypt_webserver_image }}:{{ letsencrypt_webserver_tag }}"
3131

ansible/roles/letsencrypt/templates/letsencrypt-webserver.json.j2

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{% set letsencrypt_apache_dir = 'apache2/conf-enabled' if kolla_base_distro in ['ubuntu', 'debian'] else 'httpd/conf.d' %}
22
{% set apache_binary = 'apache2' if kolla_base_distro in ['ubuntu', 'debian'] else 'httpd' %}
3+
{% set apache_user = 'www-data' if kolla_base_distro in ['ubuntu', 'debian'] else 'apache' %}
34

45
{
56
"command": "/usr/sbin/{{ apache_binary }} -DFOREGROUND",
@@ -16,5 +17,12 @@
1617
"owner": "root",
1718
"perm": "0600"
1819
}{% endif %}
20+
],
21+
"directories": [
22+
{
23+
"path": "/var/log/kolla/letsencrypt",
24+
"owner": "{{ apache_user }}",
25+
"perm": "0755"
26+
}
1927
]
2028
}

ansible/roles/neutron/templates/fwaas_driver.ini.j2

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
[fwaas]
22
enabled = true
3-
agent_version = v2
43
driver = iptables_v2
54

65
[service_providers]

ansible/roles/opensearch/tasks/post-config.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,30 @@
8787
when:
8888
- not ansible_check_mode
8989
- opensearch_retention_policy_check.status == 404
90+
91+
- name: Create OpenSearch Dashboards Index Pattern
92+
become: true
93+
kolla_toolbox:
94+
container_engine: "{{ kolla_container_engine }}"
95+
module_name: uri
96+
module_args:
97+
url: "{{ opensearch_dashboards_internal_endpoint }}/api/saved_objects/index-pattern/{{ opensearch_log_index_prefix }}-pattern"
98+
method: POST
99+
status_code: 200, 409
100+
url_username: "{{ opensearch_dashboards_user }}"
101+
url_password: "{{ opensearch_dashboards_password }}"
102+
force_basic_auth: true
103+
headers:
104+
osd-xsrf: "true"
105+
body:
106+
attributes:
107+
title: "{{ opensearch_log_index_prefix }}-*"
108+
timeFieldName: "@timestamp"
109+
body_format: json
110+
ca_path: "{{ openstack_cacert }}"
111+
register: opensearch_index_pattern_create
112+
changed_when: opensearch_index_pattern_create.status == 200
113+
delegate_to: "{{ groups['opensearch'][0] }}"
114+
run_once: true
115+
when:
116+
- not ansible_check_mode

0 commit comments

Comments
 (0)