osism 0.20250826.0__py3-none-any.whl → 0.20250827.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/netbox.py CHANGED
@@ -17,6 +17,9 @@ def setup_periodic_tasks(sender, **kwargs):
17
17
 
18
18
  @app.task(bind=True, name="osism.tasks.netbox.run")
19
19
  def run(self, action, arguments):
20
+ # Check if tasks are locked before execution
21
+ utils.check_task_lock_and_exit()
22
+
20
23
  pass
21
24
 
22
25
 
@@ -28,6 +31,8 @@ def run(self, action, arguments):
28
31
  @app.task(bind=True, name="osism.tasks.netbox.set_maintenance")
29
32
  def set_maintenance(self, device_name, state=True):
30
33
  """Set the maintenance state for a device in the NetBox."""
34
+ # Check if tasks are locked before execution
35
+ utils.check_task_lock_and_exit()
31
36
 
32
37
  lock = utils.create_redlock(
33
38
  key=f"lock_osism_tasks_netbox_set_maintenance_{device_name}",
@@ -56,6 +61,8 @@ def set_maintenance(self, device_name, state=True):
56
61
  @app.task(bind=True, name="osism.tasks.netbox.set_provision_state")
57
62
  def set_provision_state(self, device_name, state):
58
63
  """Set the provision state for a device in the NetBox."""
64
+ # Check if tasks are locked before execution
65
+ utils.check_task_lock_and_exit()
59
66
 
60
67
  lock = utils.create_redlock(
61
68
  key=f"lock_osism_tasks_netbox_set_provision_state_{device_name}",
@@ -85,6 +92,8 @@ def set_provision_state(self, device_name, state):
85
92
  @app.task(bind=True, name="osism.tasks.netbox.set_power_state")
86
93
  def set_power_state(self, device_name, state):
87
94
  """Set the provision state for a device in the NetBox."""
95
+ # Check if tasks are locked before execution
96
+ utils.check_task_lock_and_exit()
88
97
 
89
98
  lock = utils.create_redlock(
90
99
  key=f"lock_osism_tasks_netbox_set_provision_state_{device_name}",
@@ -156,6 +165,9 @@ def get_addresses_by_device_and_interface(self, device_name, interface_name):
156
165
 
157
166
  @app.task(bind=True, name="osism.tasks.netbox.manage")
158
167
  def manage(self, *arguments, publish=True, locking=False, auto_release_time=3600):
168
+ # Check if tasks are locked before execution
169
+ utils.check_task_lock_and_exit()
170
+
159
171
  netbox_manager_env = {
160
172
  "NETBOX_MANAGER_URL": str(settings.NETBOX_URL),
161
173
  "NETBOX_MANAGER_TOKEN": str(settings.NETBOX_TOKEN),
osism/tasks/openstack.py CHANGED
@@ -1,10 +1,15 @@
1
1
  # SPDX-License-Identifier: Apache-2.0
2
2
 
3
3
  from celery import Celery
4
+ import os
5
+ import shutil
4
6
  import tempfile
7
+ import yaml
8
+ from loguru import logger
5
9
 
6
10
  from osism import utils
7
11
  from osism.tasks import Config, run_command
12
+ from osism.tasks.conductor.utils import get_vault
8
13
 
9
14
  app = Celery("openstack")
10
15
  app.config_from_object(Config)
@@ -157,6 +162,208 @@ def baremetal_port_delete(self, port_or_id):
157
162
  return result
158
163
 
159
164
 
165
+ def get_cloud_password(cloud):
166
+ """
167
+ Load and decrypt the OpenStack password for a specific cloud profile
168
+ from the Ansible Vault encrypted secrets.yml file.
169
+
170
+ Args:
171
+ cloud (str): The cloud profile name
172
+
173
+ Returns:
174
+ str: The decrypted password or None if not found/decryptable
175
+ """
176
+ if not cloud:
177
+ logger.warning("No cloud parameter provided for password lookup")
178
+ return None
179
+
180
+ secrets_path = "/opt/configuration/environments/openstack/secrets.yml"
181
+ # Replace hyphens with underscores for password key (admin-system -> admin_system)
182
+ cloud_normalized = cloud.replace("-", "_")
183
+ password_key = f"os_password_{cloud_normalized}"
184
+
185
+ try:
186
+ # Check if secrets file exists
187
+ if not os.path.exists(secrets_path):
188
+ logger.warning(f"Secrets file not found: {secrets_path}")
189
+ return None
190
+
191
+ # Get vault instance for decryption
192
+ vault = get_vault()
193
+
194
+ # Load and decrypt the entire Ansible Vault encrypted file
195
+ with open(secrets_path, "rb") as f:
196
+ encrypted_data = f.read()
197
+
198
+ # Decrypt the entire file content
199
+ decrypted_data = vault.decrypt(encrypted_data).decode()
200
+
201
+ # Parse the decrypted YAML content safely
202
+ try:
203
+ decrypted_secrets = yaml.safe_load(decrypted_data)
204
+ except yaml.YAMLError as yaml_exc:
205
+ logger.error(
206
+ f"Failed to parse YAML content from decrypted secrets file: {yaml_exc}"
207
+ )
208
+ return None
209
+
210
+ if not decrypted_secrets or not isinstance(decrypted_secrets, dict):
211
+ logger.warning(
212
+ f"Empty or invalid secrets file after decryption: {secrets_path}"
213
+ )
214
+ return None
215
+
216
+ # Extract the password for the specified cloud - validate key format first
217
+ if not password_key.isidentifier() or not password_key.startswith(
218
+ "os_password_"
219
+ ):
220
+ logger.error(f"Invalid password key format: '{password_key}'")
221
+ return None
222
+
223
+ password = decrypted_secrets.get(password_key)
224
+
225
+ if password is not None:
226
+ # Convert password to string and strip whitespace
227
+ password_str = str(password).strip()
228
+ if password_str:
229
+ logger.info(f"Successfully loaded password for cloud '{cloud}'")
230
+ return password_str
231
+ else:
232
+ logger.warning(
233
+ f"Password key '{password_key}' is empty after conversion"
234
+ )
235
+ return None
236
+ else:
237
+ logger.warning(f"Password key '{password_key}' not found in secrets file")
238
+ return None
239
+
240
+ except Exception as exc:
241
+ logger.error(f"Failed to load/decrypt password for cloud '{cloud}': {exc}")
242
+ return None
243
+
244
+
245
+ def setup_cloud_environment(cloud):
246
+ """
247
+ Set up cloud configuration environment for OpenStack commands.
248
+
249
+ Creates secure.yml with cloud password and copies clouds.yaml to /tmp,
250
+ then changes working directory to /tmp.
251
+
252
+ Args:
253
+ cloud (str): The cloud profile name
254
+
255
+ Returns:
256
+ tuple: (temp_files_to_cleanup, original_cwd, success)
257
+ """
258
+ temp_files_to_cleanup = []
259
+ original_cwd = os.getcwd()
260
+
261
+ if not cloud:
262
+ logger.warning("No cloud parameter provided, skipping cloud configuration")
263
+ return temp_files_to_cleanup, original_cwd, False
264
+
265
+ password = get_cloud_password(cloud)
266
+ if not password:
267
+ logger.warning(
268
+ f"Could not load password for cloud '{cloud}', skipping cloud configuration"
269
+ )
270
+ return temp_files_to_cleanup, original_cwd, False
271
+
272
+ try:
273
+ # Create secure.yml in /tmp
274
+ secure_yml_path = "/tmp/secure.yml"
275
+ secure_yml_content = {"clouds": {cloud: {"auth": {"password": password}}}}
276
+
277
+ with open(secure_yml_path, "w") as f:
278
+ yaml.dump(secure_yml_content, f)
279
+ temp_files_to_cleanup.append(secure_yml_path)
280
+
281
+ # Try both .yaml and .yml extensions
282
+ if os.path.exists("/etc/openstack/clouds.yaml"):
283
+ shutil.copy2("/etc/openstack/clouds.yaml", "/tmp/clouds.yaml")
284
+ temp_files_to_cleanup.append("/tmp/clouds.yaml")
285
+ elif os.path.exists("/etc/openstack/clouds.yml"):
286
+ shutil.copy2("/etc/openstack/clouds.yml", "/tmp/clouds.yml")
287
+ temp_files_to_cleanup.append("/tmp/clouds.yml")
288
+ else:
289
+ logger.warning("Could not find /etc/openstack/clouds.yaml or clouds.yml")
290
+
291
+ # Change working directory to /tmp
292
+ os.chdir("/tmp")
293
+
294
+ logger.debug(f"Successfully set up cloud environment for '{cloud}'")
295
+ return temp_files_to_cleanup, original_cwd, True
296
+
297
+ except Exception as exc:
298
+ logger.error(f"Failed to set up cloud environment for '{cloud}': {exc}")
299
+ return temp_files_to_cleanup, original_cwd, False
300
+
301
+
302
+ def cleanup_cloud_environment(temp_files_to_cleanup, original_cwd):
303
+ """
304
+ Clean up temporary files and restore working directory.
305
+
306
+ Args:
307
+ temp_files_to_cleanup (list): List of temporary files to remove
308
+ original_cwd (str): Original working directory to restore
309
+ """
310
+ # Restore working directory
311
+ try:
312
+ os.chdir(original_cwd)
313
+ except Exception as exc:
314
+ logger.warning(f"Could not restore original working directory: {exc}")
315
+
316
+ # Clean up temporary files
317
+ for temp_file in temp_files_to_cleanup:
318
+ try:
319
+ if os.path.exists(temp_file):
320
+ os.remove(temp_file)
321
+ logger.debug(f"Cleaned up temporary file: {temp_file}")
322
+ except Exception as exc:
323
+ logger.warning(f"Could not remove temporary file {temp_file}: {exc}")
324
+
325
+
326
+ def run_openstack_command_with_cloud(
327
+ request_id,
328
+ command,
329
+ cloud,
330
+ arguments,
331
+ publish=True,
332
+ locking=False,
333
+ auto_release_time=3600,
334
+ ):
335
+ """
336
+ Execute an OpenStack command with cloud configuration setup.
337
+
338
+ Args:
339
+ request_id: Celery request ID
340
+ command (str): Command to execute
341
+ cloud (str): Cloud profile name
342
+ arguments (list): Command arguments
343
+ **kwargs: Additional arguments for run_command
344
+
345
+ Returns:
346
+ int: Command return code
347
+ """
348
+ temp_files_to_cleanup, original_cwd, cloud_setup_success = setup_cloud_environment(
349
+ cloud
350
+ )
351
+
352
+ try:
353
+ return run_command(
354
+ request_id,
355
+ command,
356
+ {},
357
+ *arguments,
358
+ publish=publish,
359
+ locking=locking,
360
+ auto_release_time=auto_release_time,
361
+ ignore_env=True,
362
+ )
363
+ finally:
364
+ cleanup_cloud_environment(temp_files_to_cleanup, original_cwd)
365
+
366
+
160
367
  @app.task(bind=True, name="osism.tasks.openstack.image_manager")
161
368
  def image_manager(
162
369
  self,
@@ -165,62 +372,82 @@ def image_manager(
165
372
  publish=True,
166
373
  locking=False,
167
374
  auto_release_time=3600,
168
- ignore_env=False
375
+ cloud=None,
169
376
  ):
377
+ # Check if tasks are locked before execution
378
+ utils.check_task_lock_and_exit()
379
+
170
380
  command = "/usr/local/bin/openstack-image-manager"
381
+
171
382
  if configs:
172
- with tempfile.TemporaryDirectory() as temp_dir:
173
- for config in configs:
174
- with tempfile.NamedTemporaryFile(
175
- mode="w+", suffix=".yml", dir=temp_dir, delete=False
176
- ) as temp_file:
177
- temp_file.write(config)
178
-
179
- sanitized_args = [
180
- arg for arg in arguments if not arg.startswith("--images=")
181
- ]
182
-
183
- try:
184
- images_index = sanitized_args.index("--images")
185
- sanitized_args.pop(images_index)
186
- sanitized_args.pop(images_index)
187
- except ValueError:
188
- pass
189
- sanitized_args.extend(["--images", temp_dir])
190
- rc = run_command(
191
- self.request.id,
192
- command,
193
- {},
194
- *sanitized_args,
195
- publish=publish,
196
- locking=locking,
197
- auto_release_time=auto_release_time,
198
- ignore_env=ignore_env,
199
- )
200
- return rc
383
+ # For configs case, we need to handle image directory setup and cloud configuration
384
+ temp_files_to_cleanup, original_cwd, cloud_setup_success = (
385
+ setup_cloud_environment(cloud)
386
+ )
387
+
388
+ try:
389
+ with tempfile.TemporaryDirectory() as temp_dir:
390
+ for config in configs:
391
+ with tempfile.NamedTemporaryFile(
392
+ mode="w+", suffix=".yml", dir=temp_dir, delete=False
393
+ ) as temp_file:
394
+ temp_file.write(config)
395
+
396
+ sanitized_args = [
397
+ arg for arg in arguments if not arg.startswith("--images=")
398
+ ]
399
+
400
+ try:
401
+ images_index = sanitized_args.index("--images")
402
+ sanitized_args.pop(images_index)
403
+ sanitized_args.pop(images_index)
404
+ except ValueError:
405
+ pass
406
+ sanitized_args.extend(["--images", temp_dir])
407
+
408
+ return run_command(
409
+ self.request.id,
410
+ command,
411
+ {},
412
+ *sanitized_args,
413
+ publish=publish,
414
+ locking=locking,
415
+ auto_release_time=auto_release_time,
416
+ ignore_env=True,
417
+ )
418
+ finally:
419
+ cleanup_cloud_environment(temp_files_to_cleanup, original_cwd)
201
420
  else:
202
- return run_command(
421
+ # Simple case - use the generalized helper
422
+ return run_openstack_command_with_cloud(
203
423
  self.request.id,
204
424
  command,
205
- {},
206
- *arguments,
425
+ cloud,
426
+ arguments,
207
427
  publish=publish,
208
428
  locking=locking,
209
429
  auto_release_time=auto_release_time,
210
- ignore_env=ignore_env,
211
430
  )
212
431
 
213
432
 
214
433
  @app.task(bind=True, name="osism.tasks.openstack.flavor_manager")
215
434
  def flavor_manager(
216
- self, *arguments, publish=True, locking=False, auto_release_time=3600
435
+ self,
436
+ *arguments,
437
+ publish=True,
438
+ locking=False,
439
+ auto_release_time=3600,
440
+ cloud=None,
217
441
  ):
442
+ # Check if tasks are locked before execution
443
+ utils.check_task_lock_and_exit()
444
+
218
445
  command = "/usr/local/bin/openstack-flavor-manager"
219
- return run_command(
446
+ return run_openstack_command_with_cloud(
220
447
  self.request.id,
221
448
  command,
222
- {},
223
- *arguments,
449
+ cloud,
450
+ arguments,
224
451
  publish=publish,
225
452
  locking=locking,
226
453
  auto_release_time=auto_release_time,
osism/tasks/reconciler.py CHANGED
@@ -27,6 +27,9 @@ def setup_periodic_tasks(sender, **kwargs):
27
27
 
28
28
  @app.task(bind=True, name="osism.tasks.reconciler.run")
29
29
  def run(self, publish=True, flush_cache=False):
30
+ # Check if tasks are locked before execution
31
+ utils.check_task_lock_and_exit()
32
+
30
33
  lock = utils.create_redlock(
31
34
  key="lock_osism_tasks_reconciler_run",
32
35
  auto_release_time=60,
osism/utils/__init__.py CHANGED
@@ -242,3 +242,84 @@ def create_redlock(key, auto_release_time=3600):
242
242
  masters={redis},
243
243
  auto_release_time=auto_release_time,
244
244
  )
245
+
246
+
247
+ def set_task_lock(user=None, reason=None):
248
+ """
249
+ Set task lock to prevent new tasks from starting.
250
+
251
+ Args:
252
+ user (str): User who set the lock (optional)
253
+ reason (str): Reason for the lock (optional)
254
+
255
+ Returns:
256
+ bool: True if lock was set successfully
257
+ """
258
+ try:
259
+ import json
260
+ from datetime import datetime
261
+
262
+ lock_data = {
263
+ "locked": True,
264
+ "timestamp": datetime.now().isoformat(),
265
+ "user": user or "dragon",
266
+ "reason": reason,
267
+ }
268
+
269
+ redis.set("osism:task_lock", json.dumps(lock_data))
270
+ return True
271
+ except Exception as e:
272
+ logger.error(f"Failed to set task lock: {e}")
273
+ return False
274
+
275
+
276
+ def remove_task_lock():
277
+ """
278
+ Remove task lock to allow new tasks to start.
279
+
280
+ Returns:
281
+ bool: True if lock was removed successfully
282
+ """
283
+ try:
284
+ redis.delete("osism:task_lock")
285
+ return True
286
+ except Exception as e:
287
+ logger.error(f"Failed to remove task lock: {e}")
288
+ return False
289
+
290
+
291
+ def is_task_locked():
292
+ """
293
+ Check if tasks are currently locked.
294
+
295
+ Returns:
296
+ dict: Lock status information or None if not locked
297
+ """
298
+ try:
299
+ import json
300
+
301
+ lock_data = redis.get("osism:task_lock")
302
+ if lock_data:
303
+ return json.loads(lock_data.decode("utf-8"))
304
+ return None
305
+ except Exception as e:
306
+ logger.error(f"Failed to check task lock status: {e}")
307
+ return None
308
+
309
+
310
+ def check_task_lock_and_exit():
311
+ """
312
+ Check if tasks are locked and exit with error message if they are.
313
+ Used by commands that should not run when tasks are locked.
314
+ """
315
+ lock_info = is_task_locked()
316
+ if lock_info and lock_info.get("locked"):
317
+ user = lock_info.get("user", "unknown")
318
+ timestamp = lock_info.get("timestamp", "unknown")
319
+ reason = lock_info.get("reason")
320
+
321
+ logger.error(f"Tasks are currently locked by {user} at {timestamp}")
322
+ if reason:
323
+ logger.error(f"Reason: {reason}")
324
+ logger.error("Use 'osism unlock' to remove the lock")
325
+ exit(1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: osism
3
- Version: 0.20250826.0
3
+ Version: 0.20250827.0
4
4
  Summary: OSISM manager interface
5
5
  Home-page: https://github.com/osism/python-osism
6
6
  Author: OSISM GmbH
@@ -44,7 +44,7 @@ Requires-Dist: nbcli==0.10.0.dev2
44
44
  Requires-Dist: openstacksdk==4.7.0
45
45
  Requires-Dist: paramiko==3.5.1
46
46
  Requires-Dist: pottery==3.0.1
47
- Requires-Dist: prompt-toolkit==3.0.51
47
+ Requires-Dist: prompt-toolkit==3.0.52
48
48
  Requires-Dist: pynetbox==7.5.0
49
49
  Requires-Dist: pytest-testinfra==10.2.2
50
50
  Requires-Dist: python-dateutil==2.9.0.post0
@@ -61,7 +61,7 @@ Provides-Extra: ansible
61
61
  Requires-Dist: ansible-runner==2.4.1; extra == "ansible"
62
62
  Requires-Dist: ansible-core==2.19.1; extra == "ansible"
63
63
  Provides-Extra: openstack-image-manager
64
- Requires-Dist: openstack-image-manager==0.20250508.0; extra == "openstack-image-manager"
64
+ Requires-Dist: openstack-image-manager==0.20250827.0; extra == "openstack-image-manager"
65
65
  Dynamic: author
66
66
  Dynamic: author-email
67
67
  Dynamic: classifier
@@ -4,33 +4,34 @@ osism/api.py,sha256=wVPLhPPdBcFx6vss5pX1Y2JxbaNSMh_Bqo4AB529QWI,16945
4
4
  osism/main.py,sha256=Dt2-9sLXcS-Ny4DAz7hrha-KRc7zd7BFUTRdfs_X8z4,893
5
5
  osism/settings.py,sha256=VZT1muZVYWM5Ov1eFRC7a4ZGYIdI2AFmudCm0wZ1C2Q,1898
6
6
  osism/commands/__init__.py,sha256=Ag4wX_DCgXRdoLn6t069jqb3DdRylsX2nyYkiyCx4uk,456
7
- osism/commands/apply.py,sha256=GWUccZAXlgkPYqylrCmdWcj8FCkDsPEipIIG937MeII,16833
7
+ osism/commands/apply.py,sha256=OtZ4zoSM5uAXc5-GIq7WN8Uwv6idR7Eb8xLw5JS6u38,16953
8
8
  osism/commands/baremetal.py,sha256=TeXwg4lYfxel0YkWC3z8bv9qTsJQmsI2QD6u1K4vhIM,24821
9
9
  osism/commands/compose.py,sha256=76HL9wzTJ7bFPhZk-uyfWq0n6Z74lOHn4RE0zzkHgYE,1241
10
10
  osism/commands/compute.py,sha256=cgqXWJa5wAvn-7e3FWCgX6hie_aK0yrKRkcNzjLXwDY,25799
11
- osism/commands/configuration.py,sha256=sPe8b0dVKFRbr30xoeVdAnHbGwCwgUh0xa_Vzv5pSQQ,954
11
+ osism/commands/configuration.py,sha256=XJ8RYSSoHmU_WO4sTS1UEutZwfFrMGTqzHtmndBOwok,1074
12
12
  osism/commands/console.py,sha256=0BSDKewPGAwWAJwi_WrFZ_dRMbMQzbG6IS_0gVi88J8,6276
13
13
  osism/commands/container.py,sha256=jHk5A0PXBzHGIm-1d5HQZI_POANAq7An1lZGRbqBvr0,1642
14
14
  osism/commands/get.py,sha256=ryytjtXWmlMV0NucP5tGkMZu0nIlC4xVtjRk4iMZ06c,8967
15
+ osism/commands/lock.py,sha256=MrZ5Ku6xJkic8Nlnv285V89EAgi0NQ8HZioU8urpWXA,3220
15
16
  osism/commands/log.py,sha256=QnnTTNiAoa8oj4kDOcggh0QrRAD6onxcEpLXBy7CvDg,4113
16
- osism/commands/manage.py,sha256=FaO9dbYjNHYanS98-zC498bx26oU8E3loxCczH9mfKI,12751
17
- osism/commands/netbox.py,sha256=gqXet5jHIRKFtHkbd6pQ0071jHe4r5c7DFnZWkT_lNQ,8275
18
- osism/commands/noset.py,sha256=7zDFuFMyNpo7DUOKcNiYV8nodtdMOYFp5LDPcuJhlZ8,1481
19
- osism/commands/reconciler.py,sha256=ubQfX8j13s3NuMKnT0Lt6O-szf7Z1V02AfsMQFHmO74,2209
20
- osism/commands/redfish.py,sha256=oBfxd5UBX4ED8XulEuIYziIYQqTvUKpKfcdGyg_AoiI,8431
17
+ osism/commands/manage.py,sha256=nQTsV-0D9_JLR7v53Wa7G0DcOmmfntZ557W4mcQzNw0,17095
18
+ osism/commands/netbox.py,sha256=dxljfrQ07qHdUgWWRiAxzihlRmXI2JeWWPicuuKTRww,8659
19
+ osism/commands/noset.py,sha256=0_5-LruRIbmzKP8UfV6r25DaYf0sWC958GZAl5nj04g,1697
20
+ osism/commands/reconciler.py,sha256=2svFOFEQtPd2z14_lMd0yGrKLgGJ9dh0bCnyCHhl7ms,2305
21
+ osism/commands/redfish.py,sha256=dcGMF7r4MnKrU95Iy-LFTpHZS2RGQ4kiYN2L1qBvUgg,8551
21
22
  osism/commands/server.py,sha256=avmoOv5rjOi-fN2A-27cPwOtiy2Q2j6UFtCh3QrfWAI,7512
22
- osism/commands/service.py,sha256=A1lgAlGeCJpbFFqF55DRWPcCirIgpU0dzjzVLZ0mz3k,2649
23
- osism/commands/set.py,sha256=xLBi2DzbVQo2jb3-cOIE9In5UB3vFxquQJkDN-EsfhM,1425
24
- osism/commands/sonic.py,sha256=H_KyMVUgXhiCjZ3T1VW9IzM-8yiRjhgj-JzzvgY9mSE,43622
23
+ osism/commands/service.py,sha256=aWcWoTKFWB7IU10IRRaFN0Oo-Hy0MbaEWjXASV744As,2769
24
+ osism/commands/set.py,sha256=Qbq46_jWkL9ilMtRadtjDYayd9W2XaAQlodfRy2hEiw,1641
25
+ osism/commands/sonic.py,sha256=_-pxkgddxFrEJ-6QqSSxv2_fDA5x9U4lt8esGWDa0Ds,43718
25
26
  osism/commands/status.py,sha256=X-Rcj-XuNPDBoxsGkf96NswwpmTognxz1V6E2NX2ZgY,1997
26
- osism/commands/sync.py,sha256=jOg-g8NmVOkXBI6rOuiOx2WgUJc1PplLAAAwc0VuIfw,1919
27
+ osism/commands/sync.py,sha256=C7jVtm33VhC9Mmxi1St1k3Cfgl0gFul3BSEseIv_se8,2135
27
28
  osism/commands/task.py,sha256=mwJJ7a71Lw3o_FX7j3rR0-NbPdPwMDOjbOAiiXE4uGc,543
28
- osism/commands/validate.py,sha256=ifdkelYptCXp18KX076Rb-CqluUN20bunG9ZeLs4AV8,3262
29
+ osism/commands/validate.py,sha256=xZ0XRvxel9L9-C6qDUy1SMfwSo8bL-5eTnKGPg5ZuDA,3358
29
30
  osism/commands/vault.py,sha256=llaqNN8UH8t8cCu2KmdaURvprA4zeG6izCen_W7ulPs,2029
30
31
  osism/commands/volume.py,sha256=l6oAk__dFM8KKdLTWOvuSiI7tLh9wAPZp8hwmYF-NX0,6595
31
32
  osism/commands/wait.py,sha256=2Ncx63M0AFq4fq40VZVClf1LS-WHetD8iC_mG2dY_Cw,5275
32
- osism/commands/worker.py,sha256=iraCOEhCp7WgfjfZ0-12XQYQPUjpi9rSJK5Z9JfNJk4,1651
33
- osism/data/__init__.py,sha256=izXdh0J3vPLQI7kBhJI7ibJQzPqU_nlONP0L4Cf_k6A,1504
33
+ osism/commands/worker.py,sha256=S8EBBVHSP6qQ4nek5fPclxdeHtjY4AFQF43YK0Kyly4,1777
34
+ osism/data/__init__.py,sha256=7nhKIh5lATHCVO34En60dmM9WXclucRO59ylMp_1gks,2232
34
35
  osism/data/enums.py,sha256=gItIjOK6xWuOZSkMxpMdYLRyt4ezyhzkqA7BGiah2o0,10030
35
36
  osism/data/playbooks.py,sha256=M3T3ajV-8Lt-orsRO3jAoukhaoYFr4EZ2dzYXQjt1kg,728
36
37
  osism/services/__init__.py,sha256=bG7Ffen4LvQtgnYPFEpFccsWs81t4zqqeqn9ZeirH6E,38
@@ -38,15 +39,15 @@ osism/services/event_bridge.py,sha256=roV90o9UgTnwoVbXnPR3muBk04IriVYCO_fewZ46Mq
38
39
  osism/services/listener.py,sha256=O8Xq5fEEVoNFIgFPE7GqfVqx6C4QkdWhUPUGzODFnws,14211
39
40
  osism/services/websocket_manager.py,sha256=F147kWOg8PAvbVG4aVYQVtK4mFMfPVtHxxYJXaqiAjg,11051
40
41
  osism/tasks/__init__.py,sha256=iAUs-ttUMw1nZElL631sT1ke29RvTjQjlhWPl_kGrEw,9003
41
- osism/tasks/ansible.py,sha256=-gUe6uZFhPLI3DGZHlpQlghuDKpp4Drn5IEctlV6Sv8,1300
42
- osism/tasks/ceph.py,sha256=eIQkah3Kj4INtOkF9kTjHbXJ3_J2lg48EWJKfHc-UYw,615
42
+ osism/tasks/ansible.py,sha256=wAeFqyY8EavySpOIBSgWwK3HcGXWPZCIVOaSss5irCM,1387
43
+ osism/tasks/ceph.py,sha256=Zo-92rzbJ9NDH9dbKi_JPWwekO3cYTdbmwAGSwr5l0w,726
43
44
  osism/tasks/conductor.py,sha256=WBLsoPtr0iGUzRGERs0Xt7CMYrnHQVEwNV9qXBssI3s,274
44
- osism/tasks/kolla.py,sha256=wJQpWn_01iWLkr7l7T7RNrQGfRgsgmYi4WQlTmNGvew,618
45
- osism/tasks/kubernetes.py,sha256=VzXq_VrYU_CLm4cOruqnE3Kq2ydfO9glZ3p0bp3OYoc,625
46
- osism/tasks/netbox.py,sha256=QGQGz3s0V8WvPvhEJWwo0H24aLFaZrSl-voN-axzRwY,5846
47
- osism/tasks/openstack.py,sha256=v9kkwKIr9nsedUgSQYSDW0kZBAGoE9MjKithXXeRm_I,7385
48
- osism/tasks/reconciler.py,sha256=PnGWfvfmomzbgddvyCdxul-z5ZLXxWAmrQyRCN874-s,1958
49
- osism/tasks/conductor/__init__.py,sha256=eAiaM69sVbTTDam7gCLyjF7wBCt7rd__pRFu7VdY-f8,1930
45
+ osism/tasks/kolla.py,sha256=1p0SZBTYpUvIg09czwUmnMh6LIBhleB6O1WSX1mkmJo,729
46
+ osism/tasks/kubernetes.py,sha256=hrzzDPM0Vx0_KWNxQwsQG54y1v0s2goFYJIMriX0Gh4,736
47
+ osism/tasks/netbox.py,sha256=FjEGQUZDzAVqA9cc3eQqaPv5-hPj3iI9lEc9SkuDO7M,6278
48
+ osism/tasks/openstack.py,sha256=c5PyVlSzn69Xw-nFgUf1cX3a3E_STSCNrIectiDzqPI,14871
49
+ osism/tasks/reconciler.py,sha256=vlPdOr7nbqggfVMSNez-JHZmKw8L7YmqKQnLF7TOXuQ,2045
50
+ osism/tasks/conductor/__init__.py,sha256=pS0jJqmSRIY0fHd3982KSuvCwI1gDzfj5iSFxaSMTyM,2215
50
51
  osism/tasks/conductor/config.py,sha256=n1H9_8DY90p5E4mygzKyJUl8G3WdDuGHFTp-SrmZmgU,4543
51
52
  osism/tasks/conductor/ironic.py,sha256=sxUHAzs8_Z-IaB5ZZ0ufObWiytBKiptPUWoIGWo2wcY,16440
52
53
  osism/tasks/conductor/netbox.py,sha256=xPJn-tXLqTAgW3v6L9rQ__XGHhM7ErchnyfsLY6iH14,13381
@@ -62,13 +63,13 @@ osism/tasks/conductor/sonic/device.py,sha256=ZYJA0bQ8waKWStzWUPxbcwNWa2Z_hMB3pqs
62
63
  osism/tasks/conductor/sonic/exporter.py,sha256=25L1vbi84ZQD0xNHNTWk-anTz5QRkGJskCECBkeGQw4,8882
63
64
  osism/tasks/conductor/sonic/interface.py,sha256=M876LHdFqGxUfTizzDusdzvCkDI0vCgqte5uLmOXFaY,39472
64
65
  osism/tasks/conductor/sonic/sync.py,sha256=fpgsQVwq6Hb7eeDHhLkAqx5BkaK3Ce_m_WvmWEsJyOo,9182
65
- osism/utils/__init__.py,sha256=370UHVU5BFy-1wDAxBFaRjSA-zR0KNadJPWQ6zcYRf0,7806
66
+ osism/utils/__init__.py,sha256=IEr0sR1HKg-QI_u84fs4gMldC6-EPSxvMBh2zMGu5dU,9939
66
67
  osism/utils/ssh.py,sha256=nxeEgwjJWvQCybKDp-NelMeWyODCYpaXFCBchAv4-bg,8691
67
- osism-0.20250826.0.dist-info/licenses/AUTHORS,sha256=oWotd63qsnNR945QLJP9mEXaXNtCMaesfo8ZNuLjwpU,39
68
- osism-0.20250826.0.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
69
- osism-0.20250826.0.dist-info/METADATA,sha256=XZx9uRbKYUlYtrm8yf1GJ1VCvC5K9S8RvKC01MfyXgA,2971
70
- osism-0.20250826.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
71
- osism-0.20250826.0.dist-info/entry_points.txt,sha256=h9YS3gfPc5ueU9ZXtCc60e8p4NQEuvtIH_zE0cfVqy0,4439
72
- osism-0.20250826.0.dist-info/pbr.json,sha256=m4b30t9AVMNNDITnoRLItSuq1Pl-y4G9dB3ztvi7LTk,47
73
- osism-0.20250826.0.dist-info/top_level.txt,sha256=8L8dsI9hcaGHsdnR4k_LN9EM78EhwrXRFHyAryPXZtY,6
74
- osism-0.20250826.0.dist-info/RECORD,,
68
+ osism-0.20250827.0.dist-info/licenses/AUTHORS,sha256=EKFIR9F27AvoEXp1cA6FkGbjEOFt4Rcbipr5RJc7jSs,64
69
+ osism-0.20250827.0.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
70
+ osism-0.20250827.0.dist-info/METADATA,sha256=db-Wl6Z-yJlLgkOrnSul0rhX7AsSUBS4IjbGZylytO0,2971
71
+ osism-0.20250827.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
72
+ osism-0.20250827.0.dist-info/entry_points.txt,sha256=W45YQ7MJ7BCAPZXl3F6d2FSi6An0moZQbzLn_BwGnRE,4618
73
+ osism-0.20250827.0.dist-info/pbr.json,sha256=RMUc1kb18-_rQuHlqeVqOjLNYTKhFnuNFJHUGDb-3DQ,47
74
+ osism-0.20250827.0.dist-info/top_level.txt,sha256=8L8dsI9hcaGHsdnR4k_LN9EM78EhwrXRFHyAryPXZtY,6
75
+ osism-0.20250827.0.dist-info/RECORD,,
@@ -27,6 +27,8 @@ get status = osism.commands.status:Run
27
27
  get tasks = osism.commands.get:Tasks
28
28
  get versions manager = osism.commands.get:VersionsManager
29
29
  get versions netbox = osism.commands.netbox:Versions
30
+ lock = osism.commands.lock:Lock
31
+ lock status = osism.commands.lock:LockStatus
30
32
  log ansible = osism.commands.log:Ansible
31
33
  log container = osism.commands.log:Container
32
34
  log file = osism.commands.log:File
@@ -49,6 +51,7 @@ manage compute stop = osism.commands.compute:ComputeStop
49
51
  manage dnsmasq = osism.commands.manage:Dnsmasq
50
52
  manage flavors = osism.commands.manage:Flavors
51
53
  manage image clusterapi = osism.commands.manage:ImageClusterapi
54
+ manage image gardenlinux = osism.commands.manage:ImageGardenlinux
52
55
  manage image octavia = osism.commands.manage:ImageOctavia
53
56
  manage images = osism.commands.manage:Images
54
57
  manage netbox = osism.commands.netbox:Manage
@@ -85,6 +88,7 @@ sync netbox = osism.commands.netbox:Sync
85
88
  sync sonic = osism.commands.sync:Sonic
86
89
  task list = osism.commands.get:Tasks
87
90
  task revoke = osism.commands.task:Revoke
91
+ unlock = osism.commands.lock:Unlock
88
92
  validate = osism.commands.validate:Run
89
93
  vault password set = osism.commands.vault:SetPassword
90
94
  vault password unset = osism.commands.vault:UnsetPassword
@@ -0,0 +1 @@
1
+ renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
@@ -0,0 +1 @@
1
+ {"git_version": "bbad13f", "is_release": false}
@@ -1 +0,0 @@
1
- Christian Berendt <berendt@osism.tech>
@@ -1 +0,0 @@
1
- {"git_version": "45ddd70", "is_release": false}