import click from .config import Config from .dns import DNSManager from .ingress import IngressManager from .router import RouterManager from .proxmox import ProxmoxManager from .samba import SambaManager import sys @click.group() @click.option('--config', help='Path to config file') @click.pass_context def cli(ctx, config): """LoopAware Infrastructure Management CLI""" try: ctx.obj = Config(config) except Exception as e: click.echo(f"Error: {e}", err=True) sys.exit(1) @cli.group() def proxmox(): """Manage Proxmox VMs and Containers""" pass @proxmox.command(name='list-lxcs') @click.option('--node', help='Proxmox node name') @click.pass_obj def proxmox_list_lxcs(config, node): mgr = ProxmoxManager(config, node) click.echo(mgr.list_lxcs()) @proxmox.command(name='create-lxc') @click.argument('vmid') @click.argument('template') @click.argument('hostname') @click.argument('ip') @click.argument('gateway') @click.option('--node', help='Proxmox node name') @click.option('--password', help='Root password for LXC') @click.pass_obj def proxmox_create_lxc(config, vmid, template, hostname, ip, gateway, node, password): mgr = ProxmoxManager(config, node) mgr.create_lxc(vmid, template, hostname, ip, gateway, password=password) click.echo(f"LXC {vmid} ({hostname}) created on {mgr.node_name or 'default node'}") @cli.group() def samba(): """Manage Samba AD Identity""" pass @samba.command(name='list-users') @click.pass_obj def samba_list_users(config): mgr = SambaManager(config) click.echo(mgr.list_users()) @samba.command(name='add-user') @click.argument('username') @click.argument('password') @click.pass_obj def samba_add_user(config, username, password): mgr = SambaManager(config) mgr.add_user(username, password) click.echo(f"User {username} created") @samba.command(name='add-to-group') @click.argument('group') @click.argument('username') @click.pass_obj def samba_add_to_group(config, group, username): mgr = SambaManager(config) mgr.add_to_group(group, username) click.echo(f"User {username} added to group {group}") @cli.group() def dns(): """Manage DNS and DHCP""" pass @dns.command(name='add-host') @click.argument('mac') @click.argument('ip') @click.argument('hostname') @click.pass_obj def dns_add_host(config, mac, ip, hostname): mgr = DNSManager(config) mgr.add_host(mac, ip, hostname) click.echo(f"Added host {hostname} ({ip})") @dns.command(name='remove-host') @click.argument('mac') @click.pass_obj def dns_remove_host(config, mac): mgr = DNSManager(config) mgr.remove_host(mac) click.echo(f"Removed host {mac}") @dns.command(name='add-dns') @click.argument('domain') @click.argument('ip') @click.pass_obj def dns_add_dns(config, domain, ip): mgr = DNSManager(config) mgr.add_dns(domain, ip) click.echo(f"Added DNS record for {domain} -> {ip}") @dns.command(name='remove-dns') @click.argument('domain') @click.pass_obj def dns_remove_dns(config, domain): mgr = DNSManager(config) mgr.remove_dns(domain) click.echo(f"Removed DNS record for {domain}") @dns.command(name='list') @click.pass_obj def dns_list(config): mgr = DNSManager(config) data = mgr.list() click.echo(data['hosts']) click.echo(data['dns']) @cli.group() def ingress(): """Manage HAProxy Ingress""" pass @ingress.command(name='add') @click.argument('domain') @click.argument('ip') @click.argument('port', type=int) @click.option('--https', is_flag=True, help='Target uses HTTPS') @click.pass_obj def ingress_add(config, domain, ip, port, https): mgr = IngressManager(config) mgr.add(domain, ip, port, https) click.echo(f"Added ingress for {domain}") @ingress.command(name='remove') @click.argument('domain') @click.pass_obj def ingress_remove(config, domain): mgr = IngressManager(config) mgr.remove(domain) click.echo(f"Removed ingress for {domain}") @ingress.command(name='list') @click.pass_obj def ingress_list(config): mgr = IngressManager(config) click.echo(mgr.list()) @cli.group() def router(): """Manage Router Port Forwards""" pass @router.command(name='add') @click.argument('name') @click.argument('proto') @click.argument('ext_port') @click.argument('int_ip') @click.argument('int_port') @click.pass_obj def router_add(config, name, proto, ext_port, int_ip, int_port): import ipaddress # Validate IP and Ports in CLI layer for better error messages try: ipaddress.ip_address(int_ip) except ValueError: raise click.BadParameter(f"Invalid internal IP address: {int_ip}") for p in [ext_port, int_port]: if not (1 <= int(p) <= 65535): raise click.BadParameter(f"Port {p} out of range (1-65535)") mgr = RouterManager(config) mgr.add_forward(name, proto, ext_port, int_ip, int_port) click.echo(f"Added port forward {name}") @router.command(name='remove') @click.argument('section') @click.pass_obj def router_remove(config, section): mgr = RouterManager(config) try: mgr.remove_forward(section) click.echo(f"Removed port forward {section}") except ValueError as e: click.echo(f"Error: {e}", err=True) sys.exit(1) @router.command(name='list') @click.pass_obj def router_list(config): mgr = RouterManager(config) for rule in mgr.list(): click.echo(f"[{rule['section']}] {rule['name']}: {rule['proto']} {rule['port']} -> {rule['dest']}") def main(): cli(obj={}) if __name__ == "__main__": main()