Skip to content

Instantly share code, notes, and snippets.

@webtweakers
Created January 22, 2026 16:49
Show Gist options
  • Select an option

  • Save webtweakers/4e6af09ebb64fd0352581428d22e0e56 to your computer and use it in GitHub Desktop.

Select an option

Save webtweakers/4e6af09ebb64fd0352581428d22e0e56 to your computer and use it in GitHub Desktop.
Opalstack API Proxy Script for Ansible
#!/usr/bin/env python3
"""
Universal Opalstack API Proxy Script for Ansible.
Usage: manage_opalstack.py <action> <resource> [key=value ...]
"""
import os
import sys
import json
import argparse
from opalstack.api import Api
def get_api():
api_token = os.environ.get('OPALSTACK_TOKEN')
if not api_token:
raise ValueError("Environment variable OPALSTACK_TOKEN must be set.")
return Api(token=api_token)
def exit_json(msg, changed=False, data=None):
response = {"failed": False, "msg": msg, "changed": changed, "data": data}
# Extract ID for easier Ansible access
# Opalstack 'create' usually returns a list of objects
if isinstance(data, list) and len(data) > 0 and isinstance(data[0], dict):
if 'id' in data[0]:
response['id'] = data[0]['id']
elif isinstance(data, dict) and 'id' in data:
response['id'] = data['id']
print(json.dumps(response))
sys.exit(0)
def fail_json(msg, error=None):
print(json.dumps({"failed": True, "msg": msg, "error": str(error), "id": None}))
sys.exit(1)
def find_existing(manager, resource_type, search_params):
"""Internal helper to find an existing resource matching key parameters."""
raw_data = manager.list_all()
items = []
if isinstance(raw_data, dict):
for cat in raw_data.values():
if isinstance(cat, list): items.extend(cat)
else:
items = raw_data
for item in items:
is_match = True
for k, v in search_params.items():
target_key = 'hostname' if resource_type == 'servers' and k == 'name' else k
val = item.get(target_key)
# Handle nested objects like {'id': '...', 'hostname': '...'}
if isinstance(val, dict) and 'id' in val:
val = val['id']
if str(val) != str(v):
is_match = False
break
if is_match:
return item
return None
def main():
parser = argparse.ArgumentParser(description="Universal Opalstack API Proxy")
parser.add_argument('action', help="API action: create, read, update, delete, list, or 'get'")
parser.add_argument('resource', help="API resource: psqldbs, dbusers, apps, etc.")
parser.add_argument('params', nargs='*', help="Dynamic key=value pairs for the API")
args = parser.parse_args()
try:
api = get_api()
if not hasattr(api, args.resource):
fail_json(f"Resource '{args.resource}' not found.")
manager = getattr(api, args.resource)
api_kwargs = {}
for pair in args.params:
if '=' in pair:
key, value = pair.split('=', 1)
stripped_val = value.strip()
if (stripped_val.startswith('[') and stripped_val.endswith(']')) or \
(stripped_val.startswith('{') and stripped_val.endswith('}')):
try:
value = json.loads(value)
except json.JSONDecodeError:
pass
elif value.lower() == 'true':
value = True
elif value.lower() == 'false':
value = False
api_kwargs[key] = value
# ACTION: GET
if args.action == 'get':
match = find_existing(manager, args.resource, api_kwargs)
if match:
exit_json(f"Found {args.resource}", data=match)
else:
exit_json(f"Resource {args.resource} with args {api_kwargs} not found", data={'id': None})
# ACTION: CREATE (IDEMPOTENT)
if args.action == 'create':
search_keys = ['name', 'hostname', 'server']
search_params = {k: v for k, v in api_kwargs.items() if k in search_keys}
if search_params:
existing = find_existing(manager, args.resource, search_params)
if existing:
exit_json(f"{args.resource} already exists", changed=False, data=existing)
# STANDARD API ACTIONS
if not hasattr(manager, args.action):
fail_json(f"Action '{args.action}' not supported for resource '{args.resource}'.")
method = getattr(manager, args.action)
if args.action in ['create', 'update']:
result = method([api_kwargs])
elif args.action in ['delete', 'read'] and 'id' in api_kwargs:
result = method(api_kwargs.pop('id'))
else:
result = method(api_kwargs)
changed = True if args.action in ['create', 'delete', 'update'] else False
exit_json(f"Success: {args.action} {args.resource}", changed=changed, data=result)
except Exception as e:
fail_json(f"API Error during {args.action} {args.resource}", error=e)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment