Pyinfra/Deploy automatico VM Proxmox
Pyinfra/Deploy automatico VM Proxmox
Questa pagina descrive la soluzione concreta pubblicata su bot.gazzi.net/pyinfra/. L'obiettivo non e insegnare pyinfra in astratto, ma permettere a una persona umana di capire come e fatta l'automazione, come si usa e come si mantiene.
Obiettivo
Consentire la creazione guidata di VM Proxmox via browser, con:
- autenticazione LDAP
- validazione dei parametri
- job serializzati
- log consultabili
- provisioning reale eseguito via pyinfra
Cosa vede l'utente
La form pubblica consente di inserire:
- VMID
- nome VM
- utente amministrativo iniziale
- password iniziale
- CPU
- RAM
- disco
- rete DHCP o statica
- bridge
- VLAN
- gateway
- CIDR
- scelta se avviare subito la VM
Pulsanti di supporto:
- ricerca del primo VMID libero a partire da
100 - elenco bridge disponibili
- elenco VLAN disponibili sul bridge scelto
Flusso umano
Una persona normale puo leggere il sistema cosi:
- apre la pagina
- fa login LDAP
- compila i parametri
- verifica rete e VMID
- preme Crea VM
- vede partire il job
- osserva il log recenti nella stessa pagina
Flusso tecnico
- Nginx pubblica
/pyinfra/ - Flask gestisce login, pagina, API e job
- il job lancia
pyinfra @local - il deploy pyinfra richiama un helper Python
- l'helper apre una sessione SSH verso Proxmox
- Proxmox crea la VM e la configura con cloud-init
- se la VM parte subito, il sistema aspetta il primo boot e pulisce lo snippet cloud-init
Componenti
1. Frontend Flask
Serve HTML direttamente dall'app e contiene:
- form
- validazioni lato browser
- chiamate AJAX alle API interne
- lista job recenti
2. Login LDAP
L'utente non entra direttamente se non ha credenziali LDAP valide.
3. API interne
Le API servono per:
- health check
- elenco job
- lookup bridge
- lookup VLAN
- validazione bridge + VLAN
- ricerca VMID libero
- creazione job
4. Runner pyinfra
Pyinfra viene eseguito in locale con inventario implicito @local.
5. Helper Proxmox
L'helper Python usa paramiko e comandi qm per creare la VM.
File importanti
Versionati:
scripts-repo/pyinfra-proxmox-webui/app.pyscripts-repo/pyinfra-proxmox-webui/deploys/create_vm.pyscripts-repo/pyinfra-proxmox-webui/lib/proxmox_ssh.pyscripts-repo/pyinfra-proxmox-webui/systemd/pyinfra-webui.servicescripts-repo/pyinfra-proxmox-webui/proxy01-bot-pyinfra-location.conf.examplescripts-repo/pyinfra-proxmox-webui/.env.example
Runtime:
/opt/pyinfra-webui/etc/pyinfra-webui/pyinfra-webui.env/etc/systemd/system/pyinfra-webui.service/var/lib/pyinfra-webui
Esempio ragionato di environment file
Usare placeholder, non segreti reali:
<syntaxhighlight lang="ini"> APP_HOST=0.0.0.0 APP_PORT=18180 APP_BASE_PATH=/pyinfra APP_TOKEN=CHANGEME APP_SECRET_KEY=CHANGEME APP_STATE_DIR=/var/lib/pyinfra-webui
PROXMOX_SSH_HOST=PROXMOX_HOST PROXMOX_SSH_PORT=22 PROXMOX_SSH_USER=root PROXMOX_SSH_PASS=CHANGEME
PROXMOX_IMAGE=/var/lib/vz/template/iso/noble-server-cloudimg-amd64.img PROXMOX_STORAGE=local PROXMOX_CLOUDINIT_STORAGE=local PROXMOX_BRIDGE=vmbr1 PROXMOX_GATEWAY=172.16.1.1 PROXMOX_CPU_TYPE=x86-64-v2-AES PROXMOX_CIUSER=ubuntu PROXMOX_CIPASSWORD=CHANGEME PROXMOX_CICUSTOM_USER=local:snippets/gazzi-ubuntu-user.yaml
LDAP_URI=ldap://LDAP_HOST:389 LDAP_USER_DN_TEMPLATE=uid={username},ou=People,dc=gazzi,dc=local </syntaxhighlight>
Cosa significano le variabili
APP_HOSTeAPP_PORT: bind locale del servizio FlaskAPP_BASE_PATH: prefisso usato quando l'app sta dietro reverse proxy su/pyinfraAPP_TOKEN: token opzionale per protezione addizionale APIAPP_SECRET_KEY: chiave sessione FlaskAPP_STATE_DIR: directory per log e stato jobPROXMOX_*: connessione e default di provisioningLDAP_*: endpoint e template DN per login utente
Reverse proxy
Snippet di riferimento: <syntaxhighlight lang="nginx"> location = /pyinfra {
return 301 /pyinfra/;
}
location /pyinfra/ {
proxy_pass http://BOT_GAZZI_LOCAL:18180/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; proxy_set_header X-Forwarded-Prefix /pyinfra; proxy_redirect off;
} </syntaxhighlight>
Service
<syntaxhighlight lang="ini"> [Unit] Description=Pyinfra Proxmox Web UI After=network-online.target Wants=network-online.target
[Service] Type=simple User=pyinfraweb Group=pyinfraweb WorkingDirectory=/opt/pyinfra-webui EnvironmentFile=/etc/pyinfra-webui/pyinfra-webui.env ExecStart=/opt/pyinfra-webui/venv/bin/python /opt/pyinfra-webui/app.py Restart=on-failure RestartSec=3
[Install] WantedBy=multi-user.target </syntaxhighlight>
Deploy iniziale
<syntaxhighlight lang="bash"> sudo mkdir -p /opt/pyinfra-webui /etc/pyinfra-webui /var/lib/pyinfra-webui sudo chown -R pyinfraweb:pyinfraweb /opt/pyinfra-webui /var/lib/pyinfra-webui
cd /opt/pyinfra-webui python3 -m venv venv ./venv/bin/pip install -U pip ./venv/bin/pip install -r requirements.txt
sudo install -m 0644 systemd/pyinfra-webui.service /etc/systemd/system/pyinfra-webui.service sudo systemctl daemon-reload sudo systemctl enable --now pyinfra-webui.service </syntaxhighlight>
Aggiornamento
<syntaxhighlight lang="bash"> cd /opt/pyinfra-webui ./venv/bin/pip install -r requirements.txt sudo systemctl restart pyinfra-webui.service </syntaxhighlight>
Verifiche rapide
Applicative: <syntaxhighlight lang="bash"> curl -I https://bot.gazzi.net/pyinfra/ curl -sS http://127.0.0.1:18180/api/health </syntaxhighlight>
Service: <syntaxhighlight lang="bash"> systemctl status pyinfra-webui.service journalctl -u pyinfra-webui.service -n 100 --no-pager </syntaxhighlight>
Funzionali:
- test login LDAP
- test bridge disponibili
- test VLAN disponibili
- test validazione bridge + VLAN
- test ricerca VMID libero
- test provisioning di una VM reale o di test
Esempio di percorso reale
Caso semplice:
- rete in DHCP
- bridge scelto manualmente
- nessuna VLAN
- VM avviata subito
Caso avanzato:
- rete statica
- bridge scelto da lookup
- VLAN scelta da lookup
- VMID trovato automaticamente
- utente iniziale definito dall'operatore
Sicurezza
Punti importanti:
- non mettere segreti in wiki o Git
- usare sempre HTTPS sul path pubblico
- il job parte solo a richiesta dell'utente autenticato
- la UI accetta un solo job alla volta per ridurre collisioni banali
- lo snippet cloud-init viene rimosso dopo il primo boot solo se la VM parte subito
Limiti noti
- se
start_after_create=false, lo snippet cloud-init resta finche la VM non completa il primo avvio - la validazione VLAN dipende da cio che Proxmox espone in quel momento
- la pagina non sostituisce un orchestratore completo multi-tenant: e un ingresso operativo controllato
Dove leggere il codice
Per la spiegazione dettagliata:
Per gli esempi generali: