<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="it">
	<id>https://wiki.gazzi.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Maintenance+script</id>
	<title>GazziNet - Contributi dell&#039;utente [it]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.gazzi.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Maintenance+script"/>
	<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php/Speciale:Contributi/Maintenance_script"/>
	<updated>2026-05-17T03:20:41Z</updated>
	<subtitle>Contributi dell&amp;#039;utente</subtitle>
	<generator>MediaWiki 1.40.1</generator>
	<entry>
		<id>https://wiki.gazzi.net/index.php?title=Tips_Varie&amp;diff=650</id>
		<title>Tips Varie</title>
		<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php?title=Tips_Varie&amp;diff=650"/>
		<updated>2026-04-06T22:07:36Z</updated>

		<summary type="html">&lt;p&gt;Maintenance script: Aggiunto riferimento Ventoy nella sezione Tips_Varie&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
Breve raccolta di riferimenti trasversali usati nel contesto GazziNet. La pagina serve come indice rapido per contenuti non classificati altrove ma ancora utili operativamente.&lt;br /&gt;
&lt;br /&gt;
== Risorse varie ==&lt;br /&gt;
&lt;br /&gt;
=== Sicurezza, rete e certificati ===&lt;br /&gt;
* [[TIPS SSL]]&lt;br /&gt;
* [[RUSTDESK]]&lt;br /&gt;
&lt;br /&gt;
=== Automazione e bot ===&lt;br /&gt;
* [[TIPS BOT]]&lt;br /&gt;
&lt;br /&gt;
=== Streaming e contenuti ===&lt;br /&gt;
* [[TIPS TWITCH]]&lt;br /&gt;
&lt;br /&gt;
=== Hardware e laboratorio ===&lt;br /&gt;
* [[INCISORE LASER]]&lt;br /&gt;
&lt;br /&gt;
== Ventoy ==&lt;br /&gt;
&#039;&#039;&#039;Ventoy&#039;&#039;&#039; e&#039; un tool open source per creare chiavette bootabili multi-ISO senza dover riscrivere la pendrive ogni volta.&lt;br /&gt;
&lt;br /&gt;
Riferimenti:&lt;br /&gt;
* [[Ventoy]]: guida operativa completa con installazione, uso, Secure Boot, TreeView, boot da dischi locali, plugin e persistenza&lt;br /&gt;
* [https://www.gazzi.net/2026/04/07/ventoy-guida-pratica-creare-e-usare-una-chiavetta-bootabile-multi-iso/ Articolo WordPress su Ventoy]&lt;br /&gt;
&lt;br /&gt;
== Note d&#039;uso ==&lt;br /&gt;
* Usare questa pagina come indice temporaneo o trasversale, non come contenitore definitivo.&lt;br /&gt;
* Se un argomento cresce o diventa ricorrente, spostarlo in una sezione dedicata del wiki.&lt;br /&gt;
* Mantenere qui solo voci eterogenee che non appartengono chiaramente a Linux, Windows, Database, Container o Sicurezza.&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
	<entry>
		<id>https://wiki.gazzi.net/index.php?title=Ventoy&amp;diff=649</id>
		<title>Ventoy</title>
		<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php?title=Ventoy&amp;diff=649"/>
		<updated>2026-04-06T22:07:35Z</updated>

		<summary type="html">&lt;p&gt;Maintenance script: Aggiunta guida Ventoy con uso pratico e funzioni avanzate&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
&#039;&#039;&#039;Ventoy&#039;&#039;&#039; e&#039; uno strumento open source per creare supporti bootabili multi-immagine senza dover riscrivere la chiavetta ogni volta. Il principio operativo e&#039; semplice: installi Ventoy una sola volta sul dispositivo, poi copi nella prima partizione i file immagine che vuoi avviare.&lt;br /&gt;
&lt;br /&gt;
Pagina ufficiale:&lt;br /&gt;
* [https://www.ventoy.net/en/ Ventoy]&lt;br /&gt;
* [https://www.ventoy.net/en/doc_start.html Get Started]&lt;br /&gt;
&lt;br /&gt;
== Cosa supporta ==&lt;br /&gt;
Ventoy puo&#039; avviare direttamente file:&lt;br /&gt;
* &amp;lt;code&amp;gt;.iso&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.wim&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.img&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.vhd&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;.vhdx&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.efi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Supporta in modo unificato:&lt;br /&gt;
* BIOS Legacy x86&lt;br /&gt;
* UEFI IA32&lt;br /&gt;
* UEFI x86_64&lt;br /&gt;
* UEFI ARM64&lt;br /&gt;
* UEFI MIPS64EL&lt;br /&gt;
&lt;br /&gt;
Note utili:&lt;br /&gt;
* il supporto ufficiale evidenzia oltre 1300 image file testati&lt;br /&gt;
* la chiavetta puo&#039; contenere piu&#039; immagini contemporaneamente&lt;br /&gt;
* dopo l&#039;installazione Ventoy la prima partizione puo&#039; restare usata anche come normale spazio dati&lt;br /&gt;
* l&#039;aggiornamento di Ventoy non rimuove i file gia&#039; presenti nella prima partizione&lt;br /&gt;
&lt;br /&gt;
== Flusso di utilizzo base ==&lt;br /&gt;
# scaricare il pacchetto dal sito ufficiale&lt;br /&gt;
# installare Ventoy sul dispositivo USB o altro supporto compatibile&lt;br /&gt;
# copiare nella prima partizione i file immagine da avviare&lt;br /&gt;
# fare boot dal dispositivo&lt;br /&gt;
# scegliere l&#039;immagine dal menu Ventoy&lt;br /&gt;
&lt;br /&gt;
Attenzione:&lt;br /&gt;
* durante l&#039;installazione iniziale il dispositivo viene formattato&lt;br /&gt;
* verificare con molta attenzione il disco selezionato&lt;br /&gt;
&lt;br /&gt;
== Installazione su Windows ==&lt;br /&gt;
Metodo grafico consigliato:&lt;br /&gt;
# scaricare l&#039;archivio Windows di Ventoy&lt;br /&gt;
# estrarre il pacchetto&lt;br /&gt;
# eseguire &amp;lt;code&amp;gt;Ventoy2Disk.exe&amp;lt;/code&amp;gt;&lt;br /&gt;
# selezionare il dispositivo corretto&lt;br /&gt;
# scegliere &amp;lt;code&amp;gt;Install&amp;lt;/code&amp;gt; oppure &amp;lt;code&amp;gt;Update&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note operative:&lt;br /&gt;
* per default il tool mostra solo dispositivi USB&lt;br /&gt;
* se abiliti la visualizzazione di tutti i dischi aumenta il rischio di scegliere il disco sbagliato&lt;br /&gt;
* la scelta &amp;lt;code&amp;gt;MBR&amp;lt;/code&amp;gt; o &amp;lt;code&amp;gt;GPT&amp;lt;/code&amp;gt; viene usata in fase di installazione, non di update&lt;br /&gt;
* dopo l&#039;installazione puoi riformattare la prima partizione con &amp;lt;code&amp;gt;exFAT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;FAT32&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;NTFS&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UDF&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;XFS&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Ext2/3/4&amp;lt;/code&amp;gt; se necessario&lt;br /&gt;
&lt;br /&gt;
== Installazione su Linux ==&lt;br /&gt;
=== Modalita CLI ===&lt;br /&gt;
Scarica il pacchetto Linux, estrailo e lancia come root:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sh Ventoy2Disk.sh { -i | -I | -u } /dev/sdX&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sintassi utile:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Ventoy2Disk.sh CMD [ OPTION ] /dev/sdX&lt;br /&gt;
&lt;br /&gt;
CMD:&lt;br /&gt;
  -i   installa Ventoy su sdX se non e&#039; gia&#039; presente&lt;br /&gt;
  -I   forza l&#039;installazione&lt;br /&gt;
  -u   aggiorna Ventoy su sdX&lt;br /&gt;
  -l   mostra le informazioni Ventoy sul disco&lt;br /&gt;
&lt;br /&gt;
OPTION:&lt;br /&gt;
  -r SIZE_MB  preserva spazio alla fine del disco durante l&#039;installazione&lt;br /&gt;
  -s          abilita Secure Boot&lt;br /&gt;
  -g          usa schema GPT, altrimenti MBR&lt;br /&gt;
  -L          label della partizione principale&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Esempi:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo sh Ventoy2Disk.sh -i /dev/sdb&lt;br /&gt;
sudo sh Ventoy2Disk.sh -i -g /dev/sdb&lt;br /&gt;
sudo sh Ventoy2Disk.sh -u /dev/sdb&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modalita WebUI su Linux ===&lt;br /&gt;
Ventoy fornisce anche una GUI via browser, utile quando la macchina Linux non ha desktop locale.&lt;br /&gt;
&lt;br /&gt;
Avvio:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo bash VentoyWeb.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Uso:&lt;br /&gt;
# aprire nel browser l&#039;indirizzo mostrato dal comando, in genere &amp;lt;code&amp;gt;http://127.0.0.1:24680&amp;lt;/code&amp;gt;&lt;br /&gt;
# per ascoltare su altro IP/porta usare ad esempio:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo bash VentoyWeb.sh -H 192.168.0.100 -P 8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
* di default la WebUI ascolta solo su localhost&lt;br /&gt;
* se esposta su altro IP puo&#039; essere usata da un altro computer nella stessa rete o raggiungibile&lt;br /&gt;
* per chiudere la WebUI basta chiudere il browser e terminare il processo con &amp;lt;code&amp;gt;Ctrl+C&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Copia delle immagini ==&lt;br /&gt;
Dopo l&#039;installazione:&lt;br /&gt;
* la prima partizione e&#039; quella usata per i file immagine&lt;br /&gt;
* puoi copiare i file ovunque, anche in sottodirectory&lt;br /&gt;
* Ventoy cerca ricorsivamente le immagini e le mostra nel menu in ordine alfabetico&lt;br /&gt;
* se vuoi limitare la scansione a directory specifiche puoi usare i plugin&lt;br /&gt;
&lt;br /&gt;
Tip pratici:&lt;br /&gt;
* organizzare le ISO per cartelle aiuta molto quando il numero cresce&lt;br /&gt;
* con molte immagini conviene usare la modalita TreeView&lt;br /&gt;
&lt;br /&gt;
== Modalita di navigazione e boot ==&lt;br /&gt;
=== List mode e TreeView ===&lt;br /&gt;
Per default Ventoy mostra una lista alfabetica di tutte le immagini trovate.&lt;br /&gt;
&lt;br /&gt;
Se hai molte immagini:&lt;br /&gt;
* premi &amp;lt;code&amp;gt;F3&amp;lt;/code&amp;gt; per entrare in TreeView&lt;br /&gt;
* in TreeView puoi navigare le directory reali&lt;br /&gt;
* usa &amp;lt;code&amp;gt;Enter&amp;lt;/code&amp;gt; per entrare in una sottocartella&lt;br /&gt;
* usa &amp;lt;code&amp;gt;ESC&amp;lt;/code&amp;gt; per tornare indietro&lt;br /&gt;
* se sei al livello top, &amp;lt;code&amp;gt;ESC&amp;lt;/code&amp;gt; riporta alla vista lista&lt;br /&gt;
&lt;br /&gt;
La modalita di default puo&#039; essere cambiata anche via plugin di controllo globale.&lt;br /&gt;
&lt;br /&gt;
=== Boot di file presenti su dischi locali ===&lt;br /&gt;
Ventoy supporta anche il browsing di file immagine presenti su altri dischi locali.&lt;br /&gt;
&lt;br /&gt;
Uso rapido:&lt;br /&gt;
* premi &amp;lt;code&amp;gt;F2&amp;lt;/code&amp;gt; nel menu principale&lt;br /&gt;
* naviga i dischi e scegli il file da avviare&lt;br /&gt;
* &amp;lt;code&amp;gt;ESC&amp;lt;/code&amp;gt; torna al menu precedente&lt;br /&gt;
&lt;br /&gt;
Note importanti:&lt;br /&gt;
* la funzione di browsing locale non usa tutti i plugin&lt;br /&gt;
* per compatibilita estesa con plugin, la documentazione Ventoy rimanda anche al meccanismo &amp;lt;code&amp;gt;vlnk&amp;lt;/code&amp;gt;&lt;br /&gt;
* questa funzione e&#039; comoda quando una ISO e&#039; gia&#039; su SSD/HDD e non vuoi copiarla sulla chiavetta&lt;br /&gt;
&lt;br /&gt;
== Secure Boot ==&lt;br /&gt;
Ventoy supporta Secure Boot in UEFI.&lt;br /&gt;
&lt;br /&gt;
Punti da ricordare:&lt;br /&gt;
* il supporto e&#039; presente da tempo nella documentazione ufficiale&lt;br /&gt;
* l&#039;opzione Secure Boot e&#039; disponibile nel tool di installazione&lt;br /&gt;
* al primo avvio su una macchina con Secure Boot attivo puo&#039; essere necessario completare una procedura di enrollment&lt;br /&gt;
* la procedura va fatta una sola volta per macchina&lt;br /&gt;
&lt;br /&gt;
Comportamento pratico:&lt;br /&gt;
* se il sistema mostra la schermata prevista, seguire la procedura guidata per registrare chiave o hash&lt;br /&gt;
* se la macchina mostra errori o incompatibilita, la soluzione ufficiale puo&#039; richiedere:&lt;br /&gt;
** reinstallare o aggiornare Ventoy senza supporto Secure Boot&lt;br /&gt;
** disattivare Secure Boot nel firmware&lt;br /&gt;
&lt;br /&gt;
Riferimento:&lt;br /&gt;
* [https://www.ventoy.net/en/doc_secure.html About Secure Boot in UEFI mode]&lt;br /&gt;
&lt;br /&gt;
== Plugin e personalizzazione ==&lt;br /&gt;
Ventoy permette di estendere il comportamento tramite una directory &amp;lt;code&amp;gt;/ventoy&amp;lt;/code&amp;gt; nella prima partizione e un file &amp;lt;code&amp;gt;ventoy.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Regole base:&lt;br /&gt;
* la directory &amp;lt;code&amp;gt;/ventoy&amp;lt;/code&amp;gt; va creata manualmente nella root della prima partizione&lt;br /&gt;
* &amp;lt;code&amp;gt;ventoy.json&amp;lt;/code&amp;gt; deve stare direttamente dentro &amp;lt;code&amp;gt;/ventoy&amp;lt;/code&amp;gt;&lt;br /&gt;
* il file deve essere JSON valido, UTF-8 e con oggetto root &amp;lt;code&amp;gt;{ }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Esempi di uso plugin:&lt;br /&gt;
* tema personalizzato&lt;br /&gt;
* alias di menu&lt;br /&gt;
* messaggi descrittivi&lt;br /&gt;
* auto installazione&lt;br /&gt;
* persistenza Linux&lt;br /&gt;
* protezione con password&lt;br /&gt;
* sostituzione di file di configurazione di boot&lt;br /&gt;
&lt;br /&gt;
Per configurazioni piu&#039; complesse:&lt;br /&gt;
* [https://www.ventoy.net/en/plugin.html Plugin]&lt;br /&gt;
* [https://www.ventoy.net/en/plugin_entry.html Plugin Entrypoint]&lt;br /&gt;
&lt;br /&gt;
== Persistenza Linux ==&lt;br /&gt;
Ventoy supporta la persistenza per diverse live distro Linux senza imporre una partizione dedicata separata.&lt;br /&gt;
&lt;br /&gt;
Concetto:&lt;br /&gt;
* si usa un file backend nella prima partizione&lt;br /&gt;
* il file viene associato alla ISO tramite configurazione JSON&lt;br /&gt;
* al boot Ventoy puo&#039; usare il backend corretto o mostrare un menu di scelta&lt;br /&gt;
&lt;br /&gt;
Esempio concettuale:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;persistence&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;image&amp;quot;: &amp;quot;/ISO/ubuntu.iso&amp;quot;,&lt;br /&gt;
      &amp;quot;backend&amp;quot;: &amp;quot;/persistence/ubuntu.dat&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
* distro diverse usano label differenti&lt;br /&gt;
* per alcune immagini servono opzioni aggiuntive specifiche&lt;br /&gt;
* la documentazione ufficiale elenca distro testate e formato del backend&lt;br /&gt;
&lt;br /&gt;
Riferimento:&lt;br /&gt;
* [https://www.ventoy.net/en/plugin_persistence.html Ventoy Persistence Plugin]&lt;br /&gt;
&lt;br /&gt;
== Quando ha senso usare Ventoy ==&lt;br /&gt;
Ventoy e&#039; particolarmente utile se:&lt;br /&gt;
* vuoi una chiavetta multiuso con piu&#039; installer e live image&lt;br /&gt;
* testi spesso distro Linux, strumenti rescue o installazioni Windows&lt;br /&gt;
* vuoi ridurre il tempo perso a riscrivere supporti USB&lt;br /&gt;
* vuoi organizzare immagini per cartelle e categorie&lt;br /&gt;
* vuoi mantenere una base comune ma con personalizzazioni tramite plugin&lt;br /&gt;
&lt;br /&gt;
== Limiti e attenzioni ==&lt;br /&gt;
* l&#039;installazione iniziale cancella il contenuto del dispositivo scelto&lt;br /&gt;
* non tutti i plugin valgono in tutte le modalita di browsing&lt;br /&gt;
* alcune macchine possono avere incompatibilita con Secure Boot&lt;br /&gt;
* prima di usare funzioni avanzate conviene verificare la documentazione ufficiale della singola feature&lt;br /&gt;
&lt;br /&gt;
== Collegamenti utili ==&lt;br /&gt;
* [https://www.gazzi.net/2026/04/07/ventoy-guida-pratica-creare-e-usare-una-chiavetta-bootabile-multi-iso/ Articolo WordPress su Ventoy]&lt;br /&gt;
* [https://www.ventoy.net/en/ Ventoy official site]&lt;br /&gt;
* [https://www.ventoy.net/en/doc_start.html Start to use Ventoy]&lt;br /&gt;
* [https://www.ventoy.net/en/doc_linux_webui.html Linux GUI WebUI]&lt;br /&gt;
* [https://www.ventoy.net/en/doc_treeview.html TreeView Mode]&lt;br /&gt;
* [https://www.ventoy.net/en/doc_browser.html Browse/Boot Files In Local Disk]&lt;br /&gt;
* [https://www.ventoy.net/en/doc_vlnk.html Boot Ventoy Vlnk File]&lt;br /&gt;
* [https://www.ventoy.net/en/doc_secure.html Secure Boot]&lt;br /&gt;
* [https://www.ventoy.net/en/plugin.html Plugin]&lt;br /&gt;
* [https://www.ventoy.net/en/plugin_entry.html Plugin Entrypoint]&lt;br /&gt;
* [https://www.ventoy.net/en/plugin_persistence.html Persistence Plugin]&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
	<entry>
		<id>https://wiki.gazzi.net/index.php?title=Tips_Varie&amp;diff=648</id>
		<title>Tips Varie</title>
		<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php?title=Tips_Varie&amp;diff=648"/>
		<updated>2026-04-06T22:01:07Z</updated>

		<summary type="html">&lt;p&gt;Maintenance script: Aggiunto riferimento Ventoy nella sezione Tips_Varie&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
Breve raccolta di riferimenti trasversali usati nel contesto GazziNet. La pagina serve come indice rapido per contenuti non classificati altrove ma ancora utili operativamente.&lt;br /&gt;
&lt;br /&gt;
== Risorse varie ==&lt;br /&gt;
&lt;br /&gt;
=== Sicurezza, rete e certificati ===&lt;br /&gt;
* [[TIPS SSL]]&lt;br /&gt;
* [[RUSTDESK]]&lt;br /&gt;
&lt;br /&gt;
=== Automazione e bot ===&lt;br /&gt;
* [[TIPS BOT]]&lt;br /&gt;
&lt;br /&gt;
=== Streaming e contenuti ===&lt;br /&gt;
* [[TIPS TWITCH]]&lt;br /&gt;
&lt;br /&gt;
=== Hardware e laboratorio ===&lt;br /&gt;
* [[INCISORE LASER]]&lt;br /&gt;
&lt;br /&gt;
== Ventoy ==&lt;br /&gt;
&#039;&#039;&#039;Ventoy&#039;&#039;&#039; e&#039; un tool open source per creare chiavette bootabili multi-ISO senza dover riscrivere la pendrive ogni volta.&lt;br /&gt;
&lt;br /&gt;
Riferimenti:&lt;br /&gt;
* [[Ventoy]]: guida operativa completa con installazione, uso, Secure Boot, TreeView, boot da dischi locali, plugin e persistenza&lt;br /&gt;
* [https://www.gazzi.net/2026/04/06/ventoy-guida-pratica-creare-e-usare-una-chiavetta-bootabile-multi-iso/ Articolo WordPress su Ventoy]&lt;br /&gt;
&lt;br /&gt;
== Note d&#039;uso ==&lt;br /&gt;
* Usare questa pagina come indice temporaneo o trasversale, non come contenitore definitivo.&lt;br /&gt;
* Se un argomento cresce o diventa ricorrente, spostarlo in una sezione dedicata del wiki.&lt;br /&gt;
* Mantenere qui solo voci eterogenee che non appartengono chiaramente a Linux, Windows, Database, Container o Sicurezza.&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
	<entry>
		<id>https://wiki.gazzi.net/index.php?title=Ventoy&amp;diff=647</id>
		<title>Ventoy</title>
		<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php?title=Ventoy&amp;diff=647"/>
		<updated>2026-04-06T22:01:06Z</updated>

		<summary type="html">&lt;p&gt;Maintenance script: Aggiunta guida Ventoy con uso pratico e funzioni avanzate&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
&#039;&#039;&#039;Ventoy&#039;&#039;&#039; e&#039; uno strumento open source per creare supporti bootabili multi-immagine senza dover riscrivere la chiavetta ogni volta. Il principio operativo e&#039; semplice: installi Ventoy una sola volta sul dispositivo, poi copi nella prima partizione i file immagine che vuoi avviare.&lt;br /&gt;
&lt;br /&gt;
Pagina ufficiale:&lt;br /&gt;
* [https://www.ventoy.net/en/ Ventoy]&lt;br /&gt;
* [https://www.ventoy.net/en/doc_start.html Get Started]&lt;br /&gt;
&lt;br /&gt;
== Cosa supporta ==&lt;br /&gt;
Ventoy puo&#039; avviare direttamente file:&lt;br /&gt;
* &amp;lt;code&amp;gt;.iso&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.wim&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.img&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.vhd&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;.vhdx&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.efi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Supporta in modo unificato:&lt;br /&gt;
* BIOS Legacy x86&lt;br /&gt;
* UEFI IA32&lt;br /&gt;
* UEFI x86_64&lt;br /&gt;
* UEFI ARM64&lt;br /&gt;
* UEFI MIPS64EL&lt;br /&gt;
&lt;br /&gt;
Note utili:&lt;br /&gt;
* il supporto ufficiale evidenzia oltre 1300 image file testati&lt;br /&gt;
* la chiavetta puo&#039; contenere piu&#039; immagini contemporaneamente&lt;br /&gt;
* dopo l&#039;installazione Ventoy la prima partizione puo&#039; restare usata anche come normale spazio dati&lt;br /&gt;
* l&#039;aggiornamento di Ventoy non rimuove i file gia&#039; presenti nella prima partizione&lt;br /&gt;
&lt;br /&gt;
== Flusso di utilizzo base ==&lt;br /&gt;
# scaricare il pacchetto dal sito ufficiale&lt;br /&gt;
# installare Ventoy sul dispositivo USB o altro supporto compatibile&lt;br /&gt;
# copiare nella prima partizione i file immagine da avviare&lt;br /&gt;
# fare boot dal dispositivo&lt;br /&gt;
# scegliere l&#039;immagine dal menu Ventoy&lt;br /&gt;
&lt;br /&gt;
Attenzione:&lt;br /&gt;
* durante l&#039;installazione iniziale il dispositivo viene formattato&lt;br /&gt;
* verificare con molta attenzione il disco selezionato&lt;br /&gt;
&lt;br /&gt;
== Installazione su Windows ==&lt;br /&gt;
Metodo grafico consigliato:&lt;br /&gt;
# scaricare l&#039;archivio Windows di Ventoy&lt;br /&gt;
# estrarre il pacchetto&lt;br /&gt;
# eseguire &amp;lt;code&amp;gt;Ventoy2Disk.exe&amp;lt;/code&amp;gt;&lt;br /&gt;
# selezionare il dispositivo corretto&lt;br /&gt;
# scegliere &amp;lt;code&amp;gt;Install&amp;lt;/code&amp;gt; oppure &amp;lt;code&amp;gt;Update&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note operative:&lt;br /&gt;
* per default il tool mostra solo dispositivi USB&lt;br /&gt;
* se abiliti la visualizzazione di tutti i dischi aumenta il rischio di scegliere il disco sbagliato&lt;br /&gt;
* la scelta &amp;lt;code&amp;gt;MBR&amp;lt;/code&amp;gt; o &amp;lt;code&amp;gt;GPT&amp;lt;/code&amp;gt; viene usata in fase di installazione, non di update&lt;br /&gt;
* dopo l&#039;installazione puoi riformattare la prima partizione con &amp;lt;code&amp;gt;exFAT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;FAT32&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;NTFS&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UDF&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;XFS&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Ext2/3/4&amp;lt;/code&amp;gt; se necessario&lt;br /&gt;
&lt;br /&gt;
== Installazione su Linux ==&lt;br /&gt;
=== Modalita CLI ===&lt;br /&gt;
Scarica il pacchetto Linux, estrailo e lancia come root:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sh Ventoy2Disk.sh { -i | -I | -u } /dev/sdX&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sintassi utile:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Ventoy2Disk.sh CMD [ OPTION ] /dev/sdX&lt;br /&gt;
&lt;br /&gt;
CMD:&lt;br /&gt;
  -i   installa Ventoy su sdX se non e&#039; gia&#039; presente&lt;br /&gt;
  -I   forza l&#039;installazione&lt;br /&gt;
  -u   aggiorna Ventoy su sdX&lt;br /&gt;
  -l   mostra le informazioni Ventoy sul disco&lt;br /&gt;
&lt;br /&gt;
OPTION:&lt;br /&gt;
  -r SIZE_MB  preserva spazio alla fine del disco durante l&#039;installazione&lt;br /&gt;
  -s          abilita Secure Boot&lt;br /&gt;
  -g          usa schema GPT, altrimenti MBR&lt;br /&gt;
  -L          label della partizione principale&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Esempi:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo sh Ventoy2Disk.sh -i /dev/sdb&lt;br /&gt;
sudo sh Ventoy2Disk.sh -i -g /dev/sdb&lt;br /&gt;
sudo sh Ventoy2Disk.sh -u /dev/sdb&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modalita WebUI su Linux ===&lt;br /&gt;
Ventoy fornisce anche una GUI via browser, utile quando la macchina Linux non ha desktop locale.&lt;br /&gt;
&lt;br /&gt;
Avvio:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo bash VentoyWeb.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Uso:&lt;br /&gt;
# aprire nel browser l&#039;indirizzo mostrato dal comando, in genere &amp;lt;code&amp;gt;http://127.0.0.1:24680&amp;lt;/code&amp;gt;&lt;br /&gt;
# per ascoltare su altro IP/porta usare ad esempio:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo bash VentoyWeb.sh -H 192.168.0.100 -P 8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
* di default la WebUI ascolta solo su localhost&lt;br /&gt;
* se esposta su altro IP puo&#039; essere usata da un altro computer nella stessa rete o raggiungibile&lt;br /&gt;
* per chiudere la WebUI basta chiudere il browser e terminare il processo con &amp;lt;code&amp;gt;Ctrl+C&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Copia delle immagini ==&lt;br /&gt;
Dopo l&#039;installazione:&lt;br /&gt;
* la prima partizione e&#039; quella usata per i file immagine&lt;br /&gt;
* puoi copiare i file ovunque, anche in sottodirectory&lt;br /&gt;
* Ventoy cerca ricorsivamente le immagini e le mostra nel menu in ordine alfabetico&lt;br /&gt;
* se vuoi limitare la scansione a directory specifiche puoi usare i plugin&lt;br /&gt;
&lt;br /&gt;
Tip pratici:&lt;br /&gt;
* organizzare le ISO per cartelle aiuta molto quando il numero cresce&lt;br /&gt;
* con molte immagini conviene usare la modalita TreeView&lt;br /&gt;
&lt;br /&gt;
== Modalita di navigazione e boot ==&lt;br /&gt;
=== List mode e TreeView ===&lt;br /&gt;
Per default Ventoy mostra una lista alfabetica di tutte le immagini trovate.&lt;br /&gt;
&lt;br /&gt;
Se hai molte immagini:&lt;br /&gt;
* premi &amp;lt;code&amp;gt;F3&amp;lt;/code&amp;gt; per entrare in TreeView&lt;br /&gt;
* in TreeView puoi navigare le directory reali&lt;br /&gt;
* usa &amp;lt;code&amp;gt;Enter&amp;lt;/code&amp;gt; per entrare in una sottocartella&lt;br /&gt;
* usa &amp;lt;code&amp;gt;ESC&amp;lt;/code&amp;gt; per tornare indietro&lt;br /&gt;
* se sei al livello top, &amp;lt;code&amp;gt;ESC&amp;lt;/code&amp;gt; riporta alla vista lista&lt;br /&gt;
&lt;br /&gt;
La modalita di default puo&#039; essere cambiata anche via plugin di controllo globale.&lt;br /&gt;
&lt;br /&gt;
=== Boot di file presenti su dischi locali ===&lt;br /&gt;
Ventoy supporta anche il browsing di file immagine presenti su altri dischi locali.&lt;br /&gt;
&lt;br /&gt;
Uso rapido:&lt;br /&gt;
* premi &amp;lt;code&amp;gt;F2&amp;lt;/code&amp;gt; nel menu principale&lt;br /&gt;
* naviga i dischi e scegli il file da avviare&lt;br /&gt;
* &amp;lt;code&amp;gt;ESC&amp;lt;/code&amp;gt; torna al menu precedente&lt;br /&gt;
&lt;br /&gt;
Note importanti:&lt;br /&gt;
* la funzione di browsing locale non usa tutti i plugin&lt;br /&gt;
* per compatibilita estesa con plugin, la documentazione Ventoy rimanda anche al meccanismo &amp;lt;code&amp;gt;vlnk&amp;lt;/code&amp;gt;&lt;br /&gt;
* questa funzione e&#039; comoda quando una ISO e&#039; gia&#039; su SSD/HDD e non vuoi copiarla sulla chiavetta&lt;br /&gt;
&lt;br /&gt;
== Secure Boot ==&lt;br /&gt;
Ventoy supporta Secure Boot in UEFI.&lt;br /&gt;
&lt;br /&gt;
Punti da ricordare:&lt;br /&gt;
* il supporto e&#039; presente da tempo nella documentazione ufficiale&lt;br /&gt;
* l&#039;opzione Secure Boot e&#039; disponibile nel tool di installazione&lt;br /&gt;
* al primo avvio su una macchina con Secure Boot attivo puo&#039; essere necessario completare una procedura di enrollment&lt;br /&gt;
* la procedura va fatta una sola volta per macchina&lt;br /&gt;
&lt;br /&gt;
Comportamento pratico:&lt;br /&gt;
* se il sistema mostra la schermata prevista, seguire la procedura guidata per registrare chiave o hash&lt;br /&gt;
* se la macchina mostra errori o incompatibilita, la soluzione ufficiale puo&#039; richiedere:&lt;br /&gt;
** reinstallare o aggiornare Ventoy senza supporto Secure Boot&lt;br /&gt;
** disattivare Secure Boot nel firmware&lt;br /&gt;
&lt;br /&gt;
Riferimento:&lt;br /&gt;
* [https://www.ventoy.net/en/doc_secure.html About Secure Boot in UEFI mode]&lt;br /&gt;
&lt;br /&gt;
== Plugin e personalizzazione ==&lt;br /&gt;
Ventoy permette di estendere il comportamento tramite una directory &amp;lt;code&amp;gt;/ventoy&amp;lt;/code&amp;gt; nella prima partizione e un file &amp;lt;code&amp;gt;ventoy.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Regole base:&lt;br /&gt;
* la directory &amp;lt;code&amp;gt;/ventoy&amp;lt;/code&amp;gt; va creata manualmente nella root della prima partizione&lt;br /&gt;
* &amp;lt;code&amp;gt;ventoy.json&amp;lt;/code&amp;gt; deve stare direttamente dentro &amp;lt;code&amp;gt;/ventoy&amp;lt;/code&amp;gt;&lt;br /&gt;
* il file deve essere JSON valido, UTF-8 e con oggetto root &amp;lt;code&amp;gt;{ }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Esempi di uso plugin:&lt;br /&gt;
* tema personalizzato&lt;br /&gt;
* alias di menu&lt;br /&gt;
* messaggi descrittivi&lt;br /&gt;
* auto installazione&lt;br /&gt;
* persistenza Linux&lt;br /&gt;
* protezione con password&lt;br /&gt;
* sostituzione di file di configurazione di boot&lt;br /&gt;
&lt;br /&gt;
Per configurazioni piu&#039; complesse:&lt;br /&gt;
* [https://www.ventoy.net/en/plugin.html Plugin]&lt;br /&gt;
* [https://www.ventoy.net/en/plugin_entry.html Plugin Entrypoint]&lt;br /&gt;
&lt;br /&gt;
== Persistenza Linux ==&lt;br /&gt;
Ventoy supporta la persistenza per diverse live distro Linux senza imporre una partizione dedicata separata.&lt;br /&gt;
&lt;br /&gt;
Concetto:&lt;br /&gt;
* si usa un file backend nella prima partizione&lt;br /&gt;
* il file viene associato alla ISO tramite configurazione JSON&lt;br /&gt;
* al boot Ventoy puo&#039; usare il backend corretto o mostrare un menu di scelta&lt;br /&gt;
&lt;br /&gt;
Esempio concettuale:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;persistence&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;image&amp;quot;: &amp;quot;/ISO/ubuntu.iso&amp;quot;,&lt;br /&gt;
      &amp;quot;backend&amp;quot;: &amp;quot;/persistence/ubuntu.dat&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
* distro diverse usano label differenti&lt;br /&gt;
* per alcune immagini servono opzioni aggiuntive specifiche&lt;br /&gt;
* la documentazione ufficiale elenca distro testate e formato del backend&lt;br /&gt;
&lt;br /&gt;
Riferimento:&lt;br /&gt;
* [https://www.ventoy.net/en/plugin_persistence.html Ventoy Persistence Plugin]&lt;br /&gt;
&lt;br /&gt;
== Quando ha senso usare Ventoy ==&lt;br /&gt;
Ventoy e&#039; particolarmente utile se:&lt;br /&gt;
* vuoi una chiavetta multiuso con piu&#039; installer e live image&lt;br /&gt;
* testi spesso distro Linux, strumenti rescue o installazioni Windows&lt;br /&gt;
* vuoi ridurre il tempo perso a riscrivere supporti USB&lt;br /&gt;
* vuoi organizzare immagini per cartelle e categorie&lt;br /&gt;
* vuoi mantenere una base comune ma con personalizzazioni tramite plugin&lt;br /&gt;
&lt;br /&gt;
== Limiti e attenzioni ==&lt;br /&gt;
* l&#039;installazione iniziale cancella il contenuto del dispositivo scelto&lt;br /&gt;
* non tutti i plugin valgono in tutte le modalita di browsing&lt;br /&gt;
* alcune macchine possono avere incompatibilita con Secure Boot&lt;br /&gt;
* prima di usare funzioni avanzate conviene verificare la documentazione ufficiale della singola feature&lt;br /&gt;
&lt;br /&gt;
== Collegamenti utili ==&lt;br /&gt;
* [https://www.gazzi.net/2026/04/06/ventoy-guida-pratica-creare-e-usare-una-chiavetta-bootabile-multi-iso/ Articolo WordPress su Ventoy]&lt;br /&gt;
* [https://www.ventoy.net/en/ Ventoy official site]&lt;br /&gt;
* [https://www.ventoy.net/en/doc_start.html Start to use Ventoy]&lt;br /&gt;
* [https://www.ventoy.net/en/doc_linux_webui.html Linux GUI WebUI]&lt;br /&gt;
* [https://www.ventoy.net/en/doc_treeview.html TreeView Mode]&lt;br /&gt;
* [https://www.ventoy.net/en/doc_browser.html Browse/Boot Files In Local Disk]&lt;br /&gt;
* [https://www.ventoy.net/en/doc_vlnk.html Boot Ventoy Vlnk File]&lt;br /&gt;
* [https://www.ventoy.net/en/doc_secure.html Secure Boot]&lt;br /&gt;
* [https://www.ventoy.net/en/plugin.html Plugin]&lt;br /&gt;
* [https://www.ventoy.net/en/plugin_entry.html Plugin Entrypoint]&lt;br /&gt;
* [https://www.ventoy.net/en/plugin_persistence.html Persistence Plugin]&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
	<entry>
		<id>https://wiki.gazzi.net/index.php?title=Codex_per_struttura_wiki_e_pubblicazione_articoli&amp;diff=646</id>
		<title>Codex per struttura wiki e pubblicazione articoli</title>
		<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php?title=Codex_per_struttura_wiki_e_pubblicazione_articoli&amp;diff=646"/>
		<updated>2026-03-30T23:25:13Z</updated>

		<summary type="html">&lt;p&gt;Maintenance script: Aggiunto richiamo a CartClin sotto Codex per struttura wiki e pubblicazione articoli&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
= Codex per struttura wiki e pubblicazione articoli =&lt;br /&gt;
&lt;br /&gt;
Questa pagina documenta in modo pratico come il sistema Codex e stato usato per riorganizzare il wiki GazziNet e per supportare la generazione e pubblicazione di articoli sul sito WordPress.&lt;br /&gt;
&lt;br /&gt;
== Contesto d&#039;uso ==&lt;br /&gt;
Il sistema e stato usato come agente operativo, non come semplice generatore di testo. Nel lavoro svolto ha avuto tre ruoli principali:&lt;br /&gt;
&lt;br /&gt;
* riorganizzazione del wiki MediaWiki&lt;br /&gt;
* supporto alla produzione di articoli tecnici e news sul sito&lt;br /&gt;
* automazione di task ripetitivi su tema, struttura, contenuti e pubblicazione&lt;br /&gt;
&lt;br /&gt;
== Come e stato usato sul wiki ==&lt;br /&gt;
Sul wiki l&#039;uso principale e stato editoriale e strutturale. In pratica il flusso e stato questo:&lt;br /&gt;
&lt;br /&gt;
# lettura delle pagine esistenti&lt;br /&gt;
# verifica del contenuto attuale e del markup reale servito dal sito&lt;br /&gt;
# riorganizzazione in sezioni piu leggibili&lt;br /&gt;
# normalizzazione di titoli, indici e pagine &amp;quot;Tips&amp;quot;&lt;br /&gt;
# correzione di nomi errati, redirect e link interni&lt;br /&gt;
# aggiornamento del CSS globale MediaWiki quando necessario per allineare il layout al sito principale&lt;br /&gt;
&lt;br /&gt;
Esempi pratici di attivita fatte:&lt;br /&gt;
* pulizia e reimpaginazione della home del wiki&lt;br /&gt;
* revisione di pagine come &#039;&#039;Tips Linux&#039;&#039;, &#039;&#039;Tips Windows&#039;&#039;, &#039;&#039;Tips Database&#039;&#039;, &#039;&#039;Tips Application Server&#039;&#039;, &#039;&#039;Tips Virtualizzatori&#039;&#039;&lt;br /&gt;
* correzione della pagina &#039;&#039;Conteiner&#039;&#039; in &#039;&#039;Container&#039;&#039; con redirect esplicito&lt;br /&gt;
* miglioramento della documentazione hardware della workstation locale&lt;br /&gt;
* ripristino dei controlli di editing quando erano stati nascosti dal CSS&lt;br /&gt;
&lt;br /&gt;
== Come e stato usato per gli articoli ==&lt;br /&gt;
Sul sito WordPress Codex e stato usato per:&lt;br /&gt;
&lt;br /&gt;
* generare articoli tecnici in stile coerente con la sezione &#039;&#039;GenDaIa&#039;&#039;&lt;br /&gt;
* migliorare titoli, excerpt e struttura SEO dei post esistenti&lt;br /&gt;
* pubblicare nuovi articoli con categoria corretta&lt;br /&gt;
* uniformare template, tipografia e layout tra home, hub e singoli articoli&lt;br /&gt;
&lt;br /&gt;
Il sistema e stato usato sia in modalita manuale che in modalita assistita. In pratica:&lt;br /&gt;
* su richiesta venivano creati nuovi articoli&lt;br /&gt;
* i testi venivano adattati al taglio tecnico o divulgativo richiesto&lt;br /&gt;
* i contenuti venivano poi pubblicati o corretti direttamente via WordPress&lt;br /&gt;
&lt;br /&gt;
== Flusso operativo tipico ==&lt;br /&gt;
Un flusso tipico di lavoro con Codex, in questo contesto, e stato:&lt;br /&gt;
&lt;br /&gt;
# richiesta dell&#039;obiettivo&lt;br /&gt;
# analisi del contesto reale del sito o del wiki&lt;br /&gt;
# lettura dei file o del markup pubblico&lt;br /&gt;
# proposta implicita della modifica tramite esecuzione operativa&lt;br /&gt;
# verifica del risultato sul frontend o tramite API&lt;br /&gt;
# eventuale rifinitura successiva&lt;br /&gt;
&lt;br /&gt;
Questo approccio e stato utile soprattutto quando il problema non era solo nel contenuto, ma nella combinazione di:&lt;br /&gt;
* struttura pagina&lt;br /&gt;
* stile&lt;br /&gt;
* template&lt;br /&gt;
* automazione&lt;br /&gt;
* pubblicazione&lt;br /&gt;
&lt;br /&gt;
== Dev e piattaforme ==&lt;br /&gt;
Tra i progetti documentati in questa area rientra anche:&lt;br /&gt;
&lt;br /&gt;
* [[Progetti/CartClin]]&lt;br /&gt;
&lt;br /&gt;
La pagina del progetto raccoglie la configurazione di pubblicazione di:&lt;br /&gt;
&lt;br /&gt;
* runtime applicativo su web01&lt;br /&gt;
* backend Nginx locale&lt;br /&gt;
* reverse proxy pubblico su proxy01&lt;br /&gt;
* certificato TLS Let&#039;s Encrypt per &amp;lt;code&amp;gt;cartclin.gazzi.net&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Integrazione tra Codex, ChatGPT e IA locale ==&lt;br /&gt;
Nel lavoro pratico questi tre livelli possono convivere, ma non hanno lo stesso ruolo.&lt;br /&gt;
&lt;br /&gt;
=== Codex ===&lt;br /&gt;
Codex viene usato come agente operativo: legge il contesto, controlla file, verifica pagine, modifica contenuti, esegue comandi e aggiorna strutture reali del sito o del wiki.&lt;br /&gt;
&lt;br /&gt;
=== ChatGPT ===&lt;br /&gt;
ChatGPT e utile soprattutto come interfaccia conversazionale generale: brainstorming, chiarimenti, riscrittura, sintesi, definizione dell&#039;obiettivo o raffinamento di una richiesta prima dell&#039;esecuzione operativa.&lt;br /&gt;
&lt;br /&gt;
=== IA locale ===&lt;br /&gt;
Una IA locale, per esempio tramite Ollama, e utile quando si vuole:&lt;br /&gt;
* generare contenuti senza dipendere da un servizio esterno&lt;br /&gt;
* fare test rapidi su prompt, classificazione o riscrittura&lt;br /&gt;
* mantenere alcuni flussi in ambiente locale&lt;br /&gt;
* ridurre costi ricorrenti per task ripetitivi o batch&lt;br /&gt;
&lt;br /&gt;
=== Integrazione pratica ===&lt;br /&gt;
Nel contesto GazziNet l&#039;integrazione puo essere vista cosi:&lt;br /&gt;
* ChatGPT o interfaccia simile per definire l&#039;obiettivo o iterare sulla richiesta&lt;br /&gt;
* Codex per eseguire il lavoro concreto su file, WordPress, MediaWiki e struttura del sistema&lt;br /&gt;
* IA locale per automazioni editoriali, generazione assistita o test di pipeline locali&lt;br /&gt;
&lt;br /&gt;
=== Esempio concreto ===&lt;br /&gt;
Un flusso concreto puo essere:&lt;br /&gt;
# scegliere un tema o una richiesta editoriale&lt;br /&gt;
# usare un modello locale per produrre una prima bozza&lt;br /&gt;
# usare Codex per controllare struttura, compatibilita, pubblicazione e verifica finale&lt;br /&gt;
# usare ChatGPT per rifinire tono, taglio o spiegazione quando serve un passaggio piu discorsivo&lt;br /&gt;
&lt;br /&gt;
=== Vantaggio dell&#039;approccio ibrido ===&lt;br /&gt;
L&#039;aspetto interessante non e scegliere un solo sistema, ma usare ogni componente per il compito giusto:&lt;br /&gt;
* conversazione e ideazione&lt;br /&gt;
* esecuzione tecnica&lt;br /&gt;
* generazione locale o automatizzata&lt;br /&gt;
&lt;br /&gt;
== Vantaggi pratici osservati ==&lt;br /&gt;
* riduzione del lavoro manuale ripetitivo&lt;br /&gt;
* maggiore uniformita tra pagine e sezioni diverse&lt;br /&gt;
* velocita nel passare da richiesta a modifica reale&lt;br /&gt;
* facilita nel documentare sistemi, procedure e note operative&lt;br /&gt;
* supporto concreto alla produzione di articoli e alla manutenzione del sito&lt;br /&gt;
&lt;br /&gt;
== Limiti da tenere presenti ==&lt;br /&gt;
* i contenuti generati vanno sempre riletti se riguardano temi sensibili o decisioni operative&lt;br /&gt;
* la qualita finale dipende molto dalla precisione delle richieste iniziali&lt;br /&gt;
* per automazioni editoriali o pubblicazione automatica servono comunque controlli su credenziali, permessi e qualita del testo&lt;br /&gt;
* per temi tecnici complessi resta importante la validazione umana&lt;br /&gt;
&lt;br /&gt;
== Uso sul sito GazziNet ==&lt;br /&gt;
Nel contesto GazziNet il sistema e stato quindi usato come supporto tecnico-editoriale per:&lt;br /&gt;
* rifare la struttura del wiki&lt;br /&gt;
* migliorare la coerenza tra le pagine&lt;br /&gt;
* pubblicare articoli tecnici sul sito&lt;br /&gt;
* preparare automazioni per la generazione di news e articoli&lt;br /&gt;
* documentare il sistema locale, i componenti hardware e le piattaforme usate&lt;br /&gt;
&lt;br /&gt;
== Nota finale ==&lt;br /&gt;
Il valore pratico del sistema non sta nel &amp;quot;scrivere da solo&amp;quot;, ma nel combinare analisi del contesto, modifica concreta, verifica e documentazione in un unico flusso di lavoro.&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
	<entry>
		<id>https://wiki.gazzi.net/index.php?title=Pagina_principale&amp;diff=645</id>
		<title>Pagina principale</title>
		<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php?title=Pagina_principale&amp;diff=645"/>
		<updated>2026-03-30T23:25:12Z</updated>

		<summary type="html">&lt;p&gt;Maintenance script: Aggiunto link CartClin nella sezione Dev e piattaforme della home wiki&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
&amp;lt;div class=&amp;quot;gazzi-wiki-home&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div class=&amp;quot;gazzi-wiki-hero&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h2&amp;gt;GazziNet Wiki&amp;lt;/h2&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Base tecnica interna con appunti operativi, procedure e raccolte tematiche.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;div class=&amp;quot;gazzi-wiki-grid&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;gazzi-wiki-card&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;h3&amp;gt;Sistemi&amp;lt;/h3&amp;gt;&lt;br /&gt;
      &amp;lt;ul&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;[[Tips Linux]]&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;[[Tips Windows]]&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;[[Tips Database]]&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;[[Tips Application Server]]&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;[[Tips Virtualizzatori]]&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;[[Tips Automation]]&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;[[Tips Varie]]&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;/ul&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;div class=&amp;quot;gazzi-wiki-card&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;h3&amp;gt;Rete e sicurezza&amp;lt;/h3&amp;gt;&lt;br /&gt;
      &amp;lt;ul&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;[[Sicurezza &amp;amp; Forense]]&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;[[Operatore]]&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;/ul&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;div class=&amp;quot;gazzi-wiki-card&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;h3&amp;gt;Dev e piattaforme&amp;lt;/h3&amp;gt;&lt;br /&gt;
      &amp;lt;ul&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;[[Container|Container]]&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;[[Sviluppo]]&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;[[OBS]]&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;[[HW]]&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;[[Codex per struttura wiki e pubblicazione articoli]]&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;[[Progetti/CartClin]]&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;/ul&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;div class=&amp;quot;gazzi-wiki-card&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;h3&amp;gt;Supporto&amp;lt;/h3&amp;gt;&lt;br /&gt;
      &amp;lt;ul&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;[[Corso]]&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;/ul&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
	<entry>
		<id>https://wiki.gazzi.net/index.php?title=Progetti/CartClin&amp;diff=644</id>
		<title>Progetti/CartClin</title>
		<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php?title=Progetti/CartClin&amp;diff=644"/>
		<updated>2026-03-30T23:09:58Z</updated>

		<summary type="html">&lt;p&gt;Maintenance script: Aggiunta documentazione progetto CartClin e pubblicazione cartclin.gazzi.net&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== CartClin ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CartClin&#039;&#039;&#039; e&#039; un&#039;applicazione web Python/Flask pubblicata dietro reverse proxy come:&lt;br /&gt;
&lt;br /&gt;
* URL pubblico: &amp;lt;code&amp;gt;https://cartclin.gazzi.net/&amp;lt;/code&amp;gt;&lt;br /&gt;
* host applicativo: &amp;lt;code&amp;gt;web01.gazzi.local&amp;lt;/code&amp;gt;&lt;br /&gt;
* host proxy pubblico: &amp;lt;code&amp;gt;proxy01.gazzi.local&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Architettura ==&lt;br /&gt;
&lt;br /&gt;
La pubblicazione e&#039; divisa in tre livelli:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;runtime applicativo&#039;&#039;&#039; su &amp;lt;code&amp;gt;web01&amp;lt;/code&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;backend HTTP interno&#039;&#039;&#039; su &amp;lt;code&amp;gt;web01&amp;lt;/code&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;reverse proxy e TLS&#039;&#039;&#039; su &amp;lt;code&amp;gt;proxy01&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Runtime applicativo su web01 ===&lt;br /&gt;
&lt;br /&gt;
Il codice applicativo viene installato in:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/opt/cartclin&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Componenti principali runtime:&lt;br /&gt;
&lt;br /&gt;
* virtualenv Python: &amp;lt;code&amp;gt;/opt/cartclin/venv&amp;lt;/code&amp;gt;&lt;br /&gt;
* database SQLite locale: &amp;lt;code&amp;gt;/opt/cartclin/cartclin.db&amp;lt;/code&amp;gt;&lt;br /&gt;
* cache locale per WeasyPrint/fontconfig: &amp;lt;code&amp;gt;/opt/cartclin/.cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il servizio applicativo e&#039; gestito da systemd:&lt;br /&gt;
&lt;br /&gt;
* unit file: &amp;lt;code&amp;gt;/etc/systemd/system/cartclin.service&amp;lt;/code&amp;gt;&lt;br /&gt;
* service name: &amp;lt;code&amp;gt;cartclin.service&amp;lt;/code&amp;gt;&lt;br /&gt;
* utente runtime: &amp;lt;code&amp;gt;www-data&amp;lt;/code&amp;gt;&lt;br /&gt;
* working directory: &amp;lt;code&amp;gt;/opt&amp;lt;/code&amp;gt;&lt;br /&gt;
* variabili runtime rilevanti:&lt;br /&gt;
** &amp;lt;code&amp;gt;PYTHONPATH=/opt&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;HOME=/opt/cartclin&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;XDG_CACHE_HOME=/opt/cartclin/.cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il servizio avvia:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;gunicorn --workers 2 --bind 127.0.0.1:8090 cartclin.app:app&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Backend HTTP interno su web01 ===&lt;br /&gt;
&lt;br /&gt;
Nginx locale su &amp;lt;code&amp;gt;web01&amp;lt;/code&amp;gt; pubblica l&#039;app su una porta interna dedicata:&lt;br /&gt;
&lt;br /&gt;
* vhost backend: &amp;lt;code&amp;gt;/etc/nginx/sites-available/cartclin-backend&amp;lt;/code&amp;gt;&lt;br /&gt;
* symlink attivo: &amp;lt;code&amp;gt;/etc/nginx/sites-enabled/cartclin-backend&amp;lt;/code&amp;gt;&lt;br /&gt;
* bind interno: &amp;lt;code&amp;gt;0.0.0.0:8082&amp;lt;/code&amp;gt;&lt;br /&gt;
* upstream applicativo: &amp;lt;code&amp;gt;http://127.0.0.1:8090&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test locale atteso su &amp;lt;code&amp;gt;web01&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;http://127.0.0.1:8090/&amp;lt;/code&amp;gt; risponde con redirect a &amp;lt;code&amp;gt;/login&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;http://127.0.0.1:8082/&amp;lt;/code&amp;gt; risponde come proxy locale verso l&#039;app&lt;br /&gt;
&lt;br /&gt;
=== Reverse proxy e TLS su proxy01 ===&lt;br /&gt;
&lt;br /&gt;
La pubblicazione pubblica e&#039; gestita da &amp;lt;code&amp;gt;proxy01&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* vhost: &amp;lt;code&amp;gt;/etc/nginx/sites-available/cartclin&amp;lt;/code&amp;gt;&lt;br /&gt;
* symlink attivo: &amp;lt;code&amp;gt;/etc/nginx/sites-enabled/cartclin&amp;lt;/code&amp;gt;&lt;br /&gt;
* host pubblico: &amp;lt;code&amp;gt;cartclin.gazzi.net&amp;lt;/code&amp;gt;&lt;br /&gt;
* upstream interno: &amp;lt;code&amp;gt;http://172.16.1.3:8082&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comportamento del vhost:&lt;br /&gt;
&lt;br /&gt;
* su porta 80 esegue redirect verso HTTPS&lt;br /&gt;
* su porta 443 termina TLS e inoltra verso &amp;lt;code&amp;gt;web01:8082&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Certificato TLS ==&lt;br /&gt;
&lt;br /&gt;
Il certificato pubblico e&#039; gestito con Let&#039;s Encrypt su &amp;lt;code&amp;gt;proxy01&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* certificate path: &amp;lt;code&amp;gt;/etc/letsencrypt/live/cartclin.gazzi.net/fullchain.pem&amp;lt;/code&amp;gt;&lt;br /&gt;
* private key path: &amp;lt;code&amp;gt;/etc/letsencrypt/live/cartclin.gazzi.net/privkey.pem&amp;lt;/code&amp;gt;&lt;br /&gt;
* rinnovo automatico: gestito da Certbot&lt;br /&gt;
&lt;br /&gt;
== Flusso richieste ==&lt;br /&gt;
&lt;br /&gt;
Percorso sintetico di una richiesta:&lt;br /&gt;
&lt;br /&gt;
# il client apre &amp;lt;code&amp;gt;https://cartclin.gazzi.net/&amp;lt;/code&amp;gt;&lt;br /&gt;
# &amp;lt;code&amp;gt;proxy01&amp;lt;/code&amp;gt; riceve TLS e inoltra verso &amp;lt;code&amp;gt;http://172.16.1.3:8082&amp;lt;/code&amp;gt;&lt;br /&gt;
# &amp;lt;code&amp;gt;web01&amp;lt;/code&amp;gt; inoltra verso &amp;lt;code&amp;gt;gunicorn&amp;lt;/code&amp;gt; su &amp;lt;code&amp;gt;127.0.0.1:8090&amp;lt;/code&amp;gt;&lt;br /&gt;
# l&#039;app Flask serve la login page o le route applicative&lt;br /&gt;
&lt;br /&gt;
== Verifiche rapide ==&lt;br /&gt;
&lt;br /&gt;
Su &amp;lt;code&amp;gt;web01&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;systemctl status cartclin.service&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;curl -I http://127.0.0.1:8090/&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;curl -I -H &#039;Host: web01.gazzi.local&#039; http://127.0.0.1:8082/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Su &amp;lt;code&amp;gt;proxy01&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;nginx -t&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;curl -I --resolve cartclin.gazzi.net:443:127.0.0.1 https://cartclin.gazzi.net/ -k&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;certbot certificates&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Note operative ==&lt;br /&gt;
&lt;br /&gt;
* Non versionare segreti o password nel repository.&lt;br /&gt;
* Il file SQLite fa parte del runtime applicativo e va trattato come dato operativo, non come configurazione di proxy.&lt;br /&gt;
* Se si cambia host o porta interna, aggiornare sia il backend Nginx su &amp;lt;code&amp;gt;web01&amp;lt;/code&amp;gt; sia il reverse proxy su &amp;lt;code&amp;gt;proxy01&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
	<entry>
		<id>https://wiki.gazzi.net/index.php?title=Sezione&amp;diff=641</id>
		<title>Sezione</title>
		<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php?title=Sezione&amp;diff=641"/>
		<updated>2026-03-29T20:07:24Z</updated>

		<summary type="html">&lt;p&gt;Maintenance script: Espansione&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
Breve raccolta di riferimenti sulla virtualizzazione usati nel contesto GazziNet. La pagina serve come indice rapido per verifiche host, risorse e accesso alle piattaforme dedicate.&lt;br /&gt;
&lt;br /&gt;
== Verifiche rapide ==&lt;br /&gt;
&lt;br /&gt;
=== Risorse host ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
uptime&lt;br /&gt;
lscpu&lt;br /&gt;
free -h&lt;br /&gt;
df -h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rete e bridge ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ip a&lt;br /&gt;
ip r&lt;br /&gt;
bridge link&lt;br /&gt;
ss -tulpn&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Storage e volumi ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
lsblk&lt;br /&gt;
pvs&lt;br /&gt;
vgs&lt;br /&gt;
lvs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting operativo ==&lt;br /&gt;
* Verificare sempre se il problema e sull&#039;host, sulla VM o sullo storage sottostante.&lt;br /&gt;
* Prima di agire, annotare uso CPU, RAM, spazio, stato rete e saturazione I/O.&lt;br /&gt;
* Controllare eventuali snapshot, lock, backup in corso o contenitori storage pieni.&lt;br /&gt;
&lt;br /&gt;
== Piattaforme ==&lt;br /&gt;
* [[PROXMOX]]&lt;br /&gt;
&lt;br /&gt;
== Proxmox VE ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxmox VE&#039;&#039;&#039; e una piattaforma di virtualizzazione basata su Debian che integra:&lt;br /&gt;
* &#039;&#039;&#039;KVM&#039;&#039;&#039; per macchine virtuali complete&lt;br /&gt;
* &#039;&#039;&#039;LXC&#039;&#039;&#039; per container Linux&lt;br /&gt;
* gestione storage locale e condiviso&lt;br /&gt;
* clustering, migrazione, backup e replica&lt;br /&gt;
* interfaccia web e CLI native&lt;br /&gt;
&lt;br /&gt;
=== Quando usarlo ===&lt;br /&gt;
Ha senso usare Proxmox VE quando:&lt;br /&gt;
* vuoi centralizzare gestione di VM e container&lt;br /&gt;
* ti servono snapshot, backup e restore in modo semplice&lt;br /&gt;
* vuoi crescere da host singolo a cluster&lt;br /&gt;
* vuoi usare storage locali o condivisi con un&#039;interfaccia unificata&lt;br /&gt;
&lt;br /&gt;
=== Requisiti minimi e logica di sizing ===&lt;br /&gt;
Per valutazione o lab:&lt;br /&gt;
* CPU 64 bit con supporto Intel VT/AMD-V&lt;br /&gt;
* almeno 16 GiB di spazio disco per l&#039;host, oltre allo spazio per guest&lt;br /&gt;
* almeno una NIC&lt;br /&gt;
&lt;br /&gt;
Per produzione:&lt;br /&gt;
* privilegiare hardware server stabile&lt;br /&gt;
* prevedere RAM in base ai guest, non solo all&#039;host&lt;br /&gt;
* separare se possibile traffico management, storage e migration&lt;br /&gt;
* valutare ECC RAM soprattutto se si usa ZFS&lt;br /&gt;
&lt;br /&gt;
== Installazione ==&lt;br /&gt;
&lt;br /&gt;
=== Metodo consigliato: ISO ufficiale ===&lt;br /&gt;
L&#039;installazione da ISO e il metodo raccomandato da Proxmox per utenti nuovi ed esistenti.&lt;br /&gt;
&lt;br /&gt;
Flusso tipico:&lt;br /&gt;
# scaricare la ISO ufficiale&lt;br /&gt;
# preparare la chiavetta di installazione&lt;br /&gt;
# avviare il server dalla ISO&lt;br /&gt;
# scegliere il disco o i dischi di destinazione&lt;br /&gt;
# impostare filesystem/root storage&lt;br /&gt;
# configurare hostname, password root, email e rete&lt;br /&gt;
# completare installazione e riavviare&lt;br /&gt;
&lt;br /&gt;
Durante il setup l&#039;installer:&lt;br /&gt;
* partiziona i dischi locali&lt;br /&gt;
* applica configurazioni base come lingua, timezone e rete&lt;br /&gt;
* installa Debian e i pacchetti Proxmox VE necessari&lt;br /&gt;
&lt;br /&gt;
=== Metodo alternativo: installazione su Debian ===&lt;br /&gt;
Proxmox VE puo anche essere installato sopra un sistema Debian esistente, ma questa via e consigliata solo a utenti avanzati.&lt;br /&gt;
&lt;br /&gt;
Ha senso usarla quando:&lt;br /&gt;
* hai gia un&#039;immagine Debian standard gestita da automazione&lt;br /&gt;
* vuoi maggiore controllo iniziale sul partizionamento&lt;br /&gt;
* vuoi integrare l&#039;host in procedure di bootstrap gia esistenti&lt;br /&gt;
&lt;br /&gt;
Rischi tipici:&lt;br /&gt;
* repository sbagliati o incompleti&lt;br /&gt;
* pacchetti non allineati&lt;br /&gt;
* rete e storage meno prevedibili rispetto all&#039;installer dedicato&lt;br /&gt;
&lt;br /&gt;
=== Installazione automatizzata ===&lt;br /&gt;
Proxmox documenta anche l&#039;&#039;&#039;&#039;Automated Installation&#039;&#039;&#039; per deployment unattended.&lt;br /&gt;
&lt;br /&gt;
Serve quando:&lt;br /&gt;
* devi installare piu host bare metal in modo coerente&lt;br /&gt;
* vuoi preparare una ISO con answer file&lt;br /&gt;
* vuoi demandare il post-config a strumenti come Ansible o simili&lt;br /&gt;
&lt;br /&gt;
=== Prime attivita dopo l&#039;installazione ===&lt;br /&gt;
Checklist pratica iniziale:&lt;br /&gt;
* verificare data/ora e DNS&lt;br /&gt;
* applicare aggiornamenti&lt;br /&gt;
* controllare bridge di rete&lt;br /&gt;
* controllare storage locale configurato dall&#039;installer&lt;br /&gt;
* definire repository corretti in base a subscription o no-subscription&lt;br /&gt;
* configurare backup prima di creare guest critici&lt;br /&gt;
* registrare hostname, IP management, storage e note operative&lt;br /&gt;
&lt;br /&gt;
Comandi rapidi:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
pveversion -v&lt;br /&gt;
ip a&lt;br /&gt;
bridge link&lt;br /&gt;
pvesm status&lt;br /&gt;
lsblk&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Filesystem e storage: come scegliere ==&lt;br /&gt;
&lt;br /&gt;
La scelta corretta dipende da:&lt;br /&gt;
* host singolo o cluster&lt;br /&gt;
* storage locale o condiviso&lt;br /&gt;
* bisogno di snapshot/cloni&lt;br /&gt;
* budget RAM&lt;br /&gt;
* necessita di integrita dati e replica&lt;br /&gt;
* carico prevalente: VM, container, ISO, template, backup&lt;br /&gt;
&lt;br /&gt;
=== 1. Setup predefinito LVM + LVM-thin ===&lt;br /&gt;
L&#039;installer attuale usa come default &#039;&#039;&#039;LVM&#039;&#039;&#039; sul disco locale.&lt;br /&gt;
&lt;br /&gt;
Schema tipico:&lt;br /&gt;
* volume group &amp;lt;code&amp;gt;pve&amp;lt;/code&amp;gt;&lt;br /&gt;
* logical volume &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; formattato &#039;&#039;&#039;ext4&#039;&#039;&#039; per il sistema operativo&lt;br /&gt;
* logical volume &amp;lt;code&amp;gt;swap&amp;lt;/code&amp;gt;&lt;br /&gt;
* logical volume &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; come &#039;&#039;&#039;LVM-thin&#039;&#039;&#039; per dischi VM&lt;br /&gt;
&lt;br /&gt;
Vantaggi:&lt;br /&gt;
* semplice&lt;br /&gt;
* buon default per host singolo&lt;br /&gt;
* snapshot e clone efficienti sul thin pool&lt;br /&gt;
* basso overhead RAM rispetto a ZFS&lt;br /&gt;
&lt;br /&gt;
Limiti:&lt;br /&gt;
* non condiviso tra nodi&lt;br /&gt;
* meno funzioni di integrita rispetto a ZFS&lt;br /&gt;
* meno flessibile per storage file-level generici rispetto a una directory dedicata&lt;br /&gt;
&lt;br /&gt;
Consigli pratici:&lt;br /&gt;
* buona scelta per host piccoli o medi con storage locale&lt;br /&gt;
* adatto quando vuoi partire in fretta senza complessita extra&lt;br /&gt;
* tenere backup e ISO possibilmente separati dal thin pool se il disco e piccolo&lt;br /&gt;
&lt;br /&gt;
=== 2. Directory storage su ext4 o XFS ===&lt;br /&gt;
Lo storage di tipo &#039;&#039;&#039;Directory&#039;&#039;&#039; usa una directory POSIX su filesystem Linux standard.&lt;br /&gt;
&lt;br /&gt;
Puoi usarlo per:&lt;br /&gt;
* immagini VM&lt;br /&gt;
* container&lt;br /&gt;
* template&lt;br /&gt;
* ISO&lt;br /&gt;
* backup&lt;br /&gt;
&lt;br /&gt;
Proxmox segnala che:&lt;br /&gt;
* una directory puo essere locale oppure un mount standard Linux&lt;br /&gt;
* il backend assume compatibilita POSIX&lt;br /&gt;
* non offre snapshot a livello storage&lt;br /&gt;
* con immagini &amp;lt;code&amp;gt;qcow2&amp;lt;/code&amp;gt; esiste il workaround degli snapshot interni al formato&lt;br /&gt;
&lt;br /&gt;
Quando scegliere &#039;&#039;&#039;ext4&#039;&#039;&#039;:&lt;br /&gt;
* vuoi il filesystem piu semplice e prevedibile&lt;br /&gt;
* l&#039;uso principale e backup, ISO, template o immagini non troppo complesse&lt;br /&gt;
* vuoi massima familiarita operativa&lt;br /&gt;
&lt;br /&gt;
Quando scegliere &#039;&#039;&#039;XFS&#039;&#039;&#039;:&lt;br /&gt;
* vuoi un filesystem robusto per grandi volumi file-based&lt;br /&gt;
* prevedi repository backup o directory storage di grandi dimensioni&lt;br /&gt;
* preferisci una soluzione nota per throughput e gestione file grandi&lt;br /&gt;
&lt;br /&gt;
Limite importante:&lt;br /&gt;
* se il tuo obiettivo principale sono snapshot e cloni storage-level delle VM, una directory pura non e la scelta migliore&lt;br /&gt;
&lt;br /&gt;
Regola pratica:&lt;br /&gt;
* &#039;&#039;&#039;ext4/XFS + dir&#039;&#039;&#039; vanno molto bene per ISO, template e backup&lt;br /&gt;
* per i dischi VM principali, usare spesso &#039;&#039;&#039;LVM-thin&#039;&#039;&#039; o &#039;&#039;&#039;ZFS&#039;&#039;&#039; e piu coerente&lt;br /&gt;
&lt;br /&gt;
=== 3. ZFS ===&lt;br /&gt;
ZFS combina filesystem e volume manager.&lt;br /&gt;
&lt;br /&gt;
Proxmox ne evidenzia vantaggi importanti:&lt;br /&gt;
* protezione da corruzione dati&lt;br /&gt;
* compressione&lt;br /&gt;
* snapshot&lt;br /&gt;
* cloni copy-on-write&lt;br /&gt;
* vari livelli RAID&lt;br /&gt;
* self-healing&lt;br /&gt;
* integrity checking continua&lt;br /&gt;
* replica asincrona&lt;br /&gt;
* gestione integrata da GUI e CLI&lt;br /&gt;
&lt;br /&gt;
Quando scegliere ZFS:&lt;br /&gt;
* vuoi integrita dati e checksum end-to-end&lt;br /&gt;
* vuoi snapshot e replica native&lt;br /&gt;
* hai abbastanza RAM&lt;br /&gt;
* puoi dare a ZFS accesso diretto ai dischi&lt;br /&gt;
&lt;br /&gt;
Attenzioni fondamentali:&lt;br /&gt;
* Proxmox raccomanda almeno &#039;&#039;&#039;8 GB&#039;&#039;&#039; di RAM per iniziare&lt;br /&gt;
* e raccomandata &#039;&#039;&#039;ECC RAM&#039;&#039;&#039; quando possibile&lt;br /&gt;
* non usare ZFS sopra controller RAID hardware con cache propria&lt;br /&gt;
* meglio HBA o controller in modalita IT&lt;br /&gt;
&lt;br /&gt;
Nota pratica importante:&lt;br /&gt;
* nelle installazioni nuove da Proxmox VE 8.1 il limite ARC viene impostato al &#039;&#039;&#039;10%&#039;&#039;&#039; della RAM fisica, con massimo &#039;&#039;&#039;16 GiB&#039;&#039;&#039;, scritto in &amp;lt;code&amp;gt;/etc/modprobe.d/zfs.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Scelte RAID ZFS in installazione:&lt;br /&gt;
* &#039;&#039;&#039;RAID0&#039;&#039;&#039;: massima capacita, nessuna ridondanza&lt;br /&gt;
* &#039;&#039;&#039;RAID1&#039;&#039;&#039;: mirror, adatto a 2 dischi&lt;br /&gt;
* &#039;&#039;&#039;RAID10&#039;&#039;&#039;: buone prestazioni e ridondanza, richiede almeno 4 dischi&lt;br /&gt;
* &#039;&#039;&#039;RAIDZ-1&#039;&#039;&#039;: parita singola&lt;br /&gt;
* &#039;&#039;&#039;RAIDZ-2&#039;&#039;&#039;: parita doppia&lt;br /&gt;
* &#039;&#039;&#039;RAIDZ-3&#039;&#039;&#039;: parita tripla&lt;br /&gt;
&lt;br /&gt;
Consiglio operativo:&lt;br /&gt;
* per host piccoli con 2 dischi, il mirror ZFS e spesso la scelta piu pulita&lt;br /&gt;
* per host con tanti dischi, RAID10 o RAIDZ-2 sono in genere piu prudenziali di RAIDZ-1&lt;br /&gt;
* evitare ZFS se la RAM e molto limitata o se l&#039;hardware e gia vincolato a RAID hardware non bypassabile&lt;br /&gt;
&lt;br /&gt;
=== 4. BTRFS ===&lt;br /&gt;
BTRFS esiste nel mondo Linux come filesystem moderno con snapshot e checksum, ma in Proxmox la documentazione e la pratica operativa ruotano molto di piu attorno a &#039;&#039;&#039;LVM-thin&#039;&#039;&#039; e &#039;&#039;&#039;ZFS&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Consiglio pragmatico:&lt;br /&gt;
* se ti serve una scelta stabile e ben coperta dalla documentazione Proxmox, privilegia &#039;&#039;&#039;LVM-thin&#039;&#039;&#039; o &#039;&#039;&#039;ZFS&#039;&#039;&#039;&lt;br /&gt;
* usa BTRFS solo se hai gia competenza specifica e una ragione forte per adottarlo&lt;br /&gt;
&lt;br /&gt;
== Raccomandazioni rapide ==&lt;br /&gt;
&lt;br /&gt;
=== Host singolo piccolo o homelab semplice ===&lt;br /&gt;
Scelta consigliata:&lt;br /&gt;
* installer ISO&lt;br /&gt;
* default &#039;&#039;&#039;LVM + ext4 root + LVM-thin&#039;&#039;&#039;&lt;br /&gt;
* directory separata per ISO e backup se possibile&lt;br /&gt;
&lt;br /&gt;
=== Host singolo con priorita a integrita e snapshot ===&lt;br /&gt;
Scelta consigliata:&lt;br /&gt;
* installer ISO con &#039;&#039;&#039;ZFS mirror&#039;&#039;&#039; se hai 2 dischi&lt;br /&gt;
* ECC RAM se possibile&lt;br /&gt;
* niente RAID hardware davanti a ZFS&lt;br /&gt;
&lt;br /&gt;
=== Nodo dedicato soprattutto a backup ===&lt;br /&gt;
Scelta consigliata:&lt;br /&gt;
* directory storage su &#039;&#039;&#039;ext4&#039;&#039;&#039; o &#039;&#039;&#039;XFS&#039;&#039;&#039;&lt;br /&gt;
* evitare di mescolare senza controllo backup pesanti e dischi VM critici sullo stesso spazio piccolo&lt;br /&gt;
&lt;br /&gt;
=== Cluster o crescita futura ===&lt;br /&gt;
Considerare fin da subito:&lt;br /&gt;
* separazione rete management/storage&lt;br /&gt;
* strategia storage condiviso o replica&lt;br /&gt;
* naming coerente di nodi, bridge e datastore&lt;br /&gt;
* policy di backup e restore testato&lt;br /&gt;
&lt;br /&gt;
== Errori comuni ==&lt;br /&gt;
* usare ZFS sopra RAID hardware opaco&lt;br /&gt;
* sottostimare la RAM necessaria a host e guest&lt;br /&gt;
* riempire lo storage locale con backup, ISO e template fino a saturazione&lt;br /&gt;
* scegliere directory storage pensando di avere snapshot storage-level nativi&lt;br /&gt;
* non documentare bridge, VLAN, datastore e layout dischi&lt;br /&gt;
* creare VM critiche prima di aver validato backup e ripristino&lt;br /&gt;
&lt;br /&gt;
== Verifiche utili su host Proxmox ==&lt;br /&gt;
&lt;br /&gt;
=== Versione e stato generale ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
pveversion -v&lt;br /&gt;
uptime&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rete ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ip a&lt;br /&gt;
ip r&lt;br /&gt;
bridge link&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Storage LVM ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
lsblk&lt;br /&gt;
pvs&lt;br /&gt;
vgs&lt;br /&gt;
lvs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Storage Proxmox ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
pvesm status&lt;br /&gt;
cat /etc/pve/storage.cfg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ZFS ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
zpool status&lt;br /&gt;
zfs list&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fonti ufficiali ==&lt;br /&gt;
* [https://pve.proxmox.com/pve-docs-8/chapter-pve-installation.html Installing Proxmox VE]&lt;br /&gt;
* [https://pve.proxmox.com/wiki/Quick_installation Quick installation]&lt;br /&gt;
* [https://pve.proxmox.com/wiki/Automated_Installation Automated Installation]&lt;br /&gt;
* [https://pve.proxmox.com/wiki/Logical_Volume_Manager_%28LVM%29 Logical Volume Manager (LVM)]&lt;br /&gt;
* [https://pve.proxmox.com/wiki/Storage Storage]&lt;br /&gt;
* [https://pve.proxmox.com/wiki/Storage%3A_Directory Storage: Directory]&lt;br /&gt;
* [https://pve.proxmox.com/wiki/ZFS_on_Linux ZFS on Linux]&lt;br /&gt;
* [https://pve.proxmox.com/wiki/Installation%3A_Tips_and_Tricks Installation: Tips and Tricks]&lt;br /&gt;
&lt;br /&gt;
== Note d&#039;uso ==&lt;br /&gt;
* Separare sempre i problemi di orchestrazione da quelli di singola VM.&lt;br /&gt;
* Non saturare lo storage locale con immagini, backup e template senza controllo dello spazio.&lt;br /&gt;
* Documentare hostname host, datastore coinvolto e VM interessata prima di aprire ticket o incidente.&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
	<entry>
		<id>https://wiki.gazzi.net/index.php?title=Esempi_Pyinfra&amp;diff=640</id>
		<title>Esempi Pyinfra</title>
		<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php?title=Esempi_Pyinfra&amp;diff=640"/>
		<updated>2026-03-29T19:21:07Z</updated>

		<summary type="html">&lt;p&gt;Maintenance script: Aggiunta&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Esempi Pyinfra =&lt;br /&gt;
&lt;br /&gt;
Questa pagina raccoglie esempi leggibili e progressivi: si parte dal minimo indispensabile e si arriva a pattern piu vicini a un uso reale.&lt;br /&gt;
&lt;br /&gt;
== Prima idea da fissare ==&lt;br /&gt;
Un progetto pyinfra semplice ha di solito almeno due file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
inventory.py&lt;br /&gt;
deploy.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;inventory descrive i target. Il deploy descrive cosa fare.&lt;br /&gt;
&lt;br /&gt;
== Esempio base 1: esecuzione su host locali ==&lt;br /&gt;
Se vuoi solo capire la CLI:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pyinfra @local exec -- echo &amp;quot;hello world&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Questo serve quando vuoi verificare:&lt;br /&gt;
* che pyinfra sia installato&lt;br /&gt;
* che il runner funzioni&lt;br /&gt;
* che l&#039;output arrivi correttamente&lt;br /&gt;
&lt;br /&gt;
Tradotto in pratica:&lt;br /&gt;
* se il comando parte, sai che &amp;lt;code&amp;gt;pyinfra&amp;lt;/code&amp;gt; esiste davvero sul manager&lt;br /&gt;
* se l&#039;operation viene eseguita, sai che il runner locale funziona&lt;br /&gt;
* se vedi &amp;lt;code&amp;gt;hello world&amp;lt;/code&amp;gt;, sai che l&#039;output viene raccolto correttamente&lt;br /&gt;
&lt;br /&gt;
== Esempio concreto: devo controllare 10 server ==&lt;br /&gt;
Questo e uno dei casi piu utili per capire bene come ragiona pyinfra.&lt;br /&gt;
&lt;br /&gt;
Scenario:&lt;br /&gt;
* hai una macchina &#039;&#039;&#039;manager&#039;&#039;&#039;&lt;br /&gt;
* da quella macchina vuoi controllare o configurare &#039;&#039;&#039;10 server target&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Cosa deve esserci sul manager ===&lt;br /&gt;
Sul manager devono esserci:&lt;br /&gt;
* &amp;lt;code&amp;gt;Python&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;pyinfra&amp;lt;/code&amp;gt;&lt;br /&gt;
* i file di progetto, ad esempio &amp;lt;code&amp;gt;inventory.py&amp;lt;/code&amp;gt; e &amp;lt;code&amp;gt;deploy.py&amp;lt;/code&amp;gt;&lt;br /&gt;
* connettivita SSH verso i 10 server&lt;br /&gt;
* chiavi SSH oppure credenziali adatte&lt;br /&gt;
* eventuali permessi &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; se il deploy deve fare operazioni amministrative&lt;br /&gt;
&lt;br /&gt;
In sintesi:&lt;br /&gt;
* il manager e la macchina da cui lanci i comandi&lt;br /&gt;
* il manager contiene la logica&lt;br /&gt;
* il manager non deve essere per forza uno dei 10 server target&lt;br /&gt;
&lt;br /&gt;
=== Cosa deve esserci sui 10 server target ===&lt;br /&gt;
In generale pyinfra e &#039;&#039;&#039;agentless&#039;&#039;&#039;, quindi non richiede un agente pyinfra installato sui target.&lt;br /&gt;
&lt;br /&gt;
Di solito sui target servono:&lt;br /&gt;
* accesso SSH dal manager&lt;br /&gt;
* un utente valido con cui entrare&lt;br /&gt;
* eventuale &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; se devi amministrare il sistema&lt;br /&gt;
* shell e strumenti di base coerenti con quello che vuoi eseguire&lt;br /&gt;
&lt;br /&gt;
In pratica:&lt;br /&gt;
* sui target &#039;&#039;&#039;non installi pyinfra&#039;&#039;&#039;&lt;br /&gt;
* sui target devi rendere possibile l&#039;accesso e l&#039;esecuzione delle operazioni richieste&lt;br /&gt;
&lt;br /&gt;
=== Riepilogo semplice ===&lt;br /&gt;
* manager: installi pyinfra&lt;br /&gt;
* target: non installi pyinfra&lt;br /&gt;
* manager -&amp;gt; target: usi SSH&lt;br /&gt;
&lt;br /&gt;
== Esempio con 10 server ==&lt;br /&gt;
Inventory minimale:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
all_servers = [&lt;br /&gt;
    &amp;quot;srv01.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;srv02.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;srv03.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;srv04.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;srv05.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;srv06.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;srv07.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;srv08.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;srv09.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;srv10.example.net&amp;quot;,&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Se vuoi dividere per gruppi:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
web_servers = [&lt;br /&gt;
    &amp;quot;srv01.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;srv02.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;srv03.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;srv04.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;srv05.example.net&amp;quot;,&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
db_servers = [&lt;br /&gt;
    &amp;quot;srv06.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;srv07.example.net&amp;quot;,&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
misc_servers = [&lt;br /&gt;
    &amp;quot;srv08.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;srv09.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;srv10.example.net&amp;quot;,&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Primo test vero sui 10 server ==&lt;br /&gt;
Prima di fare configurazione, conviene fare un controllo innocuo.&lt;br /&gt;
&lt;br /&gt;
Deploy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra.operations import server&lt;br /&gt;
&lt;br /&gt;
server.shell(&lt;br /&gt;
    name=&amp;quot;Show hostname and uptime&amp;quot;,&lt;br /&gt;
    commands=[&lt;br /&gt;
        &amp;quot;hostname&amp;quot;,&lt;br /&gt;
        &amp;quot;uptime&amp;quot;,&lt;br /&gt;
    ],&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Esecuzione dal manager:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pyinfra inventory.py deploy.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Questo test serve a verificare:&lt;br /&gt;
* che il manager raggiunga tutti i target&lt;br /&gt;
* che l&#039;utente SSH funzioni&lt;br /&gt;
* che pyinfra riesca a eseguire comandi su tutti i server&lt;br /&gt;
* che i risultati siano leggibili nell&#039;output&lt;br /&gt;
&lt;br /&gt;
== Esempio di controllo piu utile ==&lt;br /&gt;
Se vuoi controllare disco e un servizio:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra.operations import server&lt;br /&gt;
&lt;br /&gt;
server.shell(&lt;br /&gt;
    name=&amp;quot;Check disk&amp;quot;,&lt;br /&gt;
    commands=[&amp;quot;df -h /&amp;quot;],&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
server.shell(&lt;br /&gt;
    name=&amp;quot;Check nginx service&amp;quot;,&lt;br /&gt;
    commands=[&amp;quot;systemctl is-active nginx || true&amp;quot;],&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Questo non modifica nulla, ma ti da gia una fotografia dei 10 server.&lt;br /&gt;
&lt;br /&gt;
== Esempio di deploy reale ==&lt;br /&gt;
Se vuoi installare &amp;lt;code&amp;gt;vim&amp;lt;/code&amp;gt; e assicurarti che &amp;lt;code&amp;gt;nginx&amp;lt;/code&amp;gt; sia attivo:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra.operations import apt, systemd&lt;br /&gt;
&lt;br /&gt;
apt.packages(&lt;br /&gt;
    name=&amp;quot;Install vim&amp;quot;,&lt;br /&gt;
    packages=[&amp;quot;vim&amp;quot;],&lt;br /&gt;
    update=True,&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
systemd.service(&lt;br /&gt;
    name=&amp;quot;Ensure nginx is running&amp;quot;,&lt;br /&gt;
    service=&amp;quot;nginx&amp;quot;,&lt;br /&gt;
    running=True,&lt;br /&gt;
    enabled=True,&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In questo caso:&lt;br /&gt;
* sul manager hai solo pyinfra e i file del deploy&lt;br /&gt;
* sui target non hai pyinfra, ma hai accesso SSH e permessi adeguati&lt;br /&gt;
* pyinfra esegue il lavoro dal manager verso tutti i target&lt;br /&gt;
&lt;br /&gt;
== Come pensarlo mentalmente ==&lt;br /&gt;
Formula utile:&lt;br /&gt;
* &#039;&#039;&#039;manager = cervello&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;target = macchine su cui applicare le operazioni&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Pyinfra gira nel cervello, non nelle braccia.&lt;br /&gt;
&lt;br /&gt;
== Esempio base 2: inventory minimale ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
web_servers = [&lt;br /&gt;
    &amp;quot;web-01.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;web-02.example.net&amp;quot;,&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Qui hai solo una lista di host.&lt;br /&gt;
&lt;br /&gt;
== Esempio base 3: operation semplice ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra.operations import server&lt;br /&gt;
&lt;br /&gt;
server.shell(&lt;br /&gt;
    name=&amp;quot;Show uptime&amp;quot;,&lt;br /&gt;
    commands=[&amp;quot;uptime&amp;quot;],&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Esecuzione:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pyinfra inventory.py deploy.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Esempio base 4: installare pacchetti ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra.operations import apt&lt;br /&gt;
&lt;br /&gt;
apt.packages(&lt;br /&gt;
    name=&amp;quot;Install packages&amp;quot;,&lt;br /&gt;
    packages=[&amp;quot;vim&amp;quot;, &amp;quot;curl&amp;quot;],&lt;br /&gt;
    update=True,&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Questo e un esempio di operation idempotente: se i pacchetti sono gia presenti, pyinfra non rifara cambiamenti inutili.&lt;br /&gt;
&lt;br /&gt;
== Esempio base 5: gestire file ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra.operations import files&lt;br /&gt;
&lt;br /&gt;
files.file(&lt;br /&gt;
    name=&amp;quot;Ensure app log exists&amp;quot;,&lt;br /&gt;
    path=&amp;quot;/var/log/app.log&amp;quot;,&lt;br /&gt;
    user=&amp;quot;app&amp;quot;,&lt;br /&gt;
    group=&amp;quot;app&amp;quot;,&lt;br /&gt;
    mode=&amp;quot;644&amp;quot;,&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Esempio base 6: gestire servizi ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra.operations import systemd&lt;br /&gt;
&lt;br /&gt;
systemd.service(&lt;br /&gt;
    name=&amp;quot;Ensure nginx is running&amp;quot;,&lt;br /&gt;
    service=&amp;quot;nginx&amp;quot;,&lt;br /&gt;
    running=True,&lt;br /&gt;
    enabled=True,&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Esempio intermedio 1: dati host ==&lt;br /&gt;
Inventory:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
app_servers = [&lt;br /&gt;
    (&amp;quot;app-01.example.net&amp;quot;, {&amp;quot;install_nginx&amp;quot;: True}),&lt;br /&gt;
    (&amp;quot;app-02.example.net&amp;quot;, {&amp;quot;install_nginx&amp;quot;: False}),&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deploy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra import host&lt;br /&gt;
from pyinfra.operations import apt&lt;br /&gt;
&lt;br /&gt;
if host.data.get(&amp;quot;install_nginx&amp;quot;):&lt;br /&gt;
    apt.packages(&lt;br /&gt;
        name=&amp;quot;Install nginx&amp;quot;,&lt;br /&gt;
        packages=[&amp;quot;nginx&amp;quot;],&lt;br /&gt;
        update=True,&lt;br /&gt;
    )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Qui usi Python per rendere il deploy diverso in base ai dati del target.&lt;br /&gt;
&lt;br /&gt;
== Esempio intermedio 2: gruppi ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra import host, local&lt;br /&gt;
from pyinfra.operations import server&lt;br /&gt;
&lt;br /&gt;
if &amp;quot;web_servers&amp;quot; in host.groups:&lt;br /&gt;
    local.include(&amp;quot;tasks/web.py&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
if &amp;quot;db_servers&amp;quot; in host.groups:&lt;br /&gt;
    local.include(&amp;quot;tasks/database.py&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
server.shell(&lt;br /&gt;
    name=&amp;quot;Run everywhere&amp;quot;,&lt;br /&gt;
    commands=[&amp;quot;hostname&amp;quot;],&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Questo pattern e utile quando vuoi dividere la logica in file piu piccoli.&lt;br /&gt;
&lt;br /&gt;
== Esempio avanzato 1: deploy locale che richiama un helper Python ==&lt;br /&gt;
Questo e il pattern usato nel progetto Proxmox Web UI: pyinfra gira su &amp;lt;code&amp;gt;@local&amp;lt;/code&amp;gt; ma esegue un helper esterno che contiene la logica vera.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/usr/bin/env python3&lt;br /&gt;
import os&lt;br /&gt;
import shlex&lt;br /&gt;
from pathlib import Path&lt;br /&gt;
&lt;br /&gt;
from pyinfra.operations import server&lt;br /&gt;
&lt;br /&gt;
BASE_DIR = Path(__file__).resolve().parents[1]&lt;br /&gt;
python_bin = os.environ.get(&amp;quot;PYTHON_BIN&amp;quot;, str(BASE_DIR / &amp;quot;venv&amp;quot; / &amp;quot;bin&amp;quot; / &amp;quot;python&amp;quot;))&lt;br /&gt;
helper = BASE_DIR / &amp;quot;lib&amp;quot; / &amp;quot;proxmox_ssh.py&amp;quot;&lt;br /&gt;
cmd = f&amp;quot;{shlex.quote(python_bin)} {shlex.quote(str(helper))}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
server.shell(&lt;br /&gt;
    name=f&amp;quot;Create Proxmox VM {os.environ.get(&#039;VM_NAME&#039;, &#039;&#039;)}&amp;quot;,&lt;br /&gt;
    commands=[cmd],&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Idea pratica:&lt;br /&gt;
* pyinfra fa da orchestratore locale&lt;br /&gt;
* il file helper fa il lavoro specializzato&lt;br /&gt;
* le variabili ambiente passano i parametri del job&lt;br /&gt;
&lt;br /&gt;
== Esempio avanzato 2: usare pyinfra come backend di una UI ==&lt;br /&gt;
Nel caso GazziNet il flusso e:&lt;br /&gt;
# la UI riceve input dell&#039;utente&lt;br /&gt;
# la UI valida i campi&lt;br /&gt;
# la UI crea un job con stato e log&lt;br /&gt;
# il job lancia &amp;lt;code&amp;gt;pyinfra @local deploy.py -y&amp;lt;/code&amp;gt;&lt;br /&gt;
# pyinfra richiama un helper che esegue SSH su Proxmox&lt;br /&gt;
&lt;br /&gt;
Questa soluzione e utile quando vuoi:&lt;br /&gt;
* separare presentazione e automazione&lt;br /&gt;
* conservare log testuali chiari&lt;br /&gt;
* evitare che il browser parli direttamente con Proxmox&lt;br /&gt;
&lt;br /&gt;
== Esempio avanzato 3: rete dinamica e validazione ==&lt;br /&gt;
Nel progetto VM automatiche la UI gestisce:&lt;br /&gt;
* DHCP oppure IP statico&lt;br /&gt;
* bridge selezionabile&lt;br /&gt;
* VLAN opzionale&lt;br /&gt;
* validazione preventiva bridge + VLAN&lt;br /&gt;
* ricerca del primo VMID libero&lt;br /&gt;
&lt;br /&gt;
In questo caso pyinfra non decide la rete da solo: riceve i valori gia normalizzati e li passa al backend di provisioning.&lt;br /&gt;
&lt;br /&gt;
== Esempio dry run ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pyinfra inventory.py deploy.py --dry&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Utile prima di un deploy reale.&lt;br /&gt;
&lt;br /&gt;
== Pattern reali da ricordare ==&lt;br /&gt;
* tenere inventory e deploy in Git&lt;br /&gt;
* separare logica generale e helper specializzati&lt;br /&gt;
* validare input prima di lanciare pyinfra&lt;br /&gt;
* non mescolare segreti nei file versionati&lt;br /&gt;
* aggiungere sempre un modo semplice per verificare il risultato&lt;br /&gt;
&lt;br /&gt;
== Collegamenti ==&lt;br /&gt;
* [[Pyinfra]]&lt;br /&gt;
* [[Pyinfra/Deploy automatico VM Proxmox]]&lt;br /&gt;
* [[Pyinfra/Implementazione deploy VM linea per linea]]&lt;br /&gt;
* [[Confronto Ansible vs Pyinfra]]&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
	<entry>
		<id>https://wiki.gazzi.net/index.php?title=Pyinfra&amp;diff=639</id>
		<title>Pyinfra</title>
		<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php?title=Pyinfra&amp;diff=639"/>
		<updated>2026-03-29T19:21:07Z</updated>

		<summary type="html">&lt;p&gt;Maintenance script: Espansione&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
= Pyinfra =&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pyinfra&#039;&#039;&#039; in GazziNet va letto su due livelli:&lt;br /&gt;
* come strumento generale di automazione scritto in Python&lt;br /&gt;
* come implementazione concreta gia pubblicata su &amp;lt;code&amp;gt;bot.gazzi.net/pyinfra/&amp;lt;/code&amp;gt; per creare VM Proxmox via browser&lt;br /&gt;
&lt;br /&gt;
Questa pagina principale e pensata per una persona umana che vuole capire rapidamente:&lt;br /&gt;
* che cosa fa pyinfra&lt;br /&gt;
* quando usarlo&lt;br /&gt;
* dove trovare esempi semplici e avanzati&lt;br /&gt;
* dove trovare il runbook reale della web UI pubblicata&lt;br /&gt;
* dove trovare la spiegazione tecnica dell&#039;implementazione del deploy automatico VM&lt;br /&gt;
&lt;br /&gt;
I dettagli sensibili non vanno messi in wiki. Password, token, cookie, backend privati e dump runtime restano fuori dalla documentazione pubblica o semi-pubblica.&lt;br /&gt;
&lt;br /&gt;
== In una frase ==&lt;br /&gt;
Pyinfra e un motore di automazione che usa Python per descrivere inventory, logica e operazioni di deploy in modo piu programmabile rispetto ai playbook puramente YAML.&lt;br /&gt;
&lt;br /&gt;
== Quando ha senso usarlo ==&lt;br /&gt;
Pyinfra e adatto quando:&lt;br /&gt;
* vuoi tenere deploy e configurazioni in Git&lt;br /&gt;
* preferisci Python per logica, condizioni e riuso&lt;br /&gt;
* devi automatizzare server Linux, homelab o ambienti interni&lt;br /&gt;
* vuoi una separazione chiara tra form di input, validazione e runner di provisioning&lt;br /&gt;
&lt;br /&gt;
Pyinfra e meno adatto quando:&lt;br /&gt;
* il team vuole solo file dichiarativi molto semplici&lt;br /&gt;
* nessuno vuole mantenere codice Python&lt;br /&gt;
* l&#039;automazione deve essere totalmente guidata da un pannello visuale gia pronto&lt;br /&gt;
&lt;br /&gt;
== Concetti base ==&lt;br /&gt;
Tre idee utili da capire subito:&lt;br /&gt;
&lt;br /&gt;
=== 1. Inventory ===&lt;br /&gt;
L&#039;inventory descrive i target: host, gruppi e dati associati.&lt;br /&gt;
&lt;br /&gt;
=== 2. Operations ===&lt;br /&gt;
Le operations descrivono cosa deve essere applicato ai target, oppure uno stato che vuoi rendere vero.&lt;br /&gt;
&lt;br /&gt;
=== 3. Esecuzione ===&lt;br /&gt;
Pyinfra legge inventory e dati, prepara l&#039;ordine delle operazioni, si connette ai target ed esegue le modifiche.&lt;br /&gt;
&lt;br /&gt;
== Nel caso GazziNet ==&lt;br /&gt;
Su GazziNet pyinfra non e stato usato come esercizio teorico. E stato integrato in una soluzione completa:&lt;br /&gt;
* UI pubblica: &amp;lt;code&amp;gt;https://bot.gazzi.net/pyinfra/&amp;lt;/code&amp;gt;&lt;br /&gt;
* autenticazione: LDAP&lt;br /&gt;
* runner: &amp;lt;code&amp;gt;pyinfra&amp;lt;/code&amp;gt; locale su &amp;lt;code&amp;gt;bot.gazzi.local&amp;lt;/code&amp;gt;&lt;br /&gt;
* target: Proxmox&lt;br /&gt;
* risultato: creazione controllata di VM con parametri espliciti&lt;br /&gt;
&lt;br /&gt;
== Percorso consigliato di lettura ==&lt;br /&gt;
Se parti da zero:&lt;br /&gt;
# [[Esempi Pyinfra]]&lt;br /&gt;
# [[Pyinfra/Deploy automatico VM Proxmox]]&lt;br /&gt;
# [[Pyinfra/Implementazione deploy VM linea per linea]]&lt;br /&gt;
&lt;br /&gt;
Se vuoi solo usare la piattaforma pubblicata:&lt;br /&gt;
# [[Pyinfra/Deploy automatico VM Proxmox]]&lt;br /&gt;
&lt;br /&gt;
Se vuoi capire il codice:&lt;br /&gt;
# [[Pyinfra/Implementazione deploy VM linea per linea]]&lt;br /&gt;
&lt;br /&gt;
== Mappa delle pagine ==&lt;br /&gt;
* [[Esempi Pyinfra]]: guida pratica con esempi base e avanzati di pyinfra.&lt;br /&gt;
* [[Pyinfra/Deploy automatico VM Proxmox]]: runbook umano della web UI pubblicata su bot.gazzi.net.&lt;br /&gt;
* [[Pyinfra/Implementazione deploy VM linea per linea]]: spiegazione tecnica del codice che realizza il deploy automatico di VM.&lt;br /&gt;
* [[Confronto Ansible vs Pyinfra]]: confronto generale di approccio.&lt;br /&gt;
* [[Installazione Pyinfra su Fedora]]: installazione CLI in ambiente Fedora.&lt;br /&gt;
&lt;br /&gt;
== Domanda pratica tipica ==&lt;br /&gt;
Se devi controllare 10 server:&lt;br /&gt;
* sul &#039;&#039;&#039;manager&#039;&#039;&#039; installi &amp;lt;code&amp;gt;pyinfra&amp;lt;/code&amp;gt;, tieni inventory e deploy e da li lanci i job&lt;br /&gt;
* sui &#039;&#039;&#039;10 server target&#039;&#039;&#039; non installi &amp;lt;code&amp;gt;pyinfra&amp;lt;/code&amp;gt;, ma devi avere accesso SSH e permessi adeguati&lt;br /&gt;
&lt;br /&gt;
La spiegazione completa con esempi e nella pagina:&lt;br /&gt;
* [[Esempi Pyinfra]]&lt;br /&gt;
&lt;br /&gt;
== Esempio minimo per capire la sintassi ==&lt;br /&gt;
Inventory:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
web_servers = [&lt;br /&gt;
    &amp;quot;web-01.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;web-02.example.net&amp;quot;,&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deploy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra.operations import server&lt;br /&gt;
&lt;br /&gt;
server.shell(&lt;br /&gt;
    name=&amp;quot;Show uptime&amp;quot;,&lt;br /&gt;
    commands=[&amp;quot;uptime&amp;quot;],&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Esecuzione:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pyinfra inventory.py deploy.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Idea chiave ==&lt;br /&gt;
Nel deployment VM di GazziNet, la UI non crea direttamente la VM. La UI:&lt;br /&gt;
* raccoglie i parametri&lt;br /&gt;
* valida input e rete&lt;br /&gt;
* crea un job locale&lt;br /&gt;
&lt;br /&gt;
Poi il job chiama pyinfra, e pyinfra richiama un helper Python che parla con Proxmox via SSH.&lt;br /&gt;
&lt;br /&gt;
Questa separazione aiuta a mantenere il flusso leggibile:&lt;br /&gt;
* frontend e login&lt;br /&gt;
* validazione e stato job&lt;br /&gt;
* runner pyinfra&lt;br /&gt;
* helper Proxmox&lt;br /&gt;
&lt;br /&gt;
== Regole documentali per questa area ==&lt;br /&gt;
La wiki deve contenere:&lt;br /&gt;
* spiegazioni umane&lt;br /&gt;
* runbook&lt;br /&gt;
* esempi&lt;br /&gt;
* struttura dei file&lt;br /&gt;
* procedure di verifica&lt;br /&gt;
&lt;br /&gt;
La wiki non deve contenere:&lt;br /&gt;
* password&lt;br /&gt;
* token&lt;br /&gt;
* cookie di sessione&lt;br /&gt;
* segreti runtime&lt;br /&gt;
* dump completi di backend interni&lt;br /&gt;
&lt;br /&gt;
== Riferimenti esterni ==&lt;br /&gt;
* [https://pyinfra.com/ Sito ufficiale]&lt;br /&gt;
* [https://docs.pyinfra.com/en/3.x/ Documentazione ufficiale]&lt;br /&gt;
* [https://www.gazzi.net/pyinfra-proxmox-webui/ Scheda pubblica WordPress]&lt;br /&gt;
* [https://www.gazzi.net/2026/03/29/pyinfra-proxmox-webui-proxmox-ldap-bot-gazzi-net/ Articolo di dettaglio]&lt;br /&gt;
&lt;br /&gt;
== Repository ==&lt;br /&gt;
Sorgente versionata in:&lt;br /&gt;
* &amp;lt;code&amp;gt;scripts-repo/pyinfra-proxmox-webui/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pagine wiki versionate nello stesso repository:&lt;br /&gt;
* &amp;lt;code&amp;gt;scripts-repo/pyinfra-proxmox-webui/wiki/&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
	<entry>
		<id>https://wiki.gazzi.net/index.php?title=Pyinfra/Implementazione_deploy_VM_linea_per_linea&amp;diff=638</id>
		<title>Pyinfra/Implementazione deploy VM linea per linea</title>
		<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php?title=Pyinfra/Implementazione_deploy_VM_linea_per_linea&amp;diff=638"/>
		<updated>2026-03-29T18:57:47Z</updated>

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

		<summary type="html">&lt;p&gt;Maintenance script: Fix&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
= Pyinfra =&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pyinfra&#039;&#039;&#039; in GazziNet va letto su due livelli:&lt;br /&gt;
* come strumento generale di automazione scritto in Python&lt;br /&gt;
* come implementazione concreta gia pubblicata su &amp;lt;code&amp;gt;bot.gazzi.net/pyinfra/&amp;lt;/code&amp;gt; per creare VM Proxmox via browser&lt;br /&gt;
&lt;br /&gt;
Questa pagina principale e pensata per una persona umana che vuole capire rapidamente:&lt;br /&gt;
* che cosa fa pyinfra&lt;br /&gt;
* quando usarlo&lt;br /&gt;
* dove trovare esempi semplici e avanzati&lt;br /&gt;
* dove trovare il runbook reale della web UI pubblicata&lt;br /&gt;
* dove trovare la spiegazione tecnica dell&#039;implementazione del deploy automatico VM&lt;br /&gt;
&lt;br /&gt;
I dettagli sensibili non vanno messi in wiki. Password, token, cookie, backend privati e dump runtime restano fuori dalla documentazione pubblica o semi-pubblica.&lt;br /&gt;
&lt;br /&gt;
== In una frase ==&lt;br /&gt;
Pyinfra e un motore di automazione che usa Python per descrivere inventory, logica e operazioni di deploy in modo piu programmabile rispetto ai playbook puramente YAML.&lt;br /&gt;
&lt;br /&gt;
== Quando ha senso usarlo ==&lt;br /&gt;
Pyinfra e adatto quando:&lt;br /&gt;
* vuoi tenere deploy e configurazioni in Git&lt;br /&gt;
* preferisci Python per logica, condizioni e riuso&lt;br /&gt;
* devi automatizzare server Linux, homelab o ambienti interni&lt;br /&gt;
* vuoi una separazione chiara tra form di input, validazione e runner di provisioning&lt;br /&gt;
&lt;br /&gt;
Pyinfra e meno adatto quando:&lt;br /&gt;
* il team vuole solo file dichiarativi molto semplici&lt;br /&gt;
* nessuno vuole mantenere codice Python&lt;br /&gt;
* l&#039;automazione deve essere totalmente guidata da un pannello visuale gia pronto&lt;br /&gt;
&lt;br /&gt;
== Concetti base ==&lt;br /&gt;
Tre idee utili da capire subito:&lt;br /&gt;
&lt;br /&gt;
=== 1. Inventory ===&lt;br /&gt;
L&#039;inventory descrive i target: host, gruppi e dati associati.&lt;br /&gt;
&lt;br /&gt;
=== 2. Operations ===&lt;br /&gt;
Le operations descrivono cosa deve essere applicato ai target, oppure uno stato che vuoi rendere vero.&lt;br /&gt;
&lt;br /&gt;
=== 3. Esecuzione ===&lt;br /&gt;
Pyinfra legge inventory e dati, prepara l&#039;ordine delle operazioni, si connette ai target ed esegue le modifiche.&lt;br /&gt;
&lt;br /&gt;
== Nel caso GazziNet ==&lt;br /&gt;
Su GazziNet pyinfra non e stato usato come esercizio teorico. E stato integrato in una soluzione completa:&lt;br /&gt;
* UI pubblica: &amp;lt;code&amp;gt;https://bot.gazzi.net/pyinfra/&amp;lt;/code&amp;gt;&lt;br /&gt;
* autenticazione: LDAP&lt;br /&gt;
* runner: &amp;lt;code&amp;gt;pyinfra&amp;lt;/code&amp;gt; locale su &amp;lt;code&amp;gt;bot.gazzi.local&amp;lt;/code&amp;gt;&lt;br /&gt;
* target: Proxmox&lt;br /&gt;
* risultato: creazione controllata di VM con parametri espliciti&lt;br /&gt;
&lt;br /&gt;
== Percorso consigliato di lettura ==&lt;br /&gt;
Se parti da zero:&lt;br /&gt;
# [[Esempi Pyinfra]]&lt;br /&gt;
# [[Pyinfra/Deploy automatico VM Proxmox]]&lt;br /&gt;
# [[Pyinfra/Implementazione deploy VM linea per linea]]&lt;br /&gt;
&lt;br /&gt;
Se vuoi solo usare la piattaforma pubblicata:&lt;br /&gt;
# [[Pyinfra/Deploy automatico VM Proxmox]]&lt;br /&gt;
&lt;br /&gt;
Se vuoi capire il codice:&lt;br /&gt;
# [[Pyinfra/Implementazione deploy VM linea per linea]]&lt;br /&gt;
&lt;br /&gt;
== Mappa delle pagine ==&lt;br /&gt;
* [[Esempi Pyinfra]]: guida pratica con esempi base e avanzati di pyinfra.&lt;br /&gt;
* [[Pyinfra/Deploy automatico VM Proxmox]]: runbook umano della web UI pubblicata su bot.gazzi.net.&lt;br /&gt;
* [[Pyinfra/Implementazione deploy VM linea per linea]]: spiegazione tecnica del codice che realizza il deploy automatico di VM.&lt;br /&gt;
* [[Confronto Ansible vs Pyinfra]]: confronto generale di approccio.&lt;br /&gt;
* [[Installazione Pyinfra su Fedora]]: installazione CLI in ambiente Fedora.&lt;br /&gt;
&lt;br /&gt;
== Esempio minimo per capire la sintassi ==&lt;br /&gt;
Inventory:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
web_servers = [&lt;br /&gt;
    &amp;quot;web-01.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;web-02.example.net&amp;quot;,&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deploy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra.operations import server&lt;br /&gt;
&lt;br /&gt;
server.shell(&lt;br /&gt;
    name=&amp;quot;Show uptime&amp;quot;,&lt;br /&gt;
    commands=[&amp;quot;uptime&amp;quot;],&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Esecuzione:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pyinfra inventory.py deploy.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Idea chiave ==&lt;br /&gt;
Nel deployment VM di GazziNet, la UI non crea direttamente la VM. La UI:&lt;br /&gt;
* raccoglie i parametri&lt;br /&gt;
* valida input e rete&lt;br /&gt;
* crea un job locale&lt;br /&gt;
&lt;br /&gt;
Poi il job chiama pyinfra, e pyinfra richiama un helper Python che parla con Proxmox via SSH.&lt;br /&gt;
&lt;br /&gt;
Questa separazione aiuta a mantenere il flusso leggibile:&lt;br /&gt;
* frontend e login&lt;br /&gt;
* validazione e stato job&lt;br /&gt;
* runner pyinfra&lt;br /&gt;
* helper Proxmox&lt;br /&gt;
&lt;br /&gt;
== Regole documentali per questa area ==&lt;br /&gt;
La wiki deve contenere:&lt;br /&gt;
* spiegazioni umane&lt;br /&gt;
* runbook&lt;br /&gt;
* esempi&lt;br /&gt;
* struttura dei file&lt;br /&gt;
* procedure di verifica&lt;br /&gt;
&lt;br /&gt;
La wiki non deve contenere:&lt;br /&gt;
* password&lt;br /&gt;
* token&lt;br /&gt;
* cookie di sessione&lt;br /&gt;
* segreti runtime&lt;br /&gt;
* dump completi di backend interni&lt;br /&gt;
&lt;br /&gt;
== Riferimenti esterni ==&lt;br /&gt;
* [https://pyinfra.com/ Sito ufficiale]&lt;br /&gt;
* [https://docs.pyinfra.com/en/3.x/ Documentazione ufficiale]&lt;br /&gt;
* [https://www.gazzi.net/pyinfra-proxmox-webui/ Scheda pubblica WordPress]&lt;br /&gt;
* [https://www.gazzi.net/2026/03/29/pyinfra-proxmox-webui-proxmox-ldap-bot-gazzi-net/ Articolo di dettaglio]&lt;br /&gt;
&lt;br /&gt;
== Repository ==&lt;br /&gt;
Sorgente versionata in:&lt;br /&gt;
* &amp;lt;code&amp;gt;scripts-repo/pyinfra-proxmox-webui/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pagine wiki versionate nello stesso repository:&lt;br /&gt;
* &amp;lt;code&amp;gt;scripts-repo/pyinfra-proxmox-webui/wiki/&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
	<entry>
		<id>https://wiki.gazzi.net/index.php?title=Pyinfra/Implementazione_deploy_VM_linea_per_linea&amp;diff=636</id>
		<title>Pyinfra/Implementazione deploy VM linea per linea</title>
		<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php?title=Pyinfra/Implementazione_deploy_VM_linea_per_linea&amp;diff=636"/>
		<updated>2026-03-29T18:55:31Z</updated>

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

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

		<summary type="html">&lt;p&gt;Maintenance script: Espansione esempi Pyinfra con casi base e avanzati&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Esempi Pyinfra =&lt;br /&gt;
&lt;br /&gt;
Questa pagina raccoglie esempi leggibili e progressivi: si parte dal minimo indispensabile e si arriva a pattern piu vicini a un uso reale.&lt;br /&gt;
&lt;br /&gt;
== Prima idea da fissare ==&lt;br /&gt;
Un progetto pyinfra semplice ha di solito almeno due file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
inventory.py&lt;br /&gt;
deploy.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;inventory descrive i target. Il deploy descrive cosa fare.&lt;br /&gt;
&lt;br /&gt;
== Esempio base 1: esecuzione su host locali ==&lt;br /&gt;
Se vuoi solo capire la CLI:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pyinfra @local exec -- echo &amp;quot;hello world&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Questo serve quando vuoi verificare:&lt;br /&gt;
* che pyinfra sia installato&lt;br /&gt;
* che il runner funzioni&lt;br /&gt;
* che l&#039;output arrivi correttamente&lt;br /&gt;
&lt;br /&gt;
== Esempio base 2: inventory minimale ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
web_servers = [&lt;br /&gt;
    &amp;quot;web-01.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;web-02.example.net&amp;quot;,&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Qui hai solo una lista di host.&lt;br /&gt;
&lt;br /&gt;
== Esempio base 3: operation semplice ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra.operations import server&lt;br /&gt;
&lt;br /&gt;
server.shell(&lt;br /&gt;
    name=&amp;quot;Show uptime&amp;quot;,&lt;br /&gt;
    commands=[&amp;quot;uptime&amp;quot;],&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Esecuzione:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pyinfra inventory.py deploy.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Esempio base 4: installare pacchetti ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra.operations import apt&lt;br /&gt;
&lt;br /&gt;
apt.packages(&lt;br /&gt;
    name=&amp;quot;Install packages&amp;quot;,&lt;br /&gt;
    packages=[&amp;quot;vim&amp;quot;, &amp;quot;curl&amp;quot;],&lt;br /&gt;
    update=True,&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Questo e un esempio di operation idempotente: se i pacchetti sono gia presenti, pyinfra non rifara cambiamenti inutili.&lt;br /&gt;
&lt;br /&gt;
== Esempio base 5: gestire file ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra.operations import files&lt;br /&gt;
&lt;br /&gt;
files.file(&lt;br /&gt;
    name=&amp;quot;Ensure app log exists&amp;quot;,&lt;br /&gt;
    path=&amp;quot;/var/log/app.log&amp;quot;,&lt;br /&gt;
    user=&amp;quot;app&amp;quot;,&lt;br /&gt;
    group=&amp;quot;app&amp;quot;,&lt;br /&gt;
    mode=&amp;quot;644&amp;quot;,&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Esempio base 6: gestire servizi ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra.operations import systemd&lt;br /&gt;
&lt;br /&gt;
systemd.service(&lt;br /&gt;
    name=&amp;quot;Ensure nginx is running&amp;quot;,&lt;br /&gt;
    service=&amp;quot;nginx&amp;quot;,&lt;br /&gt;
    running=True,&lt;br /&gt;
    enabled=True,&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Esempio intermedio 1: dati host ==&lt;br /&gt;
Inventory:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
app_servers = [&lt;br /&gt;
    (&amp;quot;app-01.example.net&amp;quot;, {&amp;quot;install_nginx&amp;quot;: True}),&lt;br /&gt;
    (&amp;quot;app-02.example.net&amp;quot;, {&amp;quot;install_nginx&amp;quot;: False}),&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deploy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra import host&lt;br /&gt;
from pyinfra.operations import apt&lt;br /&gt;
&lt;br /&gt;
if host.data.get(&amp;quot;install_nginx&amp;quot;):&lt;br /&gt;
    apt.packages(&lt;br /&gt;
        name=&amp;quot;Install nginx&amp;quot;,&lt;br /&gt;
        packages=[&amp;quot;nginx&amp;quot;],&lt;br /&gt;
        update=True,&lt;br /&gt;
    )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Qui usi Python per rendere il deploy diverso in base ai dati del target.&lt;br /&gt;
&lt;br /&gt;
== Esempio intermedio 2: gruppi ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra import host, local&lt;br /&gt;
from pyinfra.operations import server&lt;br /&gt;
&lt;br /&gt;
if &amp;quot;web_servers&amp;quot; in host.groups:&lt;br /&gt;
    local.include(&amp;quot;tasks/web.py&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
if &amp;quot;db_servers&amp;quot; in host.groups:&lt;br /&gt;
    local.include(&amp;quot;tasks/database.py&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
server.shell(&lt;br /&gt;
    name=&amp;quot;Run everywhere&amp;quot;,&lt;br /&gt;
    commands=[&amp;quot;hostname&amp;quot;],&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Questo pattern e utile quando vuoi dividere la logica in file piu piccoli.&lt;br /&gt;
&lt;br /&gt;
== Esempio avanzato 1: deploy locale che richiama un helper Python ==&lt;br /&gt;
Questo e il pattern usato nel progetto Proxmox Web UI: pyinfra gira su &amp;lt;code&amp;gt;@local&amp;lt;/code&amp;gt; ma esegue un helper esterno che contiene la logica vera.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/usr/bin/env python3&lt;br /&gt;
import os&lt;br /&gt;
import shlex&lt;br /&gt;
from pathlib import Path&lt;br /&gt;
&lt;br /&gt;
from pyinfra.operations import server&lt;br /&gt;
&lt;br /&gt;
BASE_DIR = Path(__file__).resolve().parents[1]&lt;br /&gt;
python_bin = os.environ.get(&amp;quot;PYTHON_BIN&amp;quot;, str(BASE_DIR / &amp;quot;venv&amp;quot; / &amp;quot;bin&amp;quot; / &amp;quot;python&amp;quot;))&lt;br /&gt;
helper = BASE_DIR / &amp;quot;lib&amp;quot; / &amp;quot;proxmox_ssh.py&amp;quot;&lt;br /&gt;
cmd = f&amp;quot;{shlex.quote(python_bin)} {shlex.quote(str(helper))}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
server.shell(&lt;br /&gt;
    name=f&amp;quot;Create Proxmox VM {os.environ.get(&#039;VM_NAME&#039;, &#039;&#039;)}&amp;quot;,&lt;br /&gt;
    commands=[cmd],&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Idea pratica:&lt;br /&gt;
* pyinfra fa da orchestratore locale&lt;br /&gt;
* il file helper fa il lavoro specializzato&lt;br /&gt;
* le variabili ambiente passano i parametri del job&lt;br /&gt;
&lt;br /&gt;
== Esempio avanzato 2: usare pyinfra come backend di una UI ==&lt;br /&gt;
Nel caso GazziNet il flusso e:&lt;br /&gt;
# la UI riceve input dell&#039;utente&lt;br /&gt;
# la UI valida i campi&lt;br /&gt;
# la UI crea un job con stato e log&lt;br /&gt;
# il job lancia &amp;lt;code&amp;gt;pyinfra @local deploy.py -y&amp;lt;/code&amp;gt;&lt;br /&gt;
# pyinfra richiama un helper che esegue SSH su Proxmox&lt;br /&gt;
&lt;br /&gt;
Questa soluzione e utile quando vuoi:&lt;br /&gt;
* separare presentazione e automazione&lt;br /&gt;
* conservare log testuali chiari&lt;br /&gt;
* evitare che il browser parli direttamente con Proxmox&lt;br /&gt;
&lt;br /&gt;
== Esempio avanzato 3: rete dinamica e validazione ==&lt;br /&gt;
Nel progetto VM automatiche la UI gestisce:&lt;br /&gt;
* DHCP oppure IP statico&lt;br /&gt;
* bridge selezionabile&lt;br /&gt;
* VLAN opzionale&lt;br /&gt;
* validazione preventiva bridge + VLAN&lt;br /&gt;
* ricerca del primo VMID libero&lt;br /&gt;
&lt;br /&gt;
In questo caso pyinfra non decide la rete da solo: riceve i valori gia normalizzati e li passa al backend di provisioning.&lt;br /&gt;
&lt;br /&gt;
== Esempio dry run ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pyinfra inventory.py deploy.py --dry&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Utile prima di un deploy reale.&lt;br /&gt;
&lt;br /&gt;
== Pattern reali da ricordare ==&lt;br /&gt;
* tenere inventory e deploy in Git&lt;br /&gt;
* separare logica generale e helper specializzati&lt;br /&gt;
* validare input prima di lanciare pyinfra&lt;br /&gt;
* non mescolare segreti nei file versionati&lt;br /&gt;
* aggiungere sempre un modo semplice per verificare il risultato&lt;br /&gt;
&lt;br /&gt;
== Collegamenti ==&lt;br /&gt;
* [[Pyinfra]]&lt;br /&gt;
* [[Pyinfra/Deploy automatico VM Proxmox]]&lt;br /&gt;
* [[Pyinfra/Implementazione deploy VM linea per linea]]&lt;br /&gt;
* [[Confronto Ansible vs Pyinfra]]&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
	<entry>
		<id>https://wiki.gazzi.net/index.php?title=Pyinfra&amp;diff=633</id>
		<title>Pyinfra</title>
		<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php?title=Pyinfra&amp;diff=633"/>
		<updated>2026-03-29T18:55:30Z</updated>

		<summary type="html">&lt;p&gt;Maintenance script: Espansione pagina Pyinfra con struttura umana e link di approfondimento&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
= Pyinfra =&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pyinfra&#039;&#039;&#039; in GazziNet va letto su due livelli:&lt;br /&gt;
* come strumento generale di automazione scritto in Python&lt;br /&gt;
* come implementazione concreta gia pubblicata su &amp;lt;code&amp;gt;bot.gazzi.net/pyinfra/&amp;lt;/code&amp;gt; per creare VM Proxmox via browser&lt;br /&gt;
&lt;br /&gt;
Questa pagina principale e pensata per una persona umana che vuole capire rapidamente:&lt;br /&gt;
* che cosa fa pyinfra&lt;br /&gt;
* quando usarlo&lt;br /&gt;
* dove trovare esempi semplici e avanzati&lt;br /&gt;
* dove trovare il runbook reale della web UI pubblicata&lt;br /&gt;
* dove trovare la spiegazione tecnica dell&#039;implementazione del deploy automatico VM&lt;br /&gt;
&lt;br /&gt;
I dettagli sensibili non vanno messi in wiki. Password, token, cookie, backend privati e dump runtime restano fuori dalla documentazione pubblica o semi-pubblica.&lt;br /&gt;
&lt;br /&gt;
== In una frase ==&lt;br /&gt;
Pyinfra e un motore di automazione che usa Python per descrivere inventory, logica e operazioni di deploy in modo piu programmabile rispetto ai playbook puramente YAML.&lt;br /&gt;
&lt;br /&gt;
== Quando ha senso usarlo ==&lt;br /&gt;
Pyinfra e adatto quando:&lt;br /&gt;
* vuoi tenere deploy e configurazioni in Git&lt;br /&gt;
* preferisci Python per logica, condizioni e riuso&lt;br /&gt;
* devi automatizzare server Linux, homelab o ambienti interni&lt;br /&gt;
* vuoi una separazione chiara tra form di input, validazione e runner di provisioning&lt;br /&gt;
&lt;br /&gt;
Pyinfra e meno adatto quando:&lt;br /&gt;
* il team vuole solo file dichiarativi molto semplici&lt;br /&gt;
* nessuno vuole mantenere codice Python&lt;br /&gt;
* l&#039;automazione deve essere totalmente guidata da un pannello visuale gia pronto&lt;br /&gt;
&lt;br /&gt;
== Concetti base ==&lt;br /&gt;
Tre idee utili da capire subito:&lt;br /&gt;
&lt;br /&gt;
=== 1. Inventory ===&lt;br /&gt;
L&#039;inventory descrive i target: host, gruppi e dati associati.&lt;br /&gt;
&lt;br /&gt;
=== 2. Operations ===&lt;br /&gt;
Le operations descrivono cosa deve essere applicato ai target, oppure uno stato che vuoi rendere vero.&lt;br /&gt;
&lt;br /&gt;
=== 3. Esecuzione ===&lt;br /&gt;
Pyinfra legge inventory e dati, prepara l&#039;ordine delle operazioni, si connette ai target ed esegue le modifiche.&lt;br /&gt;
&lt;br /&gt;
== Nel caso GazziNet ==&lt;br /&gt;
Su GazziNet pyinfra non e stato usato come esercizio teorico. E stato integrato in una soluzione completa:&lt;br /&gt;
* UI pubblica: &amp;lt;code&amp;gt;https://bot.gazzi.net/pyinfra/&amp;lt;/code&amp;gt;&lt;br /&gt;
* autenticazione: LDAP&lt;br /&gt;
* runner: &amp;lt;code&amp;gt;pyinfra&amp;lt;/code&amp;gt; locale su &amp;lt;code&amp;gt;bot.gazzi.local&amp;lt;/code&amp;gt;&lt;br /&gt;
* target: Proxmox&lt;br /&gt;
* risultato: creazione controllata di VM con parametri espliciti&lt;br /&gt;
&lt;br /&gt;
== Percorso consigliato di lettura ==&lt;br /&gt;
Se parti da zero:&lt;br /&gt;
# [[Esempi Pyinfra]]&lt;br /&gt;
# [[Pyinfra/Deploy automatico VM Proxmox]]&lt;br /&gt;
# [[Pyinfra/Implementazione deploy VM linea per linea]]&lt;br /&gt;
&lt;br /&gt;
Se vuoi solo usare la piattaforma pubblicata:&lt;br /&gt;
# [[Pyinfra/Deploy automatico VM Proxmox]]&lt;br /&gt;
&lt;br /&gt;
Se vuoi capire il codice:&lt;br /&gt;
# [[Pyinfra/Implementazione deploy VM linea per linea]]&lt;br /&gt;
&lt;br /&gt;
== Mappa delle pagine ==&lt;br /&gt;
* [[Esempi Pyinfra]]&lt;br /&gt;
  Guida pratica con esempi base e avanzati di pyinfra.&lt;br /&gt;
* [[Pyinfra/Deploy automatico VM Proxmox]]&lt;br /&gt;
  Runbook umano della web UI pubblicata su bot.gazzi.net.&lt;br /&gt;
* [[Pyinfra/Implementazione deploy VM linea per linea]]&lt;br /&gt;
  Spiegazione tecnica del codice che realizza il deploy automatico di VM.&lt;br /&gt;
* [[Confronto Ansible vs Pyinfra]]&lt;br /&gt;
  Confronto generale di approccio.&lt;br /&gt;
* [[Installazione Pyinfra su Fedora]]&lt;br /&gt;
  Installazione CLI in ambiente Fedora.&lt;br /&gt;
&lt;br /&gt;
== Esempio minimo per capire la sintassi ==&lt;br /&gt;
Inventory:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
web_servers = [&lt;br /&gt;
    &amp;quot;web-01.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;web-02.example.net&amp;quot;,&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deploy:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from pyinfra.operations import server&lt;br /&gt;
&lt;br /&gt;
server.shell(&lt;br /&gt;
    name=&amp;quot;Show uptime&amp;quot;,&lt;br /&gt;
    commands=[&amp;quot;uptime&amp;quot;],&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Esecuzione:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pyinfra inventory.py deploy.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Idea chiave ==&lt;br /&gt;
Nel deployment VM di GazziNet, la UI non crea direttamente la VM. La UI:&lt;br /&gt;
* raccoglie i parametri&lt;br /&gt;
* valida input e rete&lt;br /&gt;
* crea un job locale&lt;br /&gt;
&lt;br /&gt;
Poi il job chiama pyinfra, e pyinfra richiama un helper Python che parla con Proxmox via SSH.&lt;br /&gt;
&lt;br /&gt;
Questa separazione aiuta a mantenere il flusso leggibile:&lt;br /&gt;
* frontend e login&lt;br /&gt;
* validazione e stato job&lt;br /&gt;
* runner pyinfra&lt;br /&gt;
* helper Proxmox&lt;br /&gt;
&lt;br /&gt;
== Regole documentali per questa area ==&lt;br /&gt;
La wiki deve contenere:&lt;br /&gt;
* spiegazioni umane&lt;br /&gt;
* runbook&lt;br /&gt;
* esempi&lt;br /&gt;
* struttura dei file&lt;br /&gt;
* procedure di verifica&lt;br /&gt;
&lt;br /&gt;
La wiki non deve contenere:&lt;br /&gt;
* password&lt;br /&gt;
* token&lt;br /&gt;
* cookie di sessione&lt;br /&gt;
* segreti runtime&lt;br /&gt;
* dump completi di backend interni&lt;br /&gt;
&lt;br /&gt;
== Riferimenti esterni ==&lt;br /&gt;
* [https://pyinfra.com/ Sito ufficiale]&lt;br /&gt;
* [https://docs.pyinfra.com/en/3.x/ Documentazione ufficiale]&lt;br /&gt;
* [https://www.gazzi.net/pyinfra-proxmox-webui/ Scheda pubblica WordPress]&lt;br /&gt;
* [https://www.gazzi.net/2026/03/29/pyinfra-proxmox-webui-proxmox-ldap-bot-gazzi-net/ Articolo di dettaglio]&lt;br /&gt;
&lt;br /&gt;
== Repository ==&lt;br /&gt;
Sorgente versionata in:&lt;br /&gt;
* &amp;lt;code&amp;gt;scripts-repo/pyinfra-proxmox-webui/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pagine wiki versionate nello stesso repository:&lt;br /&gt;
* &amp;lt;code&amp;gt;scripts-repo/pyinfra-proxmox-webui/wiki/&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
	<entry>
		<id>https://wiki.gazzi.net/index.php?title=Pyinfra/Implementazione_deploy_VM_linea_per_linea&amp;diff=632</id>
		<title>Pyinfra/Implementazione deploy VM linea per linea</title>
		<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php?title=Pyinfra/Implementazione_deploy_VM_linea_per_linea&amp;diff=632"/>
		<updated>2026-03-29T18:51:30Z</updated>

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

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

		<summary type="html">&lt;p&gt;Maintenance script: Espansione esempi Pyinfra con casi base e avanzati&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Esempi Pyinfra =&lt;br /&gt;
&lt;br /&gt;
Questa pagina raccoglie esempi leggibili e progressivi: si parte dal minimo indispensabile e si arriva a pattern piu vicini a un uso reale.&lt;br /&gt;
&lt;br /&gt;
== Prima idea da fissare ==&lt;br /&gt;
Un progetto pyinfra semplice ha di solito almeno due file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
inventory.py&lt;br /&gt;
deploy.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;inventory descrive i target. Il deploy descrive cosa fare.&lt;br /&gt;
&lt;br /&gt;
== Esempio base 1: esecuzione su host locali ==&lt;br /&gt;
Se vuoi solo capire la CLI:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
pyinfra @local exec -- echo &amp;quot;hello world&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Questo serve quando vuoi verificare:&lt;br /&gt;
* che pyinfra sia installato&lt;br /&gt;
* che il runner funzioni&lt;br /&gt;
* che l&#039;output arrivi correttamente&lt;br /&gt;
&lt;br /&gt;
== Esempio base 2: inventory minimale ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
web_servers = [&lt;br /&gt;
    &amp;quot;web-01.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;web-02.example.net&amp;quot;,&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Qui hai solo una lista di host.&lt;br /&gt;
&lt;br /&gt;
== Esempio base 3: operation semplice ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from pyinfra.operations import server&lt;br /&gt;
&lt;br /&gt;
server.shell(&lt;br /&gt;
    name=&amp;quot;Show uptime&amp;quot;,&lt;br /&gt;
    commands=[&amp;quot;uptime&amp;quot;],&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Esecuzione:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
pyinfra inventory.py deploy.py&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Esempio base 4: installare pacchetti ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from pyinfra.operations import apt&lt;br /&gt;
&lt;br /&gt;
apt.packages(&lt;br /&gt;
    name=&amp;quot;Install packages&amp;quot;,&lt;br /&gt;
    packages=[&amp;quot;vim&amp;quot;, &amp;quot;curl&amp;quot;],&lt;br /&gt;
    update=True,&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Questo e un esempio di operation idempotente: se i pacchetti sono gia presenti, pyinfra non rifara cambiamenti inutili.&lt;br /&gt;
&lt;br /&gt;
== Esempio base 5: gestire file ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from pyinfra.operations import files&lt;br /&gt;
&lt;br /&gt;
files.file(&lt;br /&gt;
    name=&amp;quot;Ensure app log exists&amp;quot;,&lt;br /&gt;
    path=&amp;quot;/var/log/app.log&amp;quot;,&lt;br /&gt;
    user=&amp;quot;app&amp;quot;,&lt;br /&gt;
    group=&amp;quot;app&amp;quot;,&lt;br /&gt;
    mode=&amp;quot;644&amp;quot;,&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Esempio base 6: gestire servizi ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from pyinfra.operations import systemd&lt;br /&gt;
&lt;br /&gt;
systemd.service(&lt;br /&gt;
    name=&amp;quot;Ensure nginx is running&amp;quot;,&lt;br /&gt;
    service=&amp;quot;nginx&amp;quot;,&lt;br /&gt;
    running=True,&lt;br /&gt;
    enabled=True,&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Esempio intermedio 1: dati host ==&lt;br /&gt;
Inventory:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
app_servers = [&lt;br /&gt;
    (&amp;quot;app-01.example.net&amp;quot;, {&amp;quot;install_nginx&amp;quot;: True}),&lt;br /&gt;
    (&amp;quot;app-02.example.net&amp;quot;, {&amp;quot;install_nginx&amp;quot;: False}),&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deploy:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from pyinfra import host&lt;br /&gt;
from pyinfra.operations import apt&lt;br /&gt;
&lt;br /&gt;
if host.data.get(&amp;quot;install_nginx&amp;quot;):&lt;br /&gt;
    apt.packages(&lt;br /&gt;
        name=&amp;quot;Install nginx&amp;quot;,&lt;br /&gt;
        packages=[&amp;quot;nginx&amp;quot;],&lt;br /&gt;
        update=True,&lt;br /&gt;
    )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Qui usi Python per rendere il deploy diverso in base ai dati del target.&lt;br /&gt;
&lt;br /&gt;
== Esempio intermedio 2: gruppi ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from pyinfra import host, local&lt;br /&gt;
from pyinfra.operations import server&lt;br /&gt;
&lt;br /&gt;
if &amp;quot;web_servers&amp;quot; in host.groups:&lt;br /&gt;
    local.include(&amp;quot;tasks/web.py&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
if &amp;quot;db_servers&amp;quot; in host.groups:&lt;br /&gt;
    local.include(&amp;quot;tasks/database.py&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
server.shell(&lt;br /&gt;
    name=&amp;quot;Run everywhere&amp;quot;,&lt;br /&gt;
    commands=[&amp;quot;hostname&amp;quot;],&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Questo pattern e utile quando vuoi dividere la logica in file piu piccoli.&lt;br /&gt;
&lt;br /&gt;
== Esempio avanzato 1: deploy locale che richiama un helper Python ==&lt;br /&gt;
Questo e il pattern usato nel progetto Proxmox Web UI: pyinfra gira su &amp;lt;code&amp;gt;@local&amp;lt;/code&amp;gt; ma esegue un helper esterno che contiene la logica vera.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env python3&lt;br /&gt;
import os&lt;br /&gt;
import shlex&lt;br /&gt;
from pathlib import Path&lt;br /&gt;
&lt;br /&gt;
from pyinfra.operations import server&lt;br /&gt;
&lt;br /&gt;
BASE_DIR = Path(__file__).resolve().parents[1]&lt;br /&gt;
python_bin = os.environ.get(&amp;quot;PYTHON_BIN&amp;quot;, str(BASE_DIR / &amp;quot;venv&amp;quot; / &amp;quot;bin&amp;quot; / &amp;quot;python&amp;quot;))&lt;br /&gt;
helper = BASE_DIR / &amp;quot;lib&amp;quot; / &amp;quot;proxmox_ssh.py&amp;quot;&lt;br /&gt;
cmd = f&amp;quot;{shlex.quote(python_bin)} {shlex.quote(str(helper))}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
server.shell(&lt;br /&gt;
    name=f&amp;quot;Create Proxmox VM {os.environ.get(&#039;VM_NAME&#039;, &#039;&#039;)}&amp;quot;,&lt;br /&gt;
    commands=[cmd],&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Idea pratica:&lt;br /&gt;
* pyinfra fa da orchestratore locale&lt;br /&gt;
* il file helper fa il lavoro specializzato&lt;br /&gt;
* le variabili ambiente passano i parametri del job&lt;br /&gt;
&lt;br /&gt;
== Esempio avanzato 2: usare pyinfra come backend di una UI ==&lt;br /&gt;
Nel caso GazziNet il flusso e:&lt;br /&gt;
# la UI riceve input dell&#039;utente&lt;br /&gt;
# la UI valida i campi&lt;br /&gt;
# la UI crea un job con stato e log&lt;br /&gt;
# il job lancia &amp;lt;code&amp;gt;pyinfra @local deploy.py -y&amp;lt;/code&amp;gt;&lt;br /&gt;
# pyinfra richiama un helper che esegue SSH su Proxmox&lt;br /&gt;
&lt;br /&gt;
Questa soluzione e utile quando vuoi:&lt;br /&gt;
* separare presentazione e automazione&lt;br /&gt;
* conservare log testuali chiari&lt;br /&gt;
* evitare che il browser parli direttamente con Proxmox&lt;br /&gt;
&lt;br /&gt;
== Esempio avanzato 3: rete dinamica e validazione ==&lt;br /&gt;
Nel progetto VM automatiche la UI gestisce:&lt;br /&gt;
* DHCP oppure IP statico&lt;br /&gt;
* bridge selezionabile&lt;br /&gt;
* VLAN opzionale&lt;br /&gt;
* validazione preventiva bridge + VLAN&lt;br /&gt;
* ricerca del primo VMID libero&lt;br /&gt;
&lt;br /&gt;
In questo caso pyinfra non decide la rete da solo: riceve i valori gia normalizzati e li passa al backend di provisioning.&lt;br /&gt;
&lt;br /&gt;
== Esempio dry run ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
pyinfra inventory.py deploy.py --dry&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Utile prima di un deploy reale.&lt;br /&gt;
&lt;br /&gt;
== Pattern reali da ricordare ==&lt;br /&gt;
* tenere inventory e deploy in Git&lt;br /&gt;
* separare logica generale e helper specializzati&lt;br /&gt;
* validare input prima di lanciare pyinfra&lt;br /&gt;
* non mescolare segreti nei file versionati&lt;br /&gt;
* aggiungere sempre un modo semplice per verificare il risultato&lt;br /&gt;
&lt;br /&gt;
== Collegamenti ==&lt;br /&gt;
* [[Pyinfra]]&lt;br /&gt;
* [[Pyinfra/Deploy automatico VM Proxmox]]&lt;br /&gt;
* [[Pyinfra/Implementazione deploy VM linea per linea]]&lt;br /&gt;
* [[Confronto Ansible vs Pyinfra]]&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
	<entry>
		<id>https://wiki.gazzi.net/index.php?title=Pyinfra&amp;diff=629</id>
		<title>Pyinfra</title>
		<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php?title=Pyinfra&amp;diff=629"/>
		<updated>2026-03-29T18:51:29Z</updated>

		<summary type="html">&lt;p&gt;Maintenance script: Espansione pagina Pyinfra con struttura umana e link di approfondimento&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
= Pyinfra =&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pyinfra&#039;&#039;&#039; in GazziNet va letto su due livelli:&lt;br /&gt;
* come strumento generale di automazione scritto in Python&lt;br /&gt;
* come implementazione concreta gia pubblicata su &amp;lt;code&amp;gt;bot.gazzi.net/pyinfra/&amp;lt;/code&amp;gt; per creare VM Proxmox via browser&lt;br /&gt;
&lt;br /&gt;
Questa pagina principale e pensata per una persona umana che vuole capire rapidamente:&lt;br /&gt;
* che cosa fa pyinfra&lt;br /&gt;
* quando usarlo&lt;br /&gt;
* dove trovare esempi semplici e avanzati&lt;br /&gt;
* dove trovare il runbook reale della web UI pubblicata&lt;br /&gt;
* dove trovare la spiegazione tecnica dell&#039;implementazione del deploy automatico VM&lt;br /&gt;
&lt;br /&gt;
I dettagli sensibili non vanno messi in wiki. Password, token, cookie, backend privati e dump runtime restano fuori dalla documentazione pubblica o semi-pubblica.&lt;br /&gt;
&lt;br /&gt;
== In una frase ==&lt;br /&gt;
Pyinfra e un motore di automazione che usa Python per descrivere inventory, logica e operazioni di deploy in modo piu programmabile rispetto ai playbook puramente YAML.&lt;br /&gt;
&lt;br /&gt;
== Quando ha senso usarlo ==&lt;br /&gt;
Pyinfra e adatto quando:&lt;br /&gt;
* vuoi tenere deploy e configurazioni in Git&lt;br /&gt;
* preferisci Python per logica, condizioni e riuso&lt;br /&gt;
* devi automatizzare server Linux, homelab o ambienti interni&lt;br /&gt;
* vuoi una separazione chiara tra form di input, validazione e runner di provisioning&lt;br /&gt;
&lt;br /&gt;
Pyinfra e meno adatto quando:&lt;br /&gt;
* il team vuole solo file dichiarativi molto semplici&lt;br /&gt;
* nessuno vuole mantenere codice Python&lt;br /&gt;
* l&#039;automazione deve essere totalmente guidata da un pannello visuale gia pronto&lt;br /&gt;
&lt;br /&gt;
== Concetti base ==&lt;br /&gt;
Tre idee utili da capire subito:&lt;br /&gt;
&lt;br /&gt;
=== 1. Inventory ===&lt;br /&gt;
L&#039;inventory descrive i target: host, gruppi e dati associati.&lt;br /&gt;
&lt;br /&gt;
=== 2. Operations ===&lt;br /&gt;
Le operations descrivono cosa deve essere applicato ai target, oppure uno stato che vuoi rendere vero.&lt;br /&gt;
&lt;br /&gt;
=== 3. Esecuzione ===&lt;br /&gt;
Pyinfra legge inventory e dati, prepara l&#039;ordine delle operazioni, si connette ai target ed esegue le modifiche.&lt;br /&gt;
&lt;br /&gt;
== Nel caso GazziNet ==&lt;br /&gt;
Su GazziNet pyinfra non e stato usato come esercizio teorico. E stato integrato in una soluzione completa:&lt;br /&gt;
* UI pubblica: &amp;lt;code&amp;gt;https://bot.gazzi.net/pyinfra/&amp;lt;/code&amp;gt;&lt;br /&gt;
* autenticazione: LDAP&lt;br /&gt;
* runner: &amp;lt;code&amp;gt;pyinfra&amp;lt;/code&amp;gt; locale su &amp;lt;code&amp;gt;bot.gazzi.local&amp;lt;/code&amp;gt;&lt;br /&gt;
* target: Proxmox&lt;br /&gt;
* risultato: creazione controllata di VM con parametri espliciti&lt;br /&gt;
&lt;br /&gt;
== Percorso consigliato di lettura ==&lt;br /&gt;
Se parti da zero:&lt;br /&gt;
# [[Esempi Pyinfra]]&lt;br /&gt;
# [[Pyinfra/Deploy automatico VM Proxmox]]&lt;br /&gt;
# [[Pyinfra/Implementazione deploy VM linea per linea]]&lt;br /&gt;
&lt;br /&gt;
Se vuoi solo usare la piattaforma pubblicata:&lt;br /&gt;
# [[Pyinfra/Deploy automatico VM Proxmox]]&lt;br /&gt;
&lt;br /&gt;
Se vuoi capire il codice:&lt;br /&gt;
# [[Pyinfra/Implementazione deploy VM linea per linea]]&lt;br /&gt;
&lt;br /&gt;
== Mappa delle pagine ==&lt;br /&gt;
* [[Esempi Pyinfra]]&lt;br /&gt;
  Guida pratica con esempi base e avanzati di pyinfra.&lt;br /&gt;
* [[Pyinfra/Deploy automatico VM Proxmox]]&lt;br /&gt;
  Runbook umano della web UI pubblicata su bot.gazzi.net.&lt;br /&gt;
* [[Pyinfra/Implementazione deploy VM linea per linea]]&lt;br /&gt;
  Spiegazione tecnica del codice che realizza il deploy automatico di VM.&lt;br /&gt;
* [[Confronto Ansible vs Pyinfra]]&lt;br /&gt;
  Confronto generale di approccio.&lt;br /&gt;
* [[Installazione Pyinfra su Fedora]]&lt;br /&gt;
  Installazione CLI in ambiente Fedora.&lt;br /&gt;
&lt;br /&gt;
== Esempio minimo per capire la sintassi ==&lt;br /&gt;
Inventory:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
web_servers = [&lt;br /&gt;
    &amp;quot;web-01.example.net&amp;quot;,&lt;br /&gt;
    &amp;quot;web-02.example.net&amp;quot;,&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deploy:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from pyinfra.operations import server&lt;br /&gt;
&lt;br /&gt;
server.shell(&lt;br /&gt;
    name=&amp;quot;Show uptime&amp;quot;,&lt;br /&gt;
    commands=[&amp;quot;uptime&amp;quot;],&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Esecuzione:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
pyinfra inventory.py deploy.py&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Idea chiave ==&lt;br /&gt;
Nel deployment VM di GazziNet, la UI non crea direttamente la VM. La UI:&lt;br /&gt;
* raccoglie i parametri&lt;br /&gt;
* valida input e rete&lt;br /&gt;
* crea un job locale&lt;br /&gt;
&lt;br /&gt;
Poi il job chiama pyinfra, e pyinfra richiama un helper Python che parla con Proxmox via SSH.&lt;br /&gt;
&lt;br /&gt;
Questa separazione aiuta a mantenere il flusso leggibile:&lt;br /&gt;
* frontend e login&lt;br /&gt;
* validazione e stato job&lt;br /&gt;
* runner pyinfra&lt;br /&gt;
* helper Proxmox&lt;br /&gt;
&lt;br /&gt;
== Regole documentali per questa area ==&lt;br /&gt;
La wiki deve contenere:&lt;br /&gt;
* spiegazioni umane&lt;br /&gt;
* runbook&lt;br /&gt;
* esempi&lt;br /&gt;
* struttura dei file&lt;br /&gt;
* procedure di verifica&lt;br /&gt;
&lt;br /&gt;
La wiki non deve contenere:&lt;br /&gt;
* password&lt;br /&gt;
* token&lt;br /&gt;
* cookie di sessione&lt;br /&gt;
* segreti runtime&lt;br /&gt;
* dump completi di backend interni&lt;br /&gt;
&lt;br /&gt;
== Riferimenti esterni ==&lt;br /&gt;
* [https://pyinfra.com/ Sito ufficiale]&lt;br /&gt;
* [https://docs.pyinfra.com/en/3.x/ Documentazione ufficiale]&lt;br /&gt;
* [https://www.gazzi.net/pyinfra-proxmox-webui/ Scheda pubblica WordPress]&lt;br /&gt;
* [https://www.gazzi.net/2026/03/29/pyinfra-proxmox-webui-proxmox-ldap-bot-gazzi-net/ Articolo di dettaglio]&lt;br /&gt;
&lt;br /&gt;
== Repository ==&lt;br /&gt;
Sorgente versionata in:&lt;br /&gt;
* &amp;lt;code&amp;gt;scripts-repo/pyinfra-proxmox-webui/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pagine wiki versionate nello stesso repository:&lt;br /&gt;
* &amp;lt;code&amp;gt;scripts-repo/pyinfra-proxmox-webui/wiki/&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
	<entry>
		<id>https://wiki.gazzi.net/index.php?title=Pyinfra&amp;diff=628</id>
		<title>Pyinfra</title>
		<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php?title=Pyinfra&amp;diff=628"/>
		<updated>2026-03-29T18:43:35Z</updated>

		<summary type="html">&lt;p&gt;Maintenance script: Aggiornamento&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
= Pyinfra =&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pyinfra&#039;&#039;&#039; in GazziNet non e solo una pagina teorica: al 2026-03-29 e stato usato per pubblicare una web UI operativa che consente di creare VM Proxmox via browser, con autenticazione LDAP e job eseguiti on demand.&lt;br /&gt;
&lt;br /&gt;
Questa pagina raccoglie il runbook sintetico della soluzione realmente messa in esercizio, con focus su deploy, configurazione, uso e verifiche. I dettagli sensibili restano fuori dalla wiki e vanno tenuti nei file runtime dedicati.&lt;br /&gt;
&lt;br /&gt;
== Cosa e stato realizzato ==&lt;br /&gt;
* UI pubblica su &amp;lt;code&amp;gt;https://bot.gazzi.net/pyinfra/&amp;lt;/code&amp;gt;&lt;br /&gt;
* autenticazione LDAP prima dell&#039;accesso alla form&lt;br /&gt;
* runner &amp;lt;code&amp;gt;pyinfra&amp;lt;/code&amp;gt; locale su &amp;lt;code&amp;gt;bot.gazzi.local&amp;lt;/code&amp;gt;&lt;br /&gt;
* provisioning VM su Proxmox via SSH e comandi &amp;lt;code&amp;gt;qm&amp;lt;/code&amp;gt;&lt;br /&gt;
* scelta di CPU, RAM, disco, bridge, VLAN, DHCP o IP statico&lt;br /&gt;
* scelta di utente e password iniziali della VM&lt;br /&gt;
* pulsante per trovare il primo VMID libero a partire da &amp;lt;code&amp;gt;100&amp;lt;/code&amp;gt;&lt;br /&gt;
* lookup bridge e VLAN direttamente dal nodo Proxmox&lt;br /&gt;
* validazione preventiva della coppia &amp;lt;code&amp;gt;bridge + vlan&amp;lt;/code&amp;gt;&lt;br /&gt;
* cleanup dello snippet cloud-init dopo il primo boot se la VM parte subito&lt;br /&gt;
&lt;br /&gt;
== Architettura logica ==&lt;br /&gt;
Flusso sintetico:&lt;br /&gt;
# utente apre &amp;lt;code&amp;gt;bot.gazzi.net/pyinfra/&amp;lt;/code&amp;gt;&lt;br /&gt;
# Nginx su &amp;lt;code&amp;gt;proxy01&amp;lt;/code&amp;gt; inoltra verso il servizio su &amp;lt;code&amp;gt;bot.gazzi.local&amp;lt;/code&amp;gt;&lt;br /&gt;
# l&#039;app Flask autentica l&#039;utente via LDAP&lt;br /&gt;
# la UI crea un job locale &amp;lt;code&amp;gt;pyinfra&amp;lt;/code&amp;gt;&lt;br /&gt;
# il job usa SSH verso Proxmox ed esegue &amp;lt;code&amp;gt;qm create&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;qm set&amp;lt;/code&amp;gt;&lt;br /&gt;
# se richiesto, la VM parte subito e viene atteso il completamento di &amp;lt;code&amp;gt;cloud-init&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parametri gestiti dalla UI ==&lt;br /&gt;
* VMID&lt;br /&gt;
* nome VM&lt;br /&gt;
* utente amministrativo iniziale&lt;br /&gt;
* password iniziale&lt;br /&gt;
* CPU&lt;br /&gt;
* RAM&lt;br /&gt;
* disco&lt;br /&gt;
* rete: DHCP o IP statico&lt;br /&gt;
* bridge Proxmox&lt;br /&gt;
* VLAN Proxmox&lt;br /&gt;
* gateway&lt;br /&gt;
* CIDR&lt;br /&gt;
* avvio automatico della VM a fine creazione&lt;br /&gt;
&lt;br /&gt;
Nota pratica:&lt;br /&gt;
* se e selezionato &#039;&#039;&#039;DHCP&#039;&#039;&#039;, i campi IP statici vengono disabilitati&lt;br /&gt;
* se si usa rete &#039;&#039;&#039;statica&#039;&#039;&#039;, IP, gateway e CIDR diventano obbligatori&lt;br /&gt;
&lt;br /&gt;
== File principali ==&lt;br /&gt;
Sorgenti versionati:&lt;br /&gt;
* &amp;lt;code&amp;gt;scripts-repo/pyinfra-proxmox-webui/app.py&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;scripts-repo/pyinfra-proxmox-webui/lib/proxmox_ssh.py&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;scripts-repo/pyinfra-proxmox-webui/deploys/create_vm.py&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;scripts-repo/pyinfra-proxmox-webui/systemd/pyinfra-webui.service&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;scripts-repo/pyinfra-proxmox-webui/proxy01-bot-pyinfra-location.conf.example&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;scripts-repo/pyinfra-proxmox-webui/.env.example&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Runtime:&lt;br /&gt;
* working directory: &amp;lt;code&amp;gt;/opt/pyinfra-webui&amp;lt;/code&amp;gt;&lt;br /&gt;
* environment file: &amp;lt;code&amp;gt;/etc/pyinfra-webui/pyinfra-webui.env&amp;lt;/code&amp;gt;&lt;br /&gt;
* service systemd: &amp;lt;code&amp;gt;/etc/systemd/system/pyinfra-webui.service&amp;lt;/code&amp;gt;&lt;br /&gt;
* state dir: &amp;lt;code&amp;gt;/var/lib/pyinfra-webui&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Esempio environment file ==&lt;br /&gt;
Non inserire password o token nella wiki. Usare placeholder e valorizzare i segreti solo nel file runtime.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
APP_HOST=0.0.0.0&lt;br /&gt;
APP_PORT=18180&lt;br /&gt;
APP_BASE_PATH=/pyinfra&lt;br /&gt;
APP_TOKEN=CHANGEME&lt;br /&gt;
APP_SECRET_KEY=CHANGEME&lt;br /&gt;
APP_STATE_DIR=/var/lib/pyinfra-webui&lt;br /&gt;
&lt;br /&gt;
PROXMOX_SSH_HOST=PROXMOX_HOST&lt;br /&gt;
PROXMOX_SSH_PORT=22&lt;br /&gt;
PROXMOX_SSH_USER=root&lt;br /&gt;
PROXMOX_SSH_PASS=CHANGEME&lt;br /&gt;
&lt;br /&gt;
PROXMOX_IMAGE=/var/lib/vz/template/iso/noble-server-cloudimg-amd64.img&lt;br /&gt;
PROXMOX_STORAGE=local&lt;br /&gt;
PROXMOX_CLOUDINIT_STORAGE=local&lt;br /&gt;
PROXMOX_BRIDGE=vmbr1&lt;br /&gt;
PROXMOX_GATEWAY=172.16.1.1&lt;br /&gt;
PROXMOX_CPU_TYPE=x86-64-v2-AES&lt;br /&gt;
PROXMOX_CIUSER=ubuntu&lt;br /&gt;
PROXMOX_CIPASSWORD=CHANGEME&lt;br /&gt;
PROXMOX_CICUSTOM_USER=local:snippets/gazzi-ubuntu-user.yaml&lt;br /&gt;
&lt;br /&gt;
LDAP_URI=ldap://LDAP_HOST:389&lt;br /&gt;
LDAP_USER_DN_TEMPLATE=uid={username},ou=People,dc=gazzi,dc=local&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Service systemd ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=Pyinfra Proxmox Web UI&lt;br /&gt;
After=network-online.target&lt;br /&gt;
Wants=network-online.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=simple&lt;br /&gt;
User=pyinfraweb&lt;br /&gt;
Group=pyinfraweb&lt;br /&gt;
WorkingDirectory=/opt/pyinfra-webui&lt;br /&gt;
EnvironmentFile=/etc/pyinfra-webui/pyinfra-webui.env&lt;br /&gt;
ExecStart=/opt/pyinfra-webui/venv/bin/python /opt/pyinfra-webui/app.py&lt;br /&gt;
Restart=on-failure&lt;br /&gt;
RestartSec=3&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Reverse proxy ==&lt;br /&gt;
Snippet Nginx di riferimento:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nginx&amp;quot;&amp;gt;&lt;br /&gt;
location = /pyinfra {&lt;br /&gt;
    return 301 /pyinfra/;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
location /pyinfra/ {&lt;br /&gt;
    proxy_pass http://BOT_GAZZI_LOCAL:18180/;&lt;br /&gt;
    proxy_http_version 1.1;&lt;br /&gt;
    proxy_set_header Host $host;&lt;br /&gt;
    proxy_set_header X-Real-IP $remote_addr;&lt;br /&gt;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
    proxy_set_header X-Forwarded-Proto $scheme;&lt;br /&gt;
    proxy_set_header X-Forwarded-Host $host;&lt;br /&gt;
    proxy_set_header X-Forwarded-Port $server_port;&lt;br /&gt;
    proxy_set_header X-Forwarded-Prefix /pyinfra;&lt;br /&gt;
    proxy_redirect off;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Deploy o aggiornamento ==&lt;br /&gt;
Sequenza minima lato host applicativo:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo mkdir -p /opt/pyinfra-webui /etc/pyinfra-webui /var/lib/pyinfra-webui&lt;br /&gt;
sudo chown -R pyinfraweb:pyinfraweb /opt/pyinfra-webui /var/lib/pyinfra-webui&lt;br /&gt;
&lt;br /&gt;
cd /opt/pyinfra-webui&lt;br /&gt;
python3 -m venv venv&lt;br /&gt;
./venv/bin/pip install -U pip&lt;br /&gt;
./venv/bin/pip install -r requirements.txt&lt;br /&gt;
&lt;br /&gt;
sudo install -m 0644 systemd/pyinfra-webui.service /etc/systemd/system/pyinfra-webui.service&lt;br /&gt;
sudo systemctl daemon-reload&lt;br /&gt;
sudo systemctl enable --now pyinfra-webui.service&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Poi:&lt;br /&gt;
* copiare o aggiornare i file dell&#039;app dentro &amp;lt;code&amp;gt;/opt/pyinfra-webui&amp;lt;/code&amp;gt;&lt;br /&gt;
* aggiornare &amp;lt;code&amp;gt;/etc/pyinfra-webui/pyinfra-webui.env&amp;lt;/code&amp;gt;&lt;br /&gt;
* riavviare il servizio&lt;br /&gt;
&lt;br /&gt;
Comandi utili:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo systemctl restart pyinfra-webui.service&lt;br /&gt;
sudo systemctl status pyinfra-webui.service&lt;br /&gt;
journalctl -u pyinfra-webui.service -n 100 --no-pager&lt;br /&gt;
curl -sS http://127.0.0.1:18180/api/health&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Comportamento del provisioning ==&lt;br /&gt;
La UI raccoglie i parametri e avvia un job che:&lt;br /&gt;
* verifica che il VMID non esista gia&lt;br /&gt;
* crea la VM cloud-init con &amp;lt;code&amp;gt;qm create&amp;lt;/code&amp;gt; e &amp;lt;code&amp;gt;qm set&amp;lt;/code&amp;gt;&lt;br /&gt;
* configura &amp;lt;code&amp;gt;net0&amp;lt;/code&amp;gt; con bridge ed eventuale tag VLAN&lt;br /&gt;
* imposta &amp;lt;code&amp;gt;ipconfig0&amp;lt;/code&amp;gt; in DHCP o statico&lt;br /&gt;
* genera uno snippet cloud-init per creare l&#039;utente richiesto con sudo&lt;br /&gt;
* opzionalmente avvia la VM&lt;br /&gt;
* se la VM viene avviata subito, aspetta guest agent e &amp;lt;code&amp;gt;cloud-init status --wait&amp;lt;/code&amp;gt;&lt;br /&gt;
* rimuove lo snippet cloud-init dopo il primo boot&lt;br /&gt;
&lt;br /&gt;
== Sicurezza ==&lt;br /&gt;
Regole da mantenere:&lt;br /&gt;
* non scrivere in wiki password, token, cookie o dump di sessione&lt;br /&gt;
* non copiare in Git segreti o backend interni inutili&lt;br /&gt;
* tenere i segreti in &amp;lt;code&amp;gt;/etc/pyinfra-webui/pyinfra-webui.env&amp;lt;/code&amp;gt;&lt;br /&gt;
* usare HTTPS sul reverse proxy pubblico&lt;br /&gt;
* mantenere il controllo umano: il job parte solo a richiesta dell&#039;utente autenticato&lt;br /&gt;
&lt;br /&gt;
Nota importante:&lt;br /&gt;
* se &amp;lt;code&amp;gt;start_after_create=false&amp;lt;/code&amp;gt;, lo snippet cloud-init non puo essere rimosso subito e va pulito dopo il primo avvio completato&lt;br /&gt;
&lt;br /&gt;
== API utili ==&lt;br /&gt;
Endpoint interni esposti dall&#039;app:&lt;br /&gt;
* &amp;lt;code&amp;gt;/api/health&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;/api/jobs&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;/api/jobs/create-vm&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;/api/network/bridges&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;/api/network/vlans&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;/api/network/validate&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;/api/proxmox/free-vmid&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Verifiche rapide ==&lt;br /&gt;
Verifiche applicative:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -I https://bot.gazzi.net/pyinfra/&lt;br /&gt;
curl -sS http://127.0.0.1:18180/api/health&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verifiche systemd:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
systemctl status pyinfra-webui.service&lt;br /&gt;
journalctl -u pyinfra-webui.service -n 50 --no-pager&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verifiche Proxmox lato UI:&lt;br /&gt;
* test lookup bridge&lt;br /&gt;
* test lookup VLAN&lt;br /&gt;
* test validazione &amp;lt;code&amp;gt;bridge + vlan&amp;lt;/code&amp;gt;&lt;br /&gt;
* test ricerca VMID libero&lt;br /&gt;
&lt;br /&gt;
== Problemi noti ==&lt;br /&gt;
* il &amp;lt;code&amp;gt;guid&amp;lt;/code&amp;gt; di WordPress o altri metadati editoriali non hanno relazione con questa automazione&lt;br /&gt;
* il cleanup automatico dello snippet cloud-init dipende dal primo boot e dalla disponibilita del guest agent&lt;br /&gt;
* la VLAN viene validata in base a cio che il nodo Proxmox espone al momento del controllo&lt;br /&gt;
&lt;br /&gt;
== Riferimenti collegati ==&lt;br /&gt;
* [[Tips Automation]]&lt;br /&gt;
* [[Esempi Pyinfra]]&lt;br /&gt;
* [[Confronto Ansible vs Pyinfra]]&lt;br /&gt;
* [[Installazione Pyinfra su Fedora]]&lt;br /&gt;
* [https://www.gazzi.net/pyinfra-proxmox-webui/ Scheda pubblica su WordPress]&lt;br /&gt;
* [https://www.gazzi.net/2026/03/29/pyinfra-proxmox-webui-proxmox-ldap-bot-gazzi-net/ Articolo di dettaglio]&lt;br /&gt;
&lt;br /&gt;
== Repository ==&lt;br /&gt;
Sorgente versionata:&lt;br /&gt;
* &amp;lt;code&amp;gt;scripts-repo/pyinfra-proxmox-webui/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Questa pagina wiki va tenuta come documentazione operativa ad alto livello e runbook sintetico. Segreti e dettagli sensibili restano fuori dalla wiki.&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
</feed>