[Из песочницы] Использование таймаута при работе с WMI через Powershell
Здравствуйте хабравчане.Недавно был опубликован урок по сбору информации о рабочих станциях с использованием 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 = «Root\CIMV2»$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 = $queryTry{$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 и запуск скрипта позволял оперативно локализовать сервера подверженные сбою.