<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="it">
	<id>https://wiki.gazzi.net/index.php?action=history&amp;feed=atom&amp;title=Pyinfra%2FImplementazione_deploy_VM_linea_per_linea</id>
	<title>Pyinfra/Implementazione deploy VM linea per linea - Cronologia</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.gazzi.net/index.php?action=history&amp;feed=atom&amp;title=Pyinfra%2FImplementazione_deploy_VM_linea_per_linea"/>
	<link rel="alternate" type="text/html" href="https://wiki.gazzi.net/index.php?title=Pyinfra/Implementazione_deploy_VM_linea_per_linea&amp;action=history"/>
	<updated>2026-05-17T03:26:23Z</updated>
	<subtitle>Cronologia della pagina su questo sito</subtitle>
	<generator>MediaWiki 1.40.1</generator>
	<entry>
		<id>https://wiki.gazzi.net/index.php?title=Pyinfra/Implementazione_deploy_VM_linea_per_linea&amp;diff=638&amp;oldid=prev</id>
		<title>Maintenance script: Aggiunta documentazione tecnica dettagliata del deploy VM</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&amp;oldid=prev"/>
		<updated>2026-03-29T18:57:47Z</updated>

		<summary type="html">&lt;p&gt;Aggiunta documentazione tecnica dettagliata del deploy VM&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;it&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Versione meno recente&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Versione delle 18:57, 29 mar 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l202&quot;&gt;Riga 202:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Riga 202:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Funzioni principali:&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Funzioni principali:&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;syncNetworkFields()&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;syncNetworkFields()&amp;lt;/code&amp;gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;: &lt;/ins&gt;disabilita i campi IP/gateway/CIDR quando la rete e DHCP&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;  &lt;/del&gt;disabilita i campi IP/gateway/CIDR quando la rete e DHCP&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;renderJobs()&amp;lt;/code&amp;gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;: &lt;/ins&gt;mostra i job recenti con stato, timing e output&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;renderJobs()&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;loadVlans()&amp;lt;/code&amp;gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;: &lt;/ins&gt;chiama &amp;lt;code&amp;gt;/api/network/vlans&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;  &lt;/del&gt;mostra i job recenti con stato, timing e output&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;loadBridges()&amp;lt;/code&amp;gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;: &lt;/ins&gt;chiama &amp;lt;code&amp;gt;/api/network/bridges&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;loadVlans()&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;loadFreeVmid()&amp;lt;/code&amp;gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;: &lt;/ins&gt;chiama &amp;lt;code&amp;gt;/api/proxmox/free-vmid?start=100&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;  &lt;/del&gt;chiama &amp;lt;code&amp;gt;/api/network/vlans&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;validateNetwork()&amp;lt;/code&amp;gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;: &lt;/ins&gt;chiama &amp;lt;code&amp;gt;/api/network/validate&amp;lt;/code&amp;gt; prima del submit&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;loadBridges()&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;loadJobs()&amp;lt;/code&amp;gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;: &lt;/ins&gt;ricarica i job recenti&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;  &lt;/del&gt;chiama &amp;lt;code&amp;gt;/api/network/bridges&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* submit handler della form&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;: &lt;/ins&gt;costruisce il payload JSON e lo invia a &amp;lt;code&amp;gt;/api/jobs/create-vm&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;loadFreeVmid()&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;  &lt;/del&gt;chiama &amp;lt;code&amp;gt;/api/proxmox/free-vmid?start=100&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;validateNetwork()&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;  &lt;/del&gt;chiama &amp;lt;code&amp;gt;/api/network/validate&amp;lt;/code&amp;gt; prima del submit&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;loadJobs()&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;  &lt;/del&gt;ricarica i job recenti&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* submit handler della form&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;  &lt;/del&gt;costruisce il payload JSON e lo invia a &amp;lt;code&amp;gt;/api/jobs/create-vm&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Traduzione pratica:&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Traduzione pratica:&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l281&quot;&gt;Riga 281:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Riga 273:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Funzioni:&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Funzioni:&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;wait_for_guest_agent()&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;wait_for_guest_agent()&amp;lt;/code&amp;gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;: &lt;/ins&gt;esegue poll su &amp;lt;code&amp;gt;qm agent VMID ping&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;  &lt;/del&gt;esegue poll su &amp;lt;code&amp;gt;qm agent VMID ping&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;guest_exec()&amp;lt;/code&amp;gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;: &lt;/ins&gt;esegue un comando dentro la VM via guest agent e aspetta l&#039;exit status&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;guest_exec()&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;  &lt;/del&gt;esegue un comando dentro la VM via guest agent e aspetta l&#039;exit status&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Uso concreto nel progetto:&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Uso concreto nel progetto:&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&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&amp;oldid=prev</id>
		<title>Maintenance script: Aggiunta documentazione tecnica dettagliata del deploy VM</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&amp;oldid=prev"/>
		<updated>2026-03-29T18:55:31Z</updated>

		<summary type="html">&lt;p&gt;Aggiunta documentazione tecnica dettagliata del deploy VM&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;it&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Versione meno recente&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Versione delle 18:55, 29 mar 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l29&quot;&gt;Riga 29:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Riga 29:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Lettura riga per riga ===&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Lettura riga per riga ===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;syntaxhighlight lang=&quot;python&quot;&lt;/del&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;pre&lt;/ins&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;#!/usr/bin/env python3&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;#!/usr/bin/env python3&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;import os&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;import os&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l36&quot;&gt;Riga 36:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Riga 36:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;from pyinfra.operations import server&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;from pyinfra.operations import server&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;syntaxhighlight&lt;/del&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;pre&lt;/ins&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Spiegazione:&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Spiegazione:&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l43&quot;&gt;Riga 43:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Riga 43:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* import dell&amp;#039;operation &amp;lt;code&amp;gt;server.shell&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* import dell&amp;#039;operation &amp;lt;code&amp;gt;server.shell&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;syntaxhighlight lang=&quot;python&quot;&lt;/del&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;pre&lt;/ins&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;BASE_DIR = Path(__file__).resolve().parents[1]&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;BASE_DIR = Path(__file__).resolve().parents[1]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&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;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&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;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;helper = BASE_DIR / &amp;quot;lib&amp;quot; / &amp;quot;proxmox_ssh.py&amp;quot;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;helper = BASE_DIR / &amp;quot;lib&amp;quot; / &amp;quot;proxmox_ssh.py&amp;quot;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;cmd = f&amp;quot;{shlex.quote(python_bin)} {shlex.quote(str(helper))}&amp;quot;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;cmd = f&amp;quot;{shlex.quote(python_bin)} {shlex.quote(str(helper))}&amp;quot;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;syntaxhighlight&lt;/del&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;pre&lt;/ins&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Spiegazione:&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Spiegazione:&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l56&quot;&gt;Riga 56:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Riga 56:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* costruisce un comando shell sicuro con quoting&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* costruisce un comando shell sicuro con quoting&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;syntaxhighlight lang=&quot;python&quot;&lt;/del&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;pre&lt;/ins&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;server.shell(&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;server.shell(&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     name=f&amp;quot;Create Proxmox VM {os.environ.get(&amp;#039;VM_NAME&amp;#039;, &amp;#039;&amp;#039;)}&amp;quot;,&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     name=f&amp;quot;Create Proxmox VM {os.environ.get(&amp;#039;VM_NAME&amp;#039;, &amp;#039;&amp;#039;)}&amp;quot;,&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     commands=[cmd],&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;     commands=[cmd],&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;)&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;syntaxhighlight&lt;/del&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;pre&lt;/ins&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Spiegazione:&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Spiegazione:&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l173&quot;&gt;Riga 173:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Riga 173:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Comando usato:&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Comando usato:&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;syntaxhighlight lang=&quot;bash&quot;&lt;/del&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;pre&lt;/ins&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;pyinfra @local deploys/create_vm.py -y&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;pyinfra @local deploys/create_vm.py -y&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;syntaxhighlight&lt;/del&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;pre&lt;/ins&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Blocco 7: interfaccia HTML ===&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Blocco 7: interfaccia HTML ===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l330&quot;&gt;Riga 330:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Riga 330:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Prima di creare la VM, il file esegue:&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Prima di creare la VM, il file esegue:&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;syntaxhighlight lang=&quot;bash&quot;&lt;/del&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;pre&lt;/ins&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;qm config &amp;lt;VMID&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;qm config &amp;lt;VMID&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;syntaxhighlight&lt;/del&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;/&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;pre&lt;/ins&gt;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Se il comando funziona, il VMID esiste gia e il job viene fermato.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Se il comando funziona, il VMID esiste gia e il job viene fermato.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&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&amp;oldid=prev</id>
		<title>Maintenance script: Aggiunta documentazione tecnica dettagliata del deploy VM</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&amp;oldid=prev"/>
		<updated>2026-03-29T18:51:30Z</updated>

		<summary type="html">&lt;p&gt;Aggiunta documentazione tecnica dettagliata del deploy VM&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Nuova pagina&lt;/b&gt;&lt;/p&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&amp;#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&amp;#039;implementazione&lt;br /&gt;
&lt;br /&gt;
Non vengono riportati segreti runtime.&lt;br /&gt;
&lt;br /&gt;
== Vista d&amp;#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&amp;#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&amp;#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&amp;#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&amp;#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(&amp;#039;VM_NAME&amp;#039;, &amp;#039;&amp;#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&amp;#039;operation include il nome VM per leggibilita&lt;br /&gt;
* l&amp;#039;operation esegue localmente il comando helper&lt;br /&gt;
&lt;br /&gt;
Idea pratica:&lt;br /&gt;
* pyinfra orchestra&lt;br /&gt;
* l&amp;#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&amp;#039;app sa stare dietro Nginx senza rompere i path&lt;br /&gt;
* l&amp;#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&amp;#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&amp;#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&amp;#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&amp;#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&amp;#039;app&lt;br /&gt;
* la UI ferma l&amp;#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&amp;#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&amp;#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&amp;#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&amp;#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&amp;#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&amp;#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&amp;#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&amp;#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>
</feed>