ilia 69a39e5e5b Add POTE app project support and improve IP conflict detection (#3)
## Summary

This PR adds comprehensive support for deploying the **POTE** application project via Ansible, along with improvements to IP conflict detection and a new app stack provisioning system for Proxmox-managed LXC containers.

## Key Features

### 🆕 New Roles
- **`roles/pote`**: Python/venv deployment role for POTE (PostgreSQL, cron jobs, Alembic migrations)
- **`roles/app_setup`**: Generic app deployment role (Node.js/systemd)
- **`roles/base_os`**: Base OS hardening role

### 🛡️ Safety Improvements
- IP uniqueness validation within projects
- Proxmox-side IP conflict detection
- Enhanced error messages for IP conflicts

### 📦 New Playbooks
- `playbooks/app/site.yml`: End-to-end app stack deployment
- `playbooks/app/provision_vms.yml`: Proxmox guest provisioning
- `playbooks/app/configure_app.yml`: OS + application configuration

## Security
-  All secrets stored in encrypted vault.yml
-  Deploy keys excluded via .gitignore
-  No plaintext secrets committed

## Testing
-  POTE successfully deployed to dev/qa/prod environments
-  All components validated (Git, PostgreSQL, cron, migrations)

Co-authored-by: ilia <ilia@levkin.ca>
Reviewed-on: #3
2026-01-01 11:19:54 -05:00
..

Role: proxmox_vm

Provision Proxmox guests via API. This role supports both:

  • LXC containers (proxmox_guest_type: lxc) via community.proxmox.proxmox
  • KVM VMs (proxmox_guest_type: kvm) via community.general.proxmox_kvm

The entry point is roles/proxmox_vm/tasks/main.yml, which dispatches to tasks/lxc.yml or tasks/kvm.yml.

Requirements

  • Ansible (project tested with modern Ansible; older 2.9-era setups may need adjustments)
  • Proxmox VE API access
  • Collections:
    • community.proxmox
    • community.general (for proxmox_kvm)
  • Python lib on the control machine:
    • proxmoxer (installed by make bootstrap / requirements.txt)

Authentication (vault-backed)

Store secrets in inventories/production/group_vars/all/vault.yml:

  • vault_proxmox_host
  • vault_proxmox_user
  • vault_proxmox_password (or token auth)
  • vault_proxmox_token_id (optional)
  • vault_proxmox_token (optional)
  • vault_ssh_public_key (used for bootstrap access where applicable)

Key variables

Common:

  • proxmox_guest_type: lxc or kvm
  • proxmox_host, proxmox_user, proxmox_node
  • proxmox_api_port (default 8006)
  • proxmox_validate_certs (default false)

LXC (tasks/lxc.yml):

  • lxc_vmid, lxc_hostname
  • lxc_ostemplate (e.g. local:vztmpl/debian-12-standard_*.tar.zst)
  • lxc_storage (default local-lvm)
  • lxc_network_bridge (default vmbr0)
  • lxc_ip (CIDR), lxc_gateway
  • lxc_cores, lxc_memory_mb, lxc_swap_mb, lxc_rootfs_size_gb

KVM (tasks/kvm.yml):

  • vm_id, vm_name
  • vm_cores, vm_memory, vm_disk_size
  • vm_storage, vm_network_bridge
  • cloud-init parameters used by the existing KVM provisioning flow

Safety guardrails

LXC provisioning includes a VMID collision guardrail:

  • If the target VMID already exists but the guest name does not match the expected name, provisioning fails.
  • Override only if you really mean it: -e allow_vmid_collision=true

Example usage

Provisioning is typically orchestrated by playbooks/app/provision_vms.yml, but you can call the role directly:

- name: Provision one LXC
  hosts: localhost
  connection: local
  gather_facts: false
  tasks:
    - name: Create/update container
      ansible.builtin.include_role:
        name: proxmox_vm
      vars:
        proxmox_guest_type: lxc
        lxc_vmid: 9301
        lxc_hostname: projectA-dev
        lxc_ip: "10.0.10.101/24"
        lxc_gateway: "10.0.10.1"