Hé, le scripteur ! Calcul de disponibilité de serveur
The Microsoft Scripting Guys
haut est inscrit et vers le bas est arrêté. Semble plutôt évident, sauf, c'est-à-dire, lorsque vous parlons disponibilité du serveur. Connaître la disponibilité, vous devez connaître le temps d'arrêt. Presque chaque administrateur de réseau s'inquiète de disponibilité du serveur. (Sauf, c'est-à-dire, lorsqu'il est soucieux sur Arrêt du serveur.) La plupart des administrateurs ont des objectifs de disponibilité et devez fournir des rapports de disponibilité pour gestion supérieure.
Quel est le nombre big ? Il semble que vous pouvez utiliser la classe WMI Win32_OperatingSystem, qui possède deux propriétés qui doivent rendent cette opération assez simple : LastBootUpTime et LocalDateTime. Tout ce que vous avez à faire, vous pensez, est soustraire la LastBootUptime à partir de la LocalDateTime, tout est de droite avec le monde et vous pouvez même accéder intercepter un neuf failles rapide avant dîner.
Donc déclenche des Windows PowerShell pour interroger la classe WMI Win32_OperatingSystem et sélectionnez les propriétés, comme illustré ici :
PS C:\> $wmi = Get-WmiObject -Class Win32_OperatingSystem
PS C:\> $wmi.LocalDateTime - $wmi.LastBootUpTime
Mais lorsque vous exécutez ces commandes, vous êtes accueilli pas avec le temps de fonctionnement convivial de votre serveur, mais par le message d'erreur plutôt mournful vous voyez dans La figure 1 .
La figure 1 une erreur est a renvoyé en essayant de soustraire des valeurs d'heure UTC WMI (cliquez sur l'image pour l'agrandir)
Le message d'erreur est peut-être un peu trompeur: « incorrecte constante numérique ». Hein ? Vous savez que, alors qu'un nombre et vous savez qu'une constante est, mais que ce avez à faire avec le temps ?
Lorsque confronté messages d'erreur étrange, il est préférable d'afficher directement les données que le script tente à analyser. En outre, avec Windows PowerShell, il généralement est judicieux pour voir quel type de données est en cours utilisé.
Pour examiner les données que le script est à l'aide, vous pouvez de simplement l'imprimer à l'écran. Voici ce que vous obtenez :
PS C:\> $wmi.LocalDateTime
20080905184214.290000-240
Le nombre semble un peu bizarre. Quel type d'une date est ce ? Pour savoir, utilisez la méthode GetType. La bonne chose sur GetType est qu'il est presque toujours disponible. Tout ce que vous avez à faire est l'appeler. Et Voici la source du problème, la valeur LocalDateTime est déclarée sous forme de chaîne, pas en tant que valeur System.DateTime :
PS C:\> $wmi.LocalDateTime.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String
System.Object
Si vous souhaitez soustraire même temps depuis un autre moment, assurez-vous que vous êtes confronté à des valeurs de temps des chaînes de pas. Il est facile à faire avec la méthode ConvertToDateTime, Windows PowerShell ajoute à toutes les classes WMI :
PS C:\> $wmi = Get-WmiObject -Class Win32_OperatingSystem
PS C:\> $wmi.ConvertToDateTime($wmi.LocalDateTime) –
$wmi.ConvertToDateTime($wmi.LastBootUpTime)
Lorsque vous avez soustrait une valeur de temps à une autre, vous sont conservés avec une instance d'un objet System.TimeSpan. Cela signifie que vous pouvez choisir comment afficher vos informations de disponibilité sans devoir effectuer de nombreuses opérations arithmétiques. Vous devez choisir uniquement la propriété pour afficher (et j'espère que vous comptez votre disponibilité dans TotalDays et non dans TotalMilliseconds). L'affichage par défaut de l'objet System.TimeSpan est illustrée ci-dessous :
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
Le problème avec cette méthode est qu'il uniquement indique vous combien de temps le serveur ont été des depuis le dernier redémarrage. Il ne calcule pas temps d'arrêt. Cela est où haut est vers le bas, pour calculer le temps de fonctionnement, vous devez d'abord connaître le temps d'indisponibilité.
Comment vous savoir combien de temps que le serveur a été enfoncée ? Pour ce faire, vous devez connaître lorsque le serveur commence et lorsqu'il s'arrête. Vous pouvez obtenir ces informations du journal des événements système. Un des premier processus qui démarre sur votre serveur ou station de travail est le journal des événements et une des dernière choses qui cesser à un serveur arrêt est le journal des événements. Chacun de ces démarrage/arrêt événements génère un eventID, 6005 lorsque le journal des événements démarre et 6006 lorsque le journal des événements arrête. la figure 2 illustre un exemple d'un journal des événements.
La figure 2 le service Journal des événements démarre peu après le démarrage de l'ordinateur (cliquez sur l'image pour l'agrandir)
Par collecte les événements 6005 et 6006 à partir du journal système, leur tri et soustraction le démarrage de la s'arrête, vous pouvez déterminer combien de temps que le serveur n'a vers le bas entre les redémarrages. Si vous avez puis soustrait cette quantité du nombre de minutes pendant la période de temps en question, vous pouvez calculer le pourcentage de disponibilité du serveur. C'est l'approche effectuée dans le script CalculateSystemUpTimeFromEventLog.ps1, qui est affichée dans la figure 3 .
La figure 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."
Le script commence en utilisant l'instruction de paramètre pour définir quelques paramètres de ligne de commande dont les valeurs vous pouvez modifier lorsque vous exécuter le script à partir de la ligne de commande. La première, $ NumberOfDays, permet de spécifier un autre nombre de jours à utiliser dans l'état de disponibilité. (Notez que J'AI avez fourni une valeur par défaut de 30 jours dans le script donc vous pouvez exécute le script sans avoir à fournir une valeur pour le paramètre. De cours, vous pouvez modifier cette si nécessaire.)
La seconde, [commutateur] $ débogage, est un paramètre commuté vous permet d'obtenir certaines informations de débogage à partir du script si vous incluez il sur la ligne de commande lors de l'exécution du script. Ces informations peuvent aider vous pensez plus confiants dans les résultats que vous obtenir à partir du script. Il peut arriver lorsque le message de service-Arrêt de journal des événements 6006 n'est pas présent, peut-être suite d'une défaillance irrémédiable du serveur qui rendu Impossible d'écrire dans le journal des événements, provoque le script pour Soustrait une valeur de disponibilité à partir d'une autre valeur disponibilité et biaiser les résultats.
Une fois que la variable de débogage $ est fournie à partir de la ligne de commande, il est présent sur la variable : lecteur. Dans ce cas, la valeur de la variable debugPreference $ est définie pour continuer, qui signifie que le script continuera à s'exécuter et toute valeur fournie pour le débogage de l'écriture sera visible. Notez que, par défaut, la valeur de $ debugPreference est silentlycontinue, si vous ne définissez pas la valeur de debugPreference $ pour continuer, le script est exécuté, mais toute valeur fournie au débogage de l'écriture sera silencieuse (c'est-à-dire, qu'il ne sera pas visible).
Lorsque le script s'exécute, le résultat répertorie chaque les occurrences des entrées de journal des événements 6005 et 6006 (comme vous pouvez le voir dans la figure 4 ) et affiche le calcul de disponibilité. Ces informations, vous pouvez vérifier la précision des résultats.
La figure 4 mode débogage affiche un suivi de chaque valeur d'heure qui est ajouté au calcul de disponibilité (cliquez sur l'image pour l'agrandir)
L'étape suivante consiste à créer une instance de l'objet System.TimeSpan. Vous pouvez utiliser la cmdlet New-Object pour créer un objet timespan par défaut que vous utiliseriez pour effectuer des calculs de date-différence :
PS C:\> [timespan]$ts = New-Object system.timespan
Mais Windows PowerShell a en fait une cmdlet New-TimeSpan pour créer un objet timespan, donc il est judicieux de l'utiliser. À l'aide de cette cmdlet facilite le script de la lecture et l'objet qui est créé revient à l'objet timespan créé avec New-Object.
Maintenant, vous pouvez initialiser quelques variables, commençant par $ currentTime, qui sert à contenir la valeur la date et l'heure actuelle. Vous obtenir ces informations à partir de la cmdlet Get-date :
$currentTime = get-Date
Ensuite, initialiser les deux variables contenant les numéros D'id_événement Démarrage et arrêt. Vous ne devez fait cela, mais le code sera plus facile à lire et plus facile à résoudre si vous évitez d'incorporer les deux en tant que chaîne littérale valeurs.
L'étape suivante consiste à créer un minutesInPeriod variable appelée $ pour contenir le résultat du calcul utilisé pour déterminer le nombre de minutes pendant la période de temps en question :
$minutesInPeriod = (24*60)*$NumberOfDays
Enfin, vous devez créer la variable $ startingDate, qui va contenir un objet System.DateTime qui représente l'heure début de la période de rapport. La date sera minuit de la date début de la période :
$startingDate = (Get-Date -Hour 00 -Minute 00 -Second 00).adddays(-$numberOfDays)
Une fois les variables créées, vous récupérer les événements du journal des événements et stocker les résultats de la requête dans la variable d'événements $. Utilisez la cmdlet Get-journal des événements pour interroger le journal des événements, indiquant « système » comme le nom du journal. Dans Windows PowerShell 2.0, vous pouvez utiliser un paramètre –source pour réduire les informations dont a besoin d'être triées hors de la cmdlet Where-Object. Mais dans Windows PowerShell 1.0, vous n'avez pas ce choix et devez donc trier tous les événements non filtrés renvoyés par la requête. Ainsi, pipeline les événements à la cmdlet Where-Object pour filtrer les écritures journal événement approprié. En examinant le filtre Where-Object, vous verrez pourquoi les scripteurs avait créer les variables pour stocker les paramètres.
La commande lit beaucoup mieux que si vous avez utilisé les littéraux de chaîne. Les eventIDs get sont égaux pour startUpID $ ou $ shutDownID. Vous devez également vous assurer de la propriété timeGenerated de l'entrée du journal des événements est supérieure ou égale à la startingDate $, comme suit :
$events = Get-EventLog -LogName system |
Where-Object { $_.eventID -eq $startUpID -OR $_.eventID -eq $shutDownID -and $_.TimeGenerated -ge $startingDate }
Gardez à l'esprit que cette commande est uniquement exécutée localement. Dans Windows PowerShell 2.0, vous pouvez utiliser le paramètre –computerName pour rendre cette commande à distance.
L'étape suivante consiste à créer un objet liste triée. Pourquoi ? Étant donné que lorsque vous parcourez la collection d'événements, vous ne sont pas forcément ordre dans lequel les entrées du journal des événements sont signalées. Même si vous pipeline les objets le cmdlet Sort-Object et la banque le résultat en une variable, lorsque vous itérer sur les objets et stocker les résultats dans une table de hachage, vous ne peut pas être certain que la liste conservera les résultats de la procédure de tri.
Pour sidestep ces problèmes frustrante et difficiles à débogage, vous créer une instance de l'objet System.Collections.SortedList, l'aide le constructeur par défaut de l'objet. Le constructeur par défaut indique la liste triée pour trier les dates par ordre chronologique. Stocker l'objet liste triée vide dans la variable sortedList $ :
$sortedList = New-object system.collections.sortedlist
Après avoir créé l'objet de liste triée, vous devez remplir. Pour ce faire, utilisez l'instruction ForEach et passez en revue la collection d'entrées de journal des événements stockées dans la variable entrées $. Lorsque vous parcourez la collection, la variable d'événement $ effectue le suivi de votre position dans la collection. Vous pouvez utiliser la méthode add pour ajouter deux propriétés à l'objet System.Collections.SortedList. La liste triée vous permet d'ajouter une clé et la propriété value (similaire à un objet Dictionary sauf qu'elle permet également d'index dans la collection comme un tableau ne). Ajoutez la propriété timegenerated comme la clé et le eventID que la propriété valeur :
ForEach($event in $events)
{
$sortedList.Add( $event.timeGenerated,
$event.eventID )
} #end foreach event
Ensuite, vous calculer la disponibilité du serveur en cours. Pour ce faire, vous utilisez l'entrée de journal des événements la plus récente dans la liste triée. Notez que cela est toujours une instance 6005 parce que si l'entrée la plus récente était 6006, le serveur seraient encore vers le bas. Puisque l'index est égale à zéro en fonction, l'entrée la plus récente aura la valeur – 1 nombre.
Pour récupérer la valeur de génération de temps, vous devez examiner la propriété clé de la liste triée. Pour obtenir la valeur d'index, utilisez la propriété nombre et soustraire un dans il. Puis soustrait le temps de que l'événement 6005 a été généré à la valeur heure date stockée dans la variable currenttime $ vous rempli précédemment. Vous pouvez imprimer les résultats du calcul de ce uniquement si le script est exécuté en mode débogage. Ce code est illustré ici :
$uptime = $currentTime -
$sortedList.keys[$($sortedList.Keys.Count-1)]
Write-Debug "Current uptime $uptime"
Il est désormais temps de l'objet de liste triée en revue et calculer la disponibilité du serveur. Étant donné que vous utilisez l'objet de liste System.Collections.Sorted, vous tirer parti du fait que vous pouvez indexer dans la liste. Pour ce faire, utilisez l'instruction, commence au nombre de-2 car nous avons utilisé compter-1 précédemment pour le montant actuel de disponibilité.
Nous allons compter en amont pour obtenir la disponibilité pour la condition spécifiée dans la deuxième position de l'instruction est lorsque l'élément est supérieure ou égale à 0. En troisième position de l'instruction utiliser--, qui est Décrémenter la valeur de élément $ par un. Vous utilisez la cmdlet Write-débogage pour imprimer la valeur du numéro d'index si le script est exécuté avec le commutateur –debug. Également onglet via à l'aide de la ` pas caractères et imprimer la valeur d'heure timegenerated. Cette section de code est illustrée ci-dessous :
For($item = $sortedList.Count-2 ; $item -ge
0 ; $item--)
{
Write-Debug "$item `t `t $($sortedList.
GetByIndex($item)) `t `
$($sortedList.Keys[$item])"
Si la valeur Id_événement est égale à 6005, qui est la valeur Id_événement démarrage, vous calculer le montant de disponibilité en soustrayant de l'heure de début de la valeur temps d'arrêt précédent. Stocker cette valeur dans la variable de disponibilité $. Si vous êtes en mode débogage, vous pouvez utiliser la cmdlet Write-débogage pour imprimer ces valeurs à l'écran :
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
Enfin, vous devez générer le rapport. Reprendre la nom de l'ordinateur de l'ordinateur système environnement variable. Vous utilisez l'heure actuelle, stocké dans la valeur startingdate $, et vous afficher le compte-rendu de disponibilité total pour la période. L'identificateur de format {0:n2} vous permet d'imprimer le numéro à deux chiffres. Ensuite, calculer le pourcentage de temps en divisant le nombre de minutes disponibilité par le nombre de minutes dans la période couverte par l'état. Le même identificateur de format permet d'imprimer la valeur à deux décimales. Pour vous amuser, vous pouvez également calculer le pourcentage de disponibilité et imprimer des deux valeurs, comme suit :
"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."
Dans ce cas maintenant les scripteurs revenir à la question originale : lorsqu'est vers le bas jusqu'à ? Vous à présent voir que vous ne pouvez pas parler disponibilité sans entreprendre d'interruption en considération. Si vous pensez que c'était la laisser s'amuser sur Internet, consultez l'autre « Hé, Scripting Guy » colonnes sur TechNet ou accédez à la Centre de scripts.
Problèmes de version
Lors du test le script CalculateSystemUptimeFromEventLog.ps1 sur son ordinateur portable, Michael Murgolo contribuant éditeur exécuté sur une erreur plus ennuyeux. J'AI a donné le script à mon ami JIT, et il a rencontré le même message d'erreur. Quel était cette erreur ? Eh bien, il est :
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'erreur "ne peut pas indexer dans un tableau null, » est une indication que le tableau n'a pas créée correctement. Donc je dug dans le code qui crée le tableau :
ForEach($event in $events)
{
$sortedList.Add( $event.timeGenerated,
$event.eventID )
} #end foreach event
Au final, ce code était bien. Ce qui a été provoque l'erreur ?
Ensuite, J'AI décidé d'examiner l'objet SortedList. Pour ce faire, J'AI écrit un script simple qui crée une instance de la classe System.Collections.SortedList et y ajouter des informations. À ce stade, J'AI utilisé la propriété clés pour imprimer la liste des clés de. Voici ce code :
$aryList = 1,2,3,4,5
$sl = New-Object Collections.SortedList
ForEach($i in $aryList)
{
$sl.add($i,$i)
}
$sl.keys
Sur mon ordinateur, ce code fonctionne correctement. Sur ordinateur de JIT Échec. Bummer. Mais au moins il pointait me dans le sens de droit. Le problème, il s'avère, est qu'il y a un bogue avec la System.Collections.SortedList dans Windows PowerShell 1.0. Et J'AI doit être exécuté la version la plus récente de la encore pour être publié Windows PowerShell 2.0 où ce bogue a été résolu, et ainsi, le code s'exécute correctement.
Ainsi, dans laquelle qui laisser notre script ? En fait, la classe SortedList a une méthode appelée GetKey, et cette méthode fonctionne sur Windows PowerShell 1.0 et 2.0 de PowerShell Windows. Ainsi, pour la version 1.0 du script, nous modifier le code pour utiliser GetKey au lieu de parcourant la collection de clés. Dans la version 2.0 du script, nous ajouter une balise qui nécessite la version 2.0 de Windows PowerShell. Si vous essayez d'exécuter ce script sur un ordinateur Windows PowerShell 1.0, simplement quitterez le script et vous obtiendrez l'erreur.
Michael pointe également les quelque chose qui n'est pas un bogue mais est lié à un compte de conception. Il a noté que le script pas correctement détectera disponibilité si vous en veille prolongée ou en veille de l'ordinateur. Cela est vrai, que nous ne pas détecter ou recherchez ces événements.
En réalité, cependant, je ne suis pas préoccuper de disponibilité sur mon ordinateur portable. Je suis uniquement intéressé par disponibilité sur un serveur et j'ont encore respecter le serveur qui est en veille ou mise en prolongée. Il peut arriver, bien sûr et il peut être un moyen intéressant pour économiser Électricité dans le centre de données, mais il n'est pas quelque chose que j'ai rencontré encore. Me permettant de savoir si vos serveurs de veille prolongée. Vous pouvez atteindre me joindre au Scripter@Microsoft.com.
Dr. Du Scripto script Perplexer
Le défi mensuel qui teste non seulement les votre capacité de résolution de puzzles mais également vos compétences en matière de scripts.
Décembre 2008 : commandes PowerShell
La liste ci-dessous contient des commandes de Windows PowerShell 21. Le carré contienne les mêmes commandes, mais ils sont masqués. Votre travail consiste à trouver les commandes, qui peuvent être masqués horizontalement, verticalement ou diagonalement (vers l'avant ou vers l'arrière).
EXPORT-CSV | FORMAT-LIST | FORMAT-TABLE |
GET-ACL | GET-ALIAS | GET-CHILDITEM |
GET-EMPLACEMENT | APPELER-ARTICLES | OBJET DE MESURE |
NOUVEAU ITEMPROPERTY | OUT-HOST | OUT-NULL |
REMOVE-PSSNAPIN | SET-LISTE DE CONTRÔLE D'ACCÈS | SET-TRACESOURCE |
FRACTIONNEMENT-PATH | START-VEILLE | STOP-SERVICE |
SUSPENDRE / SERVICE | ÉCRITURE-DEBUG | AVERTISSEMENT DE L'ÉCRITURE |
RÉPONSE :
Dr. Du Scripto script Perplexer
Réponse : décembre 2008 : commandes PowerShell
Wilson Édition est consultant senior chez Microsoft et un expert de script bien connu. Il est un Trainer de Certified pour le Microsoft qui fournit un atelier de Windows PowerShell courante à Microsoft premier clients dans le monde entier. Il a écrit huit livres notamment plusieurs de scripts Windows et a contribué à presque une douzaine autres livres. Édition contient plus de 20 certifications de secteur d'activité. Craig Liebendorfer est wordsmith et longtime éditeur Web de Microsoft. Craig ne peut pas pensez toujours il y a un travail paie lui pour travailler avec des mots chaque jour. Une des ses choses favoris est irreverent d'humour, afin qu'il doit ajuster droite ici. Il considère que sa fille magnificent à son accomplishment plus élevé dans la vie.
Craig Liebendorfer est wordsmith et longtime éditeur Web de Microsoft. Craig can’t pensez toujours il ’s un travail paie lui pour travailler avec des mots chaque jour. Une des ses choses favoris est irreverent d'humour, afin qu'il doit ajuster droite ici. Il considère que sa fille magnificent à son accomplishment plus élevé dans la vie.