АНДРЕЙ БИРЮКОВ
Управляем серверами в реальном времени с помощью WSH-сценариев
Проблемы, связанные с перебоями электроснабжения, уже давно являются головной болью для системных администраторов и специалистов по обслуживанию сети. Системы, предлагаемые компаниями-производителями, не всегда полностью отвечают необходимым требованиям. Сегодня мы расскажем вам, как использовать WSH-сценарии для взаимодействия серверов с системами бесперебойного питания.
Вместо предисловия
На тему использования различных сценариев в задачах системного администрирования уже написана масса всевозможных статей. Особенно это касается Linux/UNIX-операционных систем, обладающих мощными языками сценариев, которые позволяют выполнять большое количество различных административных задач.
А что же Windows? В Windows имеется WSH – Windows Script Host, это основной инструмент для всех административных сценариев, поскольку все административные сценарии выполняются внутри WSH. Сценарии WSH можно писать на языках Jscript .NET, VBScript, Perl, Python и REXX. В данной статье все примеры сценариев приводятся на VBScript.
Документация по WSH предлагает использовать сценарии преимущественно для сбора информации о системе, работы с файловой системой или изменении различных системных настроек. Однако сегодня мы поговорим об использовании сценариев для автоматизированного мониторинга и управления Windows-серверами.
Постановка задачи
Причина, по которой возникла необходимость в написании сценария для автоматизированного управления Windows-серверами, довольно проста, особенно в свете недавних проблем с подачей электропитания. У заказчика имелось несколько серверов Windows 2003, и требовалось организовать бесперебойное электропитание на основе APC UPS. В комплекте с UPS поставлялось программное обеспечение, в состав которого входил агент для взаимодействия с UPS. Один из серверов подключался к источнику бесперебойного питания через COM-порт и на этом сервере был установлен агент, который в случае отключения питания должен был корректно завершить работу сервера. А вот на остальных серверах такой агент без подключения к источнику через СОМ-порт не работал, и следовательно, эти сервера в случае исчезновения питания проработали бы ровно столько, насколько хватило бы заряда аккумулятора, а потом просто отключатся, что, очевидно, совсем не хорошо. Компания-производитель предлагает в качестве решения проблемы приобрести специальное оборудование, проще говоря, СОМ-свитч, однако в силу ряда причин нас подобный вариант не устроил.
Тогда и был написан сценарий на VBScript, который отслеживал в журнале событий появление сообщения об отключении питания на том сервере, где установлен агент, и затем, если по прошествии некоторого периода времени питание не восстанавливалось, начинал корректно выключать сервера, при этом отправляя администратору письмо по электронной почте с уведомлением об отключении.
На примере такого сценария мы и рассмотрим реализацию задачи управления серверами с помощью VBScript. Думаю, примеры программ и методы работы, изложенные в статье, могут быть полезны не только при решении описанной проблемы, но также и для других задач автоматизации системного администрирования в реальном времени. Итак, приступим к реализации. Прежде всего создадим текстовый файл с расширением vbs. Для выполнения сценария достаточно будет лишь запустить созданный vbs-файл.
Читаем журнал событий (Event Log)
Основой разрабатываемого сценария является цикл, который осуществляет поиск в журнале событий вхождений искомого сообщения. Такие сообщения могут быть двух видов: сообщение об отключении питания и о его восстановлении.
Рисунок 1. Журнал событий Event Log, содержащий сообщения от агента источника бесперебойного питания
При отключении питания в журнале событий появляется сообщение следующего вида (см. рис. 2).
Рисунок 2. Сообщение об отключении основного питания
При восстановлении питания агент создаст в журнале событий следующее сообщение (см. рис. 3).
Рисунок 3. Сообщение о восстановлении основного питания
Итак, мы определили, как выглядят сообщения, отправляемые агентом источника бесперебойного питания. В журнале событий нам необходимо искать сообщения с полем Description вида: «UPS On Battery» или «Utility Power Restored», которые будут сигнализировать о потере питания и его восстановлении соответственно.
Реализация поиска данных сообщений в журнале событий может выглядеть, например, вот так:
Листинг 1. Поиск сообщений в журнале событий
Option Explicit
Dim objWMI, objItem ' Objects // Объявляем переменные
Dim strComputer
Dim intRecordNum, intRec, colLoggedEvents
Dim strAdmin, strAdmin2, i, intrecordNum2, iteration
' WMI Core Section
// Сервер, на котором выполняется сценарий
strComputer="127.0.0.1"
Set objWMI = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" _
& strComputer & "\root\cimv2")
iteration=2 // счетчик итераций
// искомые строки – отключение питания
strAdmin = "UPS On Battery"
// искомые строки – восстановление питания
strAdmin2 = "Utility Power Restored"
Do While 1=1 // цикл бесконечный
// запрос по журналу событий. Раздел Applications
Set colLoggedEvents = objWMI.ExecQuery ("Select * from Win32_NTLogEvent Where Logfile = 'Application'" )
// счетчики записей
intRecordNum=0 // счетчик найденных записей
intRec=0 // счетчик всех записей
intRecordNum2=0
For Each objItem in colLoggedEvents
// ищем первое вхождение записи
// на отключение
If intRecordNum=0 Then
If InStr(1,objItem.message,strAdmin,1) Then intRecordNum = intRec+1
End If
// ищем первое вхождение записи
// на восстановление
If intRecordNum2=0 Then
If InStr(1,objItem.message,strAdmin2,1) Then intRecordNum2 = intRec +1
End If
intRec=intrec+1
// если все нашли, выходим из цикла
If intRecordNum>0 AND intrecordNum2>0 Then Exit For
Next
/ если не нашли записей об отключении,
// то и записи о восстановлении не нужны
If intrecordNum=0 Then intRecordNum2=0
If iteration=0 Then
shutdown
// а здесь будет находиться ссылка на процедуру
// отключения питания на других серверах
End If
If intrecordNum<intrecordNum2 Then
iteration=iteration-1
End If
If intrecordNum2=0 Then
If intRecordNum>0 Then iteration=iteration-1
End If
loop
WScript.Sleep 10000 // Задержка перед следующей итерацией
Следует немного пояснить принцип работы данного сценария, а также смысл некоторых переменных. Одной из ключевых переменных, используемых в работе сценария, является переменная iteration.
Данная переменная определяет количество итераций, а проще говоря, временной интервал, в течение которого наши сервера будут работать от источника бесперебойного питания, ожидая восстановления питания в электрической сети. Это сделано специально, чтобы в случаях кратковременного пропадания питания, на несколько секунд, сервера не начали автоматически отключаться. Этот интервал времени зависит от емкости аккумуляторов вашего источника бесперебойного питания и должен определяться опытным путем.
Итак, в случае, если сценарий находит вхождение сообщения об отключении питания, счетчик iteration уменьшается на единицу, при достижении нулевого значения мы вызываем процедуру shutdown, о которой речь пойдет ниже.
Также следует обратить внимание на последнюю команду сценария, которая определяет период бездействия цикла перед выполнением итерации, в миллисекундах. Указанный в листинге интервал в десять секунд на практике скорее всего окажется крайне мал. Опыт показывает, что интервал должен быть не менее минуты, так как уже в течении одной минуты любой источник бесперебойного питания просто обязан держать напряжение. К тому же поиск по всему журналу событий может отнимать довольно значительные ресурсы системы. Для борьбы с этим можно рекомендовать понизить приоритет процесса со «среднего» на «ниже среднего».
Вообще читателю, искушенному в программировании, алгоритм, приведенный в Листинге 1, может показаться немного странным и нуждающимся в доработке. Такое мнение будет вполне справедливо, так как, составляя алгоритм, я, основываясь на своем опыте, сделал ряд допущений, тем самым существенно его упростив и сделав более удобочитаемым, не в ущерб качеству. Но, возможно, в вашем конкретном случае потребуется что-либо доработать или исправить. Однако продолжим, следующим этапом у нас будет процедура отключения питания.
Отключаем сервера
Итак, перед нами стоит задача программно отключить питание на всех серверах нашей сети. Для этого прежде всего необходимо завести на всех серверах (в случае, если в сети нет домена) или в домене учетную запись, наделенную соответствующими правами.
Я бы очень не рекомендовал использовать для этих целей учетную запись администратора, так как в исходном тексте необходимо указать пароль учетной записи, которая используется для выполнения отключения. Также из соображений безопасности необходимо ограничить доступ к данному сценарию так, чтобы его могли читать/запускать только администраторы. Подобная настройка выходит за рамки данной статьи, однако я думаю, что любой системный администратор без труда справится с этой задачей.
Листинг 2. Процедура отключения питания
Sub Shutdown
On Error Resume Next // если ошибка, переходим к следующему
Dim arrComputers, objLocator
Dim login, password, domain
// Перечисляем имена серверов, которые должны быть выключены
arrComputers = Array("Computer1","Computer2","Computer3")
login="operator" // учетная запись с правом на shutdown
password="password" // пароль
domain="TEST" // домен
For Each strComputer In arrComputers
Set objLocator = CreateObject("WbemScripting.SWbemLocator")
Set objWMIService = objLocator.ConnectServer(strComputer, "root\cimv2", login, password, domain)
Set colOperatingSystems = objWMIService.ExecQuery ("Select * from Win32_OperatingSystem")
For Each objOperatingSystem in colOperatingSystems
// непосредственно shutdown
ObjOperatingSystem.Shutdown(1)
Next
// после отключения уведомим администратора
sendmail(strComputer)
Next
WScript.Quit // сценарий завершает свою работу
End Sub
В данной процедуре прежде всего необходимо указать имена серверов и доменную учетную запись обладающей правами на отключение системы. В примере приведена доменная учетная запись, однако, думаю, не составит большого труда внести изменения в текст программы, указав учетные записи для конкретных серверов.
Также в тексте мы встречаем упоминание о процедуре отправки сообщений администратору или службе технической поддержки. В качестве параметра данной процедуре передается имя отключенного сервера.
Рассмотрим процесс отправки уведомлений более подробно.
Отправляем уведомления
На самом деле вполне логично отправить уведомление всем заинтересованным лицам о том, что тот или иной сервер был отключен в связи с потерей питания. В частности, мы сбережем нервную систему системного администратора, который, не зная причины, почему посреди ночи его сервера не пингуются, в ужасе помчится из дома на работу. А так наш сисадмин будет уже заранее знать причину отключения и по крайней мере не будет излишне переживать по поводу возможной потери данных. Также подобное уведомление будет весьма полезно, если сервера находятся на аутсорсинге, либо в компании приходящий администратор.
Конечно, кто-то может возразить, что у агента, который взаимодействует с источником бесперебойного питания, уже есть возможность отправки уведомления администратору об отключении питания. Однако этот агент отправит уведомление об отключении питания, а нас интересует информация об отключении каждого сервера, а не только того, на котором установлен агент.
Итак, обосновав необходимость отправки уведомления, приступим к описанию практической реализации. Современные технологии связи позволяют различные способы уведомления администратора о различных событиях в сети. Например, мне приходилось слышать о системе, которая в случае возникновения внештатной ситуации звонила админу по заранее заданному телефону и проигрывала mp3-файл, содержавший голосовую информацию. Но в такие крайности мы вдаваться не будем – отправим уведомление при помощи электронной почты. В принципе, с помощью того метода, который будет описан далее, наверняка можно отправлять и ICQ-сообщения и даже SMS (хотя из-за различных ограничений, введенных операторами сотовой связи, это сделать будет крайне затруднительно).
Раньше для отправки сообщений посредством сценариев Windows использовался почтовый клиент Outlook. При этом применялся сценарий следующего вида:
Листинг 3. Отправка письма с помощью Outlook
Dim OutlookObject, OutMail
Set OutlookObject = CreateObject("Outlook.Application")
Set OutMail = OutlookObject.CreateItem(0)
OutMail.to = "test@test.ru"
OutMail.Subject = "Тема сообщения"
OutMail.Body = "Тело сообщения"
OutMail.Send
Рисунок 4. Запрос на отправку сообщения
К тому же практически любой антивирус заблокирует работу сценария.
По понятным причинам нас это совершенно не устраивает. Но существует другой способ отправки почтовых сообщений сценарием. Данная процедура была найдена мной в MSDN [1].
Листинг 4. Отправка письма
// Получаем в качестве параметра имя сервера
Sub sendmail(server)
// Инициализируем переменные
Dim iMsg
Dim iConf
Dim Flds
Dim str
Const cdoSendUsingPickup = 1
Set iMsg = CreateObject("CDO.Message")
Set iConf = CreateObject("CDO.Configuration")
// Подготавливаем поля для отправки по SMTP
Set Flds = iConf.Fields
With Flds
.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = cdoSendUsingPickup
.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverpickupdirectory") = "c:\inetpub\mailroot\pickup"
.Update
End With
// Непосредственно текст сообщения
str= "Уважаемый администратор. Сервер " & server & " был отключен "& Now &" в связи с потерей электропитания. "
// заполоняем все поля сообщения
With iMsg
Set .Configuration = iConf
.To = "admin@server.com" // Поле «Кому»
.From = "ups@example.com" // Поле «От кого»
// Тема письма
.Subject = "Отключение питания на сервере " & server
.HTMLBody = str
.Send
End With
// Очищаем переменные
Set iMsg = Nothing
Set iConf = Nothing
Set Flds = Nothing
End Sub
Данная процедура отправит письмо по указанному адресу. В результате получателю придет сообщение примерно следующего содержания:
Уважаемый администратор. Сервер MyServer был отключен 01.10.2005 23:03:31 в связи с потерей электропитания. |
При необходимости процедуру можно легко подправить, для того чтобы отправлялось сразу несколько писем различным адресатам.
Таким образом, мы решили третью и последнюю задачу, которая ставилась перед нашим сценарием WSH. Исходный код всего сценария прилагается к статье.
Также хотелось бы обратить ваше внимание еще на один щекотливый момент. В случае восстановления питания необходимо снова включить отключенные сервера. Для этого прежде всего надо в BIOS каждого из серверов установить свойство «Wake Up On LAN ON». Я не думаю, что это может создать какие-то трудности в повседневной работе хотя бы потому, что сервера должны быть всегда включены, их не выключают на ночь. Теперь нам необходимо создать какой-либо трафик с помощью нашего сценария. Сделать это можно с помощью фрагмента одной из процедур, описанной ранее. Далее приводится фрагмент Листинга 1, в котором при обнаружении сообщения о восстановлении питания вызывается процедура WakeUp, осуществляющая пинг каждого из серверов (Листинг 6 соответственно). Обратите внимание на использованный в Листинге 6 метод Run. С помощью него можно выполнить любую команду, причем (как показано в Листинге 6) это делается в режиме hide. Если в вашем случае необходимо видимое окно, просто в качестве второго параметра для метода Run укажите единицу. Более подробное описание данного метода и его параметров можно найти в [1].
Листинг 5. Добавление вызова процедуры Wakeup в сценарий Листинга 1
……………..
// Ищем первое вхождение записи на восстановление
If intRecordNum2=0 Then
If InStr(1,objItem.message,strAdmin2,1) Then intRecordNum2 = intRec +1
WakeUp
End If
…………..
Листинг 6. Процедура WakeUp
Sub WakeUp
On Error Resume Next // если ошибка, переходим к следующему
Dim arrComputers
Dim WshShell, str
// Перечисляем имена серверов, которые должны быть выключены
arrComputers = Array("Computer1","Computer2","Computer3")
Set WshShell = CreateObject("WScript.Shell")
For Each strComputer In arrComputers
str="ping "& strComputer
//запускаем в режиме hide
Return = WshShell.Run(str, 0)
Next
End Sub
Заключение
Итак, мы рассмотрели основные аспекты написания сценария для автоматизированного управления серверами Windows при работе с источником бесперебойного питания. Теперь в случае отключения электропитания все наши сервера не окажутся предоставлены сами себе в ожидании полной разрядки аккумулятора, а будут корректно выключены без потери данных.
Очевидно, что алгоритмы и процедуры, изложенные в данной статье, можно с тем же успехом использовать и для решения других задач автоматизации управления серверами и аудита журнала событий с отправкой уведомлений администратору. Например, для наблюдения за корректностью работы тех или иных сервисов и приложений, результатов проведения резервного копирования и других административных действий.
Ссылка:
- http://msdn.microsoft.com – содержит много примеров с исходными текстами сценариев.