osism 0.20250919.0__py3-none-any.whl → 0.20251004.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
osism/tasks/__init__.py CHANGED
@@ -1,13 +1,21 @@
1
1
  # SPDX-License-Identifier: Apache-2.0
2
2
 
3
+ import fcntl
4
+ import json
3
5
  import os
4
6
  import re
5
7
  import subprocess
8
+ import yaml
9
+ from datetime import datetime, timezone
10
+ from pathlib import Path
6
11
 
7
12
  from loguru import logger
8
13
 
9
14
  from osism import utils
10
15
 
16
+ # Regex pattern for extracting hosts from Ansible output
17
+ HOST_PATTERN = re.compile(r"^(ok|changed|failed|skipping|unreachable):\s+\[([^\]]+)\]")
18
+
11
19
 
12
20
  class Config:
13
21
  broker_connection_retry_on_startup = True
@@ -30,6 +38,109 @@ class Config:
30
38
  }
31
39
 
32
40
 
41
+ def get_container_version(worker):
42
+ """Read container version from YAML version file.
43
+
44
+ Args:
45
+ worker: The runtime container name (osism-ansible, kolla-ansible, ceph-ansible, osism-kubernetes)
46
+
47
+ Returns:
48
+ str: The container version, "latest" if empty, or "unknown" if not found
49
+
50
+ Examples:
51
+ >>> get_container_version("osism-ansible")
52
+ "7.0.5a"
53
+ >>> get_container_version("kolla-ansible")
54
+ "18.1.0"
55
+ >>> get_container_version("osism-kubernetes")
56
+ "1.29.0"
57
+
58
+ Note:
59
+ If the version parameter in the YAML file is an empty string (""),
60
+ the function returns "latest" as the default value.
61
+ """
62
+ version_file = Path(f"/interface/versions/{worker}.yml")
63
+
64
+ try:
65
+ if not version_file.exists():
66
+ logger.debug(f"Version file not found: {version_file}")
67
+ return "unknown"
68
+
69
+ with open(version_file, "r") as f:
70
+ version_data = yaml.safe_load(f)
71
+
72
+ # Convert worker name to version parameter name
73
+ # osism-ansible -> osism_ansible_version
74
+ # kolla-ansible -> kolla_ansible_version
75
+ # ceph-ansible -> ceph_ansible_version
76
+ version_key = f"{worker.replace('-', '_')}_version"
77
+
78
+ version = version_data.get(version_key, "unknown")
79
+
80
+ # If version is empty string, use "latest" as default
81
+ if version == "":
82
+ version = "latest"
83
+ logger.debug(f"Version parameter empty for {worker}, using 'latest'")
84
+
85
+ logger.debug(f"Read version {version} for {worker} from {version_file}")
86
+ return version
87
+
88
+ except Exception as e:
89
+ logger.warning(f"Failed to read version from {version_file}: {e}")
90
+ return "unknown"
91
+
92
+
93
+ def log_play_execution(
94
+ request_id, worker, environment, role, hosts=None, arguments=None, result="started"
95
+ ):
96
+ """Log Ansible play execution to central tracking file.
97
+
98
+ Args:
99
+ request_id: The Celery task request ID for correlation
100
+ worker: The runtime container (osism-ansible, kolla-ansible, ceph-ansible, osism-kubernetes)
101
+ environment: The environment parameter
102
+ role: The playbook/role that was executed
103
+ hosts: List of hosts the play was executed against (default: empty list)
104
+ arguments: Command-line arguments passed to ansible-playbook (default: None)
105
+ result: Execution result - "started", "success", or "failure"
106
+ """
107
+ log_file = Path("/share/ansible-execution-history.json")
108
+
109
+ # Get runtime version from YAML version file
110
+ runtime_version = get_container_version(worker)
111
+
112
+ # Use provided hosts or empty list
113
+ if hosts is None:
114
+ hosts = []
115
+
116
+ execution_record = {
117
+ "timestamp": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
118
+ "request_id": request_id,
119
+ "worker": worker,
120
+ "worker_version": runtime_version,
121
+ "environment": environment,
122
+ "role": role,
123
+ "hosts": hosts,
124
+ "arguments": arguments if arguments else "",
125
+ "result": result,
126
+ }
127
+
128
+ try:
129
+ # Create directory if it doesn't exist
130
+ log_file.parent.mkdir(parents=True, exist_ok=True)
131
+
132
+ # Append with file locking for thread safety
133
+ with open(log_file, "a") as f:
134
+ fcntl.flock(f.fileno(), fcntl.LOCK_EX)
135
+ try:
136
+ f.write(json.dumps(execution_record) + "\n")
137
+ finally:
138
+ fcntl.flock(f.fileno(), fcntl.LOCK_UN)
139
+ except Exception as e:
140
+ # Log warning but don't fail the execution
141
+ logger.warning(f"Failed to log play execution to {log_file}: {e}")
142
+
143
+
33
144
  def run_ansible_in_environment(
34
145
  request_id,
35
146
  worker,
@@ -41,6 +152,7 @@ def run_ansible_in_environment(
41
152
  auto_release_time=3600,
42
153
  ):
43
154
  result = ""
155
+ extracted_hosts = set() # Local set for host deduplication
44
156
 
45
157
  if type(arguments) == list:
46
158
  joined_arguments = " ".join(arguments)
@@ -73,6 +185,17 @@ def run_ansible_in_environment(
73
185
  if ansible_vault_password:
74
186
  env["VAULT"] = "/ansible-vault.py"
75
187
 
188
+ # Log play execution start
189
+ log_play_execution(
190
+ request_id=request_id,
191
+ worker=worker,
192
+ environment=environment,
193
+ role=role,
194
+ hosts=None, # Hosts will be empty at start, filled at completion
195
+ arguments=joined_arguments,
196
+ result="started",
197
+ )
198
+
76
199
  # NOTE: Consider arguments in the future
77
200
  if locking:
78
201
  lock = utils.create_redlock(
@@ -110,8 +233,8 @@ def run_ansible_in_environment(
110
233
  env=env,
111
234
  )
112
235
 
113
- # execute roles from kubernetes
114
- elif worker == "kubernetes":
236
+ # execute roles from osism-kubernetes
237
+ elif worker == "osism-kubernetes":
115
238
  if locking:
116
239
  lock.acquire()
117
240
 
@@ -157,12 +280,30 @@ def run_ansible_in_environment(
157
280
 
158
281
  while p.poll() is None:
159
282
  line = p.stdout.readline().decode("utf-8")
283
+
284
+ # Extract hosts from Ansible output
285
+ match = HOST_PATTERN.match(line.strip())
286
+ if match:
287
+ hostname = match.group(2)
288
+ extracted_hosts.add(hostname) # Local set (automatic deduplication)
289
+
160
290
  if publish:
161
291
  utils.push_task_output(request_id, line)
162
292
  result += line
163
293
 
164
294
  rc = p.wait(timeout=60)
165
295
 
296
+ # Log play execution result
297
+ log_play_execution(
298
+ request_id=request_id,
299
+ worker=worker,
300
+ environment=environment,
301
+ role=role,
302
+ hosts=sorted(list(extracted_hosts)), # Direct pass of extracted hosts
303
+ arguments=joined_arguments,
304
+ result="success" if rc == 0 else "failure",
305
+ )
306
+
166
307
  if publish:
167
308
  utils.finish_task_output(request_id, rc=rc)
168
309
 
osism/tasks/kubernetes.py CHANGED
@@ -21,7 +21,7 @@ def run(self, environment, playbook, arguments, publish=True, auto_release_time=
21
21
 
22
22
  return run_ansible_in_environment(
23
23
  self.request.id,
24
- "kubernetes",
24
+ "osism-kubernetes",
25
25
  environment,
26
26
  playbook,
27
27
  arguments,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: osism
3
- Version: 0.20250919.0
3
+ Version: 0.20251004.0
4
4
  Summary: OSISM manager interface
5
5
  Home-page: https://github.com/osism/python-osism
6
6
  Author: OSISM GmbH
@@ -25,14 +25,14 @@ License-File: AUTHORS
25
25
  Requires-Dist: ClusterShell==1.9.3
26
26
  Requires-Dist: GitPython==3.1.45
27
27
  Requires-Dist: Jinja2==3.1.6
28
- Requires-Dist: PyYAML==6.0.2
28
+ Requires-Dist: PyYAML==6.0.3
29
29
  Requires-Dist: ara==1.7.3
30
30
  Requires-Dist: celery[redis]==5.5.3
31
31
  Requires-Dist: cliff==4.11.0
32
32
  Requires-Dist: deepdiff==8.6.1
33
33
  Requires-Dist: docker==7.1.0
34
34
  Requires-Dist: dtrack-auditor==1.5.0
35
- Requires-Dist: fastapi==0.116.2
35
+ Requires-Dist: fastapi==0.118.0
36
36
  Requires-Dist: flower==2.0.1
37
37
  Requires-Dist: hiredis==3.2.1
38
38
  Requires-Dist: jc==1.25.5
@@ -42,7 +42,7 @@ Requires-Dist: kubernetes==33.1.0
42
42
  Requires-Dist: loguru==0.7.3
43
43
  Requires-Dist: nbcli==0.10.0.dev2
44
44
  Requires-Dist: openstacksdk==4.7.1
45
- Requires-Dist: paramiko==3.5.1
45
+ Requires-Dist: paramiko==4.0.0
46
46
  Requires-Dist: pottery==3.0.1
47
47
  Requires-Dist: prompt-toolkit==3.0.52
48
48
  Requires-Dist: pynetbox==7.5.0
@@ -53,7 +53,7 @@ Requires-Dist: sqlmodel==0.0.25
53
53
  Requires-Dist: sushy==5.7.1
54
54
  Requires-Dist: tabulate==0.9.0
55
55
  Requires-Dist: transitions==0.9.3
56
- Requires-Dist: uvicorn[standard]==0.35.0
56
+ Requires-Dist: uvicorn[standard]==0.37.0
57
57
  Requires-Dist: validators==0.35.0
58
58
  Requires-Dist: watchdog==6.0.0
59
59
  Requires-Dist: websockets==15.0.1
@@ -38,12 +38,12 @@ osism/services/__init__.py,sha256=bG7Ffen4LvQtgnYPFEpFccsWs81t4zqqeqn9ZeirH6E,38
38
38
  osism/services/event_bridge.py,sha256=roV90o9UgTnwoVbXnPR3muBk04IriVYCO_fewZ46Mq8,12016
39
39
  osism/services/listener.py,sha256=O8Xq5fEEVoNFIgFPE7GqfVqx6C4QkdWhUPUGzODFnws,14211
40
40
  osism/services/websocket_manager.py,sha256=F147kWOg8PAvbVG4aVYQVtK4mFMfPVtHxxYJXaqiAjg,11051
41
- osism/tasks/__init__.py,sha256=iAUs-ttUMw1nZElL631sT1ke29RvTjQjlhWPl_kGrEw,9003
41
+ osism/tasks/__init__.py,sha256=l_a7IfYm_NUn6XOokfJBYjTuMZGyvGHcvDUOMWSeYGo,13814
42
42
  osism/tasks/ansible.py,sha256=wAeFqyY8EavySpOIBSgWwK3HcGXWPZCIVOaSss5irCM,1387
43
43
  osism/tasks/ceph.py,sha256=Zo-92rzbJ9NDH9dbKi_JPWwekO3cYTdbmwAGSwr5l0w,726
44
44
  osism/tasks/conductor.py,sha256=WBLsoPtr0iGUzRGERs0Xt7CMYrnHQVEwNV9qXBssI3s,274
45
45
  osism/tasks/kolla.py,sha256=1p0SZBTYpUvIg09czwUmnMh6LIBhleB6O1WSX1mkmJo,729
46
- osism/tasks/kubernetes.py,sha256=hrzzDPM0Vx0_KWNxQwsQG54y1v0s2goFYJIMriX0Gh4,736
46
+ osism/tasks/kubernetes.py,sha256=LyFKjtByQryNl67ZgKjnr-csEODDqYf3LvjURf_qhK0,742
47
47
  osism/tasks/netbox.py,sha256=FjEGQUZDzAVqA9cc3eQqaPv5-hPj3iI9lEc9SkuDO7M,6278
48
48
  osism/tasks/openstack.py,sha256=c5PyVlSzn69Xw-nFgUf1cX3a3E_STSCNrIectiDzqPI,14871
49
49
  osism/tasks/reconciler.py,sha256=vlPdOr7nbqggfVMSNez-JHZmKw8L7YmqKQnLF7TOXuQ,2045
@@ -65,11 +65,11 @@ osism/tasks/conductor/sonic/interface.py,sha256=M876LHdFqGxUfTizzDusdzvCkDI0vCgq
65
65
  osism/tasks/conductor/sonic/sync.py,sha256=fpgsQVwq6Hb7eeDHhLkAqx5BkaK3Ce_m_WvmWEsJyOo,9182
66
66
  osism/utils/__init__.py,sha256=IEr0sR1HKg-QI_u84fs4gMldC6-EPSxvMBh2zMGu5dU,9939
67
67
  osism/utils/ssh.py,sha256=nxeEgwjJWvQCybKDp-NelMeWyODCYpaXFCBchAv4-bg,8691
68
- osism-0.20250919.0.dist-info/licenses/AUTHORS,sha256=DJIRsjyrFxKjFvmpUNDRDBS04nRiJ5B6FpKcDcfnoGM,36
69
- osism-0.20250919.0.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
70
- osism-0.20250919.0.dist-info/METADATA,sha256=yUP8maQEdZTw_S98mpMq_gjQY-oRTPxIZPxH3l7vUus,2971
71
- osism-0.20250919.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
72
- osism-0.20250919.0.dist-info/entry_points.txt,sha256=W45YQ7MJ7BCAPZXl3F6d2FSi6An0moZQbzLn_BwGnRE,4618
73
- osism-0.20250919.0.dist-info/pbr.json,sha256=PAMccwqoOE9e-txmqk4y_4SaKyVuFto85oajizESJwM,47
74
- osism-0.20250919.0.dist-info/top_level.txt,sha256=8L8dsI9hcaGHsdnR4k_LN9EM78EhwrXRFHyAryPXZtY,6
75
- osism-0.20250919.0.dist-info/RECORD,,
68
+ osism-0.20251004.0.dist-info/licenses/AUTHORS,sha256=oWotd63qsnNR945QLJP9mEXaXNtCMaesfo8ZNuLjwpU,39
69
+ osism-0.20251004.0.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
70
+ osism-0.20251004.0.dist-info/METADATA,sha256=AyzRodVxbpGRzpJZM9nl7_8BBf-iAjb25mOWR8sl3UM,2971
71
+ osism-0.20251004.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
72
+ osism-0.20251004.0.dist-info/entry_points.txt,sha256=W45YQ7MJ7BCAPZXl3F6d2FSi6An0moZQbzLn_BwGnRE,4618
73
+ osism-0.20251004.0.dist-info/pbr.json,sha256=O4D8iZGKdlEwIsuOJIdJnESPh6JYeG__nO6RBvorNPU,47
74
+ osism-0.20251004.0.dist-info/top_level.txt,sha256=8L8dsI9hcaGHsdnR4k_LN9EM78EhwrXRFHyAryPXZtY,6
75
+ osism-0.20251004.0.dist-info/RECORD,,
@@ -0,0 +1 @@
1
+ Christian Berendt <berendt@osism.tech>
@@ -0,0 +1 @@
1
+ {"git_version": "e546bf8", "is_release": false}
@@ -1 +0,0 @@
1
- janhorstmann <horstmann@osism.tech>
@@ -1 +0,0 @@
1
- {"git_version": "6f46bd9", "is_release": false}