Здравствуйте читатели.
Недавно был опубликован урок по сбору информации о рабочих станциях с использованием PowerShell. В комментариях были рекомендации по реализации некоторого функционала с помощью WMI. Это напомнило мне о некоторых нюансах функционирования WMI на серверах и рабочих станциях, с которыми пришлось столкнуться на работе.
Нюанс заключался в том что время от времени хаотичным образом практически без какой либо зависимости WMI начинал функционировать некорректно, что приводило к сбою скрипта по сбору данных о времени работы серверов (к слову скрипт получал UpTime сервера по WMI). Аналогичная проблема возникала и у коллег занимающихся мониторингом серверов по WMI с помощью WhatsUp. К сожалению после анализа проблемы причины выяснить не удалось и было принято волевое решение что во всем виновата нестабильность WMI тем более что в большинстве случаев единственное что объединяло сервера — ОС семейства Windows 2003 и XP. Проблема нестабильности была заброшена в долгий ящик но задача мониторинга серверов никуда не делась.
Расскажу немного о скрипте. Имелся определенный набор серверов. Сервера перезагружались с определенной периодичностью. Задача скрипта — сбор данных о времени работы серверов и рассылка ежедневного отчета о том какие сервера и когда осуществляли перезагрузку. Так как скрипт занимался опросом серверов раз в день то было решено не мудрить параллельную работу и опрашивать сервера по очереди. Однако из-за проблемы с WMI время от времени скрипт просто переставал работать так как зависал при попытке получить данные по WMI с какого либо сервера.
Спустя некоторое время было найдено решение — использование параметра $wmi.options.timeout при обращении по WMI. Совместно со связкой try catch это позволило исключить какие либо проблемы при опросе по WMI и зафиксировать время в течении которого скрипт работает с одним сервером. Весь скрипт целиком не публикую, но привожу шаблон составленный для работы с WMI с использованием таймаута:
#функция для конвертирования даты, к работе с таймаутом отношения не имеет, но используется в примере для обработки полученных данных
function WMIDateStringToDate($Bootup) {
[System.Management.ManagementDateTimeconverter]::ToDateTime($Bootup)
}$system=«Server01»
$NameSpace = «RootCIMV2»
$wmi = [WMISearcher]""
$wmi.options.timeout = '0:0:05' #set timeout to 5 seconds
$query = 'Select * from Win32_OperatingSystem'
$wmi.scope.path = "\$system$NameSpace"
$wmi.query = $query
Try{
$wmiresult = $wmi.Get()
foreach ($wmioutput in $wmiresult){
#здесь обрабатываем данные полученные по WMI например время включения сервера:
$Bootup = $wmioutput.LastBootUpTime
$LastBootUpTime = WMIDateStringToDate($Bootup)
$now=Get-Date
$Uptime = $now — $lastBootUpTime
$d = $Uptime.Days
$h = $Uptime.Hours
$m = $uptime.Minutes
$ms= $uptime.Milliseconds
$a = "$count. $System Up for: {0} days, {1} hours, {2}.{3} minutes" -f $d,$h,$m,$ms
}
}
Catch {
#возникли ошибки при получении данных
}
Таким образом меняя переменную $system на имя сервера и переменную $query на необходимый вам запрос можно получить любые доступные по WMI данные не переживая что скрипт зависнет на пол пути и останется в подвешенном состоянии.
Скрипт на основе этого кода успешно функционирует пол года не дав ни одного сбоя и по сей день, более того за это время было несколько сбоев с другими сервисами которые влекли за собой проблемы с отзывчивостью WMI и запуск скрипта позволял оперативно локализовать сервера подверженные сбою.
Автор: kumbr_87