Buongiorno Mr Script! Il calcolo del tempo di attività server

The Microsoft Scripting Guys

alto è attiva e down verso il basso. Sembra piuttosto ovvia, tranne, ovvero quando sta parlando di tempo di attività server. Conoscere il tempo di attività, è necessario conoscere i tempi di inattività. Quasi ogni amministratore di rete è preoccupato per il tempo di attività server. (Tranne che, ovvero una volta ha worried sulla inattività del server.) Maggior parte degli amministratori sono gli obiettivi di tempo di attività e necessario fornire il tempo di attività di report per gestione superiore.

Qual è la quantità di grandi? Sembra che è possibile utilizzare la classe WMI Win32_OperatingSystem, che dispone di due proprietà che dovrebbe rendere tale operazione piuttosto semplice: LastBootUpTime e LocalDateTime. Per eseguire, si ritiene che, è sufficiente sottrarre LastBootUptime da LocalDateTime, quindi è anche possibile passare intercettare un rapido fori nove prima cena tutto è corretto con il mondo.

Così generato fino a Windows PowerShell per eseguire query alla classe WMI Win32_OperatingSystem e selezionare le proprietà, come illustrato di seguito:

PS C:\> $wmi = Get-WmiObject -Class Win32_OperatingSystem
PS C:\> $wmi.LocalDateTime - $wmi.LastBootUpTime

Ma quando si eseguono questi comandi, sono visualizzata non con il tempo di attività descrittivo del server, ma il messaggio di errore invece mournful è visualizzare in Nella figura 1 .

fig01.gif

Nella figura 1 viene restituito un errore durante il tentativo di sottrazione di valori ora UTC WMI fare clic su Immagine per una visualizzazione ingrandita

Il messaggio di errore è probabilmente un po'fuorviante: "non valida costante numerica." ? Si sa come un numero è e si sa come è una costante, ma ciò che questo dispone per volta?

Durante il corso con messaggi di errore strano, è consigliabile esaminare direttamente i dati che sta tentando di analizzare lo script. Inoltre, con Windows PowerShell è in genere opportuno visualizzare utilizzato il tipo di dati.

Per esaminare i dati che è mediante lo script, è possibile semplicemente stamparla sullo schermo. Ecco what you get:

PS C:\> $wmi.LocalDateTime
20080905184214.290000-240

Il numero sembra un po'strane. Il tipo di una data si tratta? Per individuare, utilizzare il metodo GetType. La buona cosa Sull'gettype è che quasi sempre disponibile. È necessario eseguire è chiamare. Di seguito viene l'origine del problema, il valore LocalDateTime è essere riportato come stringa, non come valore System.DateTime:

PS C:\> $wmi.LocalDateTime.gettype()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True     True     String
System.Object

Se si desidera sottrarre una volta da un altro momento, assicurarsi che sta gestione i valori di ora e non le stringhe. Questo è semplice eseguire utilizzando il metodo ConvertToDateTime, che Windows PowerShell aggiunge tutte le classi WMI:

PS C:\> $wmi = Get-WmiObject -Class Win32_OperatingSystem
PS C:\> $wmi.ConvertToDateTime($wmi.LocalDateTime) –
$wmi.ConvertToDateTime($wmi.LastBootUpTime)

Quando si sottrae un valore di ora da un altro, rimangono con un'istanza di un oggetto System.TimeSpan. Ciò significa che è possibile scegliere come visualizzare le informazioni del tempo di attività senza dover eseguire numerose operazioni aritmetiche. È necessario selezionare solo la proprietà da visualizzare (e probabilmente è possibile contare il tempo di attività nel TotalDays e non nel TotalMilliseconds). La visualizzazione predefinita dell'oggetto System.TimeSpan è illustrata di seguito:

Days              : 0
Hours             : 0
Minutes           : 40
Seconds           : 55
Milliseconds      : 914
Ticks             : 24559148010
TotalDays         : 0.0284249398263889
TotalHours        : 0.682198555833333
TotalMinutes      : 40.93191335
TotalSeconds      : 2455.914801
TotalMilliseconds : 2455914.801

Il problema con questo metodo è che solo indica quanto tempo il server è stato dall'ultimo riavvio. Non Calcola tempo di inattività. È in cui alto verso il basso, per calcolare il tempo di attività, è necessario conoscere il tempo di inattività.

