import paramiko ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect('140.44.4.11', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10) orm_provisioning_code = """ from awx.main.models import ( Organization, Inventory, Group, Host, Credential, Project, JobTemplate, Schedule ) # 1. Organization org, created = Organization.objects.get_or_create(name='Snarfnet Homelab', defaults={'description': 'Snarfnet Homelab Infrastructure Automation'}) print(f"Organization: {org.name} (id={org.id}, created={created})") # 2. Inventory inv, created = Inventory.objects.get_or_create( name='Snarfnet Master Inventory', defaults={'organization': org, 'description': 'Master Inventory across all 28 Homelab hosts'} ) if not inv.organization: inv.organization = org inv.save() print(f"Inventory: {inv.name} (id={inv.id}, created={created})") # 3. Groups & Hosts definition GROUPS = { 'k3s_aerodrome_masters': { 'vars': {'ansible_user': 'root', 'cluster': 'aerodrome', 'node_role': 'master'}, 'hosts': { 'sninfr2lnxctl01': '140.44.4.11', 'sninfr2lnxctl02': '140.44.4.12', 'sninfr2lnxctl03': '140.44.4.13', } }, 'k3s_aerodrome_workers': { 'vars': {'ansible_user': 'root', 'cluster': 'aerodrome', 'node_role': 'worker'}, 'hosts': { 'sninfr2lnxwrk01': '140.44.4.21', 'sninfr2lnxwrk02': '140.44.4.22', 'sninfr2lnxwrk03': '140.44.4.23', 'sninfr2lnxwrk04': '140.44.4.24', 'sninfr2lnxwrk05': '140.44.4.25', 'sninfr2lnxwrk06': '140.44.4.26', 'sninfr2lnxwrk07': '140.44.4.27', } }, 'k3s_spaceport_masters': { 'vars': {'ansible_user': 'root', 'cluster': 'spaceport', 'node_role': 'master'}, 'hosts': { 'sninfr0lnxctl01': '140.44.4.41', 'sninfr0lnxctl02': '140.44.4.42', 'sninfr0lnxctl03': '140.44.4.43', 'sninfr0lnxctl04': '140.44.4.44', } }, 'k3s_spaceport_workers': { 'vars': {'ansible_user': 'root', 'cluster': 'spaceport', 'node_role': 'worker'}, 'hosts': { 'sninfr0lnxwrk01': '140.44.4.51', 'sninfr0lnxwrk02': '140.44.4.52', 'sninfr0lnxwrk03': '140.44.4.53', 'sninfr0lnxwrk04': '140.44.4.54', 'sninfr0lnxwrk05': '140.44.4.55', 'sninfr0lnxwrk06': '140.44.4.56', } }, 'swarm_shipyard': { 'vars': {'ansible_user': 'snarf', 'ansible_become': True, 'cluster': 'shipyard'}, 'hosts': { 'snshpd4lnxswm01': '140.44.4.71', 'snshpd4lnxswm02': '140.44.4.72', 'snshpd4lnxswm03': '140.44.4.73', 'snshpd4lnxswm04': '140.44.4.74', 'snshpd4lnxswm05': '140.44.4.75', } }, 'ai_pi_cluster': { 'vars': {'ansible_user': 'snarf', 'ansible_become': True}, 'hosts': { 'sninfr4lnxaih01': '140.44.4.7', 'sninfr4lnxaih02': '140.44.4.8', } }, 'smart_home_nodes': { 'vars': {'ansible_user': 'snarf', 'ansible_become': True}, 'hosts': { 'homeassistant': '14.4.4.20', 'nutserver': '14.4.4.44', } }, 'synology_nas': { 'vars': {}, 'hosts': { 'sn_nas': '14.10.10.71', 'snarfnas': '14.10.10.72', } } } import yaml for grp_name, data in GROUPS.items(): grp, _ = Group.objects.get_or_create(name=grp_name, inventory=inv) grp.variables = yaml.dump(data.get('vars', {})) grp.save() for h_name, ip in data.get('hosts', {}).items(): h, _ = Host.objects.get_or_create(name=h_name, inventory=inv) h.variables = yaml.dump({'ansible_host': ip}) h.save() if not grp.hosts.filter(id=h.id).exists(): grp.hosts.add(h) print(f"Total Hosts in '{inv.name}': {inv.hosts.count()}") print(f"Total Groups in '{inv.name}': {inv.groups.count()}") # 4. Project proj, created = Project.objects.get_or_create( name='Snarfnet Infrastructure Playbooks', defaults={ 'organization': org, 'scm_type': 'git', 'scm_url': 'https://git.snarfnet.net/Snarf/SnarfCode.git', 'scm_branch': 'main', 'scm_clean': True, 'scm_delete_on_update': True, 'scm_update_on_launch': True, 'description': 'Snarfnet Ansible Playbooks from local Gitea' } ) print(f"Project: {proj.name} (id={proj.id}, created={created})") # 5. Credentials lookup cred_root = Credential.objects.filter(name__icontains='root').first() cred_snarf = Credential.objects.filter(name__icontains='snarf').first() print(f"Root Credential: {cred_root.name if cred_root else 'None'}") print(f"Snarf Credential: {cred_snarf.name if cred_snarf else 'None'}") # 6. Job Templates TEMPLATES = [ { 'name': 'Daily Drift & Agent Health Audit', 'playbook': 'ansible/playbooks/daily_drift_audit.yml', 'desc': 'Verifies time synchronization, Zabbix agent, and SSH access daily' }, { 'name': 'Weekly OS & Security Patching', 'playbook': 'ansible/playbooks/weekly_os_patching.yml', 'desc': 'Rolling serial-1 apt update and dist-upgrade with reboot verification' }, { 'name': 'Aerodrome K3s Pre-Upgrade Check', 'playbook': 'ansible/playbooks/k3s_aerodrome_precheck.yml', 'desc': 'Validates Aerodrome masters and creates on-demand etcd snapshot' } ] for t in TEMPLATES: jt, created = JobTemplate.objects.get_or_create( name=t['name'], defaults={ 'project': proj, 'inventory': inv, 'playbook': t['playbook'], 'description': t['desc'], 'job_type': 'run', } ) if cred_root and not jt.credentials.filter(id=cred_root.id).exists(): jt.credentials.add(cred_root) if cred_snarf and not jt.credentials.filter(id=cred_snarf.id).exists(): jt.credentials.add(cred_snarf) print(f"Job Template: {jt.name} (id={jt.id}, created={created})") print("AWX Provisioning Complete!") """ cmd = f"kubectl exec -i -n awx -c awx-task deployment/awx-task -- awx-manage shell_plus --quiet << 'EOF'\n{orm_provisioning_code}\nEOF" stdin, stdout, stderr = ssh.exec_command(cmd) print(stdout.read().decode()) print(stderr.read().decode()) ssh.close()