Introduzione
Tra venerdì e questa mattina ho risolto un problema che da parecchio tempo
assillava il Tech-Team, la squadra di esperti IT certificati MS/Oracle interna
all'azienda dove lavoro. Immaginate lo scenario: un super-server
- di cui purtroppo non so darvi le caratteristiche hardware precise -
su cui gira Virtual Server 2005, in grado di ospitare qualcosa come circa una quindicina
di macchine virtuali attive più o meno contemporaneamente. Il problema è che
molto spesso queste VM rimangono attive anche quando non effettivamente
necessarie, rubando memoria e risorse a tutte le altre.
Il Tech-Team ha ricercato per molto tempo in rete un'utility che potesse scatenare lo
shutdown di una macchina, quando questa risulta inattiva per un po' di tempo
(con un po' di tempo intendo un periodo di tempo variabile da
VM a VM), ma con scarso risultato. La maggior parte delle
utility infatti controllano tastiera e/o mouse, ma questo non va sempre bene, perchè in alcuni
casi ci si collega via TS e non ci si trova fisicamente davanti al PC
in questione. Venerdì mattina, quasi per caso, ci siamo ritrovati davanti alla macchina del
caffè ed abbiamo discusso di questo problema. Mi sono quindi offerto volontario per dare
una mano, sviluppando un'applicazione managed capace di monitorare il periodo di
idle del sistema (occupazione della CPU) e di spegnere la VM quando
necessario.
Performance Counter
Ho
utilizzato un Performance Counter (PC) per attivare il monitoraggio
dell'occupazione della CPU.
PerformanceCounter cpu;
cpu = new PerformanceCounter();
cpu.CategoryName = "Processor";
cpu.CounterName = "% Processor Time";
cpu.InstanceName = "_Total";
Inizialmente avevo sviluppato un'applicazione Windows Forms, per una mia
predisposizione naturale, poi sono passato alla creazione di un Windows Service
che è più consono allo scopo preposto. Comunque sia, ho utilizzato un
System.Timers.Timer per ottenere ad intervalli regolari il valore del
PC:
float value = cpu.NextValue();
Non voglio illustrarvi la logica in base alla quale scateno lo shutdown,
perchè è talmente particolare e ben congegnata che la considero mia proprietà
intellettuale, insieme alle persone con le quali l'ho studiata. Vi basti sapere
che lo shutdown avviene ovviamente dopo un certo periodo di tempo nel quale la
CPU rimane al di sotto di una certa soglia (ma ci sono di mezzo comunque altre
logiche).
Grazie ad una classe che viene serializzata/deserializzata su un file XML,
salvo e carico i parametri in base ai quali il servizio decide come comportarsi.
Questo mi permette di decidere, ad esempio, qual'è il valore di soglia della CPU
(per determinare quando considerare una certa VM come inattiva), l'intervallo di
campionamento, se avere il log attivo o meno, etc. etc. Il log viene scritto fra
i log di sistema attraverso il componente EventLog trascinato banalmente sul
designer del servizio stesso: dal momento che gli utenti di tale servizio
saranno sistemisti, è molto più semplice metterli in condizione di usare uno
strumento come l'Event Viewer che conoscono alla perfezione, piuttosto che
inventarsi qualcosa da zero di sana pianta.
Windows Service e le Exception non gestite
Questa mattina
ho creato il mio primo Windows Service davvero utile a qualcuno. Non ho trovato grandi difficoltà, è
sufficiente sfruttare il template del progetto disponibile in VS2005 e fare
l'override dei metodi:
protected override void OnStart(string[] args)
{ }
protected override void OnStop()
{ }
La cosa comoda è che possiamo eventualmente sostituire in tempo reale il file
EXE appartenente al servizio, prelevandolo dalla directory dove è stato appena
compilato e copiandolo nella directory dove il servizio è stato installato.
Ovviamente, è necessario che il servizio sia stoppato, altrimenti il
file EXE risulta in uso.
Se posso darvi un consiglio, gestite tutti gli errori nello Start e nello
Stop, perchè se vi scappa una Exception non gestita non riuscirete più ad
avviare il servizio o a stopparlo. Quest'ultimo caso è un po' più grave, perchè
non riuscirete più a sostituire l'eseguibile e dovrete per forza riavviare il PC
(se il servizio è impostato come avvio manuale).
E fare fare lo shutdown?
Per spegnere la VM, ho
utilizzato questa classe WindowsController, scaricabile liberamente da
qui. Questa classe dispone di un set di metodi statici che, sfruttando
P/Invoke, danno la possibilità di fare una cosa tipo questa:
WindowsController.ExitWindows(RestartOptions.PowerOff, true);
Da notare che RestartOptions è una Enum che contiene le seguenti voci:
public enum RestartOptions
{
LogOff = 0,
PowerOff = 8,
Reboot = 2,
ShutDown = 1,
Suspend = -1,
Hibernate = -2
}
Il Tech-Team è davvero contento, adesso. Hanno girovagato per la Rete senza
bussola, senza sapere che la soluzione era a portata di mano, bisognava soltanto
raggiungerla!