Come scoprire quanto tempo il server è stato premuto A tale scopo, è necessario conoscere quando si inizia il server e quando Arresta. È possibile ottenere queste informazioni dal Registro eventi sistema. Uno dei processi primo che inizia nel server o workstation è il registro eventi e uno degli aspetti ultimi che interrompono quando viene arrestato un server è il registro eventi. Ciascuna di esse avviare o arrestare eventi genera un event­ID, 6005 all'avvio il registro eventi e 6006 quando si interrompe il registro eventi. Nella figura 2 viene illustrato un esempio di un registro eventi di avvio.

fig02.gif

Nella figura 2 il servizio Registro eventi viene avviato subito dopo l'avvio del computer fare clic su Immagine per una visualizzazione ingrandita

Raccolta degli eventi 6005 e 6006 dal Registro di sistema, l'ordinamento e sottrazione inizia dall'si interrompe, è possibile determinare del tempo che il server non è verso il basso tra riavvio. Se quindi si sottrarre Importo dal numero di minuti durante il periodo di tempo in questione, è possibile calcolare la percentuale di tempo di attività server. Si tratta dell'approccio adottato nello script di CalculateSystemUpTimeFromEventLog.ps1 illustrato nella Figura 3 .

Nella figura 3 CalculateSystemUpTimeFromEventLog 3

#---------------------------------------------------------------
# CalculateSystemUpTimeFromEventLog.ps1
# ed wilson, msft, 9/6/2008
# Creates a system.TimeSpan object to subtract date values
# Uses a .NET Framework class, system.collections.sortedlist to sort the events from eventlog.
#---------------------------------------------------------------
#Requires -version 2.0
Param($NumberOfDays = 30, [switch]$debug)

if($debug) { $DebugPreference = " continue" }

[timespan]$uptime = New-TimeSpan -start 0 -end 0
$currentTime = get-Date
$startUpID = 6005
$shutDownID = 6006
$minutesInPeriod = (24*60)*$NumberOfDays
$startingDate = (Get-Date -Hour 00 -Minute 00 -Second 00).adddays(-$numberOfDays)

Write-debug "'$uptime $uptime" ; start-sleep -s 1
write-debug "'$currentTime $currentTime" ; start-sleep -s 1
write-debug "'$startingDate $startingDate" ; start-sleep -s 1

$events = Get-EventLog -LogName system | 
Where-Object { $_.eventID -eq  $startUpID -OR $_.eventID -eq $shutDownID `
  -and $_.TimeGenerated -ge $startingDate } 

write-debug "'$events $($events)" ; start-sleep -s 1

$sortedList = New-object system.collections.sortedlist

ForEach($event in $events)
{
 $sortedList.Add( $event.timeGenerated, $event.eventID )
} #end foreach event
$uptime = $currentTime - $sortedList.keys[$($sortedList.Keys.Count-1)]
Write-Debug "Current uptime $uptime"

For($item = $sortedList.Count-2 ; $item -ge 0 ; $item -- )
{ 
 Write-Debug "$item `t `t $($sortedList.GetByIndex($item)) `t `
   $($sortedList.Keys[$item])" 
 if($sortedList.GetByIndex($item) -eq $startUpID)
 {
  $uptime += ($sortedList.Keys[$item+1] - $sortedList.Keys[$item])
  Write-Debug "adding uptime. `t uptime is now: $uptime"
 } #end if  
} #end for item 

"Total up time on $env:computername since $startingDate is " + "{0:n2}" -f `
  $uptime.TotalMinutes + " minutes."
$UpTimeMinutes = $Uptime.TotalMinutes
$percentDownTime = "{0:n2}" -f (100 - ($UpTimeMinutes/$minutesInPeriod)*100)
$percentUpTime = 100 - $percentDowntime

"$percentDowntime% downtime and $percentUpTime% uptime."

Lo script inizia utilizzando l'istruzione di parametro per definire un paio di parametri della riga di comando i cui valori modificabili quando si esegue lo script dalla riga di comando. Il primo, $ NumberOfDays, consente di specificare un numero di giorni da utilizzare nel report tempo di attività diverso. (Si noti che È specificato un valore predefinito di 30 giorni nello script è possibile eseguire lo script senza dover fornire un valore per il parametro. Naturalmente, è possibile modificare questo se necessario.)

Il secondo, [parametro] $ debug, è un parametro switched che consente di ottenere alcune informazioni di debug dello script se includerlo nella riga di comando quando si esegue lo script. Queste informazioni consentono di ritiene più sicuri nei risultati che è ottenere dallo script. Potrebbe essersi gli orari in cui il messaggio di interruzione del servizio del registro eventi 6006 non è presente, ad esempio a seguito di un errore irreversibile del server che rendering Impossibile scrivere nel log eventi, causando lo script per sottrarre un valore di tempo di attività da un altro valore di tempo di attività e inclinare i risultati.

Dopo la variabile di debug $ viene fornita dalla riga di comando, è presente della variabile: unità. In questo caso, il valore della variabile $ debugPreference è impostato per continuare, ovvero lo script continuerà a funzionare e qualsiasi valore fornito per il debug di scrittura sarà visibile. Si noti che, per impostazione predefinita, il valore di $ debugPreference è silentlycontinue, se non si imposta il valore di $ debugPreference per continuare, lo script verrà eseguito, ma qualsiasi valore specificato per il debug di scrittura sarà invisibile all'utente (ovvero non sarà visibile).

Quando viene eseguito lo script, l'output risultante sono elencati ogni occorrenza delle voci di log eventi 6005 e 6006 (come si vede nella Figura 4 ) e viene visualizzato il calcolo del tempo di attività. Utilizzando queste informazioni, è possibile verificare l'accuratezza dei risultati.

fig04.gif

Nella figura 4 modalità debug visualizza una traccia di ogni valore di ora che viene aggiunto il calcolo del tempo di attività fare clic su Immagine per una visualizzazione ingrandita

Il passaggio successivo consiste nel creare un'istanza dell'oggetto System.TimeSpan. È possibile utilizzare il cmdlet oggetto nuovo per creare un oggetto timespan predefinito che è necessario utilizzare per eseguire calcoli della data-differenza:

PS C:\> [timespan]$ts = New-Object system.timespan

Ma Windows PowerShell ha effettivamente un cmdlet TimeSpan nuovo per creare un oggetto timespan, pertanto è opportuno utilizzarla. Utilizzando il cmdlet semplifica lo script di lettura e l'oggetto creato è equivalente all'oggetto timespan creato con un oggetto nuovo.

A questo punto, è possibile inizializzare alcune variabili, a partire da currentTime di $, viene utilizzato per contenere il valore di data e ora corrente. È ottenere queste informazioni dal cmdlet Get-Data:

$currentTime = get-Date

Successivamente, inizializzare le due variabili contenenti i numeri di parametro eventID avvio e arresto del sistema. Non è effettivamente necessario eseguire questa operazione, ma il codice sarà più facile da leggere e più semplice di risoluzione dei problemi se evitare di incorporare i due come valori letterali stringa.

Il passaggio successivo consiste nel creare minutesInPeriod una variabile denominata $ per memorizzare il risultato del calcolo utilizzato per stabilire il numero di minuti durante il periodo di tempo in questione:

$minutesInPeriod = (24*60)*$NumberOfDays

Infine, è necessario creare la variabile $ startingDate, che conterrà un oggetto System.DateTime che rappresenta l'ora iniziale del periodo contabile. La data sarà dalla mezzanotte della data di inizio per il periodo:

$startingDate = (Get-Date -Hour 00 -Minute 00 -Second 00).adddays(-$numberOfDays)

Dopo aver create le variabili, è possibile recuperare gli eventi dal log eventi e memorizzare i risultati della query nella variabile $ eventi. Utilizzare il cmdlet Get-registro eventi per eseguire query nel log eventi, se si specifica "sistema" come nome del log. In Windows PowerShell 2.0, è possibile utilizzare un parametro –source per ridurre la quantità di informazioni che devono verrà ordinata nel cmdlet Where-Object. Ma in Windows PowerShell 1.0, non dispone di tale scelta e pertanto necessario ordinare tramite tutti gli eventi non filtrati restituiti dalla query. Pertanto, pipeline gli eventi per il cmdlet Where-Object per filtrare i movimenti del log eventi appropriato. Come si esamina il filtro WHERE-Object, verrà visualizzato perché gli Scripting Guys aveva consente di creare le variabili per contenere i parametri.

Il comando legge molto meglio rispetto a se è stato utilizzato stringhe letterali. L'eventIDs get sono uguali startUpID $ o $ shutDownID. Verificare inoltre la proprietà timeGenerated di voce del log eventi è maggiore o uguale a startingDate $, come segue:

$events = Get-EventLog -LogName system |
Where-Object { $_.eventID -eq  $startUpID -OR $_.eventID -eq $shutDownID -and $_.TimeGenerated -ge $startingDate }

Tenere presente che questo comando verrà eseguito solo localmente. In Windows Power­Shell 2.0, è possibile utilizzare il parametro –computerName per rendere il comando lavorare in modalità remota.

Il passaggio successivo consiste nel creare un oggetto elenco ordinato. Perché? Poiché quando scorrere l'insieme di eventi, non sono necessariamente ordine in cui le voci del registro eventi vengono segnalate. Anche se la pipeline degli oggetti per l'oggetto ordinamento cmdlet e un archivio che i risultati nuovamente in una variabile, quando si scorrere gli oggetti e memorizzare i risultati in una tabella hash è non può essere determinato che l'elenco gestisce i risultati della procedura ordinamento.

In per sidestep questi problemi frustrante e difficile da debug, è necessario creare un'istanza dell'oggetto System.Collections.SortedList utilizzando il costruttore predefinito per l'oggetto. Il costruttore predefinito indica l'elenco ordinato per ordinare le date in ordine cronologico. Memorizzare l'oggetto elenco ordinato vuoto nella variabile $ sortedList:

$sortedList = New-object system.collections.sortedlist

Dopo aver creato l'oggetto elenco ordinato, è necessario compilarlo. A tale scopo, utilizzare l'istruzione ForEach e scorrere l'insieme delle voci del registro eventi memorizzato nella variabile $ voci. Come scorrere l'insieme, la variabile di evento $ tiene traccia della posizione nell'insieme. È possibile utilizzare il metodo componente per aggiungere due proprietà all'oggetto System.Collections.SortedList. Elenco ordinato consente di aggiungere una chiave e una proprietà value (simile a un oggetto Dictionary, ad eccezione del fatto che consente inoltre indice nell'insieme come una matrice non). Aggiungere la proprietà timegenerated come la chiave ed event­ID come la proprietà di valore:

ForEach($event in $events)
{
 $sortedList.Add( $event.timeGenerated,
 $event.eventID )
} #end foreach event

Successivamente, è possibile calcolare il tempo di attività corrente del server. A tale scopo, è possibile utilizzare ultima voce del registro eventi nell'elenco ordinato. Si noti che questo sempre è un'istanza 6005 perché se la voce più recente 6006, il server sarebbe comunque verso il basso. Poiché l'indice è in base zero, la voce più recente sarà-1 il conteggio.

Per recuperare il valore generato per volta, è necessario esaminare la proprietà chiave dell'elenco ordinato. Per ottenere il valore di indice, utilizzare la proprietà count e sottrarre uno dalla. Quindi sottrarre l'ora che dell'evento 6005 è stato generato dal valore tempo Data memorizzato nella variabile $ currenttime compilato in precedenza. È possibile stampare i risultati di questo calcolo solo se lo script viene eseguito in modalità di debug. Questo codice è riportato di seguito:

$uptime = $currentTime -
$sortedList.keys[$($sortedList.Keys.Count-1)]
Write-Debug "Current uptime $uptime"

È ora possibile scorrere l'oggetto elenco ordinato e calcolare il tempo di attività per il server. Poiché si utilizza l'oggetto elenco System.Collections.Sorted, è sarà possibile sfruttare il fatto che è possibile indicizzare nell'elenco. A tale scopo, utilizzare il per istruzione, partire il conteggio-2 perché è utilizzato conteggio-1 precedente per la quantità di tempo di attività corrente.

Verrà per contare con le versioni precedenti per ottenere il tempo di attività, pertanto la condizione specificato nella seconda posizione dell'istruzione for è quando l'elemento è maggiore o uguale a 0. Nella terza posizione dell'istruzione si utilizza-, che verrà decrementare il valore dell'elemento $ da uno. Il cmdlet debug scrittura consente di stampare il valore del numero di indice se lo script viene eseguito con l'opzione –debug. Scheda anche su utilizzando il ` t carattere e stampa il valore di ora timegenerated. In questa sezione di codice è illustrata di seguito:

For($item = $sortedList.Count-2 ; $item -ge 
  0 ; $item--)
{ 
 Write-Debug "$item `t `t $($sortedList.
 GetByIndex($item)) `t `
   $($sortedList.Keys[$item])" 

Se il valore di parametro eventID è uguale a 6005, ovvero il valore di parametro eventID di avvio, è possibile calcolare la quantità di tempo di attività sottraendo il tempo di avvio dal valore i tempi di inattività precedente. Memorizzare questo valore nella variabile $ tempo di attività. Se si è in modalità di debug, è possibile utilizzare il cmdlet di debug di scrittura per stampare questi valori dello stesso sullo schermo:

 if($sortedList.GetByIndex($item) -eq $startUpID)
 {
  $uptime += ($sortedList.Keys[$item+1] 
  - $sortedList.Keys[$item])
  Write-Debug "adding uptime. `t uptime is now: $uptime"
 } #end if  
} #end for item

Infine, è necessario generare il report. Prendere nome computer dal computer variabile di ambiente di sistema. Utilizzare l'ora corrente archiviato nel valore del startingdate $ e visualizzare i totali minuti di tempo di attività per il periodo. L'identificatore di formato {0:n2} consente di stampare il numero a due cifre. Successivamente, calcolare la percentuale del tempo di inattività dividendo il numero di minuti di tempo di attività per il numero di minuti nel periodo interessato dal report. Utilizzare l'identificatore di formato stesso per stampare il valore a due cifre decimali. Per ampliare ulteriormente le vostre conoscenze, è possibile inoltre calcolare la percentuale di tempo di attività e quindi stampare entrambi i valori in questo modo:

"Total up time on $env:computername since $startingDate is " + "{0:n2}" -f `
  $uptime.TotalMinutes + " minutes."
$UpTimeMinutes = $Uptime.TotalMinutes
$percentDownTime = "{0:n2}" -f (100 - ($UpTimeMinutes/$minutesInPeriod)*100)
$percentUpTime = 100 - $percentDowntime
"$percentDowntime% downtime and $percentUpTime% uptime."

In questo caso ora Scripting Guys tornare alla domanda originale: quando si è alto verso il basso? È ora possibile visualizzare è non parlare tempo di attività senza eseguire i tempi di inattività in considerazione. Se è considerata che questa era divertente, consultare l'altro " Hey, Scripting Guy " colonne in TechNet o Vai al Script Center.

Problemi di versione

Durante la verifica lo script Calculate­System­Up­timeFromEventLog.ps1 sul suo computer portatile, con editor Michael Murgolo stato eseguito su un errore più irritante. Lo script È assegnato a mio amico JIT e ha eseguito nello stesso errore. Ciò che era tale errore? Anche in questo campo è:

PS C:\> C:\fso\CalculateSystemUpTimeFromEventLog.ps1
Cannot index into a null array.
At C:\fso\CalculateSystemUpTimeFromEventLog.ps1:36 char:43 + $uptime = 
$currentTime - $sortedList.keys[$ <<<< ($sortedList.Keys.Count-1)]
Total up time on LISBON since 09/02/2008 00:00:00 is 0.00 minutes.
100.00% downtime and 0% uptime.

L'errore "Impossibile indicizzare in una matrice null," viene indica che la matrice non è stato creato correttamente. Pertanto, è possibile dug nel codice che crea la matrice:

ForEach($event in $events)
{
 $sortedList.Add( $event.timeGenerated,
 $event.eventID )
} #end foreach event

In conclusione, tale codice è fine. Ciò che è stato hanno provocato l'errore?

Successivamente, HO deciso di esaminare l'oggetto della classe SortedList. A tale scopo, HO scritto un semplice script che crea un'istanza della classe System.Collections.SortedList e aggiunge alcune informazioni. A questo punto, è possibile utilizzare la proprietà delle chiavi per stampare l'elenco delle chiavi. Di seguito è che il codice:

$aryList = 1,2,3,4,5
$sl = New-Object Collections.SortedList
ForEach($i in $aryList)
{
 $sl.add($i,$i)
}

$sl.keys

Nel computer, questo codice funziona correttamente. Nel computer del JIT, Impossibile. Bummer. Ma almeno punta mi nella direzione giusta. Il problema, come si scopre, è che è un bug con System.Collections.SortedList in Windows PowerShell 1.0. E eseguita sia in esecuzione la versione più recente dell'ancora rilasciata da Windows PowerShell 2.0 in cui tale bug è stato corretto e quindi il codice viene eseguito correttamente.

Pertanto, in cui che lasciare lo script? Come si scopre, la classe SortedList dispone di un metodo denominato GetKey e tale metodo funziona su sia Windows Power­Shell 1.0 e 2.0 di Power­Shell di Windows. Per la versione 1.0 dello script, è pertanto modificare il codice per l'utilizzo di GetKey anziché di scorre l'insieme di chiavi. Nella versione 2.0 dello script, è possibile aggiungere un tag che richiede la versione 2.0 di Windows PowerShell. Se si tenta di eseguire questo script su un computer Windows PowerShell 1.0, lo script terminerà semplicemente e non sarà possibile ottenere l'errore.

Michael anche indicato da un'operazione che non è un bug ma è correlato a un fattore di progettazione. Si noti che lo script non correttamente rileverà tempo di attività se si ibernazione o sospensione del computer. Questo è su true, è non rilevare o cercare questi eventi.

In realtà, tuttavia, non non è ritiene con tempi di attività in mio computer desktop o portatile. Desidero solo nel tempo di attività su un server e devono ancora soddisfare il server che è in modalità sospensione o entra in ibernazione. Il problema può verificarsi, naturalmente e potrebbe essere un modo interessante per risparmiare energia elettrica nel centro dati, ma non qualcosa che È stato rilevato ancora. Consente di sapere se ibernazione i server. È possibile raggiungere me in Scripter@Microsoft.com.

Dr. Perplexer Scripting Scripto

La richiesta mensile di test non solo la possibilità di risolvere puzzle, ma anche le proprie competenze di script.

Dicembre 2008: comandi PowerShell

L'elenco seguente contiene i comandi di Windows PowerShell 21. Il quadrato contiene gli stessi comandi, ma sono nascosti. Il processo è trovare i comandi che potrebbero essere nascosti in senso orizzontale, verticale o in diagonale il puntatore del mouse avanti o indietro.

CSV EXPORT ELENCO DI FORMATO TABLE FORMAT
ELENCO DI CONTROLLO DI ACCESSO GET ALIAS GET CHILDITEM GET
UBICAZIONE GET ARTICOLO RICHIAMARE OBJECT MISURE
NEW ITEMPROPERTY OUT-HOST OUT-NULL
PSSNAPIN REMOVE ELENCO DI CONTROLLO DI ACCESSO SET TRACESOURCE SET
PERCORSO DI SPLIT SOSPENSIONE START SERVIZIO DI STOP
SERVIZIO DI SOSPENSIONE DEBUG DI SCRITTURA AVVISO SCRITTURA

\\msdnmagtst\MTPS\TechNet\issues\en\2008\12\HeyScriptingGuy - 1208\Figures\puzzle.gif

RISPOSTA:

Dr. Perplexer Scripting Scripto

Risposta: dicembre 2008: comandi PowerShell

fig12.gif

Wilson ed è un consulente senior in Microsoft e un esperto di script noto. È un trainer Certified Microsoft che recapita un workshop di Windows PowerShell comuni clienti Premier Microsoft nel mondo. Ha scritto libri otto inclusi diversi in Windows script e ha contribuito a libri quasi una dozzina. Ed contiene più di 20 certificazioni del settore. Craig Liebendorfer è wordsmith e longtime editor Web di Microsoft. Craig ancora non ritiene c'è un processo che paga gli lavorare con le parole ogni giorno. Uno dei suoi elementi preferiti è irreverent umorismo in modo che deve adattare destra in questa posizione. Si considera sua figlia magnificent da suoi accomplishment maggiore nella vita.

Craig Liebendorfer è wordsmith e longtime editor Web di Microsoft. Craig ancora can’t ritiene ’s esiste un processo che paga gli lavorare con le parole ogni giorno. Uno dei suoi elementi preferiti è irreverent umorismo in modo che deve adattare destra in questa posizione. Si considera sua figlia magnificent da suoi accomplishment maggiore nella vita.