Pyinfra/Implementazione deploy VM linea per linea
Pyinfra/Implementazione deploy VM linea per linea
Questa pagina documenta l'implementazione del deploy automatico di macchine virtuali pubblicata su bot.gazzi.net/pyinfra/.
Obiettivo della pagina:
- far capire il flusso a una persona umana
- spiegare il ruolo di ogni file
- descrivere il codice in ordine logico
- collegare il comportamento osservabile alle righe principali dell'implementazione
Non vengono riportati segreti runtime.
Vista d'insieme
I file che contano sono tre:
app.py: web UI, login LDAP, API, job queuedeploys/create_vm.py: entry point pyinfralib/proxmox_ssh.py: logica concreta di creazione VM su Proxmox
Sequenza completa:
- l'utente invia la form
app.pyvalida il payload e crea il job- il job esegue
pyinfra @local deploys/create_vm.py -y create_vm.pyrichiamalib/proxmox_ssh.pyproxmox_ssh.pyesegue i comandiqm, gestisce cloud-init e cleanup
File 1: deploys/create_vm.py
Questo file e volutamente piccolo. Il suo scopo non e contenere logica complessa, ma fare da ponte tra pyinfra e l'helper specializzato.
Lettura riga per riga
#!/usr/bin/env python3 import os import shlex from pathlib import Path from pyinfra.operations import server
Spiegazione:
- shebang Python
- import minimi
- import dell'operation
server.shell
BASE_DIR = Path(__file__).resolve().parents[1]
python_bin = os.environ.get("PYTHON_BIN", str(BASE_DIR / "venv" / "bin" / "python"))
helper = BASE_DIR / "lib" / "proxmox_ssh.py"
cmd = f"{shlex.quote(python_bin)} {shlex.quote(str(helper))}"
Spiegazione:
- calcola la base del progetto
- legge il Python del virtualenv da variabile ambiente oppure usa il default
- individua l'helper vero da eseguire
- costruisce un comando shell sicuro con quoting
server.shell(
name=f"Create Proxmox VM {os.environ.get('VM_NAME', '')}",
commands=[cmd],
)
Spiegazione:
- pyinfra vede una singola operation
- il nome dell'operation include il nome VM per leggibilita
- l'operation esegue localmente il comando helper
Idea pratica:
- pyinfra orchestra
- l'helper specializzato realizza la logica
File 2: app.py
Questo file ha quattro responsabilita:
- leggere la configurazione
- fare autenticazione LDAP
- servire UI e API
- creare e monitorare i job
= Blocco 1: costanti e caricamento environment
Righe utili: 16-64
Che cosa succede:
- viene definita la base del progetto
- viene caricato
/etc/pyinfra-webui/pyinfra-webui.env - viene inizializzata la directory stato
- vengono lette le variabili principali di app, LDAP e Proxmox
- Flask viene configurato con secret key e cookie di sessione
- vengono preparate strutture dati e lock per i job
Perche e importante:
- tutta la logica runtime dipende da questo blocco
- separa configurazione e codice
- permette di non versionare i segreti
= Blocco 2: autenticazione e URL dietro proxy
Righe utili: 67-140
Funzioni:
now_iso(): timestamp uniforme dei jobrequire_token(): token opzionale per APIforwarded_prefix()eurl_with_prefix(): supporto a/pyinfradietro reverse proxyis_authenticated()erequire_login(): controllo sessionevalidate_username(): sanifica lo username LDAPldap_authenticate(): bind LDAP con template DN
Traduzione umana:
- l'app sa stare dietro Nginx senza rompere i path
- l'utente deve autenticarsi via LDAP
- eventuali API possono richiedere un token aggiuntivo se configurato
= Blocco 3: lettura dati da Proxmox
Righe utili: 121-189
Qui trovi:
proxmox_run(): apre una connessione SSH a Proxmox per eseguire comandidiscover_vlans(): parsifica l'output dibridge vlan showdiscover_bridges(): legge i bridge conip -o link show type bridgediscover_used_vmids(): legge i VMID esistenti conqm listfirst_free_vmid(): cerca il primo VMID libero da un valore iniziale
Perche serve:
- la UI non usa valori statici
- l'utente puo interrogare Proxmox prima del submit
= Blocco 4: validazione input
Righe utili: 192-263
Qui l'app controlla:
- range di interi
- formato nome VM
- formato utente Linux
- lunghezza minima password
- modalita rete
- formato bridge
- VLAN opzionale
- IP, gateway e CIDR se la rete e statica
Risultato:
- l'input viene normalizzato in
spec - i segreti vengono separati in
secrets
Questa separazione e importante:
specpuo essere esposto nel jobsecretsva trattato con piu cautela
= Blocco 5: costruzione environment job
Righe utili: 266-290
build_job_env() converte il dizionario del job in variabili ambiente da passare al runner.
Questo blocco e il ponte tra:
- input raccolto dalla UI
- variabili ambiente lette da
proxmox_ssh.py
Esempi:
spec["vmid"]diventaVM_IDspec["bridge"]diventaVM_BRIDGE- la password admin diventa
VM_ADMIN_PASSWORD
= Blocco 6: job queue e logging
Righe utili: 293-347
Componenti:
append_job(): inserisce il job nelle strutture in memoriawrite_job_log(): salva il log su file inAPP_STATE_DIRrun_job(): lancia pyinfra in subprocesscreate_job(): impedisce job concorrenti e avvia il thread
Punto chiave:
active_job_lockfa da semaforo per evitare due creazioni VM contemporanee
Comando usato:
pyinfra @local deploys/create_vm.py -y
Blocco 7: interfaccia HTML
Righe utili: 350-560
La parte HTML/CSS contiene:
- layout pagina
- campi del form
- sezione job recenti
Campi operativi importanti:
- VMID
- nome VM
- utente sudo
- password sudo
- CPU, RAM, disco
- rete, bridge, VLAN
- gateway, CIDR, IP statico
- checkbox avvio automatico
Nota:
- le righe di stile puro servono a presentazione e leggibilita, non alla logica di deploy
Blocco 8: JavaScript della UI
Righe utili: 563-726
Funzioni principali:
syncNetworkFields()
disabilita i campi IP/gateway/CIDR quando la rete e DHCP
renderJobs()
mostra i job recenti con stato, timing e output
loadVlans()
chiama /api/network/vlans
loadBridges()
chiama /api/network/bridges
loadFreeVmid()
chiama /api/proxmox/free-vmid?start=100
validateNetwork()
chiama /api/network/validate prima del submit
loadJobs()
ricarica i job recenti
- submit handler della form
costruisce il payload JSON e lo invia a /api/jobs/create-vm
Traduzione pratica:
- il browser non parla direttamente con Proxmox
- parla solo con le API dell'app
- la UI ferma l'utente prima se bridge/VLAN non sono validi
Blocco 9: login e API backend
Righe utili: 733-938
Route principali:
/api/health/login/logout/api/jobs/api/network/vlans/api/network/bridges/api/network/validate/api/proxmox/free-vmid/api/jobs/create-vm
Punto critico:
/api/jobs/create-vmfa il passaggio da richiesta web a job reale
Il flusso della route finale e:
- richiede login
- opzionalmente richiede token
- legge il JSON
- valida il payload
- crea il job
- restituisce
202 Accepted
File 3: lib/proxmox_ssh.py
Questo file e il cuore del provisioning.
= Blocco 1: lettura environment
Righe utili: 11-29
Funzioni:
env_required()env_int()env_bool()
Servono a:
- leggere parametri obbligatori
- convertire interi
- interpretare flag booleani
= Blocco 2: classe ProxmoxSSH
Righe utili: 32-75
Questa classe:
- apre la sessione SSH
- esegue comandi
- stampa output e errori nel log del job
- scrive file remoti via SFTP
Perche e utile:
- centralizza tutte le interazioni con Proxmox
= Blocco 3: attesa guest agent ed esecuzione comandi guest
Righe utili: 77-116
Funzioni:
wait_for_guest_agent()
esegue poll su qm agent VMID ping
guest_exec()
esegue un comando dentro la VM via guest agent e aspetta l'exit status
Uso concreto nel progetto:
- attendere che cloud-init abbia davvero finito prima di fare cleanup
= Blocco 4: helper di quoting
Righe utili: 119-124
q()usashlex.quoteper shell quotingjq()usajson.dumpsper serializzare stringhe nel cloud-config
Questo evita errori banali di quoting e parsing.
= Blocco 5: lettura parametri del job
Righe utili: 127-151
Il file legge dall'environment:
- VMID, nome, utente, password
- CPU, RAM, disco
- rete, bridge, VLAN
- IP, gateway, CIDR
- flag avvio automatico
- storage, immagine cloud-init e default runtime
Qui il job passa da "payload web" a "parametri concreti di provisioning".
= Blocco 6: costruzione rete
Righe utili: 153-164
Se la rete e statica:
- richiede IP, gateway e CIDR
- costruisce
ipconfig0nel formato Proxmox
Se la rete e DHCP:
- usa
ip=dhcp
Poi costruisce net0:
- NIC virtio
- bridge obbligatorio
- firewall abilitato
- eventuale tag VLAN
= Blocco 7: verifica collisione VMID
Righe utili: 166-169
Prima di creare la VM, il file esegue:
qm config <VMID>
Se il comando funziona, il VMID esiste gia e il job viene fermato.
= Blocco 8: generazione snippet cloud-init
Righe utili: 171-191
Questo e uno dei punti piu importanti:
- crea un file
pyinfra-user-<VMID>.yaml - lo salva in
/var/lib/vz/snippets/ - definisce l'utente richiesto
- assegna gruppi
admesudo - imposta shell bash
- abilita sudo senza password
- inserisce la password iniziale
- abilita password auth SSH
- disabilita root
Perche esiste:
- il deploy deve creare un utente iniziale specificato dall'operatore
= Blocco 9: lista comandi qm
Righe utili: 193-224
Questo array contiene i passi di creazione macchina:
qm createcon nome, BIOS, machine type, RAM, CPU, NIC e serialeqm set --efidisk0qm set --scsihwqm set --scsi0 import-from=IMAGEqm set --ide2 CLOUDINITqm set --boot order=scsi0qm set --ciuserqm set --ipconfig0qm resize
Idea pratica:
- la VM nasce come cloud image Proxmox correttamente preparata
= Blocco 10: password cloud-init, cicustom e avvio
Righe utili: 226-238
Passi:
- se c'e una password, esegue
qm set --cipassword - applica lo snippet custom con
qm set --cicustom user=... - se esiste un default
PROXMOX_CICUSTOM_USER, lo ignora e lo segnala a log - se richiesto, avvia la VM con
qm start
= Blocco 11: attesa primo boot e cleanup
Righe utili: 240-251
Se la VM parte subito:
- aspetta il guest agent
- esegue dentro la VM
cloud-init status --wait - rimuove il riferimento
cicustom - cancella lo snippet da
/var/lib/vz/snippets/
Se la VM non parte subito:
- scrive un warning
- lo snippet non puo essere eliminato immediatamente
Questo blocco esiste per ridurre il tempo in cui la password resta scritta nel file cloud-init sul nodo Proxmox.
= Blocco 12: messaggio finale
Riga utile: 253
Se tutto va bene, il log termina con il messaggio di creazione VM completata.
Esempio di flusso completo
Caso:
- VMID 120
- nome
demo.gazzi.local - utente
gazzinet - rete DHCP
- bridge
vmbr1
Flusso:
- l'utente compila la form
- JavaScript invia JSON a
/api/jobs/create-vm parse_payload()valida tutti i campicreate_job()apre il jobrun_job()lancia pyinfracreate_vm.pyrichiama l'helperproxmox_ssh.pycostruisce la VM- se la VM parte subito, il sistema aspetta cloud-init e rimuove lo snippet
Cosa leggere se devi fare modifiche
Se devi cambiare la UI:
app.pyblocchi HTML e JavaScript
Se devi cambiare validazioni:
app.pyblocchiparse_payload()e API network
Se devi cambiare il provisioning Proxmox:
lib/proxmox_ssh.py
Se devi cambiare il modo in cui pyinfra lancia l'helper:
deploys/create_vm.py
Limiti di questa documentazione
Questa pagina e molto piu dettagliata del normale, ma continua a privilegiare:
- chiarezza
- leggibilita umana
- raggruppamento per blocchi logici
Non descrive ogni singola riga di CSS decorativo, perche quella parte non governa il deploy automatico VM. La logica operativa invece e coperta integralmente.