homelab/ansible/archive/scripts/generate_inventory.py

236 lines
8.2 KiB
Python

#!/usr/bin/env python3
import sys
import yaml
from pathlib import Path
class VaultSafeLoader(yaml.SafeLoader):
pass
def vault_constructor(loader, node):
return loader.construct_scalar(node)
VaultSafeLoader.add_constructor('!vault', vault_constructor)
def load_sot(path):
with open(path, 'r') as f:
return yaml.load(f, Loader=VaultSafeLoader)
def ip_for(info, prefer_desired=False):
# Default behavior for flat-network operation:
# prefer current_ip and only use desired_ip as fallback.
# For migration windows, set prefer_desired=True.
current_ip = info.get('current_ip')
desired_ip = info.get('desired_ip')
if prefer_desired:
if isinstance(desired_ip, str) and desired_ip:
return desired_ip
if isinstance(current_ip, str) and current_ip:
return current_ip
return None
if isinstance(current_ip, str) and current_ip:
return current_ip
if isinstance(desired_ip, str) and desired_ip:
return desired_ip
return None
def is_retired(info):
status = str(info.get('lifecycle_status', '')).strip().lower()
return status in {'retired', 'retired-shutdown', 'shutdown'}
def is_onboarding_tbd(info):
status = str(info.get('onboarding_status', '')).strip().lower()
return status.startswith('tbd')
def is_active(info):
if is_retired(info):
return False
if is_onboarding_tbd(info):
return False
if info.get('ansible_managed') is False:
return False
return True
def main():
import argparse
p = argparse.ArgumentParser()
p.add_argument('--sot', required=True)
p.add_argument('--out', required=True)
p.add_argument(
'--prefer-desired',
action='store_true',
help='Prefer desired_ip over current_ip when generating inventory (use during migrations).',
)
args = p.parse_args()
sot = load_sot(args.sot)
# Prefer the non-reserved key; keep fallback for older SoT files.
hosts = sot.get('lab_hosts', sot.get('hosts', {})) or {}
edge_host = (sot.get('edge_routing') or {}).get('edge_host') or {}
edge_host_ip = edge_host.get('ip')
out_lines = []
out_lines.append('# Generated inventory from ../group_vars/all.yml')
out_lines.append('')
# watchtower
out_lines.append('# --- Watchtower (local controller) ---')
out_lines.append('[watchtower]')
out_lines.append('localhost ansible_connection=local')
out_lines.append('')
# proxmox
out_lines.append('# --- Proxmox Cluster (management) ---')
out_lines.append('[proxmox_cluster]')
for name, info in hosts.items():
if info.get('role') == 'proxmox' and is_active(info):
ip = ip_for(info, prefer_desired=args.prefer_desired)
if ip:
out_lines.append(
f"{name} ansible_host={ip} ansible_user=root "
"ansible_ssh_private_key_file=/home/chester/.ssh/id_ed25519 ansible_port=22"
)
out_lines.append('')
out_lines.append('[proxmox_cluster:vars]')
out_lines.append('ansible_user=root')
out_lines.append('ansible_become=true')
out_lines.append('ansible_python_interpreter=/usr/bin/python3')
out_lines.append('')
# swarm managers
out_lines.append('# --- Swarm Managers ---')
out_lines.append('[swarm_managers]')
for name, info in hosts.items():
if (info.get('role') == 'swarm_manager' or name.startswith('swarm-manager')) and is_active(info):
ip = ip_for(info, prefer_desired=args.prefer_desired)
if ip:
out_lines.append(f"{name} ansible_host={ip}")
out_lines.append('')
# swarm workers
out_lines.append('# --- Swarm Workers ---')
out_lines.append('[swarm_workers]')
for name, info in hosts.items():
if (info.get('role') == 'swarm_worker' or name.startswith('swarm-worker')) and is_active(info):
ip = ip_for(info, prefer_desired=args.prefer_desired)
if ip:
out_lines.append(f"{name} ansible_host={ip}")
out_lines.append('')
out_lines.append('[swarm_hosts:children]')
out_lines.append('swarm_managers')
out_lines.append('swarm_workers')
out_lines.append('')
out_lines.append('[swarm_hosts:vars]')
out_lines.append('ansible_user=chester')
out_lines.append('ansible_ssh_private_key_file=/home/chester/.ssh/id_ed25519')
out_lines.append('')
# standalone ubuntu VMs
out_lines.append('# --- Standalone Ubuntu VMs ---')
out_lines.append('[standalone_ubuntu]')
for name, info in hosts.items():
if info.get('role') in {'standalone_vm', 'standalone_ubuntu'} and is_active(info):
ip = ip_for(info, prefer_desired=args.prefer_desired)
if ip:
out_lines.append(f"{name} ansible_host={ip}")
out_lines.append('')
out_lines.append('[standalone_ubuntu:vars]')
out_lines.append('ansible_user=chester')
out_lines.append('ansible_ssh_private_key_file=/home/chester/.ssh/id_ed25519')
out_lines.append('')
# heimdall edge host
out_lines.append('# --- Heimdall (Edge Router / Traefik host) ---')
out_lines.append('[heimdall_hosts]')
heimdall_info = hosts.get('heimdall', {})
heimdall_ip = ip_for(heimdall_info, prefer_desired=args.prefer_desired) or edge_host_ip
if heimdall_info and is_active(heimdall_info) and heimdall_ip:
out_lines.append(f"heimdall ansible_host={heimdall_ip}")
out_lines.append('')
out_lines.append('[heimdall_hosts:vars]')
out_lines.append('ansible_user=chester')
out_lines.append('ansible_ssh_private_key_file=/home/chester/.ssh/id_ed25519')
out_lines.append('')
# ai_grid
out_lines.append('# --- AI Grid ---')
out_lines.append('[ai_grid]')
for name, info in hosts.items():
if (info.get('role') == 'ai_node' or name.startswith('ai-')) and is_active(info):
ip = ip_for(info, prefer_desired=args.prefer_desired)
if ip:
out_lines.append(f"{name} ansible_host={ip}")
out_lines.append('')
# docker hosts
out_lines.append('# --- Docker Hosts ---')
out_lines.append('[docker_hosts]')
for name, info in hosts.items():
if info.get('role') in {'docker_host', 'standalone_vm', 'standalone_ubuntu'} and is_active(info):
ip = ip_for(info, prefer_desired=args.prefer_desired)
if ip:
out_lines.append(f"{name} ansible_host={ip}")
out_lines.append('')
# storage
out_lines.append('# --- Storage ---')
out_lines.append('[storage]')
for name, info in hosts.items():
if (info.get('role') == 'nas' or name in ('synology', 'terramaster')) and is_active(info):
ip = ip_for(info, prefer_desired=args.prefer_desired)
if ip:
out_lines.append(f"{name} ansible_host={ip} ansible_scp_if_ssh=True")
out_lines.append('')
out_lines.append('# --- Lifecycle: Onboarding TBD ---')
out_lines.append('[onboarding_tbd]')
for name, info in hosts.items():
if is_onboarding_tbd(info):
ip = ip_for(info, prefer_desired=args.prefer_desired)
if ip:
out_lines.append(f"{name} ansible_host={ip}")
out_lines.append('')
out_lines.append('# --- Lifecycle: Retired / Shutdown ---')
out_lines.append('[retired_hosts]')
for name, info in hosts.items():
if is_retired(info):
ip = ip_for(info, prefer_desired=args.prefer_desired)
if ip:
out_lines.append(f"{name} ansible_host={ip}")
out_lines.append('')
out_lines.append('# --- Aggregate grouping ---')
out_lines.append('[ubuntu_lab:children]')
out_lines.append('swarm_managers')
out_lines.append('swarm_workers')
out_lines.append('standalone_ubuntu')
out_lines.append('ai_grid')
out_lines.append('docker_hosts')
out_lines.append('storage')
out_lines.append('')
out_lines.append('[ubuntu_lab:vars]')
out_lines.append('ansible_user=chester')
out_lines.append('ansible_ssh_private_key_file=/home/chester/.ssh/id_ed25519')
out_path = Path(args.out)
out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_text('\n'.join(out_lines) + '\n')
if __name__ == '__main__':
main()