Compare commits
3 Commits
2.7.2
...
f01689a4ac
| Author | SHA1 | Date | |
|---|---|---|---|
|
f01689a4ac
|
|||
|
7afbfe0593
|
|||
|
f354595954
|
+233
-10
@@ -328,7 +328,7 @@
|
|||||||
<div class="label">Guida per Alunni</div>
|
<div class="label">Guida per Alunni</div>
|
||||||
<h1>Simulatore RAID<br>Guida Completa all'Uso</h1>
|
<h1>Simulatore RAID<br>Guida Completa all'Uso</h1>
|
||||||
<p class="subtitle">Manuale passo passo per imparare a gestire array RAID con il terminale simulato</p>
|
<p class="subtitle">Manuale passo passo per imparare a gestire array RAID con il terminale simulato</p>
|
||||||
<p class="meta">Informatica di Sistema · ITIS · Versione simulatore v2.3</p>
|
<p class="meta">Informatica di Sistema · ITIS · Versione simulatore v2.8</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- INDICE -->
|
<!-- INDICE -->
|
||||||
@@ -346,8 +346,9 @@
|
|||||||
<li><a href="#s9">Stati dei dischi e del volume</a></li>
|
<li><a href="#s9">Stati dei dischi e del volume</a></li>
|
||||||
<li><a href="#s10">La documentazione integrata</a></li>
|
<li><a href="#s10">La documentazione integrata</a></li>
|
||||||
<li><a href="#s11">Il report finale</a></li>
|
<li><a href="#s11">Il report finale</a></li>
|
||||||
<li><a href="#s12">Errori comuni da evitare</a></li>
|
<li><a href="#s12">Errori non fatali: CRC, Overheat, Slow</a></li>
|
||||||
<li><a href="#s13">Conclusione</a></li>
|
<li><a href="#s13">Errori comuni da evitare</a></li>
|
||||||
|
<li><a href="#s14">Conclusione</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -756,7 +757,20 @@
|
|||||||
<div class="cmd-card-head"><code>scenario list</code><span class="status-badge st-info">Scenari</span></div>
|
<div class="cmd-card-head"><code>scenario list</code><span class="status-badge st-info">Scenari</span></div>
|
||||||
<div class="cmd-card-body">
|
<div class="cmd-card-body">
|
||||||
<div class="cmd-row"><div class="cmd-label">Cosa fa</div><div>Mostra l'elenco di tutti gli scenari disponibili nel simulatore, con i loro nomi identificativi.</div></div>
|
<div class="cmd-row"><div class="cmd-label">Cosa fa</div><div>Mostra l'elenco di tutti gli scenari disponibili nel simulatore, con i loro nomi identificativi.</div></div>
|
||||||
<div class="cmd-row"><div class="cmd-label">Risultato atteso</div><div>Elenco di nomi come: <code>raid0_fail</code>, <code>raid1_onefail</code>, <code>raid5_1fail</code>, <code>raid5_2fail</code>, <code>raid6_2fail</code>, <code>raid10_pairfail</code>, <code>rebuild_interrupted</code>, <code>wrong_size_spare</code>, <code>crc_errors</code>, <code>overheat</code>.</div></div>
|
<div class="cmd-row"><div class="cmd-label">Risultato atteso</div><div>Elenco degli scenari disponibili:
|
||||||
|
<ul>
|
||||||
|
<li><code>raid0_fail</code> — RAID0: un guasto → FAILED, conclude restore da backup</li>
|
||||||
|
<li><code>raid1_onefail</code> — RAID1: sostituzione disco + rebuild</li>
|
||||||
|
<li><code>raid5_1fail</code> — RAID5: recovery con 1 disco guasto</li>
|
||||||
|
<li><code>raid5_2fail</code> — RAID5: 2 guasti → FAILED, conclude restore da backup</li>
|
||||||
|
<li><code>raid6_2fail</code> — RAID6: recovery con 2 dischi guasti (uno alla volta)</li>
|
||||||
|
<li><code>raid10_pairfail</code> — RAID10: mirror pair perso → FAILED</li>
|
||||||
|
<li><code>rebuild_interrupted</code> — rebuild interrotto da powerfail, poi riavviato</li>
|
||||||
|
<li><code>wrong_size_spare</code> — spare troppo piccolo (errore) poi corretto</li>
|
||||||
|
<li><code>crc_errors</code> — disco in stato CRC: diagnosi + replace cable o conclude</li>
|
||||||
|
<li><code>overheat</code> — disco in OVERHEAT: diagnosi + cool down o conclude</li>
|
||||||
|
</ul>
|
||||||
|
</div></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1031,12 +1045,47 @@ SMART overall-health: FAILED!
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="cmd-card">
|
<div class="cmd-card">
|
||||||
<div class="cmd-card-head"><code>conclude <testo></code><span class="status-badge st-info">Scenari</span></div>
|
<div class="cmd-card-head"><code>conclude <testo></code><span class="status-badge st-info">Scenari</span></div>
|
||||||
<div class="cmd-card-body">
|
<div class="cmd-card-body">
|
||||||
<div class="cmd-row"><div class="cmd-label">Cosa fa</div><div>Registra una conclusione testuale per lo scenario corrente. Utilizzato negli scenari dove i dati sono persi (RAID0 fail, RAID5 2fail, RAID10 pairfail) e l'azione corretta è diagnosticare e concludere che serve un restore da backup.</div></div>
|
<div class="cmd-row"><div class="cmd-label">Cosa fa</div><div>Registra una conclusione testuale per lo scenario corrente. Utilizzato negli scenari dove i dati sono persi (RAID0 fail, RAID5 2fail, RAID10 pairfail) e l'azione corretta è diagnosticare e concludere che serve un restore da backup.</div></div>
|
||||||
<div class="cmd-row"><div class="cmd-label">Esempio</div><div><code>conclude restore da backup</code></div></div>
|
<div class="cmd-row"><div class="cmd-label">Esempio</div><div><code>conclude restore da backup</code></div></div>
|
||||||
<div class="cmd-row"><div class="cmd-label">Quando usarlo</div><div>Quando lo scenario richiede di riconoscere che l'array è FAILED e non recuperabile tramite RAID. Anche per scenari di errori non fatali come crc_errors e overheat.</div></div>
|
<div class="cmd-row"><div class="cmd-label">Quando usarlo</div><div>Quando lo scenario richiede di riconoscere che l'array è FAILED e non recuperabile tramite RAID. Anche per scenari di errori non fatali come crc_errors e overheat, come alternativa ai nuovi comandi hardware simulati.</div></div>
|
||||||
|
<div class="cmd-row"><div class="cmd-label">Conclusioni frequenti</div><div>
|
||||||
|
<ul>
|
||||||
|
<li><code>conclude restore da backup</code> — array FAILED, dati persi</li>
|
||||||
|
<li><code>conclude sostituire cavo SATA</code> — disco in stato CRC, causa probabile cavo</li>
|
||||||
|
<li><code>conclude migliorare raffreddamento</code> — disco in OVERHEAT</li>
|
||||||
|
<li><code>conclude monitorare e pianificare sostituzione</code> — disco SLOW</li>
|
||||||
|
</ul>
|
||||||
|
</div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cmd-card">
|
||||||
|
<div class="cmd-card-head"><code>replace cable /dev/sdX</code><span class="status-badge st-warn">Simulazione</span></div>
|
||||||
|
<div class="cmd-card-body">
|
||||||
|
<div class="cmd-row"><div class="cmd-label">Cosa fa</div><div>Simula la sostituzione fisica del cavo SATA collegato al disco specificato. Azzera il contatore UDMA_CRC_Error_Count, resetta il link SATA e riporta il disco da stato CRC a OK.</div></div>
|
||||||
|
<div class="cmd-row"><div class="cmd-label">Quando usarlo</div><div>Dopo aver diagnosticato un disco in stato CRC con <code>dmesg | tail</code> e <code>smartctl</code>, se Reallocated_Sector_Ct = 0 (problema di cavo, non di disco). Nello scenario <code>crc_errors</code>.</div></div>
|
||||||
|
<div class="cmd-row"><div class="cmd-label">Esempio</div><div><code>replace cable /dev/sdd</code></div></div>
|
||||||
|
<div class="cmd-row"><div class="cmd-label">Risultato atteso</div><div>Il disco torna a stato OK. Nel terminale viene mostrato: cavo rimosso e installato, link SATA reset, CRC counter azzerato, suggerimento di monitorare nelle ore successive.</div></div>
|
||||||
|
<div class="cmd-row"><div class="cmd-label">Errori possibili</div><div>Se il disco non è in stato CRC: "replace cable: non è in stato CRC". Questo comando funziona solo su dischi con errori CRC.</div></div>
|
||||||
|
<div class="cmd-row"><div class="cmd-label">Punteggio</div><div>Nello scenario <code>crc_errors</code>: +8 punti per aver eseguito la sostituzione corretta del cavo.</div></div>
|
||||||
|
<div class="cmd-row"><div class="cmd-label">Nota importante</div><div>Se dopo la sostituzione del cavo il contatore CRC ricominciasse a crescere, il problema sarebbe nel disco o nel controller, non nel cavo. In quel caso si procederebbe con la sostituzione del disco.</div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cmd-card">
|
||||||
|
<div class="cmd-card-head"><code>cool down /dev/sdX</code><span class="status-badge st-warn">Simulazione</span></div>
|
||||||
|
<div class="cmd-card-body">
|
||||||
|
<div class="cmd-row"><div class="cmd-label">Cosa fa</div><div>Simula un intervento di raffreddamento sul disco specificato: pulizia delle ventole, miglioramento dell'airflow nel case, spaziatura dei dischi. Riporta la temperatura nella zona ottimale e il disco da OVERHEAT a OK.</div></div>
|
||||||
|
<div class="cmd-row"><div class="cmd-label">Quando usarlo</div><div>Dopo aver diagnosticato un disco in stato OVERHEAT con <code>smartctl</code> e <code>dmesg | tail</code>. Nello scenario <code>overheat</code>.</div></div>
|
||||||
|
<div class="cmd-row"><div class="cmd-label">Esempio</div><div><code>cool down /dev/sdc</code></div></div>
|
||||||
|
<div class="cmd-row"><div class="cmd-label">Risultato atteso</div><div>La temperatura scende da 58–62°C a ~38°C. Il disco torna a stato OK. Il throttling termico viene rimosso. Il terminale suggerisce comunque di pianificare la sostituzione preventiva.</div></div>
|
||||||
|
<div class="cmd-row"><div class="cmd-label">Errori possibili</div><div>Se il disco non è in stato OVERHEAT: "cool down: non è in stato OVERHEAT".</div></div>
|
||||||
|
<div class="cmd-row"><div class="cmd-label">Punteggio</div><div>Nello scenario <code>overheat</code>: +8 punti per aver eseguito l'intervento di raffreddamento corretto.</div></div>
|
||||||
|
<div class="cmd-row"><div class="cmd-label">Nota importante</div><div>Il raffreddamento risolve il sintomo immediato, ma un disco che ha lavorato a lungo a temperature critiche è comunque a rischio. Il consiglio è di pianificare la sostituzione preventiva con <code>mdadm --fail</code> + <code>--remove</code> + <code>--add</code> + <code>--rebuild</code>.</div></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1161,8 +1210,8 @@ SMART overall-health: FAILED!
|
|||||||
<tr><td><span class="status-badge st-info">SPARE</span></td><td>Disco aggiunto con --add ma non ancora usato per il rebuild. Pronto per essere utilizzato nella ricostruzione.</td><td>Avviare il rebuild con --rebuild.</td></tr>
|
<tr><td><span class="status-badge st-info">SPARE</span></td><td>Disco aggiunto con --add ma non ancora usato per il rebuild. Pronto per essere utilizzato nella ricostruzione.</td><td>Avviare il rebuild con --rebuild.</td></tr>
|
||||||
<tr><td><span class="status-badge st-warn">REBUILDING</span></td><td>Disco in fase di ricostruzione. I dati vengono riscritti progressivamente su questo disco. L'array è DEGRADED durante il rebuild.</td><td>Attendere il completamento. Non spegnere il sistema durante il rebuild.</td></tr>
|
<tr><td><span class="status-badge st-warn">REBUILDING</span></td><td>Disco in fase di ricostruzione. I dati vengono riscritti progressivamente su questo disco. L'array è DEGRADED durante il rebuild.</td><td>Attendere il completamento. Non spegnere il sistema durante il rebuild.</td></tr>
|
||||||
<tr><td><span class="status-badge st-info">SLOW</span></td><td>Disco che risponde lentamente. Degrada le prestazioni dell'intero array. Non è ancora guasto, ma è a rischio.</td><td>Monitorare con smartctl. Pianificare la sostituzione.</td></tr>
|
<tr><td><span class="status-badge st-info">SLOW</span></td><td>Disco che risponde lentamente. Degrada le prestazioni dell'intero array. Non è ancora guasto, ma è a rischio.</td><td>Monitorare con smartctl. Pianificare la sostituzione.</td></tr>
|
||||||
<tr><td><span class="status-badge st-warn">OVERHEAT</span></td><td>Temperatura critica (sopra i 55–60°C). Il disco rischia di guastarsi a breve. Il sistema segnala l'allarme termico.</td><td>Migliorare il raffreddamento. Pianificare la sostituzione preventiva. Usare conclude per registrare la diagnosi.</td></tr>
|
<tr><td><span class="status-badge st-warn">OVERHEAT</span></td><td>Temperatura critica (sopra i 55–60°C). Il disco rischia di guastarsi a breve. Il sistema segnala l'allarme termico.</td><td>Usare <code>smartctl</code> e <code>dmesg | tail</code>. Poi: <code>cool down /dev/sdX</code> per simulare l'intervento di raffreddamento, oppure <code>conclude migliorare raffreddamento</code>. Pianificare sostituzione preventiva.</td></tr>
|
||||||
<tr><td><span class="status-badge st-purple">CRC</span></td><td>Errori CRC rilevati. Spesso indica un problema di cavo SATA o di connessione, non necessariamente del disco stesso.</td><td>Usare smartctl per controllare Reallocated_Sector_Ct. Se è 0, il problema è probabilmente il cavo. Usare conclude per registrare la diagnosi.</td></tr>
|
<tr><td><span class="status-badge st-purple">CRC</span></td><td>Errori CRC rilevati. Spesso indica un problema di cavo SATA o di connessione, non necessariamente del disco stesso.</td><td>Usare <code>dmesg | tail</code> e <code>smartctl</code>. Se Reallocated = 0: usare <code>replace cable /dev/sdX</code> per simulare la sostituzione del cavo, oppure <code>conclude sostituire cavo SATA</code>.</td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h3>Stati del volume (array)</h3>
|
<h3>Stati del volume (array)</h3>
|
||||||
@@ -1231,7 +1280,175 @@ SMART overall-health: FAILED!
|
|||||||
|
|
||||||
|
|
||||||
<!-- ══════════════════════════════════════════════════════════ -->
|
<!-- ══════════════════════════════════════════════════════════ -->
|
||||||
<h2 id="s12">Errori comuni da evitare</h2>
|
<hr class="section">
|
||||||
|
|
||||||
|
<!-- ══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="s12">Errori non fatali: CRC, Overheat, Slow</h2>
|
||||||
|
|
||||||
|
<p>Non tutti i problemi di un disco richiedono la sostituzione immediata. Il simulatore distingue tre stati di allerta che segnalano un disco in difficoltà ma ancora funzionante. Capire come gestirli — e come chiuderli correttamente — è fondamentale per l'esame.</p>
|
||||||
|
|
||||||
|
<div class="callout info">
|
||||||
|
<div class="icon">💡</div>
|
||||||
|
<div>La differenza fondamentale: CRC e Overheat non sono guasti fisici immediati. FAILED lo è. Reagire allo stesso modo a tutti e tre è un errore di metodo valutato negativamente.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Griglia decisionale rapida</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>Problema</th><th>Sintomo nel simulatore</th><th>Prima azione</th><th>Decisione logica</th><th>Chiusura corretta</th></tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong style="color:#5521b5">CRC errors</strong></td>
|
||||||
|
<td>Stato <code>.crc</code>, CRC counter > 0</td>
|
||||||
|
<td><code>dmesg | tail</code><br><code>smartctl -a /dev/sdX</code></td>
|
||||||
|
<td>Realloc = 0 → cavo<br>Realloc > 50 → disco</td>
|
||||||
|
<td><code>replace cable /dev/sdX</code><br><em>oppure</em> <code>conclude sostituire cavo SATA</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong style="color:#92400e">Overheat</strong></td>
|
||||||
|
<td>Stato <code>.overheat</code>, temp > 55°C</td>
|
||||||
|
<td><code>smartctl -a /dev/sdX</code><br><code>dmesg | tail</code></td>
|
||||||
|
<td>Non è guasto ora, ma è imminente → non aspettare</td>
|
||||||
|
<td><code>cool down /dev/sdX</code><br><em>oppure</em> <code>conclude migliorare raffreddamento</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong style="color:#1a56db">Slow disk</strong></td>
|
||||||
|
<td>Stato <code>.slow</code>, IOPS ridotti</td>
|
||||||
|
<td><code>smartctl -a /dev/sdX</code><br>verifica Current_Pending_Sector</td>
|
||||||
|
<td>Pending < 20 → monitora<br>Pending > 20 → sostituisci</td>
|
||||||
|
<td><code>conclude monitorare e pianificare sostituzione</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong style="color:#9b1c1c">FAILED (array)</strong></td>
|
||||||
|
<td>Array in stato FAILED</td>
|
||||||
|
<td><code>cat /proc/mdstat</code><br><code>mdadm --detail /dev/md0</code></td>
|
||||||
|
<td>Guasti oltre la tolleranza → nessun rebuild possibile</td>
|
||||||
|
<td><code>conclude restore da backup</code></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>CRC errors — procedura completa</h3>
|
||||||
|
<p>Gli errori CRC (Cyclic Redundancy Check) segnalano che i dati hanno subito corruzione durante il trasferimento sul cavo SATA. <strong>Non è detto che il disco sia guasto</strong>: il problema è spesso nel cavo o nel connettore.</p>
|
||||||
|
|
||||||
|
<div class="steps">
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-num">1</div>
|
||||||
|
<div class="step-content">
|
||||||
|
<strong>Leggo il log kernel</strong>
|
||||||
|
<pre><span class="prompt">raidlab(OK)$</span> dmesg | tail
|
||||||
|
<span class="warn">[04231.001] WARN: /dev/sdd: UDMA CRC errors detected</span>
|
||||||
|
[04231.100] ata4.00: exception Emask 0x0 SAct — link reset</pre>
|
||||||
|
Il kernel segnala errori di trasmissione su /dev/sdd. Non dice ancora se è il cavo o il disco.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-num">2</div>
|
||||||
|
<div class="step-content">
|
||||||
|
<strong>Analizzo i dati SMART</strong>
|
||||||
|
<pre><span class="prompt">raidlab(OK)$</span> smartctl -a /dev/sdd
|
||||||
|
SMART overall-health: <span class="ok">PASSED</span> (but CRC errors)
|
||||||
|
|
||||||
|
5 Reallocated_Sector_Ct <span class="ok">0</span> ← nessun settore danneggiato
|
||||||
|
199 UDMA_CRC_Error_Count <span class="warn">24</span> ← errori di trasmissione
|
||||||
|
194 Temperature_Celsius 35</pre>
|
||||||
|
Reallocated = 0: il disco è integro fisicamente. Il problema è nel percorso del segnale (cavo, connettore).
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-num">3</div>
|
||||||
|
<div class="step-content">
|
||||||
|
<strong>NON faccio fail/remove immediato</strong>
|
||||||
|
Il disco non è guasto. Rimuoverlo sarebbe un errore di valutazione. Si interviene sul cavo.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-num">4</div>
|
||||||
|
<div class="step-content">
|
||||||
|
<strong>Sostituisco il cavo</strong>
|
||||||
|
<pre><span class="prompt">raidlab(OK)$</span> replace cable /dev/sdd
|
||||||
|
[sostituzione cavo SATA su /dev/sdd]
|
||||||
|
UDMA_CRC_Error_Count: 24 → 0 (azzerato dal reset)
|
||||||
|
Stato disco: CRC → <span class="ok">OK</span>
|
||||||
|
→ Monitorare nelle prossime ore con smartctl</pre>
|
||||||
|
Oppure, se si vuole solo registrare la decisione: <code>conclude sostituire cavo SATA</code>.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="callout warn">
|
||||||
|
<div class="icon">⚠️</div>
|
||||||
|
<div><strong>Se CRC continua a crescere dopo il cambio cavo:</strong> a quel punto il problema è nel disco o nel controller. Si procede con la sequenza normale: <code>mdadm --fail</code> → <code>--remove</code> → <code>--add</code> → <code>--rebuild</code>.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Overheat — procedura completa</h3>
|
||||||
|
<p>Un disco surriscaldato (sopra i 55–60°C) non è ancora guasto, ma è un <strong>pre-failure</strong>: il guasto è imminente. Non bisogna aspettare.</p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr><th>Temperatura</th><th>Stato</th><th>Azione</th></tr>
|
||||||
|
<tr><td>< 45°C</td><td>Ottimale</td><td>Nessuna</td></tr>
|
||||||
|
<tr><td>45–55°C</td><td>Accettabile</td><td>Monitorare</td></tr>
|
||||||
|
<tr><td>55–60°C</td><td>Attenzione</td><td>Migliorare ventilazione</td></tr>
|
||||||
|
<tr><td>> 60°C</td><td>Critico</td><td>Intervenire subito, pianificare sostituzione</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="steps">
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-num">1</div>
|
||||||
|
<div class="step-content">
|
||||||
|
<strong>Analizzo SMART e dmesg</strong>
|
||||||
|
<pre><span class="prompt">raidlab(OK)$</span> smartctl -a /dev/sdc
|
||||||
|
SMART overall-health: <span class="warn">PASSED (but temperature high)</span>
|
||||||
|
|
||||||
|
194 Temperature_Celsius <span class="warn">61</span> ← critico: >60°C
|
||||||
|
5 Reallocated_Sector_Ct 0
|
||||||
|
199 UDMA_CRC_Error_Count 0</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-num">2</div>
|
||||||
|
<div class="step-content">
|
||||||
|
<strong>Non avvio rebuild con disco in overheat</strong>
|
||||||
|
Un rebuild su un disco già a 61°C lo porterebbe certamente al guasto. Prima si raffredda, poi eventualmente si sostituisce.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-num">3</div>
|
||||||
|
<div class="step-content">
|
||||||
|
<strong>Intervengo sul raffreddamento</strong>
|
||||||
|
<pre><span class="prompt">raidlab(OK)$</span> cool down /dev/sdc
|
||||||
|
[intervento di raffreddamento su /dev/sdc]
|
||||||
|
Temperatura: 61°C → <span class="ok">38°C</span> (zona ottimale)
|
||||||
|
Throttling termico: rimosso
|
||||||
|
Stato disco: OVERHEAT → <span class="ok">OK</span>
|
||||||
|
→ Pianificare sostituzione preventiva</pre>
|
||||||
|
Oppure: <code>conclude migliorare raffreddamento</code>.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="callout tip">
|
||||||
|
<div class="icon">🔧</div>
|
||||||
|
<div><strong>Approccio "pro" (livello esame alto):</strong> dopo aver raffreddato, si pianifica la sostituzione preventiva prima del guasto improvviso: <code>mdadm --fail /dev/md0 /dev/sdc</code> → <code>--remove</code> → <code>--add /dev/sdf</code> → <code>--rebuild</code>. Così si fa il rebuild in condizioni controllate invece di subirlo d'emergenza.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Slow disk — gestione</h3>
|
||||||
|
<p>Un disco lento degrada le prestazioni dell'intero array (RAID è veloce quanto il suo disco più lento). Non è ancora guasto, ma può diventarlo.</p>
|
||||||
|
<ul>
|
||||||
|
<li>Verificare con <code>smartctl -a /dev/sdX</code> il valore <strong>Current_Pending_Sector</strong>.</li>
|
||||||
|
<li>Pending < 20: monitorare regolarmente. Concludere con <code>conclude monitorare e pianificare sostituzione</code>.</li>
|
||||||
|
<li>Pending > 20: il rischio di FAILED è elevato. Procedere con sostituzione preventiva.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Principio fondamentale — ogni prova deve concludersi</h3>
|
||||||
|
<p>Un buon amministratore di sistema non si ferma alla diagnosi. Ogni sessione deve avere un risultato verificabile e documentato. Nel simulatore, il punteggio non è completo senza una chiusura esplicita:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>CRC</strong> → cavo sostituito (e CRC stabile) <em>oppure</em> conclusione registrata</li>
|
||||||
|
<li><strong>Overheat</strong> → raffreddamento applicato, sostituzione pianificata <em>oppure</em> conclusione registrata</li>
|
||||||
|
<li><strong>Slow</strong> → monitoraggio documentato <em>oppure</em> rebuild preventivo completato</li>
|
||||||
|
<li><strong>FAILED</strong> → rebuild completato e array OK <em>oppure</em> restore da backup documentato</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<hr class="section">
|
||||||
|
|
||||||
|
<h2 id="s13">Errori comuni da evitare</h2>
|
||||||
|
|
||||||
<h3>1. Confondere RAID 1 con RAID 10</h3>
|
<h3>1. Confondere RAID 1 con RAID 10</h3>
|
||||||
<p>RAID 1 fa mirroring su tutti i dischi: con 4 dischi da 1 TB la capacità è 1 TB e la tolleranza è 3 guasti. RAID 10 divide i dischi in coppie mirror e le mette in stripe: con 4 dischi da 1 TB la capacità è 2 TB ma si tollera 1 solo guasto per coppia. Non sono la stessa cosa. In RAID 10, se si guastano entrambi i dischi della stessa coppia, l'array è FAILED anche se tutti gli altri dischi funzionano perfettamente.</p>
|
<p>RAID 1 fa mirroring su tutti i dischi: con 4 dischi da 1 TB la capacità è 1 TB e la tolleranza è 3 guasti. RAID 10 divide i dischi in coppie mirror e le mette in stripe: con 4 dischi da 1 TB la capacità è 2 TB ma si tollera 1 solo guasto per coppia. Non sono la stessa cosa. In RAID 10, se si guastano entrambi i dischi della stessa coppia, l'array è FAILED anche se tutti gli altri dischi funzionano perfettamente.</p>
|
||||||
@@ -1257,9 +1474,15 @@ SMART overall-health: FAILED!
|
|||||||
<h3>8. Aggiungere uno spare troppo piccolo</h3>
|
<h3>8. Aggiungere uno spare troppo piccolo</h3>
|
||||||
<p>Se si aggiunge un disco con dimensione inferiore a quella dei dischi dell'array, il rebuild fallirà con l'errore "spare too small". Prima di aggiungere un disco con --add, verificare sempre la dimensione con <code>fdisk -l</code> o con il parametro --size nel comando --add.</p>
|
<p>Se si aggiunge un disco con dimensione inferiore a quella dei dischi dell'array, il rebuild fallirà con l'errore "spare too small". Prima di aggiungere un disco con --add, verificare sempre la dimensione con <code>fdisk -l</code> o con il parametro --size nel comando --add.</p>
|
||||||
|
|
||||||
|
<h3>9. Usare replace cable o cool down sul disco sbagliato</h3>
|
||||||
|
<p>Il comando <code>replace cable /dev/sdX</code> funziona <em>solo</em> su dischi in stato CRC. Se il disco è in stato FAILED, OVERHEAT o OK, il comando restituisce un errore. Analogamente, <code>cool down /dev/sdX</code> funziona solo su dischi in stato OVERHEAT. Usare questi comandi sul disco sbagliato non produce effetti e il terminale avvisa con un messaggio esplicito. Controllare sempre lo stato con <code>mdadm --detail /dev/md0</code> prima di applicare questi interventi.</p>
|
||||||
|
|
||||||
|
<h3>10. Chiudere uno scenario senza concludere</h3>
|
||||||
|
<p>Gli scenari <code>crc_errors</code> e <code>overheat</code> richiedono due checkpoint per essere completati: la diagnosi (dmesg + smartctl) e la chiusura (replace cable / cool down <em>oppure</em> conclude). Se si esegue solo la diagnosi e ci si ferma, il punteggio rimane parziale e lo scenario non viene marcato come risolto. Ogni prova deve avere una fine esplicita e verificabile.</p>
|
||||||
|
|
||||||
|
|
||||||
<!-- ══════════════════════════════════════════════════════════ -->
|
<!-- ══════════════════════════════════════════════════════════ -->
|
||||||
<h2 id="s13">Conclusione</h2>
|
<h2 id="s14">Conclusione</h2>
|
||||||
|
|
||||||
<p>Il simulatore RAID è uno strumento completo per imparare a gestire array RAID in modo pratico e sicuro. Attraverso l'uso del terminale simulato si acquisisce familiarità con i comandi reali di Linux e si sviluppa un metodo di lavoro ordinato e metodico.</p>
|
<p>Il simulatore RAID è uno strumento completo per imparare a gestire array RAID in modo pratico e sicuro. Attraverso l'uso del terminale simulato si acquisisce familiarità con i comandi reali di Linux e si sviluppa un metodo di lavoro ordinato e metodico.</p>
|
||||||
|
|
||||||
@@ -1278,7 +1501,7 @@ SMART overall-health: FAILED!
|
|||||||
<div>Consiglio finale: non imparare solo i comandi a memoria. Capire il <em>perché</em> di ogni passaggio è molto più utile. Perché si legge prima mdstat? Perché si rimuove il disco prima di aggiungerne uno nuovo? Ragionare sul metodo, non solo eseguire meccanicamente.</div>
|
<div>Consiglio finale: non imparare solo i comandi a memoria. Capire il <em>perché</em> di ogni passaggio è molto più utile. Perché si legge prima mdstat? Perché si rimuove il disco prima di aggiungerne uno nuovo? Ragionare sul metodo, non solo eseguire meccanicamente.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p style="text-align:center;margin-top:40px;color:var(--muted);font-size:13px">— Fine della guida — Simulatore RAID v2.3 · Informatica di Sistema</p>
|
<p style="text-align:center;margin-top:40px;color:var(--muted);font-size:13px">— Fine della guida — Simulatore RAID v2.8 · Informatica di Sistema</p>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+455
-112
@@ -106,11 +106,10 @@
|
|||||||
label{display:flex;flex-direction:column;gap:6px;font-size:12px;color:var(--muted)}
|
label{display:flex;flex-direction:column;gap:6px;font-size:12px;color:var(--muted)}
|
||||||
select,input[type="number"],input[type="text"]{
|
select,input[type="number"],input[type="text"]{
|
||||||
height:40px;border-radius:12px;border:1px solid rgba(255,255,255,.14);
|
height:40px;border-radius:12px;border:1px solid rgba(255,255,255,.14);
|
||||||
background:#0d1220;color:rgba(255,255,255,.92);
|
background:rgba(10,14,28,.55);color:rgba(255,255,255,.92);
|
||||||
padding:0 12px;outline:none;box-shadow:inset 0 0 0 1px rgba(255,255,255,.04);
|
padding:0 12px;outline:none;box-shadow:inset 0 0 0 1px rgba(255,255,255,.04);
|
||||||
font-family:var(--sans)
|
font-family:var(--sans)
|
||||||
}
|
}
|
||||||
select option{background:#0d1220;color:rgba(255,255,255,.92);}
|
|
||||||
select:focus,input:focus{border-color:rgba(167,139,250,.55);box-shadow:0 0 0 4px rgba(167,139,250,.15)}
|
select:focus,input:focus{border-color:rgba(167,139,250,.55);box-shadow:0 0 0 4px rgba(167,139,250,.15)}
|
||||||
.controls{display:grid;grid-template-columns:1.1fr .7fr .7fr;gap:10px}
|
.controls{display:grid;grid-template-columns:1.1fr .7fr .7fr;gap:10px}
|
||||||
@media (max-width: 720px){.controls{grid-template-columns:1fr}}
|
@media (max-width: 720px){.controls{grid-template-columns:1fr}}
|
||||||
@@ -959,43 +958,59 @@
|
|||||||
display:flex;align-items:center;gap:10px;flex-wrap:wrap;
|
display:flex;align-items:center;gap:10px;flex-wrap:wrap;
|
||||||
background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.07);
|
background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.07);
|
||||||
border-radius:10px;padding:8px 12px;
|
border-radius:10px;padding:8px 12px;
|
||||||
|
color:var(--text);
|
||||||
}
|
}
|
||||||
.tf-fault-dev{
|
.tf-fault-dev{
|
||||||
font-family:var(--mono);font-size:13px;font-weight:700;color:var(--text);
|
font-family:var(--mono);font-size:13px;font-weight:700;color:var(--text);
|
||||||
min-width:52px;flex-shrink:0;
|
min-width:52px;flex-shrink:0;
|
||||||
}
|
}
|
||||||
.tf-fault-select{
|
/* ── Custom dropdown (sostituisce <select> nativo) ── */
|
||||||
background:#111827;border:1px solid rgba(255,255,255,.18);
|
.tf-fault-select{ display:none; } /* nascosto, usato solo per leggere il valore */
|
||||||
border-radius:8px;padding:6px 28px 6px 10px;color:rgba(255,255,255,.92);font-size:12.5px;
|
|
||||||
font-family:var(--sans);outline:none;cursor:pointer;
|
|
||||||
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='rgba(255,255,255,.55)' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
|
|
||||||
background-repeat:no-repeat;background-position:right 9px center;appearance:none;
|
|
||||||
transition:border-color .2s;
|
|
||||||
}
|
|
||||||
.tf-fault-select option{background:#111827;color:rgba(255,255,255,.92);}
|
|
||||||
.tf-fault-select:focus{border-color:rgba(167,139,250,.6);}
|
|
||||||
.tf-fault-select.sel-FAILED{border-color:rgba(251,113,133,.6);color:var(--bad);}
|
|
||||||
.tf-fault-select.sel-FAILED option{color:rgba(255,255,255,.92);}
|
|
||||||
.tf-fault-select.sel-CRC{border-color:rgba(251,191,36,.6);color:var(--warn);}
|
|
||||||
.tf-fault-select.sel-CRC option{color:rgba(255,255,255,.92);}
|
|
||||||
.tf-fault-select.sel-OVERHEAT{border-color:rgba(251,146,60,.6);color:#fb923c;}
|
|
||||||
.tf-fault-select.sel-OVERHEAT option{color:rgba(255,255,255,.92);}
|
|
||||||
.tf-fault-select.sel-SLOW{border-color:rgba(96,165,250,.55);color:var(--info);}
|
|
||||||
.tf-fault-select.sel-SLOW option{color:rgba(255,255,255,.92);}
|
|
||||||
.tf-fault-desc{font-size:11.5px;color:var(--muted2);flex:1;line-height:1.35;}
|
|
||||||
|
|
||||||
/* Rebuild didactic banner */
|
.cdd-wrap{
|
||||||
.rebuild-banner{
|
position:relative; display:inline-block; min-width:220px; flex-shrink:0;
|
||||||
margin-top:10px;padding:12px 14px;border-radius:16px;
|
|
||||||
border:1px solid rgba(251,191,36,.40);
|
|
||||||
background:linear-gradient(135deg, rgba(251,191,36,.10), rgba(251,146,60,.07));
|
|
||||||
display:flex;align-items:flex-start;gap:12px;
|
|
||||||
animation: rb-pulse 2s ease-in-out infinite;
|
|
||||||
}
|
}
|
||||||
@keyframes rb-pulse{0%,100%{border-color:rgba(251,191,36,.35)}50%{border-color:rgba(251,191,36,.70)}}
|
.cdd-btn{
|
||||||
.rb-icon{font-size:20px;flex-shrink:0;line-height:1}
|
display:flex; align-items:center; justify-content:space-between; gap:8px;
|
||||||
.rb-body{font-size:12.5px;color:rgba(255,255,255,.88);line-height:1.6}
|
background:rgba(255,255,255,.07); border:1px solid rgba(255,255,255,.18);
|
||||||
.rb-body b{color:#FBBF24}
|
border-radius:8px; padding:7px 10px 7px 12px;
|
||||||
|
color:rgba(255,255,255,.92); font-size:12.5px; font-family:var(--sans);
|
||||||
|
cursor:pointer; user-select:none; white-space:nowrap;
|
||||||
|
transition:border-color .2s, background .2s;
|
||||||
|
}
|
||||||
|
.cdd-btn:hover{ background:rgba(255,255,255,.10); border-color:rgba(255,255,255,.28); }
|
||||||
|
.cdd-btn.open{ border-color:rgba(167,139,250,.55); background:rgba(167,139,250,.10); }
|
||||||
|
.cdd-btn .cdd-arrow{ flex-shrink:0; opacity:.55; transition:transform .15s; }
|
||||||
|
.cdd-btn.open .cdd-arrow{ transform:rotate(180deg); }
|
||||||
|
|
||||||
|
/* colori stato selezionato */
|
||||||
|
.cdd-btn.val-FAILED{ border-color:rgba(251,113,133,.5); color:var(--bad); }
|
||||||
|
.cdd-btn.val-CRC{ border-color:rgba(251,191,36,.5); color:var(--warn); }
|
||||||
|
.cdd-btn.val-OVERHEAT{ border-color:rgba(251,146,60,.5); color:#fb923c; }
|
||||||
|
.cdd-btn.val-SLOW{ border-color:rgba(96,165,250,.45); color:var(--info); }
|
||||||
|
|
||||||
|
.cdd-menu{
|
||||||
|
display:none; position:absolute; top:calc(100% + 4px); left:0; z-index:9999;
|
||||||
|
min-width:100%; background:rgba(15,18,35,.97);
|
||||||
|
border:1px solid rgba(255,255,255,.18); border-radius:10px;
|
||||||
|
box-shadow:0 12px 40px rgba(0,0,0,.6); overflow:hidden;
|
||||||
|
backdrop-filter:blur(12px);
|
||||||
|
}
|
||||||
|
.cdd-menu.open{ display:block; }
|
||||||
|
.cdd-opt{
|
||||||
|
padding:9px 14px; font-size:12.5px; font-family:var(--sans);
|
||||||
|
color:rgba(255,255,255,.88); cursor:pointer; white-space:nowrap;
|
||||||
|
transition:background .12s;
|
||||||
|
}
|
||||||
|
.cdd-opt:hover{ background:rgba(167,139,250,.18); }
|
||||||
|
.cdd-opt.selected{ background:rgba(167,139,250,.12); color:#fff; font-weight:700; }
|
||||||
|
.cdd-opt[data-val="FAILED"]{ color:var(--bad); }
|
||||||
|
.cdd-opt[data-val="CRC"]{ color:var(--warn); }
|
||||||
|
.cdd-opt[data-val="OVERHEAT"]{ color:#fb923c; }
|
||||||
|
.cdd-opt[data-val="SLOW"]{ color:var(--info); }
|
||||||
|
.cdd-opt[data-val=""]:hover,.cdd-opt[data-val=""].selected{ color:rgba(255,255,255,.88); }
|
||||||
|
|
||||||
|
.tf-fault-desc{font-size:11.5px;color:rgba(255,255,255,.75);flex:1;line-height:1.35;}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -1073,17 +1088,6 @@
|
|||||||
<div class="mini" id="miniStatus">—</div>
|
<div class="mini" id="miniStatus">—</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Banner didattico rebuild -->
|
|
||||||
<div id="rebuildBanner" style="display:none" class="rebuild-banner">
|
|
||||||
<div class="rb-icon">⚡</div>
|
|
||||||
<div class="rb-body">
|
|
||||||
<b>MODALITÀ DIDATTICA — Rebuild accelerato</b><br>
|
|
||||||
<span id="rbSpeedText">Velocità aumentata di <b id="rbFactor">—</b>×</span> ·
|
|
||||||
<span>ETA simulato: <b id="rbEtaSim">—</b></span> ·
|
|
||||||
<span>Tempo reale stimato: <b id="rbEtaReal">—</b></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row2">
|
<div class="row2">
|
||||||
<div class="miniCard">
|
<div class="miniCard">
|
||||||
<h3>📈 Telemetria (sim)</h3>
|
<h3>📈 Telemetria (sim)</h3>
|
||||||
@@ -2918,6 +2922,243 @@ IOPS con rebuild attivo: ~<span style="color:var(--warn)">550</span> (
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<hr class="doc-divider">
|
||||||
|
|
||||||
|
<!-- GRIGLIA DECISIONALE RAPIDA -->
|
||||||
|
<div class="cmd-card" style="border-color:rgba(34,211,238,.25)">
|
||||||
|
<div class="cmd-card-header" style="border-bottom-color:rgba(34,211,238,.15)">
|
||||||
|
<div class="cmd-card-title">
|
||||||
|
<span class="cmd-number" style="background:linear-gradient(135deg,rgba(34,211,238,.5),rgba(167,139,250,.3))">📋</span>
|
||||||
|
<span style="font-size:14px;font-weight:900;color:rgba(34,211,238,.95)">Griglia decisionale — problema → risoluzione</span>
|
||||||
|
</div>
|
||||||
|
<span class="cmd-badge" style="background:rgba(34,211,238,.1);border-color:rgba(34,211,238,.3);color:rgba(34,211,238,.9)">Riepilogo esame</span>
|
||||||
|
</div>
|
||||||
|
<div class="cmd-card-body">
|
||||||
|
<p class="cmd-desc" style="margin-bottom:12px">Ogni sessione di esercitazione <b>deve concludersi</b> con una risoluzione verificabile. Non basta diagnosticare: bisogna chiudere il problema.</p>
|
||||||
|
<div style="overflow-x:auto">
|
||||||
|
<table style="width:100%;border-collapse:collapse;font-size:12.5px;font-family:var(--mono)">
|
||||||
|
<thead>
|
||||||
|
<tr style="background:rgba(255,255,255,.06);color:rgba(255,255,255,.55);font-size:11px;text-transform:uppercase;letter-spacing:.06em">
|
||||||
|
<th style="padding:8px 10px;text-align:left;border-bottom:1px solid rgba(255,255,255,.1)">Problema</th>
|
||||||
|
<th style="padding:8px 10px;text-align:left;border-bottom:1px solid rgba(255,255,255,.1)">Sintomo nel sim</th>
|
||||||
|
<th style="padding:8px 10px;text-align:left;border-bottom:1px solid rgba(255,255,255,.1)">Prima azione</th>
|
||||||
|
<th style="padding:8px 10px;text-align:left;border-bottom:1px solid rgba(255,255,255,.1)">Decisione logica</th>
|
||||||
|
<th style="padding:8px 10px;text-align:left;border-bottom:1px solid rgba(255,255,255,.1)">Chiusura corretta</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr style="border-bottom:1px solid rgba(255,255,255,.06)">
|
||||||
|
<td style="padding:9px 10px;color:rgba(167,139,250,.95);font-weight:700">CRC errors</td>
|
||||||
|
<td style="padding:9px 10px;color:rgba(255,255,255,.7)">stato <code>.crc</code>, CRC counter >0</td>
|
||||||
|
<td style="padding:9px 10px;color:rgba(255,255,255,.7)"><code>dmesg | tail</code><br><code>smartctl -a /dev/sdX</code></td>
|
||||||
|
<td style="padding:9px 10px;color:rgba(255,255,255,.7)">Realloc=0? → cavo<br>Realloc>50? → disco</td>
|
||||||
|
<td style="padding:9px 10px"><code style="color:rgba(167,139,250,.9)">conclude sostituire cavo SATA</code><br><span style="color:rgba(255,255,255,.4);font-size:11px">oppure: conclude sostituire disco</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr style="border-bottom:1px solid rgba(255,255,255,.06);background:rgba(255,255,255,.02)">
|
||||||
|
<td style="padding:9px 10px;color:rgba(251,191,36,.95);font-weight:700">Overheat</td>
|
||||||
|
<td style="padding:9px 10px;color:rgba(255,255,255,.7)">stato <code>.overheat</code>, temp >55°C</td>
|
||||||
|
<td style="padding:9px 10px;color:rgba(255,255,255,.7)"><code>smartctl -a /dev/sdX</code><br><code>dmesg | tail</code></td>
|
||||||
|
<td style="padding:9px 10px;color:rgba(255,255,255,.7)">Non è guasto ora, ma imminente → non aspettare</td>
|
||||||
|
<td style="padding:9px 10px"><code style="color:rgba(251,191,36,.9)">conclude migliorare raffreddamento</code><br><span style="color:rgba(255,255,255,.4);font-size:11px">opz: conclude pianificare sostituzione</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr style="border-bottom:1px solid rgba(255,255,255,.06)">
|
||||||
|
<td style="padding:9px 10px;color:rgba(96,165,250,.95);font-weight:700">Slow disk</td>
|
||||||
|
<td style="padding:9px 10px;color:rgba(255,255,255,.7)">stato <code>.slow</code>, IOPS ridotti</td>
|
||||||
|
<td style="padding:9px 10px;color:rgba(255,255,255,.7)"><code>smartctl -a /dev/sdX</code><br>verifica Pending_Sector</td>
|
||||||
|
<td style="padding:9px 10px;color:rgba(255,255,255,.7)">Pending<20? → monitora<br>Pending>20? → sostituisci</td>
|
||||||
|
<td style="padding:9px 10px"><code style="color:rgba(96,165,250,.9)">conclude monitorare e pianificare</code><br><span style="color:rgba(255,255,255,.4);font-size:11px">oppure: sostituzione preventiva</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr style="border-bottom:1px solid rgba(255,255,255,.06);background:rgba(255,255,255,.02)">
|
||||||
|
<td style="padding:9px 10px;color:var(--err);font-weight:700">FAILED (1 disco)</td>
|
||||||
|
<td style="padding:9px 10px;color:rgba(255,255,255,.7)">stato <code>.failed</code>, array degradato</td>
|
||||||
|
<td style="padding:9px 10px;color:rgba(255,255,255,.7)"><code>cat /proc/mdstat</code><br><code>mdadm --detail /dev/md0</code></td>
|
||||||
|
<td style="padding:9px 10px;color:rgba(255,255,255,.7)">RAID1/5/6/10 tollerano → rebuild</td>
|
||||||
|
<td style="padding:9px 10px"><code style="color:var(--ok)">mdadm --remove ... --add ... --rebuild</code><br><span style="color:rgba(255,255,255,.4);font-size:11px">poi: verifica array OK</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding:9px 10px;color:var(--err);font-weight:700">FAILED (array)</td>
|
||||||
|
<td style="padding:9px 10px;color:rgba(255,255,255,.7)">array in stato FAILED</td>
|
||||||
|
<td style="padding:9px 10px;color:rgba(255,255,255,.7)"><code>cat /proc/mdstat</code><br><code>mdadm --detail /dev/md0</code></td>
|
||||||
|
<td style="padding:9px 10px;color:rgba(255,255,255,.7)">RAID0 / RAID5 2 guasti → no recovery</td>
|
||||||
|
<td style="padding:9px 10px"><code style="color:var(--err)">conclude restore da backup</code></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="doc-divider">
|
||||||
|
|
||||||
|
<!-- CHIUSURA COMPLETA CRC -->
|
||||||
|
<div class="cmd-card" style="border-color:rgba(167,139,250,.22)">
|
||||||
|
<div class="cmd-card-header" style="border-bottom-color:rgba(167,139,250,.12)">
|
||||||
|
<div class="cmd-card-title">
|
||||||
|
<span class="cmd-number" style="background:linear-gradient(135deg,rgba(167,139,250,.5),rgba(167,139,250,.3))">🔌</span>
|
||||||
|
<span style="font-size:14px;font-weight:900;color:rgba(167,139,250,.95)">Chiusura completa — CRC errors (cavo SATA)</span>
|
||||||
|
</div>
|
||||||
|
<span class="cmd-badge" style="background:rgba(167,139,250,.1);border-color:rgba(167,139,250,.3);color:rgba(167,139,250,.9)">Procedura completa</span>
|
||||||
|
</div>
|
||||||
|
<div class="cmd-card-body">
|
||||||
|
<div class="cmd-two-col">
|
||||||
|
<div>
|
||||||
|
<div class="cmd-section-label">Sequenza completa nel simulatore</div>
|
||||||
|
<ol style="margin:0;padding-left:18px;display:flex;flex-direction:column;gap:6px;font-size:13px;color:rgba(255,255,255,.78)">
|
||||||
|
<li><b>Rilevo il problema:</b> il disco mostra stato CRC, il contatore cresce</li>
|
||||||
|
<li><b>Leggo il log kernel:</b> <code>dmesg | tail</code> → vedo <code>UDMA CRC error count increased</code></li>
|
||||||
|
<li><b>Analizzo SMART:</b> <code>smartctl -a /dev/sdX</code> → Reallocated=0, CRC alto</li>
|
||||||
|
<li><b>Decido:</b> Reallocated=0 → causa probabile è il cavo, non il disco</li>
|
||||||
|
<li><b>NON faccio fail/remove immediato</b> → non è un guasto disco</li>
|
||||||
|
<li><b>Intervengo:</b> <code>replace cable /dev/sdX</code> — simula la sostituzione fisica del cavo SATA</li>
|
||||||
|
<li><b>Oppure concludo:</b> <code>conclude sostituire cavo SATA</code> — per registrare solo la decisione</li>
|
||||||
|
</ol>
|
||||||
|
<div class="tip-callout" style="margin-top:14px;border-left-color:rgba(167,139,250,.7);background:rgba(167,139,250,.06)">
|
||||||
|
<div class="tip-icon">⚠️</div>
|
||||||
|
<div class="tip-body"><b>Se CRC continua a crescere dopo cambio cavo:</b> a quel punto il disco è inaffidabile → <code>mdadm --fail</code> + <code>--remove</code> + <code>--add</code> nuovo disco → rebuild.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="cmd-section-label">Cosa valuta il simulatore</div>
|
||||||
|
<div class="cmd-output" style="font-size:12px">
|
||||||
|
✅ <span style="color:var(--ok)">checkpoint diag</span>: hai eseguito dmesg + smartctl
|
||||||
|
✅ <span style="color:var(--ok)">checkpoint explained</span>: hai scritto conclude ...
|
||||||
|
✅ <span style="color:var(--ok)">punteggio</span>: +16 pt (scenario completato)
|
||||||
|
|
||||||
|
❌ <span style="color:var(--err)">Non valida</span>: mdadm --fail immediato senza diagnosi
|
||||||
|
❌ <span style="color:var(--err)">Non valida</span>: nessuna conclusione esplicita
|
||||||
|
</div>
|
||||||
|
<div class="cmd-section-label" style="margin-top:10px">Stato finale atteso</div>
|
||||||
|
<div class="cmd-output" style="font-size:12px">
|
||||||
|
/dev/md0: <span style="color:var(--ok)">active</span> raid5 sda sdb sdc <span style="color:rgba(167,139,250,.9)">sdd[C]</span>
|
||||||
|
→ sdd rimane in array (non è guasto)
|
||||||
|
→ si sostituisce il cavo fisico
|
||||||
|
→ si monitora: se CRC si stabilizza → OK
|
||||||
|
→ se CRC cresce → allora si esclude il disco
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="doc-divider">
|
||||||
|
|
||||||
|
<!-- CHIUSURA COMPLETA OVERHEAT -->
|
||||||
|
<div class="cmd-card" style="border-color:rgba(251,191,36,.22)">
|
||||||
|
<div class="cmd-card-header" style="border-bottom-color:rgba(251,191,36,.12)">
|
||||||
|
<div class="cmd-card-title">
|
||||||
|
<span class="cmd-number" style="background:linear-gradient(135deg,rgba(251,191,36,.5),rgba(251,113,133,.3))">🌡</span>
|
||||||
|
<span style="font-size:14px;font-weight:900;color:rgba(251,191,36,.95)">Chiusura completa — Overheat (temperatura critica)</span>
|
||||||
|
</div>
|
||||||
|
<span class="cmd-badge" style="background:rgba(251,191,36,.1);border-color:rgba(251,191,36,.3);color:var(--warn)">Procedura completa</span>
|
||||||
|
</div>
|
||||||
|
<div class="cmd-card-body">
|
||||||
|
<div class="cmd-two-col">
|
||||||
|
<div>
|
||||||
|
<div class="cmd-section-label">Sequenza completa nel simulatore</div>
|
||||||
|
<ol style="margin:0;padding-left:18px;display:flex;flex-direction:column;gap:6px;font-size:13px;color:rgba(255,255,255,.78)">
|
||||||
|
<li><b>Rilevo il problema:</b> disco in stato OVERHEAT, temperatura alta in telemetria</li>
|
||||||
|
<li><b>Analizzo SMART:</b> <code>smartctl -a /dev/sdX</code> → <code>Temperature_Celsius: 60+</code></li>
|
||||||
|
<li><b>Leggo il log kernel:</b> <code>dmesg | tail</code> → vedo <code>temperature critical</code></li>
|
||||||
|
<li><b>Decido:</b> overheat ≠ guasto immediato, ma è un pre-failure → non aspetto</li>
|
||||||
|
<li><b>Non avvio rebuild</b> con disco in overheat → peggiorerebbe</li>
|
||||||
|
<li><b>Intervengo:</b> <code>cool down /dev/sdX</code> — simula pulizia ventole e miglioramento airflow</li>
|
||||||
|
<li><b>Oppure concludo:</b> <code>conclude migliorare raffreddamento</code> — se si vuole solo registrare la decisione</li>
|
||||||
|
</ol>
|
||||||
|
<div class="tip-callout" style="margin-top:14px;border-left-color:rgba(251,191,36,.7);background:rgba(251,191,36,.05)">
|
||||||
|
<div class="tip-icon">🔧</div>
|
||||||
|
<div class="tip-body"><b>Se vuoi essere "pro" (livello esame):</b> dopo aver raffreddato il sistema, pianifichi la sostituzione preventiva: <code>mdadm --fail</code> + <code>--remove</code> + <code>--add</code> nuovo disco → rebuild controllato. Così eviti il guasto improvviso.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="cmd-section-label">Cosa valuta il simulatore</div>
|
||||||
|
<div class="cmd-output" style="font-size:12px">
|
||||||
|
✅ <span style="color:var(--ok)">checkpoint diag</span>: hai eseguito smartctl + dmesg
|
||||||
|
✅ <span style="color:var(--ok)">checkpoint explained</span>: hai scritto conclude ...
|
||||||
|
✅ <span style="color:var(--ok)">punteggio</span>: +16 pt (scenario completato)
|
||||||
|
|
||||||
|
❌ <span style="color:var(--err)">Non valida</span>: avviare rebuild con disco in overheat
|
||||||
|
❌ <span style="color:var(--err)">Non valida</span>: non fare diagnosi e concludere subito
|
||||||
|
</div>
|
||||||
|
<div class="cmd-section-label" style="margin-top:10px">Differenza fondamentale</div>
|
||||||
|
<div class="cmd-output" style="font-size:12px">
|
||||||
|
OVERHEAT → <span style="color:rgba(251,191,36,.9)">rischio imminente</span>
|
||||||
|
→ NON aspetti → pianifichi sostituzione
|
||||||
|
|
||||||
|
CRC → <span style="color:rgba(167,139,250,.9)">problema di comunicazione</span>
|
||||||
|
→ NON panico → diagnosi → cavo prima
|
||||||
|
|
||||||
|
FAILED → <span style="color:var(--err)">guasto reale</span>
|
||||||
|
→ sostituzione immediata → rebuild
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="doc-divider">
|
||||||
|
|
||||||
|
<!-- CHIUSURA COMPLETA SLOW DISK -->
|
||||||
|
<div class="cmd-card" style="border-color:rgba(96,165,250,.22)">
|
||||||
|
<div class="cmd-card-header" style="border-bottom-color:rgba(96,165,250,.12)">
|
||||||
|
<div class="cmd-card-title">
|
||||||
|
<span class="cmd-number" style="background:linear-gradient(135deg,rgba(96,165,250,.5),rgba(34,211,238,.3))">🐢</span>
|
||||||
|
<span style="font-size:14px;font-weight:900;color:rgba(96,165,250,.95)">Chiusura completa — Slow disk (disco lento)</span>
|
||||||
|
</div>
|
||||||
|
<span class="cmd-badge" style="background:rgba(96,165,250,.1);border-color:rgba(96,165,250,.3);color:var(--info)">Procedura completa</span>
|
||||||
|
</div>
|
||||||
|
<div class="cmd-card-body">
|
||||||
|
<div class="cmd-two-col">
|
||||||
|
<div>
|
||||||
|
<div class="cmd-section-label">Sequenza completa nel simulatore</div>
|
||||||
|
<ol style="margin:0;padding-left:18px;display:flex;flex-direction:column;gap:6px;font-size:13px;color:rgba(255,255,255,.78)">
|
||||||
|
<li><b>Rilevo il problema:</b> IOPS calati, disco in stato SLOW</li>
|
||||||
|
<li><b>Analizzo SMART:</b> <code>smartctl -a /dev/sdX</code> → <code>Current_Pending_Sector > 0</code></li>
|
||||||
|
<li><b>Valuto entità:</b> Pending <20 → monitoro / Pending >20 → sostituisco</li>
|
||||||
|
<li><b>Se monitoro:</b> <code>conclude monitorare e pianificare sostituzione</code></li>
|
||||||
|
<li><b>Se sostituisco:</b> <code>mdadm --fail</code> + <code>--remove</code> + <code>--add</code> + <code>--rebuild</code></li>
|
||||||
|
<li><b>Verifico:</b> <code>cat /proc/mdstat</code> → array torna OK</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="cmd-section-label">Cosa osservi in telemetria</div>
|
||||||
|
<div class="cmd-output" style="font-size:12px">
|
||||||
|
IOPS normali: 1200 (RAID5 4 dischi)
|
||||||
|
Con 1 SLOW: <span style="color:var(--info)">~720</span> (-40%)
|
||||||
|
Con rebuild: <span style="color:var(--warn)">~550</span> (-55%)
|
||||||
|
|
||||||
|
→ il rebuild su disco lento è doppiamente penalizzante
|
||||||
|
→ preferibile farlo in orario di bassa attività
|
||||||
|
</div>
|
||||||
|
<div class="tip-callout" style="margin-top:10px;border-left-color:rgba(96,165,250,.7);background:rgba(96,165,250,.05)">
|
||||||
|
<div class="tip-icon">📊</div>
|
||||||
|
<div class="tip-body"><b>Regola pratica:</b> un disco lento è come un campanello d'allarme. Non è ancora guasto, ma sta dicendo che lo diventerà. Agire in anticipo significa fare il rebuild in sicurezza invece di subirlo d'emergenza.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="doc-divider">
|
||||||
|
|
||||||
|
<!-- PRINCIPIO FONDAMENTALE: OGNI PROVA DEVE CONCLUDERSI -->
|
||||||
|
<div class="tip-callout" style="border-left-color:rgba(34,211,238,.7);background:rgba(34,211,238,.06);padding:14px 16px">
|
||||||
|
<div class="tip-icon">🎯</div>
|
||||||
|
<div class="tip-body" style="font-size:13px">
|
||||||
|
<b style="color:rgba(34,211,238,.95);font-size:14px">Principio fondamentale — ogni prova deve concludersi</b><br><br>
|
||||||
|
Un buon amministratore di sistema non si ferma alla diagnosi. Ogni sessione deve avere un <b>risultato verificabile</b>:
|
||||||
|
<ul style="margin:8px 0 0 0;padding-left:16px;display:flex;flex-direction:column;gap:4px;color:rgba(255,255,255,.78)">
|
||||||
|
<li>CRC → <b>monitorato e stabilizzato</b> dopo cambio cavo (o disco sostituito)</li>
|
||||||
|
<li>Overheat → <b>raffreddamento migliorato</b>, sostituzione pianificata o eseguita</li>
|
||||||
|
<li>Slow disk → <b>monitoraggio attivo</b> o rebuild preventivo completato</li>
|
||||||
|
<li>FAILED → <b>rebuild completato</b> e array tornato OK (o restore da backup documentato)</li>
|
||||||
|
</ul>
|
||||||
|
<div style="margin-top:10px;padding:8px 12px;background:rgba(0,0,0,.25);border-radius:6px;font-family:var(--mono);font-size:12px;color:rgba(255,255,255,.7)">
|
||||||
|
Nel simulatore: ogni scenario si chiude con <code>conclude <testo></code> oppure con l'array che torna in stato <span style="color:var(--ok)">OK</span>. Senza una chiusura esplicita, il punteggio non è completo.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div><!-- end proc-errori -->
|
</div><!-- end proc-errori -->
|
||||||
|
|
||||||
<!-- ─────────────────────────────────────────────────────── -->
|
<!-- ─────────────────────────────────────────────────────── -->
|
||||||
@@ -3180,6 +3421,8 @@ $ rm /mnt/verifica.txt
|
|||||||
<tr><td>mdadm /dev/md0 --rebuild</td><td class="ct-warn">Recovery</td><td>Avvia il rebuild dell'array</td><td class="ct-ok">✅ Sì</td></tr>
|
<tr><td>mdadm /dev/md0 --rebuild</td><td class="ct-warn">Recovery</td><td>Avvia il rebuild dell'array</td><td class="ct-ok">✅ Sì</td></tr>
|
||||||
<tr><td>mdadm --stop-rebuild /dev/md0</td><td class="ct-warn">Recovery</td><td>Interrompe il rebuild</td><td class="ct-ok">✅ Sì</td></tr>
|
<tr><td>mdadm --stop-rebuild /dev/md0</td><td class="ct-warn">Recovery</td><td>Interrompe il rebuild</td><td class="ct-ok">✅ Sì</td></tr>
|
||||||
<tr><td>powerfail</td><td class="ct-warn">Simulazione</td><td>Simula blackout, interrompe rebuild</td><td class="ct-ok">✅ Sì</td></tr>
|
<tr><td>powerfail</td><td class="ct-warn">Simulazione</td><td>Simula blackout, interrompe rebuild</td><td class="ct-ok">✅ Sì</td></tr>
|
||||||
|
<tr><td>replace cable /dev/sdX</td><td class="ct-warn">Simulazione</td><td>Sostituisce cavo SATA su disco in stato CRC → azzera errori, ripristina OK</td><td class="ct-ok">✅ Sì</td></tr>
|
||||||
|
<tr><td>cool down /dev/sdX</td><td class="ct-warn">Simulazione</td><td>Intervento raffreddamento su disco in OVERHEAT → normalizza temperatura, ripristina OK</td><td class="ct-ok">✅ Sì</td></tr>
|
||||||
<tr><td>mkfs.ext4 /dev/md0</td><td class="ct-ok">Filesystem</td><td>Crea filesystem ext4 sull'array</td><td class="ct-ok">✅ Sì</td></tr>
|
<tr><td>mkfs.ext4 /dev/md0</td><td class="ct-ok">Filesystem</td><td>Crea filesystem ext4 sull'array</td><td class="ct-ok">✅ Sì</td></tr>
|
||||||
<tr><td>mount /dev/md0 /mnt</td><td class="ct-ok">Filesystem</td><td>Monta il filesystem</td><td class="ct-ok">✅ Sì</td></tr>
|
<tr><td>mount /dev/md0 /mnt</td><td class="ct-ok">Filesystem</td><td>Monta il filesystem</td><td class="ct-ok">✅ Sì</td></tr>
|
||||||
<tr><td>umount /mnt</td><td class="ct-ok">Filesystem</td><td>Smonta il filesystem</td><td class="ct-ok">✅ Sì</td></tr>
|
<tr><td>umount /mnt</td><td class="ct-ok">Filesystem</td><td>Smonta il filesystem</td><td class="ct-ok">✅ Sì</td></tr>
|
||||||
@@ -3224,7 +3467,7 @@ $ rm /mnt/verifica.txt
|
|||||||
window.scrollTo({top:0, behavior:"smooth"});
|
window.scrollTo({top:0, behavior:"smooth"});
|
||||||
}
|
}
|
||||||
$("tab-lab").addEventListener("click", ()=>showPage("tab-lab"));
|
$("tab-lab").addEventListener("click", ()=>showPage("tab-lab"));
|
||||||
$("tab-ex").addEventListener("click", ()=>showPage("tab-ex"));
|
$("tab-ex").addEventListener("click", ()=>{ showPage("tab-ex"); tfUpdateDiskFaultRows(); });
|
||||||
$("tab-doc").addEventListener("click", ()=>showPage("tab-doc"));
|
$("tab-doc").addEventListener("click", ()=>showPage("tab-doc"));
|
||||||
|
|
||||||
document.querySelectorAll(".doc-tab").forEach(btn => {
|
document.querySelectorAll(".doc-tab").forEach(btn => {
|
||||||
@@ -3271,7 +3514,7 @@ $ rm /mnt/verifica.txt
|
|||||||
diskSizeGB: 1000,
|
diskSizeGB: 1000,
|
||||||
disks: [],
|
disks: [],
|
||||||
spares: [],
|
spares: [],
|
||||||
rebuild: { active:false, start:0, etaSec:0, progress:0, realSec:0, speedFactor:1 },
|
rebuild: { active:false, start:0, etaSec:0, progress:0 },
|
||||||
fs: { created:false, mountedAt:null, uuid:null, label:null, files: new Map() },
|
fs: { created:false, mountedAt:null, uuid:null, label:null, files: new Map() },
|
||||||
dmesg: [],
|
dmesg: [],
|
||||||
score: 0,
|
score: 0,
|
||||||
@@ -3386,7 +3629,16 @@ $ rm /mnt/verifica.txt
|
|||||||
|
|
||||||
let rebuildTimer=null;
|
let rebuildTimer=null;
|
||||||
function rebuildEstimateSec(){
|
function rebuildEstimateSec(){
|
||||||
const sizeGB=state.diskSizeGB,n=state.disks.length;
|
// Durata simulata fissa: 8-10 secondi reali (indipendente dalla dimensione disco)
|
||||||
|
let simSec=9;
|
||||||
|
if(state.raidLevel===6)simSec=10;
|
||||||
|
if(state.raidLevel===1)simSec=8;
|
||||||
|
if(state.disks.some(d=>d.state===DiskState.SLOW))simSec=Math.min(simSec+1,10);
|
||||||
|
if(state.disks.some(d=>d.state===DiskState.OVERHEAT))simSec=Math.min(simSec+1,10);
|
||||||
|
return simSec;
|
||||||
|
}
|
||||||
|
function rebuildRealTimeMsg(){
|
||||||
|
const sizeGB=state.diskSizeGB;
|
||||||
let speed=1.6;
|
let speed=1.6;
|
||||||
if(state.raidLevel===5)speed=1.1;
|
if(state.raidLevel===5)speed=1.1;
|
||||||
if(state.raidLevel===6)speed=0.85;
|
if(state.raidLevel===6)speed=0.85;
|
||||||
@@ -3395,13 +3647,11 @@ $ rm /mnt/verifica.txt
|
|||||||
if(state.raidLevel===0)speed=2.0;
|
if(state.raidLevel===0)speed=2.0;
|
||||||
if(state.disks.some(d=>d.state===DiskState.SLOW))speed*=0.65;
|
if(state.disks.some(d=>d.state===DiskState.SLOW))speed*=0.65;
|
||||||
if(state.disks.some(d=>d.state===DiskState.OVERHEAT))speed*=0.75;
|
if(state.disks.some(d=>d.state===DiskState.OVERHEAT))speed*=0.75;
|
||||||
speed*=(1+Math.log2(Math.max(2,n))*0.12);
|
const realSec=Math.round(sizeGB/speed);
|
||||||
const realSec=Math.max(60,Math.round(sizeGB/speed));
|
if(realSec<120)return realSec+" secondi";
|
||||||
// Modalità didattica: cappato a 10–15s, calcola fattore di accelerazione
|
if(realSec<3600){const m=Math.round(realSec/60);return"~"+m+" minuti";}
|
||||||
const didacticSec=Math.round(10+Math.random()*5); // 10–15s
|
const h=Math.floor(realSec/3600),m=Math.round((realSec%3600)/60);
|
||||||
state.rebuild.realSec=realSec;
|
return m>0?"~"+h+"h "+m+"min":"~"+h+" ore";
|
||||||
state.rebuild.speedFactor=Math.round(realSec/didacticSec);
|
|
||||||
return didacticSec;
|
|
||||||
}
|
}
|
||||||
function stopRebuild(silent=false){ state.rebuild.active=false;state.rebuild.progress=0; state.disks.forEach(d=>{ if(d.state===DiskState.REBUILDING){d.state=DiskState.FAILED;d.progress=0;} }); if(rebuildTimer){clearInterval(rebuildTimer);rebuildTimer=null;} if(!silent)pushDmesg("warn","md0: rebuild interrupted"); render(); }
|
function stopRebuild(silent=false){ state.rebuild.active=false;state.rebuild.progress=0; state.disks.forEach(d=>{ if(d.state===DiskState.REBUILDING){d.state=DiskState.FAILED;d.progress=0;} }); if(rebuildTimer){clearInterval(rebuildTimer);rebuildTimer=null;} if(!silent)pushDmesg("warn","md0: rebuild interrupted"); render(); }
|
||||||
function startRebuild(){
|
function startRebuild(){
|
||||||
@@ -3414,13 +3664,10 @@ $ rm /mnt/verifica.txt
|
|||||||
if(spare.sizeGB<state.diskSizeGB){ pushDmesg("err",`${spare.dev}: spare too small (${spare.sizeGB}GB < ${state.diskSizeGB}GB)`); return termPrint(`mdadm: spare troppo piccolo (${spare.sizeGB}GB).`,"err"); }
|
if(spare.sizeGB<state.diskSizeGB){ pushDmesg("err",`${spare.dev}: spare too small (${spare.sizeGB}GB < ${state.diskSizeGB}GB)`); return termPrint(`mdadm: spare troppo piccolo (${spare.sizeGB}GB).`,"err"); }
|
||||||
state.disks[failedIdx].state=DiskState.REBUILDING; state.disks[failedIdx].progress=0; state.spares.shift();
|
state.disks[failedIdx].state=DiskState.REBUILDING; state.disks[failedIdx].progress=0; state.spares.shift();
|
||||||
state.rebuild.active=true; state.rebuild.start=Date.now(); state.rebuild.etaSec=rebuildEstimateSec(); state.rebuild.progress=0;
|
state.rebuild.active=true; state.rebuild.start=Date.now(); state.rebuild.etaSec=rebuildEstimateSec(); state.rebuild.progress=0;
|
||||||
const realMin=Math.round(state.rebuild.realSec/60);
|
const realMsg=rebuildRealTimeMsg();
|
||||||
const realHr=(state.rebuild.realSec>=3600)?(` = ${(state.rebuild.realSec/3600).toFixed(1)}h`):"";
|
pushDmesg("info",`md0: rebuild started (simulazione ~${state.rebuild.etaSec}s)`);
|
||||||
pushDmesg("info",`md0: rebuild started (ETA ~ ${state.rebuild.etaSec}s — MODALITÀ DIDATTICA x${state.rebuild.speedFactor})`);
|
termPrint(`mdadm: rebuild avviato — completamento in ~${state.rebuild.etaSec}s (simulazione).`,"ok");
|
||||||
termPrint(`mdadm: rebuild avviato.`,"ok");
|
termPrint(`⚠ Tempo reale equivalente su hardware reale con ${state.diskSizeGB}GB: ${realMsg}.`,"warn");
|
||||||
termPrint(` ⚡ Velocità aumentata di ${state.rebuild.speedFactor}× per uso didattico`,"warn");
|
|
||||||
termPrint(` ⏱ ETA simulato : ~${state.rebuild.etaSec}s`,"warn");
|
|
||||||
termPrint(` 🕐 Tempo reale : ~${realMin} min${realHr} (disco ${state.diskSizeGB} GB, RAID${state.raidLevel})`,"warn");
|
|
||||||
if(state.scenario.name&&!state.scenario.checkpoints.rebuild){ state.scenario.checkpoints.rebuild=true; addScore(6,"(scenario) rebuild avviato."); }
|
if(state.scenario.name&&!state.scenario.checkpoints.rebuild){ state.scenario.checkpoints.rebuild=true; addScore(6,"(scenario) rebuild avviato."); }
|
||||||
rebuildTimer=setInterval(()=>{
|
rebuildTimer=setInterval(()=>{
|
||||||
if(!state.rebuild.active)return;
|
if(!state.rebuild.active)return;
|
||||||
@@ -3432,12 +3679,8 @@ $ rm /mnt/verifica.txt
|
|||||||
state.rebuild.active=false;state.rebuild.progress=0;
|
state.rebuild.active=false;state.rebuild.progress=0;
|
||||||
state.disks.forEach(d=>{ if(d.state===DiskState.REBUILDING){d.state=DiskState.OK;d.progress=0;d.smart.realloc=0;d.smart.pending=0;d.smart.crc=0;d.smart.temp=33;} });
|
state.disks.forEach(d=>{ if(d.state===DiskState.REBUILDING){d.state=DiskState.OK;d.progress=0;d.smart.realloc=0;d.smart.pending=0;d.smart.crc=0;d.smart.temp=33;} });
|
||||||
pushDmesg("info","md0: rebuild completed");
|
pushDmesg("info","md0: rebuild completed");
|
||||||
termPrint("mdadm: rebuild completato. ✅","ok");
|
termPrint("mdadm: rebuild completato.","ok");
|
||||||
state.rebuildCount+=1;
|
state.rebuildCount+=1;
|
||||||
const rf=state.rebuild.speedFactor||1, rs=state.rebuild.realSec||0;
|
|
||||||
const rm=Math.round(rs/60), rh=(rs>=3600)?` = ${(rs/3600).toFixed(1)}h`:"";
|
|
||||||
termPrint(` ℹ Ricorda: nella realtà questa operazione avrebbe richiesto ~${rm} min${rh}`,"warn");
|
|
||||||
termPrint(` (accelerato di ${rf}× per uso didattico)`,"warn");
|
|
||||||
if(state.scenario.name&&!state.scenario.checkpoints.ok&&volumeStatus()===VolState.OK){ state.scenario.checkpoints.ok=true; addScore(8,"(scenario) array OK."); }
|
if(state.scenario.name&&!state.scenario.checkpoints.ok&&volumeStatus()===VolState.OK){ state.scenario.checkpoints.ok=true; addScore(8,"(scenario) array OK."); }
|
||||||
clearInterval(rebuildTimer);rebuildTimer=null;
|
clearInterval(rebuildTimer);rebuildTimer=null;
|
||||||
}
|
}
|
||||||
@@ -3477,7 +3720,7 @@ $ rm /mnt/verifica.txt
|
|||||||
const n=normalizeDiskCount(diskCountEl.value); diskCountEl.value=String(n);
|
const n=normalizeDiskCount(diskCountEl.value); diskCountEl.value=String(n);
|
||||||
state.disks=Array.from({length:n},(_,i)=>makeDisk(i,state.diskSizeGB)); state.spares=[];
|
state.disks=Array.from({length:n},(_,i)=>makeDisk(i,state.diskSizeGB)); state.spares=[];
|
||||||
resetFS(); if(rebuildTimer){clearInterval(rebuildTimer);rebuildTimer=null;}
|
resetFS(); if(rebuildTimer){clearInterval(rebuildTimer);rebuildTimer=null;}
|
||||||
state.rebuild={active:false,start:0,etaSec:0,progress:0,realSec:0,speedFactor:1}; state.mode="LAB"; state.exerciseOn=false;
|
state.rebuild={active:false,start:0,etaSec:0,progress:0}; state.mode="LAB"; state.exerciseOn=false;
|
||||||
state.scenario={name:null,goalHtml:null,solution:[],checkpoints:{},done:false,startedAt:null,seed:null};
|
state.scenario={name:null,goalHtml:null,solution:[],checkpoints:{},done:false,startedAt:null,seed:null};
|
||||||
state.score=0;state.hints=0;state.actions=[];
|
state.score=0;state.hints=0;state.actions=[];
|
||||||
scoreEl.textContent="0";hintsEl.textContent="0";
|
scoreEl.textContent="0";hintsEl.textContent="0";
|
||||||
@@ -3510,8 +3753,8 @@ $ rm /mnt/verifica.txt
|
|||||||
raid10_pairfail:()=>{ createArray();raidLevelEl.value="10";state.raidLevel=10;diskCountEl.value="6";state.disks=Array.from({length:6},(_,i)=>makeDisk(i,1000));diskSizeEl.value="1000";state.diskSizeGB=1000;state.disks[0].state=DiskState.FAILED;state.disks[1].state=DiskState.FAILED;pushDmesg("err","RAID10: mirror pair lost => array FAILED (scenario)");state.mode="EXERCISE";state.exerciseOn=true;state.scenario={name:"raid10_pairfail",goalHtml:`RAID10: perso un mirror pair → array <b>FAILED</b>. Obiettivo: diagnostica + conclusione corretta: <code>conclude restore da backup</code>.`,solution:["cat /proc/mdstat","mdadm --detail /dev/md0","conclude restore da backup"],checkpoints:{diag:false,explained:false},done:false,startedAt:Date.now(),seed:"raid10_pairfail"};termPrint("Scenario caricato: RAID10 pair fail (atteso FAILED)","info");render(); },
|
raid10_pairfail:()=>{ createArray();raidLevelEl.value="10";state.raidLevel=10;diskCountEl.value="6";state.disks=Array.from({length:6},(_,i)=>makeDisk(i,1000));diskSizeEl.value="1000";state.diskSizeGB=1000;state.disks[0].state=DiskState.FAILED;state.disks[1].state=DiskState.FAILED;pushDmesg("err","RAID10: mirror pair lost => array FAILED (scenario)");state.mode="EXERCISE";state.exerciseOn=true;state.scenario={name:"raid10_pairfail",goalHtml:`RAID10: perso un mirror pair → array <b>FAILED</b>. Obiettivo: diagnostica + conclusione corretta: <code>conclude restore da backup</code>.`,solution:["cat /proc/mdstat","mdadm --detail /dev/md0","conclude restore da backup"],checkpoints:{diag:false,explained:false},done:false,startedAt:Date.now(),seed:"raid10_pairfail"};termPrint("Scenario caricato: RAID10 pair fail (atteso FAILED)","info");render(); },
|
||||||
rebuild_interrupted:()=>{ createArray();raidLevelEl.value="5";state.raidLevel=5;diskCountEl.value="4";state.disks=Array.from({length:4},(_,i)=>makeDisk(i,1000));diskSizeEl.value="1000";state.diskSizeGB=1000;state.disks[2].state=DiskState.FAILED;pushDmesg("warn","/dev/sdc: disk FAILED (scenario)");state.mode="EXERCISE";state.exerciseOn=true;state.scenario={name:"rebuild_interrupted",goalHtml:`RAID5 degradato. Obiettivo: sostituire disco, avviare rebuild e interromperlo (blackout) con <code>powerfail</code>, poi riavviarlo.`,solution:["mdadm --remove /dev/md0 /dev/sdc","mdadm --add /dev/md0 /dev/sde","mdadm --rebuild /dev/md0","powerfail","mdadm --rebuild /dev/md0","cat /proc/mdstat"],checkpoints:{started:false,stopped:false,restarted:false,ok:false},done:false,startedAt:Date.now(),seed:"rebuild_interrupted"};termPrint("Scenario caricato: rebuild_interrupted","info");render(); },
|
rebuild_interrupted:()=>{ createArray();raidLevelEl.value="5";state.raidLevel=5;diskCountEl.value="4";state.disks=Array.from({length:4},(_,i)=>makeDisk(i,1000));diskSizeEl.value="1000";state.diskSizeGB=1000;state.disks[2].state=DiskState.FAILED;pushDmesg("warn","/dev/sdc: disk FAILED (scenario)");state.mode="EXERCISE";state.exerciseOn=true;state.scenario={name:"rebuild_interrupted",goalHtml:`RAID5 degradato. Obiettivo: sostituire disco, avviare rebuild e interromperlo (blackout) con <code>powerfail</code>, poi riavviarlo.`,solution:["mdadm --remove /dev/md0 /dev/sdc","mdadm --add /dev/md0 /dev/sde","mdadm --rebuild /dev/md0","powerfail","mdadm --rebuild /dev/md0","cat /proc/mdstat"],checkpoints:{started:false,stopped:false,restarted:false,ok:false},done:false,startedAt:Date.now(),seed:"rebuild_interrupted"};termPrint("Scenario caricato: rebuild_interrupted","info");render(); },
|
||||||
wrong_size_spare:()=>{ createArray();raidLevelEl.value="5";state.raidLevel=5;diskCountEl.value="4";state.disks=Array.from({length:4},(_,i)=>makeDisk(i,1000));diskSizeEl.value="1000";state.diskSizeGB=1000;state.disks[1].state=DiskState.FAILED;pushDmesg("warn","/dev/sdb: disk FAILED (scenario)");state.mode="EXERCISE";state.exerciseOn=true;state.scenario={name:"wrong_size_spare",goalHtml:`RAID5 degradato. Obiettivo: provare spare più piccolo (errore), poi spare corretto e rebuild.<br>Prova: <code>mdadm --add /dev/md0 /dev/sdz --size 500</code> → rebuild deve fallire.`,solution:["mdadm --add /dev/md0 /dev/sdz --size 500","mdadm --rebuild /dev/md0","mdadm --add /dev/md0 /dev/sde","mdadm --rebuild /dev/md0","cat /proc/mdstat"],checkpoints:{triedSmall:false,addedOk:false,ok:false},done:false,startedAt:Date.now(),seed:"wrong_size_spare"};termPrint("Scenario caricato: wrong_size_spare","info");render(); },
|
wrong_size_spare:()=>{ createArray();raidLevelEl.value="5";state.raidLevel=5;diskCountEl.value="4";state.disks=Array.from({length:4},(_,i)=>makeDisk(i,1000));diskSizeEl.value="1000";state.diskSizeGB=1000;state.disks[1].state=DiskState.FAILED;pushDmesg("warn","/dev/sdb: disk FAILED (scenario)");state.mode="EXERCISE";state.exerciseOn=true;state.scenario={name:"wrong_size_spare",goalHtml:`RAID5 degradato. Obiettivo: provare spare più piccolo (errore), poi spare corretto e rebuild.<br>Prova: <code>mdadm --add /dev/md0 /dev/sdz --size 500</code> → rebuild deve fallire.`,solution:["mdadm --add /dev/md0 /dev/sdz --size 500","mdadm --rebuild /dev/md0","mdadm --add /dev/md0 /dev/sde","mdadm --rebuild /dev/md0","cat /proc/mdstat"],checkpoints:{triedSmall:false,addedOk:false,ok:false},done:false,startedAt:Date.now(),seed:"wrong_size_spare"};termPrint("Scenario caricato: wrong_size_spare","info");render(); },
|
||||||
crc_errors:()=>{ createArray();raidLevelEl.value="5";state.raidLevel=5;diskCountEl.value="4";state.disks=Array.from({length:4},(_,i)=>makeDisk(i,1000));diskSizeEl.value="1000";state.diskSizeGB=1000;state.disks[3].state=DiskState.CRC;state.disks[3].smart.crc=24;pushDmesg("warn","/dev/sdd: UDMA CRC errors detected (scenario)");state.mode="EXERCISE";state.exerciseOn=true;state.scenario={name:"crc_errors",goalHtml:`CRC errors: probabile cavo/connessione. Obiettivo: diagnosticare con <code>dmesg | tail</code> e <code>smartctl -a /dev/sdd</code> e concludere: <code>conclude sostituire cavo SATA</code>.`,solution:["dmesg | tail","smartctl -a /dev/sdd","conclude sostituire cavo SATA"],checkpoints:{diag:false,explained:false},done:false,startedAt:Date.now(),seed:"crc_errors"};termPrint("Scenario caricato: crc_errors","info");render(); },
|
crc_errors:()=>{ createArray();raidLevelEl.value="5";state.raidLevel=5;diskCountEl.value="4";state.disks=Array.from({length:4},(_,i)=>makeDisk(i,1000));diskSizeEl.value="1000";state.diskSizeGB=1000;state.disks[3].state=DiskState.CRC;state.disks[3].smart.crc=24;pushDmesg("warn","/dev/sdd: UDMA CRC errors detected (scenario)");state.mode="EXERCISE";state.exerciseOn=true;state.scenario={name:"crc_errors",goalHtml:`CRC errors: probabile cavo/connessione. Diagnosi: <code>dmesg | tail</code> e <code>smartctl -a /dev/sdd</code>. Intervento: <code>replace cable /dev/sdd</code> oppure concludere con <code>conclude sostituire cavo SATA</code>.`,solution:["dmesg | tail","smartctl -a /dev/sdd","replace cable /dev/sdd"],checkpoints:{diag:false,explained:false,cableReplaced:false},done:false,startedAt:Date.now(),seed:"crc_errors"};termPrint("Scenario caricato: crc_errors","info");render(); },
|
||||||
overheat:()=>{ createArray();raidLevelEl.value="6";state.raidLevel=6;diskCountEl.value="5";state.disks=Array.from({length:5},(_,i)=>makeDisk(i,1000));diskSizeEl.value="1000";state.diskSizeGB=1000;state.disks[2].state=DiskState.OVERHEAT;state.disks[2].smart.temp=60;pushDmesg("warn","/dev/sdc: temperature critical (scenario)");state.mode="EXERCISE";state.exerciseOn=true;state.scenario={name:"overheat",goalHtml:`Disco in overheat. Obiettivo: diagnosi (<code>smartctl</code> + <code>dmesg | tail</code>) e conclusione: <code>conclude migliorare raffreddamento</code>.`,solution:["smartctl -a /dev/sdc","dmesg | tail","conclude migliorare raffreddamento"],checkpoints:{diag:false,explained:false},done:false,startedAt:Date.now(),seed:"overheat"};termPrint("Scenario caricato: overheat","info");render(); }
|
overheat:()=>{ createArray();raidLevelEl.value="6";state.raidLevel=6;diskCountEl.value="5";state.disks=Array.from({length:5},(_,i)=>makeDisk(i,1000));diskSizeEl.value="1000";state.diskSizeGB=1000;state.disks[2].state=DiskState.OVERHEAT;state.disks[2].smart.temp=60;pushDmesg("warn","/dev/sdc: temperature critical (scenario)");state.mode="EXERCISE";state.exerciseOn=true;state.scenario={name:"overheat",goalHtml:`Disco in overheat. Diagnosi: <code>smartctl -a /dev/sdc</code> + <code>dmesg | tail</code>. Intervento: <code>cool down /dev/sdc</code> oppure concludere con <code>conclude migliorare raffreddamento</code>.`,solution:["smartctl -a /dev/sdc","dmesg | tail","cool down /dev/sdc"],checkpoints:{diag:false,explained:false,cooled:false},done:false,startedAt:Date.now(),seed:"overheat"};termPrint("Scenario caricato: overheat","info");render(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
function loadScenario(name){ const fn=scenarios[name]; if(!fn)return termPrint(`scenario: '${name}' non trovato. Usa 'scenario list'.`,"err"); fn(); scenarioCheck(); }
|
function loadScenario(name){ const fn=scenarios[name]; if(!fn)return termPrint(`scenario: '${name}' non trovato. Usa 'scenario list'.`,"err"); fn(); scenarioCheck(); }
|
||||||
@@ -3596,7 +3839,7 @@ $ rm /mnt/verifica.txt
|
|||||||
function mdadmRemove(dev){ if(state.rebuild.active)return{ok:false,msg:"mdadm: rebuild in corso, remove bloccato."}; const d=state.disks.find(x=>x.dev===dev); if(!d)return{ok:false,msg:`mdadm: ${dev}: non è membro dell'array.`}; if(!(d.state===DiskState.FAILED||d.state===DiskState.REMOVED))return{ok:false,msg:`mdadm: ${dev}: rimozione solo se FAILED/REMOVED (didattica).`}; d.state=DiskState.REMOVED;pushDmesg("warn",`${dev}: removed from array`); if(state.scenario.name&&!state.scenario.checkpoints.removed){if(["raid5_1fail","raid1_onefail","rebuild_interrupted","wrong_size_spare","raid6_2fail"].includes(state.scenario.name)){state.scenario.checkpoints.removed=true;addScore(5,"(scenario) remove fatto.");}} return{ok:true,msg:`mdadm: ${dev} rimosso (REMOVED).`}; }
|
function mdadmRemove(dev){ if(state.rebuild.active)return{ok:false,msg:"mdadm: rebuild in corso, remove bloccato."}; const d=state.disks.find(x=>x.dev===dev); if(!d)return{ok:false,msg:`mdadm: ${dev}: non è membro dell'array.`}; if(!(d.state===DiskState.FAILED||d.state===DiskState.REMOVED))return{ok:false,msg:`mdadm: ${dev}: rimozione solo se FAILED/REMOVED (didattica).`}; d.state=DiskState.REMOVED;pushDmesg("warn",`${dev}: removed from array`); if(state.scenario.name&&!state.scenario.checkpoints.removed){if(["raid5_1fail","raid1_onefail","rebuild_interrupted","wrong_size_spare","raid6_2fail"].includes(state.scenario.name)){state.scenario.checkpoints.removed=true;addScore(5,"(scenario) remove fatto.");}} return{ok:true,msg:`mdadm: ${dev} rimosso (REMOVED).`}; }
|
||||||
function mdadmAdd(dev,sizeOverrideGB=null){ if(state.disks.some(x=>x.dev===dev)||state.spares.some(x=>x.dev===dev))return{ok:false,msg:`mdadm: ${dev}: già presente.`}; let size=sizeOverrideGB??state.diskSizeGB; size=clamp(parseInt(size,10),10,200000); const idx=state.spares.length; const spare=makeDisk(state.disks.length+idx,size); spare.dev=dev;spare.name=`spare${idx+1}`;spare.state=DiskState.SPARE; state.spares.push(spare);pushDmesg("info",`${dev}: added as spare (${size}GB)`); if(state.scenario.name){if(["raid5_1fail","raid1_onefail","rebuild_interrupted","raid6_2fail"].includes(state.scenario.name)&&!state.scenario.checkpoints.added){state.scenario.checkpoints.added=true;addScore(5,"(scenario) spare aggiunto.");}if(state.scenario.name==="wrong_size_spare"){if(size<state.diskSizeGB&&!state.scenario.checkpoints.triedSmall){state.scenario.checkpoints.triedSmall=true;addScore(5,"(scenario) spare piccolo provato.");}else if(size>=state.diskSizeGB&&!state.scenario.checkpoints.addedOk){state.scenario.checkpoints.addedOk=true;addScore(5,"(scenario) spare corretto aggiunto.");}}} return{ok:true,msg:`mdadm: aggiunto ${dev} come SPARE (${size}GB).`}; }
|
function mdadmAdd(dev,sizeOverrideGB=null){ if(state.disks.some(x=>x.dev===dev)||state.spares.some(x=>x.dev===dev))return{ok:false,msg:`mdadm: ${dev}: già presente.`}; let size=sizeOverrideGB??state.diskSizeGB; size=clamp(parseInt(size,10),10,200000); const idx=state.spares.length; const spare=makeDisk(state.disks.length+idx,size); spare.dev=dev;spare.name=`spare${idx+1}`;spare.state=DiskState.SPARE; state.spares.push(spare);pushDmesg("info",`${dev}: added as spare (${size}GB)`); if(state.scenario.name){if(["raid5_1fail","raid1_onefail","rebuild_interrupted","raid6_2fail"].includes(state.scenario.name)&&!state.scenario.checkpoints.added){state.scenario.checkpoints.added=true;addScore(5,"(scenario) spare aggiunto.");}if(state.scenario.name==="wrong_size_spare"){if(size<state.diskSizeGB&&!state.scenario.checkpoints.triedSmall){state.scenario.checkpoints.triedSmall=true;addScore(5,"(scenario) spare piccolo provato.");}else if(size>=state.diskSizeGB&&!state.scenario.checkpoints.addedOk){state.scenario.checkpoints.addedOk=true;addScore(5,"(scenario) spare corretto aggiunto.");}}} return{ok:true,msg:`mdadm: aggiunto ${dev} come SPARE (${size}GB).`}; }
|
||||||
|
|
||||||
function helpText(){ return["Comandi (simulazione):"," help | clear | report | export"," scenario list | scenario load <n>"," hint | solution (teacher)"," powerfail (simula blackout: interrompe rebuild)","","Sistema:"," uptime (mostra il tempo di esecuzione del simulatore)","","RAID (entrambe le sintassi accettate):"," cat /proc/mdstat"," mdadm --detail /dev/md0"," mdadm --fail /dev/md0 /dev/sdX (oppure: mdadm /dev/md0 --fail /dev/sdX)"," mdadm --remove /dev/md0 /dev/sdX (oppure: mdadm /dev/md0 --remove /dev/sdX)"," mdadm --add /dev/md0 /dev/sdX [--size <GB>]"," mdadm --rebuild /dev/md0"," mdadm --stop-rebuild /dev/md0","","Dischi & OS:"," unplug /dev/sdX"," lsblk | fdisk -l | blkid"," dmesg | tail"," smartctl -a /dev/sdX"," df -h","","Filesystem:"," mkfs.ext4 /dev/md0"," mount /dev/md0 /mnt | umount /mnt"," ls /mnt | touch /mnt/file | echo \"testo\" > /mnt/file | cat /mnt/file | rm /mnt/file","","Conclusioni (scenari FAILED o non-RAID):"," conclude <testo>"].join("\n"); }
|
function helpText(){ return["Comandi (simulazione):"," help | clear | report | export"," scenario list | scenario load <n>"," hint | solution (teacher)"," powerfail (simula blackout: interrompe rebuild)","","Sistema:"," uptime (mostra il tempo di esecuzione del simulatore)","","RAID (entrambe le sintassi accettate):"," cat /proc/mdstat"," mdadm --detail /dev/md0"," mdadm --fail /dev/md0 /dev/sdX (oppure: mdadm /dev/md0 --fail /dev/sdX)"," mdadm --remove /dev/md0 /dev/sdX (oppure: mdadm /dev/md0 --remove /dev/sdX)"," mdadm --add /dev/md0 /dev/sdX [--size <GB>]"," mdadm --rebuild /dev/md0"," mdadm --stop-rebuild /dev/md0","","Dischi & OS:"," unplug /dev/sdX"," lsblk | fdisk -l | blkid"," dmesg | tail"," smartctl -a /dev/sdX"," df -h","","Filesystem:"," mkfs.ext4 /dev/md0"," mount /dev/md0 /mnt | umount /mnt"," ls /mnt | touch /mnt/file | echo \"testo\" > /mnt/file | cat /mnt/file | rm /mnt/file","","Interventi hardware (simulati):"," replace cable /dev/sdX (sostituisce cavo SATA su disco in stato CRC)"," cool down /dev/sdX (intervento raffreddamento su disco in stato OVERHEAT)","","Conclusioni (scenari FAILED o non-RAID):"," conclude <testo>"].join("\n"); }
|
||||||
function makeReport(){
|
function makeReport(){
|
||||||
const vol=volumeStatus(),cap=capacityGB(state.raidLevel,state.disks.length,state.diskSizeGB),lines=[];
|
const vol=volumeStatus(),cap=capacityGB(state.raidLevel,state.disks.length,state.diskSizeGB),lines=[];
|
||||||
lines.push("RAID LAB REPORT");lines.push("===============");lines.push(`Timestamp: ${new Date().toISOString()}`);lines.push(`ExerciseCode: ${state.exgen.code||"(none)"}`);lines.push(`Scenario: ${state.scenario.name||"(none)"}`);lines.push(`Mode: ${state.mode} Teacher: ${state.teacher?"ON":"OFF"}`);lines.push(`RAID: ${state.raidLevel} Members: ${state.disks.length} DiskSizeGB: ${state.diskSizeGB}`);lines.push(`CapacityGB: ${cap} Status: ${vol}`);lines.push(`Score: ${state.score} Hints: ${state.hints}`);lines.push("");lines.push("Disks:");
|
lines.push("RAID LAB REPORT");lines.push("===============");lines.push(`Timestamp: ${new Date().toISOString()}`);lines.push(`ExerciseCode: ${state.exgen.code||"(none)"}`);lines.push(`Scenario: ${state.scenario.name||"(none)"}`);lines.push(`Mode: ${state.mode} Teacher: ${state.teacher?"ON":"OFF"}`);lines.push(`RAID: ${state.raidLevel} Members: ${state.disks.length} DiskSizeGB: ${state.diskSizeGB}`);lines.push(`CapacityGB: ${cap} Status: ${vol}`);lines.push(`Score: ${state.score} Hints: ${state.hints}`);lines.push("");lines.push("Disks:");
|
||||||
@@ -3635,19 +3878,6 @@ $ rm /mnt/verifica.txt
|
|||||||
lampEl.className=`lamp ${lampClass(vol)}`;
|
lampEl.className=`lamp ${lampClass(vol)}`;
|
||||||
bigStatusEl.textContent=`STATUS: ${vol}`;
|
bigStatusEl.textContent=`STATUS: ${vol}`;
|
||||||
miniStatusEl.textContent=`RAID${L} · members=${n} · failed=${failedCount()} · removed=${removedCount()} · spares=${state.spares.length} · rebuild=${state.rebuild.active?state.rebuild.progress.toFixed(1)+"%":"no"}`;
|
miniStatusEl.textContent=`RAID${L} · members=${n} · failed=${failedCount()} · removed=${removedCount()} · spares=${state.spares.length} · rebuild=${state.rebuild.active?state.rebuild.progress.toFixed(1)+"%":"no"}`;
|
||||||
// Banner didattico rebuild
|
|
||||||
if(state.rebuild.active){
|
|
||||||
const leftSim=Math.max(0,Math.round(state.rebuild.etaSec-(Date.now()-state.rebuild.start)/1000));
|
|
||||||
const realSec=state.rebuild.realSec||0;
|
|
||||||
const realMin=Math.round(realSec/60);
|
|
||||||
const realStr=realSec>=3600?`~${(realSec/3600).toFixed(1)} ore`:`~${realMin} min`;
|
|
||||||
rbFactorEl.textContent=state.rebuild.speedFactor||"?";
|
|
||||||
rbEtaSimEl.textContent=`${leftSim}s (${state.rebuild.progress.toFixed(0)}%)`;
|
|
||||||
rbEtaRealEl.textContent=`${realStr} (disco ${state.diskSizeGB} GB, RAID${L})`;
|
|
||||||
rebuildBannerEl.style.display="flex";
|
|
||||||
} else {
|
|
||||||
rebuildBannerEl.style.display="none";
|
|
||||||
}
|
|
||||||
pillModeEl.textContent=state.mode;
|
pillModeEl.textContent=state.mode;
|
||||||
promptTextEl.textContent=`raidlab(${vol})$`;
|
promptTextEl.textContent=`raidlab(${vol})$`;
|
||||||
if(!state.fs.created)fsChipEl.textContent="fs: none";
|
if(!state.fs.created)fsChipEl.textContent="fs: none";
|
||||||
@@ -3760,6 +3990,62 @@ $ rm /mnt/verifica.txt
|
|||||||
if(c0==="rm"&&t[1]){const r=fileRm(t[1]);if(r.msg)termPrint(r.msg,r.ok?"dim":"err");render();scenarioCheck();return;}
|
if(c0==="rm"&&t[1]){const r=fileRm(t[1]);if(r.msg)termPrint(r.msg,r.ok?"dim":"err");render();scenarioCheck();return;}
|
||||||
if(c0==="cat"&&t[1]&&t[1]!=="/proc/mdstat"){const r=fileCat(t[1]);termPrint(r.msg,r.ok?"dim":"err");render();scenarioCheck();return;}
|
if(c0==="cat"&&t[1]&&t[1]!=="/proc/mdstat"){const r=fileCat(t[1]);termPrint(r.msg,r.ok?"dim":"err");render();scenarioCheck();return;}
|
||||||
if(c0==="echo"){const gt=t.indexOf(">");if(gt<0)return termPrint(t[1],"dim");const text=t.slice(1,gt).join(" ");const path=t[gt+1];if(!path)return termPrint("echo: manca destinazione.","err");const r=fileEchoTo(text,path);if(r.msg)termPrint(r.msg,r.ok?"dim":"err");render();scenarioCheck();return;}
|
if(c0==="echo"){const gt=t.indexOf(">");if(gt<0)return termPrint(t[1],"dim");const text=t.slice(1,gt).join(" ");const path=t[gt+1];if(!path)return termPrint("echo: manca destinazione.","err");const r=fileEchoTo(text,path);if(r.msg)termPrint(r.msg,r.ok?"dim":"err");render();scenarioCheck();return;}
|
||||||
|
if(c0==="replace"&&t[1]==="cable"&&t[2]){
|
||||||
|
const dev=t[2].replace(/^\/dev\//,"");
|
||||||
|
const d=state.disks.find(x=>x.dev==="/dev/"+dev)||state.disks.find(x=>x.dev===dev);
|
||||||
|
if(!d)return termPrint(`replace cable: /dev/${dev} non trovato nell'array.`,"err");
|
||||||
|
if(d.state!==DiskState.CRC)return termPrint(`replace cable: /dev/${dev} non è in stato CRC. Questo comando serve solo per dischi con errori CRC.`,"warn");
|
||||||
|
const oldCrc=d.smart.crc;
|
||||||
|
d.smart.crc=0;
|
||||||
|
d.state=DiskState.OK;
|
||||||
|
pushDmesg("info",`/dev/${dev}: SATA cable replaced — link reset, CRC counter cleared`);
|
||||||
|
pushDmesg("info",`ata${d.id+1}.00: SATA link up (cable substitution, ok)`);
|
||||||
|
termPrint(
|
||||||
|
`[sostituzione cavo SATA su /dev/${dev}]\n`+
|
||||||
|
` Cavo vecchio rimosso, nuovo cavo installato.\n`+
|
||||||
|
` Link SATA reset: il controller reinizializza il canale.\n`+
|
||||||
|
` UDMA_CRC_Error_Count: ${oldCrc} → 0 (azzerato dal reset)\n`+
|
||||||
|
` Stato disco: CRC → OK\n`+
|
||||||
|
`\n`+
|
||||||
|
`→ Monitorare nelle prossime ore con 'smartctl -a /dev/${dev}'\n`+
|
||||||
|
` Se CRC rimane 0: il problema era il cavo (risolto).\n`+
|
||||||
|
` Se CRC torna a crescere: il problema è nel disco o nel controller.`,
|
||||||
|
"ok"
|
||||||
|
);
|
||||||
|
if(state.scenario&&state.scenario.name==="crc_errors"){
|
||||||
|
if(!state.scenario.checkpoints.cableReplaced){state.scenario.checkpoints.cableReplaced=true;addScore(8,"(scenario) cavo SATA sostituito correttamente.");}
|
||||||
|
if(!state.scenario.checkpoints.explained){state.scenario.checkpoints.explained=true;addScore(6,"(scenario) risoluzione applicata.");}
|
||||||
|
}
|
||||||
|
render();scenarioCheck();return;
|
||||||
|
}
|
||||||
|
if(c0==="cool"&&t[1]==="down"&&t[2]){
|
||||||
|
const dev=t[2].replace(/^\/dev\//,"");
|
||||||
|
const d=state.disks.find(x=>x.dev==="/dev/"+dev)||state.disks.find(x=>x.dev===dev);
|
||||||
|
if(!d)return termPrint(`cool down: /dev/${dev} non trovato nell'array.`,"err");
|
||||||
|
if(d.state!==DiskState.OVERHEAT)return termPrint(`cool down: /dev/${dev} non è in stato OVERHEAT. Questo comando serve solo per dischi surriscaldati.`,"warn");
|
||||||
|
const oldTemp=d.smart.temp;
|
||||||
|
d.smart.temp=38+(d.id%4);
|
||||||
|
d.state=DiskState.OK;
|
||||||
|
pushDmesg("info",`/dev/${dev}: temperature normalized (${oldTemp}°C → ${d.smart.temp}°C)`);
|
||||||
|
pushDmesg("info",`ata${d.id+1}.00: temperature within safe range — thermal throttling removed`);
|
||||||
|
termPrint(
|
||||||
|
`[intervento di raffreddamento su /dev/${dev}]\n`+
|
||||||
|
` Azioni simulate: pulizia ventole, spaziatura dischi, miglioramento airflow.\n`+
|
||||||
|
` Temperatura: ${oldTemp}°C → ${d.smart.temp}°C (zona ottimale: 20-45°C)\n`+
|
||||||
|
` Throttling termico: rimosso\n`+
|
||||||
|
` Stato disco: OVERHEAT → OK\n`+
|
||||||
|
`\n`+
|
||||||
|
`→ Il disco è di nuovo operativo, ma va monitorato.\n`+
|
||||||
|
` Consiglio: pianificare sostituzione preventiva (disco già termicamente stressato).\n`+
|
||||||
|
` Usa 'smartctl -a /dev/${dev}' per verificare gli altri attributi SMART.`,
|
||||||
|
"ok"
|
||||||
|
);
|
||||||
|
if(state.scenario&&state.scenario.name==="overheat"){
|
||||||
|
if(!state.scenario.checkpoints.cooled){state.scenario.checkpoints.cooled=true;addScore(8,"(scenario) raffreddamento applicato correttamente.");}
|
||||||
|
if(!state.scenario.checkpoints.explained){state.scenario.checkpoints.explained=true;addScore(6,"(scenario) risoluzione applicata.");}
|
||||||
|
}
|
||||||
|
render();scenarioCheck();return;
|
||||||
|
}
|
||||||
if(c0==="conclude"){
|
if(c0==="conclude"){
|
||||||
const text=t.slice(1).join(" ");if(!text)return termPrint("Uso: conclude <testo spiegazione>","err");
|
const text=t.slice(1).join(" ");if(!text)return termPrint("Uso: conclude <testo spiegazione>","err");
|
||||||
termPrint("Annotazione registrata (didattica).","ok");
|
termPrint("Annotazione registrata (didattica).","ok");
|
||||||
@@ -3782,13 +4068,14 @@ $ rm /mnt/verifica.txt
|
|||||||
}
|
}
|
||||||
function teacherDecode(raw){
|
function teacherDecode(raw){
|
||||||
try{
|
try{
|
||||||
const s=raw.trim().toUpperCase().replace(/\s/g,"");
|
const s=raw.trim().replace(/\s/g,"");
|
||||||
if(!s.startsWith("TC-"))return null;
|
// Accetta sia maiuscolo che minuscolo per il prefisso TC-
|
||||||
const inner=s.slice(3);
|
if(!s.toUpperCase().startsWith("TC-"))return null;
|
||||||
|
const inner=s.slice(3); // preserva il case originale del base64
|
||||||
const lastDash=inner.lastIndexOf("-");
|
const lastDash=inner.lastIndexOf("-");
|
||||||
if(lastDash<0)return null;
|
if(lastDash<0)return null;
|
||||||
const b64=inner.slice(0,lastDash);
|
const b64=inner.slice(0,lastDash); // base64: case-sensitive, non toccare
|
||||||
const cs2=inner.slice(lastDash+1);
|
const cs2=inner.slice(lastDash+1).toUpperCase(); // checksum: solo questo in maiuscolo
|
||||||
const alpha="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
const alpha="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||||
let cs=0; for(let i=0;i<b64.length;i++)cs=(cs+b64.charCodeAt(i))%1296;
|
let cs=0; for(let i=0;i<b64.length;i++)cs=(cs+b64.charCodeAt(i))%1296;
|
||||||
const c1=alpha[Math.floor(cs/36)], c2=alpha[cs%36];
|
const c1=alpha[Math.floor(cs/36)], c2=alpha[cs%36];
|
||||||
@@ -3853,15 +4140,29 @@ $ rm /mnt/verifica.txt
|
|||||||
capSpan.textContent=tfCapacityText(level,n,parseInt($("tfDiskSize").value)||1000)+" —";
|
capSpan.textContent=tfCapacityText(level,n,parseInt($("tfDiskSize").value)||1000)+" —";
|
||||||
preview.appendChild(capSpan);
|
preview.appendChild(capSpan);
|
||||||
|
|
||||||
// salva selezioni precedenti
|
// salva selezioni precedenti (leggi dal data-value del cdd-btn)
|
||||||
const existing={};
|
const existing={};
|
||||||
$("tfDiskFaultRows").querySelectorAll(".tf-fault-select").forEach(sel=>{
|
$("tfDiskFaultRows").querySelectorAll(".cdd-btn").forEach(btn=>{
|
||||||
existing[sel.dataset.dev]=sel.value;
|
existing[btn.dataset.dev]=btn.dataset.value||"";
|
||||||
});
|
});
|
||||||
|
|
||||||
// genera chip preview e righe fault
|
// genera chip preview e righe fault
|
||||||
const faultGrid=$("tfDiskFaultRows");
|
const faultGrid=$("tfDiskFaultRows");
|
||||||
faultGrid.innerHTML="";
|
faultGrid.innerHTML="";
|
||||||
|
|
||||||
|
// chiudi tutti i dropdown aperti al click fuori
|
||||||
|
if(!window._cddDocListener){
|
||||||
|
window._cddDocListener=true;
|
||||||
|
document.addEventListener("click",(e)=>{
|
||||||
|
if(!e.target.closest(".cdd-wrap")){
|
||||||
|
document.querySelectorAll(".cdd-menu.open").forEach(m=>{
|
||||||
|
m.classList.remove("open");
|
||||||
|
m.previousElementSibling&&m.previousElementSibling.classList.remove("open");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
for(let i=0;i<n;i++){
|
for(let i=0;i<n;i++){
|
||||||
const dev="/dev/"+devNames[i];
|
const dev="/dev/"+devNames[i];
|
||||||
const chip=document.createElement("span");
|
const chip=document.createElement("span");
|
||||||
@@ -3873,33 +4174,74 @@ $ rm /mnt/verifica.txt
|
|||||||
// riga fault
|
// riga fault
|
||||||
const row=document.createElement("div");
|
const row=document.createElement("div");
|
||||||
row.className="tf-fault-row";
|
row.className="tf-fault-row";
|
||||||
|
|
||||||
|
// label disco
|
||||||
const devLabel=document.createElement("span");
|
const devLabel=document.createElement("span");
|
||||||
devLabel.className="tf-fault-dev";
|
devLabel.className="tf-fault-dev";
|
||||||
|
devLabel.style.color="rgba(255,255,255,.92)";
|
||||||
devLabel.textContent=dev;
|
devLabel.textContent=dev;
|
||||||
const sel=document.createElement("select");
|
|
||||||
sel.className="tf-fault-select";
|
// ── custom dropdown ──
|
||||||
sel.dataset.dev=dev;
|
const savedVal=existing[dev]||"";
|
||||||
sel.dataset.idx=String(i);
|
const wrap=document.createElement("div");
|
||||||
|
wrap.className="cdd-wrap";
|
||||||
|
|
||||||
|
const btn=document.createElement("div");
|
||||||
|
btn.className="cdd-btn"+(savedVal?` val-${savedVal}`:"");
|
||||||
|
btn.dataset.dev=dev;
|
||||||
|
btn.dataset.idx=String(i);
|
||||||
|
btn.dataset.value=savedVal;
|
||||||
|
btn.innerHTML=`<span class="cdd-label">${faultLabels[savedVal]||faultLabels[""]}</span>`+
|
||||||
|
`<svg class="cdd-arrow" width="11" height="7" viewBox="0 0 11 7" fill="none">`+
|
||||||
|
`<path d="M1 1l4.5 4.5L10 1" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>`;
|
||||||
|
|
||||||
|
const menu=document.createElement("div");
|
||||||
|
menu.className="cdd-menu";
|
||||||
Object.entries(faultLabels).forEach(([val,label])=>{
|
Object.entries(faultLabels).forEach(([val,label])=>{
|
||||||
const opt=document.createElement("option");
|
const opt=document.createElement("div");
|
||||||
opt.value=val; opt.textContent=label;
|
opt.className="cdd-opt"+(val===savedVal?" selected":"");
|
||||||
sel.appendChild(opt);
|
opt.dataset.val=val;
|
||||||
});
|
opt.textContent=label;
|
||||||
// ripristina selezione precedente se stesso disco
|
opt.addEventListener("click",(e)=>{
|
||||||
if(existing[dev])sel.value=existing[dev];
|
e.stopPropagation();
|
||||||
const desc=document.createElement("span");
|
const prev=btn.dataset.value||"";
|
||||||
desc.className="tf-fault-desc";
|
btn.dataset.value=val;
|
||||||
desc.textContent=faultDescriptions[sel.value]||"";
|
btn.querySelector(".cdd-label").textContent=label;
|
||||||
sel.addEventListener("change",()=>{
|
btn.className="cdd-btn"+(val?` val-${val}`:"");
|
||||||
desc.textContent=faultDescriptions[sel.value]||"";
|
menu.querySelectorAll(".cdd-opt").forEach(o=>o.classList.toggle("selected",o.dataset.val===val));
|
||||||
// aggiorna colore select
|
menu.classList.remove("open");
|
||||||
sel.className="tf-fault-select"+(sel.value?` sel-${sel.value}`:"");
|
btn.classList.remove("open");
|
||||||
|
// aggiorna desc
|
||||||
|
desc.textContent=faultDescriptions[val]||"";
|
||||||
// aggiorna chip preview
|
// aggiorna chip preview
|
||||||
const chipEl=$(`tf-chip-${i}`);
|
const chipEl=$(`tf-chip-${i}`);
|
||||||
if(chipEl)chipEl.className="tf-disk-chip"+(sel.value?` fault-${sel.value}`:"");
|
if(chipEl)chipEl.className="tf-disk-chip"+(val?` fault-${val}`:"");
|
||||||
});
|
});
|
||||||
|
menu.appendChild(opt);
|
||||||
|
});
|
||||||
|
|
||||||
|
btn.addEventListener("click",(e)=>{
|
||||||
|
e.stopPropagation();
|
||||||
|
const wasOpen=menu.classList.contains("open");
|
||||||
|
// chiudi tutti gli altri
|
||||||
|
document.querySelectorAll(".cdd-menu.open").forEach(m=>{
|
||||||
|
m.classList.remove("open");
|
||||||
|
m.previousElementSibling&&m.previousElementSibling.classList.remove("open");
|
||||||
|
});
|
||||||
|
if(!wasOpen){ menu.classList.add("open"); btn.classList.add("open"); }
|
||||||
|
});
|
||||||
|
|
||||||
|
wrap.appendChild(btn);
|
||||||
|
wrap.appendChild(menu);
|
||||||
|
|
||||||
|
// desc
|
||||||
|
const desc=document.createElement("span");
|
||||||
|
desc.className="tf-fault-desc";
|
||||||
|
desc.style.color="rgba(255,255,255,.75)";
|
||||||
|
desc.textContent=faultDescriptions[savedVal]||"";
|
||||||
|
|
||||||
row.appendChild(devLabel);
|
row.appendChild(devLabel);
|
||||||
row.appendChild(sel);
|
row.appendChild(wrap);
|
||||||
row.appendChild(desc);
|
row.appendChild(desc);
|
||||||
faultGrid.appendChild(row);
|
faultGrid.appendChild(row);
|
||||||
}
|
}
|
||||||
@@ -3918,8 +4260,9 @@ $ rm /mnt/verifica.txt
|
|||||||
|
|
||||||
// raccogli guasti
|
// raccogli guasti
|
||||||
const faults=[];
|
const faults=[];
|
||||||
$("tfDiskFaultRows").querySelectorAll(".tf-fault-select").forEach(sel=>{
|
$("tfDiskFaultRows").querySelectorAll(".cdd-btn").forEach(btn=>{
|
||||||
if(sel.value) faults.push({idx:parseInt(sel.dataset.idx),dev:sel.dataset.dev,type:sel.value});
|
const val=btn.dataset.value||"";
|
||||||
|
if(val) faults.push({idx:parseInt(btn.dataset.idx),dev:btn.dataset.dev,type:val});
|
||||||
});
|
});
|
||||||
|
|
||||||
// validazione
|
// validazione
|
||||||
@@ -3977,7 +4320,7 @@ $ rm /mnt/verifica.txt
|
|||||||
state.disks=Array.from({length:n},(_,i)=>makeDisk(i,size));
|
state.disks=Array.from({length:n},(_,i)=>makeDisk(i,size));
|
||||||
state.spares=[];
|
state.spares=[];
|
||||||
if(rebuildTimer){clearInterval(rebuildTimer);rebuildTimer=null;}
|
if(rebuildTimer){clearInterval(rebuildTimer);rebuildTimer=null;}
|
||||||
state.rebuild={active:false,start:0,etaSec:0,progress:0,realSec:0,speedFactor:1};
|
state.rebuild={active:false,start:0,etaSec:0,progress:0};
|
||||||
state.score=0;state.hints=0;state.actions=[];
|
state.score=0;state.hints=0;state.actions=[];
|
||||||
state.mode="EXERCISE";state.exerciseOn=true;
|
state.mode="EXERCISE";state.exerciseOn=true;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user