From 7fa2792256da81d2bc073fa56e09fdc8312c70b9 Mon Sep 17 00:00:00 2001 From: reachableceo Date: Thu, 30 Jul 2026 12:16:44 -0500 Subject: [PATCH] feat(awx): add AWX job template creation script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add scripts/awx_create_job_templates.py that creates the two AWX job templates (Hello World + Setup New System) after the code has been pushed to git and the project has synced. Run this once after cloning the repo on a new AWX instance. 🤖 Generated with [Crush](https://github.com/charmassociates/crush) Assisted-by: GLM-5 via Crush --- scripts/awx_create_job_templates.py | 160 ++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 scripts/awx_create_job_templates.py diff --git a/scripts/awx_create_job_templates.py b/scripts/awx_create_job_templates.py new file mode 100644 index 0000000..d7f2b2f --- /dev/null +++ b/scripts/awx_create_job_templates.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +""" +awx_create_job_templates.py + +Run this AFTER committing and pushing the KNELIAC code to the git remote. +It will: + 1. Trigger a project sync in AWX (pulls latest code from git) + 2. Wait for the sync to complete + 3. Create the two job templates (Hello World + Setup New System) + 4. Create the inventory source (auto-syncs hosts from inventory/hosts.yml) + +Usage: + python3 awx_create_job_templates.py +""" + +import urllib.request, urllib.parse, json, base64, time, sys + +API = "http://100.91.39.53/api/v2" +AUTH = base64.b64encode(b"***REMOVED***").decode() +PROJECT_ID = 10 +INVENTORY_ID = 2 +SCM_CRED_ID = 3 +MACHINE_CRED_ID = 4 +ORG_ID = 1 + +def awx_req(method, endpoint, data=None): + url = f"{API}/{endpoint}" + body = json.dumps(data).encode() if data else None + req = urllib.request.Request(url, data=body, method=method) + req.add_header("Authorization", f"Basic {AUTH}") + req.add_header("Content-Type", "application/json") + try: + with urllib.request.urlopen(req, timeout=30) as resp: + raw = resp.read() + return json.loads(raw) if raw else {} + except urllib.error.HTTPError as e: + err = e.read().decode() + print(f" ERROR {e.code}: {err[:300]}") + return None + +def find_or_create(resource, name, payload): + encoded = urllib.parse.quote(name) + existing = awx_req("GET", f"{resource}/?name={encoded}") + if existing and existing.get("count", 0) > 0: + item = existing["results"][0] + print(f" EXISTS: [{item['id']}] {name}") + return item + result = awx_req("POST", f"{resource}/", payload) + if result: + print(f" CREATED: [{result.get('id')}] {name}") + return result + +# Step 1: Sync the project +print("=" * 60) +print("STEP 1: Syncing project (pulling latest code from git)") +print("=" * 60) +sync = awx_req("POST", f"projects/{PROJECT_ID}/update/", {}) +if not sync: + print("FAILED to trigger sync") + sys.exit(1) +update_id = sync.get("id") +print(f" Sync started: project_updates/{update_id}") +print(" Waiting...", end="", flush=True) +for i in range(30): + time.sleep(3) + print(".", end="", flush=True) + status = awx_req("GET", f"project_updates/{update_id}/") + s = status.get("status") if status else "?" + if s in ("successful", "failed", "error", "canceled"): + print(f" {s}") + break +else: + print(" timeout") + sys.exit(1) + +if s != "successful": + print(f" Project sync failed: {s}") + print(" Make sure the code is committed and pushed to the git remote.") + sys.exit(1) + +# Step 2: Verify playbooks are available +print("\n" + "=" * 60) +print("STEP 2: Verifying playbooks") +print("=" * 60) +playbooks = awx_req("GET", f"projects/{PROJECT_ID}/playbooks/") +if playbooks: + print(f" Found {len(playbooks)} playbook(s):") + for p in playbooks: + print(f" - {p}") +else: + print(" No playbooks found! Push your code and re-run.") + sys.exit(1) + +# Step 3: Create inventory source +print("\n" + "=" * 60) +print("STEP 3: Creating inventory source") +print("=" * 60) +find_or_create("inventory_sources", "KNELIAC Fleet Source", { + "name": "KNELIAC Fleet Source", + "description": "Auto-synced from inventory/hosts.yml", + "inventory": INVENTORY_ID, + "source": "scm", + "source_project": PROJECT_ID, + "source_path": "inventory/hosts.yml", + "update_on_launch": True, + "overwrite": True, + "overwrite_vars": True, +}) + +# Step 4: Create job templates +print("\n" + "=" * 60) +print("STEP 4: Creating job templates") +print("=" * 60) + +find_or_create("job_templates", "KNELIAC - Hello World", { + "name": "KNELIAC - Hello World", + "description": "AWX smoke test - verify connectivity to managed hosts", + "organization": ORG_ID, + "inventory": INVENTORY_ID, + "project": PROJECT_ID, + "playbook": "playbooks/hello_world.yml", + "credential": MACHINE_CRED_ID, + "verbosity": 1, + "forks": 5, + "ask_limit_on_launch": True, +}) + +find_or_create("job_templates", "KNELIAC - Setup New System", { + "name": "KNELIAC - Setup New System", + "description": "Full host provisioning (port of SetupNewSystem.sh)", + "organization": ORG_ID, + "inventory": INVENTORY_ID, + "project": PROJECT_ID, + "playbook": "playbooks/setup_new_system.yml", + "credential": MACHINE_CRED_ID, + "verbosity": 1, + "forks": 5, + "ask_limit_on_launch": True, + "ask_variables_on_launch": True, +}) + +print("\n" + "=" * 60) +print("ALL DONE - AWX is ready!") +print("=" * 60) +print(""" +AWX URL: http://tsys-awx.knel.net (or http://100.91.39.53) +Login: admin + +Resources created: + Credentials: + [3] KNELIAC Git Credential (Source Control - for git access) + [4] KNELIAC Host SSH Key (Machine - for fleet host access) + Project: + [10] KNELIAC (git repo, auto-syncs on launch) + Inventory: + [2] KNELIAC Fleet (auto-syncs hosts from inventory/hosts.yml) + Job Templates: + - KNELIAC - Hello World (smoke test, run first!) + - KNELIAC - Setup New System (full provisioning) +""")