Одной из наиболее важных задач, которую сетевые администраторы решают ежедневно, является обеспечение доступности систем. В случае отключения системы от сети, необходимо уведомить об этом администраторов, чтобы они смогли снова обеспечить доступ к системе. А так как время является важным фактором высокой работоспособности сети, администраторам необходимо средство мониторинга, которое может быстро сообщить об отключении системы.

Perl является идеальным языком написания сценариев для создания такого средства. Однако, хотя написать сценарий Perl для слежения за системами просто, создать систему уведомлений не так уж легко.

Если честно, то вы можете легко написать сценарий мониторинга, который создает файл журнала, регистрирует данные журнала событий Win32 или даже отсылает почтовые сообщения. Но если в момент сбоя вы работаете на компьютере, вам требуется предупреждение, которое появляется прямо у вас перед глазами. Вы можете принять решение о написании кода, который во всплывающем окне открывает форму диалога с сообщением. Но если при использовании этого подхода за время вашего отсутствия появляется множество предупреждений, вам придется просмотреть большое количество диалоговых окон, и нажать кнопку OK в каждом из них. Лучшим способом обеспечения вывода предупреждений является область уведомлений в панели задач (taskbar status area, TSA).

TSA - это область панели задач Windows (обычно в правом нижнем углу экрана), где рядом с системными часами размещаются разнообразные значки. Эта область предоставляет пользователю информацию о действиях приложения. Например, когда служба Windows Update заканчивает загрузку нового набора исправлений, появляется значок, который информирует пользователя, что набор готов к установке.

Приложения используют TSA все больше и больше, но сценарии Perl не могут пользоваться этим преимуществом, потому что изначально поддержка регистрации значков в области TSA отсутствует (хотя компания ActiveState предлагает средство, которое может перекомпилировать сценарий Perl в приложение TSA, называемое PerlTray). Однако вы можете добавить такую поддержку. Давайте разберемся, как работает TSA и как добавить поддержку работы с областью TSA в сценарий мониторинга систем.

Как работает TSA

Когда приложение регистрирует значок в области TSA, его значок отображается рядом с другими. Приложение может добавить столько значков, сколько ему нужно, но большинство приложений отображает лишь один значок. После регистрации значка, он остается видимым до тех пор, пока приложение не удалит значок или не будет закрыто. Если приложение не может удалить значок перед завершением работы, значок останется видимым до тех пор, пока пользователь не укажет на него указателем мыши.

При изменении ситуации, приложение может обновить свой значок в области TSA, чтобы отразить изменившиеся условия. Например, когда приложение работает успешно, его значок может изображать зеленый сигнал светофора, а когда приложение сталкивается с проблемой, значок может быть изменен на красный сигнал. После разрешения проблемы, значок может быть изменен обратно на зеленый сигнал. Такое визуальное представление состояния приложения является простым и быстрым способом уведомления пользователя о проблемах, требующих его внимания.

Помимо значка, приложение может размещать подсказки. Подсказка содержит текст, который ассоциируется со значком приложения. Текст обычно представляет собой сообщение, которое поясняет детали. В предыдущем примере, когда значок изменяется на красный сигнал, подсказка могла бы содержать текст «Недостаточно свободного места на диске» или «Невозможно выполнить запрос». Пользователи видят эту подсказку, когда задерживают указатель мыши на значке. Подсказка появляется поверх значка. Например, на Экране 1 изображена подсказка для «смайлика».

Приложение может изменить подсказку в любой момент. Таким образом, подсказка может предоставить краткое описание того, что происходит в данный момент, не заставляя пользователя открывать файл журнала и читать содержимое журнала или открывать окно и пролистывать выводимые данные журнала. Изменения значка и обновления подсказки являются великолепными средствами обратной связи, которые приложение может использовать для информирования пользователя об изменении своего состояния.

Однако, когда происходит важное событие, от приложения может потребоваться нечто большее, чем просто обновление текста и изменение значка. Приложению может потребоваться привлечь внимание пользователей, чтобы они взглянули на значок и подсказку. Если пользователи глубоко увлечены написанием делового предложения, обновлением таблицы бюджета или пытаются разложить партию пасьянса, они могут благодушно не замечать изменившийся значок или обновленную подсказку.

TSA имеет возможность вывода всплывающего окна с предупреждением, которое гарантированно привлечет внимание пользователей. Оно похожее на воздушный шар, и является достаточно большим, чтобы хранить заголовок и подробное описание проблемы. Вдобавок предупреждение может добавить значок, который свидетельствует о важности проблемы. Например, на Экране 2 изображено предупреждение, использующее значок ошибки (в системе Windows XP этот значок представляет собой красный круг с белым крестом), чтобы показать высокий уровень серьезности. Другие возможные значки включают в себя восклицательный и вопросительный знаки.

Добавление поддержки работы с TSA в сценарий

Теперь, когда вы знаете как работает TSA, давайте разберемся, как добавить поддержку работы с TSA в сценарий мониторинга систем. Этот сценарий, HostMonitor.pl, см. Листинг 5, решает две задачи. Во-первых, он предоставляет работающий сценарий, который мы можете использовать для мониторинга удаленных систем в сети. Он информирует вас, когда одна или несколько систем становятся недоступными. Этот сценарий полезен всем, кому требуется проводить отслеживание систем. Во-вторых, сценарий HostMonitor демонстрирует, как сценарии Perl могут взаимодействовать с TSA. Это знание полезно, если у вас есть сценарии, которые получат выгоду от подобного взаимодействия с TSA.

Я написал сценарий HostMonitor для работы в системе XP, но он должен также работать и в других системах Windows. Сценарию требуются три пакета языка Perl, которые не поставляются со всеми версиями оболочки Perl. Вот эти пакеты:

  • Win32::API - Хотя сценарий HostMonitor явно не загружает пакет Win32::API, компонент Win32::API::Prototype требует наличия пакета Win32::API. Используйте службу Perl Package Manager (PPM) для установки пакета Win32::API. Выполните команду
    ppm install win32-api
  • Win32::API::Prototype - пакет Win32::API::Prototype сильно упрощает использование компонента Win32::API. Используйте службу PPM для установки пакета Win32::API::Prototype. Выполните команду
    	ppm install
    	  http://www.roth.net/perl/ packages
    /win32-api-prototype.ppd

Хотя эта команда в данной статье разбита на несколько строк, вы должны вводить ее одной строкой. Это относится и к другим многострочным командам в данной статье.

  • Win32::PingICMP - пакет Win32::PingICMP представляет собой модуль опроса со спецификой технологии Win32. Выполните команду
    ppm install win32-pingicmp

Сценарий HostMonitor легко использовать. Для запуска сценария вы указываете IP-адреса или имена систем, которые вы хотите отслеживать. Например, если вы хотите указать системы с помощью их IP-адресов, вы можете использовать команду

Perl HostMonitor.pl
192.168.1.1 192.168.1.87

Если вы хотите указать системы с помощью их имен, команда может выглядеть следующим образом

Perl HostMonitor.pl
Server1 Machine2

Вы можете запускать сценарий для любого количества систем.

Вместе с IP-адресами и именами систем, вы можете указать несколько дополнительных параметров:

  • -m MAX_COUNT, где MAX_COUNT - разрешенное количество неудачных опросов, при превышении которого сценарий считает систему недоступной. Если вы не установили параметр -m, сценарий признает систему недоступной после пяти неудачных опросов. Ваше желание изменить это значение будет зависеть от уровней трафика, сетевой архитектуры и загруженности ваших систем.
  • -i SECONDS, где SECONDS - количество секунд между попытками опроса. Чем короче интервал, тем быстрее вы будете предупреждены о проблемах. Однако более короткий интервал также подразумевает создание дополнительного трафика. Если вы не устанавливаете параметр -I, сценарий осуществляет попытки опроса с интервалом в 10 секунд.
  • -t SECONDS, где SECONDS - количество секунд, в течение которых ожидается ответ на опрос. Если это количество превышается, сценарий прекращает ожидание и решает, что опрос провалился. Если вы не устанавливаете параметр -t, сценарий ожидает ответ на опрос в течение одной секунды. Для малых закрытых сетей, одной секунды вполне достаточно. Но, при обращении к системе через Internet или посредством медленного модемного соединения, оправдано использование более высоких значений.

Вы можете указывать дополнительные параметры до или после системных IP-адресов или имен. Например, предположим, вы хотите осуществить мониторинг системы, которая находится поблизости и всегда обеспечивает надежные ответы на опросы. Вы можете использовать команду

Perl HostMonitor.pl router -t 1 -m 1

Параметры применяются ко всем указанным системам.

Как работает сценарий

Сценарий HostMonitor должен быстро провести небольшую подготовку, прежде чем он сможет опрашивать удаленные системы. Требуется создать несколько констант, загрузить несколько функций, загрузить и добавить значки, и создать важный глобальный объект.

Листинг 1 отображает код, который создает константы и загружает три функции из пакета Win32::API::Prototype functions. Код фрагмента A Листинга 1 определяет множество значений констант. Этот код начинает с заполнения списка %CONSTANTS именами и значениями констант, используемых во всем сценарии. После создания списка, код обрабатывает каждые имя и значение и создает анонимный сегмент кода, который возвращает значение константы. В дальнейшем код связывает сегмент кода с глобальным объектом, который имеет то же имя, что и константа.

Для тех из вас, кто не знаком с этой технологией написания сценариев, поясню - это «нечестный» способ создания константы (хотя возможно наиболее применяемый способ создания констант в языке Perl). Язык Perl не имеет реальной поддержки постоянных значений, поэтому вы должны создать либо переменную, которая содержит значение (которое может быть случайно изменено), либо процедуру, которая возвращает желаемое значение. Сценарий HostMonitor использует последний вариант, но делает это динамически вместо того, чтобы вручную писать код процедуры для каждой константы. Преимущество динамической генерации процедур для констант в том, что так проще добавлять или удалять константы из списка %CONSTANTS.

Код фрагмента B в Листинге вызывает метод ApiLink модуля Win32::API::Prototype. Этот метод обеспечивает простой способ взаимодействия с компонентом Win32::API и загружает библиотеки DLL, для того чтобы сценарий могут вызывать функции из данных библиотек. В этом блоке описываются три функции, используемые по всему телу сценария.

Листинг 2 отображает код, который загружает и добавляет значки в TSA и создает важный глобальный объект. Чтобы добавить значок в TSA, вы должны сначала загрузить его в память. Код фрагмента A Листинга 2 занимается именно этим. Он проверяет папку, в которой находится сценарий HostMonitor и загружает все файлы значков, которые соответствуют маске $Config{icon_path_mask}. По умолчанию, файлами значков являются файлы HostMonitor_Normal.ico и HostMonitor_Error.ico. Файл HostMonitor_Normal.ico предоставляет значок со «смайликом» для обозначения нормального состояния. Файл HostMonitor_Error.ico предоставляет значок с восклицательным знаком для обозначения состояния ошибки. Функция системы Windows LoadImage загружает каждый значок. Функция возвращает численное значение, известное как дескриптор значка, которое используется для идентификации значка. Дескриптор добавляется в список %IconList.

Код фрагмента A также создает список %IconData, который используется во всем сценарии для хранения данных, которые будут применены к ярлыку TSA. В этом списке есть один важный ключ: ключ window. Этому ключу назначается значение, возвращаемое после вызова функции системы Windows CreateWindowEx. Эта функция создает окно класса Ghost, которое не отображается и используется исключительно для получения сообщений от других процессов. Значение, возвращаемое функцией CreateWindowEx - дескриптор окна. Этот дескриптор используется для последующего создания в сценарии значка TSA.

Фрагмент B в Листинге 2 отображает код, который добавляет значок в TSA. Сначала, код дает TSA команду выделить место под значок посредством вызова процедуры CreateIcon(). Далее, код обновляет TSA, добавляя значок (в данном случае, значок со смайликом), после чего вызывает процедуру UpdateNormalStatus() для добавления подсказки. Последняя строка фрагмента B перехватывает сигнал прерывания сценария ($SIG{'INT'}). Без этой строки, если бы сценарий завершался, до того как дал команду TSA удалить значок, значок продолжал бы отображаться, как если бы сценарий еще работал. Только после перемещения вашей мыши над «осиротелым» значком, система Windows поймет, что значок необходимо удалить. Чтобы предотвратить эту ситуацию, сценарий перехватывает сигнал прерывания. Таким образом, если вы используете комбинацию клавиш Ctrl+C для остановки сценария, процедура TerminateScript() удалит значок TSA. Однако если сценарий завершиться внезапно (например, оно будет завершен с помощью службы Task Manager или команды die), значок останется в TSA , до тех пор, пока вы вручную не щелкните по нему мышью.

Фрагмент C отображает последний блок кода настройки, который запускается перед тем, как сценарий начнет мониторинг систем. Этот код создает глобальный объект Win32::PingICMP ($PingObject). Сценарий будет использовать один такой объект для опроса всех систем. Этот сценарий использует объекта Win32::PingICMP вместо более распространенного модуля Net::Ping, потому что модуль Net::Ping работает только на системах Win32, если сценарий исполняется под учетной записью администратора.

По окончании подготовительной работы сценарий, наконец, может начать опрос систем. Сценарий снова и снова вызывает различные процедуры, чтобы опросить системы и проверить, успешно ли проходят опросы. Первой из этих процедур является процедура PingHosts(), которая отражена в Листинге 3. Эта простая процедура переводит в цифровой вид имя каждой системы. Эти имена представляются в качестве ключей в передаваемой ссылке на список $HostList. Для каждой системы процедура PingHosts() вызывает метод опроса глобального объекта $PingObject, представленный фрагмента A Листинга 3. Если первый опрос прошел неудачно, процедура уменьшает счетчик опросов для системы на 1 и записывает дату и время. В дальнейшем сценарий использует информацию о дате и времени, чтобы определить, как долго система была недоступна. Если следующий опрос неудачен, процедура снова уменьшает счетчик опроса системы на 1, но не записывает дату или время. Если опрос прошел удачно, процедура сбрасывает счетчик опросов в исходное состояние.

После завершения процедуры PingHosts(), сценарий вызывает процедуру CheckHosts(). Процедура CheckHosts()сначала проверяет, не установлено ли на одном из счетчиков опроса систем значения 0. Если значение счетчика - 0, процедура обновляет строки $AlertMessage и $Message, добавляя данные о недоступной системе. Она также увеличивает значение переменной $iDownCount на 1.

Переменная $iDownCount отслеживает, сколько систем недоступно на данный момент. Если переменная $iDownCount не равна 0, существует, по крайней мере, одна недоступная система. В ответ процедура CheckHosts() выводит предупреждение, вызвая процедуру Alert(). Когда счетчик $iDownCount равен 0, все узлы доступны и процедура проверяет, отображается ли до сих пор предупреждение. Если это так, то она снимает предупреждение. В ином случае, процедура CheckHosts() вызывает процедуру UpdateNormalStatus(), чтобы обновить подсказки для значка TSA.

Как я только что сказал, процедура Alert() выводит предупреждение. TSA имеет ограничения объема выводимой информации, поэтому процедура Alert() сначала сокращает заголовок предупреждения, текст предупреждения и текст подсказки. Далее, процедура устанавливает флаг, чтобы сценарий знал, что процедура в данный момент отображает предупреждение. Потом процедура обновляет список %IconData. Наконец, процедура Alert() вызывает процедуру UpdateTSAIcon().

Подобным образом процедуры ClearAlert(), ChangeIcon() и ChangeText() обновляют список %IconData, после чего вызывают процедуру UpdateTSAIcon(). Имейте ввиду, что каждый вызов процедуры UpdateTSAIcon() передается во флаг. Этот флаг показывает, что информация из списка %IconData будет использоваться.

В Листинге 4 отображена процедура UpdateTSAIcon(). Эта процедура заключает в себе все возможности TSA. Все действия сценария в отношении TSA осуществляются с помощью этой процедуры. Работа процедуры UpdateTSAIcon() начинается с создания упакованной скалярной переменной $pNotifyIconData. Эта переменная использует константу NOTIFYICONDATA как шаблон упаковки, который создает структуру данных NOTIFYICONDATA по аналогии с программами C или C++. Эта структура данных содержит информацию, необходимую системе для обработки сообщений TSA.

Структура NOTIFYICONDATA содержит определенные элементы. Первый элемент - размер (в байтах) структуры данных. Об остальных элементах вы можете узнать на странице http://msdn.microsoft.com/library/enus/shellcc/platform/shell/reference/structures/notifyicondata.asp. Для определения будущего размера структуры процедура UpdateTSAIcon() упаковывает структуру данных, состоящую из одних нулей, как показано фрагмента A Листинга 4. После того как процедура определяет размер, она заполняет структуру данных текущими значениями из списка %IconData, как показано фрагмента B Листинга 4.

После упаковки переменной $pNotifyIconData с текущими значениями, процедура UpdateTSAIcon() передает ее в функцию системы Windows Shell_NotifyIcon. Эта функция создает, изменяет и удаляет значки из TSA. Подробно ознакомиться с этой функцией вы можете на странице http://msdn.microsoft.com/library/enus/shellcc/platform/shell/reference/functions/shell_notifyicon.asp.

Последняя процедура, заслуживающая упоминая - процедура TerminateScript(). Сценарий HostMonitor вызывает эту процедуру, при использовании комбинации клавиш Ctrl+C для завершения сценария. Процедура TerminateScript() вызывает процедуру RemoveIcon(), которая в свою очередь вызывает процедуру UpdateTSAIcon(). Далее, процедура UpdateTSAIcon() удаляет значок сценария из TSA.

TSA - друг автора сценариев

Авторы сценариев мало используют TSA. Причины без сомнения кроются в сложности такой работы. Но как видно из сценария HostMonitor, использование TSA не только возможно, но вероятно и куда проще, чем вы могли подумать. Используя данный сценарий как шаблон, вы можете добавить значок TSA в любой сценарий.


Листинг 1. Код, который создает константы и загружает функции.
# НАЧАЛО ФРАГМЕНТА A
%CONSTANTS = (
 IMAGE_ICON => 1,
 LR_LOADFROMFILE => 0x0010,
 LR_DEFAULTSIZE => 0x0040,
 LR_SHARED => 0x8000,
 NIM_ADD => 0x00000000,
 NIM_MODIFY => 0x00000001,
 NIM_DELETE => 0x00000002,
 NIF_MESSAGE => 0x00000001,
 NIF_ICON => 0x00000002,
 NIF_TIP => 0x00000004,
 NIF_INFO => 0x00000010,
 NIIF_NONE => 0x00000000,
 NIIF_INFO => 0x00000001,
 NIIF_WARNING => 0x00000002,
 NIIF_ERROR => 0x00000003,
 WM_QUIT => 0x0012,
 WM_APP => 0x8000,
 NOTIFYICONDATA => "LLLLLLa128LLa256La64L" );
foreach my $ConstantName ( keys( %CONSTANTS ) )
{
 *{"main::$ConstantName"} =
 eval( "sub { return( $CONSTANTS{$ConstantName} ); } ");
}
# КОНЕЦ ФРАГМЕНТА A
# НАЧАЛО ФРАГМЕНТА B
ApiLink( "shell32.dll", "BOOLEAN Shell_NotifyIcon (
 DWORD dwMessage, PVOID pNotifyIconData )" );
ApiLink( "user32.dll", "HANDLE LoadImage( HANDLE hinst,
 LPCTSTR lpszName, UINT uType, int cxDesired,
 int cyDesired, UINT fuLoad )" );
ApiLink( "user32.dll", "HANDLE CreateWindowEx(
 DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR
 lpWindowName, DWORD dwStyle, int x, int y, int nWidth,
 int nHeight, HANDLE hWndParent, HANDLE hMenu,
 HANDLE hInstance, PVOID lpParam )" ) || die PrintError();
# КОНЕЦ ФРАГМЕНТА B

ЛИСТИНГ 2. Код, который загружает и добавляет значки, а также создает важный глобальный объект
# НАЧАЛО ФРАГМЕНТА A
while( glob( "$SCRIPT_PATH$Config{icon_path_mask}" ) )
{
 my( $Name ) = ( $_ =~ /([^_]+?).ico$/i );
 print "Loading icon named '$Name'
";
 $IconList{lc $Name} = LoadImage( 0, $_, &IMAGE_ICON, 0, 0,
  &LR_DEFAULTSIZE | &LR_SHARED | &LR_LOADFROMFILE );
}
%IconData = (
 title => "Server Monitor",
 icon => 0,
 icon_id => $$,
 message_id => &WM_APP + 1,
 window => CreateWindowEx( 0, "GHOST", "Perl Message
  Only Window:$$", 0, 100, 100, 500, 500, 0, 0, 0, 0 ),
 hover_text => "",
 balloon_text => "",
 balloon_title => "",
 info_flags => 0 );
# КОНЕЦ ФРАГМЕНТА A
# НАЧАЛО ФРАГМЕНТА B
CreateIcon();
ChangeIcon( $IconList{normal} );
UpdateNormalStatus();
$SIG{'INT'} = "TerminateScript";
# КОНЕЦ ФРАГМЕНТА B
# НАЧАЛО ФРАГМЕНТА C
if( ! ( $PingObject = Win32::PingICMP->new( "icmp", $Config{ping_timeout} ) ) )
{
 print "Unable to create a ping object.
";
 exit;
# КОНЕЦ ФРАГМЕНТА C

ЛИСТИНГ 3. Процедура "PingHosts()"
sub PingHosts
{
 my( $PingObject, $HostList ) = @_;
 return unless( defined $PingObject );
 foreach my $HostName ( keys( %$HostList ) )
 {
  my $Host = $HostList->{$HostName};
# НАЧАЛО ФРАГМЕНТА A
  if( ! $PingObject->ping( $HostName ) )
# КОНЕЦ ФРАГМЕНТА A
  {
   $Host->{reported_time} = time()
    if( $Config{ping_max_count} == $Host->{count} );
   $Host->{count}--;
  }
  else
  {
   $Host->{count} = $Config{ping_max_count};
  }
 }
 return;
}

ЛИСТИНГ 4. Процедура UpdateTSAIcon()
sub UpdateTSAIcon
{
 my( $IconData, $Flags, $Function ) = @_;
# НАЧАЛО ФРАГМЕНТА A
 my( $pNotifyIconData ) = pack( &NOTIFYICONDATA, ( 0 ) );
# КОНЕЦ ФРАГМЕНТА A
# НАЧАЛО ФРАГМЕНТА B
 $pNotifyIconData = pack( &NOTIFYICONDATA, (
  length( $pNotifyIconData ),
  $IconData->{window},
  $IconData->{icon_id},
  $Flags | &NIF_MESSAGE,
  $IconData->{message_id},
  $IconData->{icon},
  $IconData->{hover_text},
  0,
  0,
  $IconData->{balloon_text},
  10000, # Balloon timeout value (milliseconds)
  $IconData->{balloon_title},
  $IconData->{info_flags}
 ) );
# КОНЕЦ ФРАГМЕНТА B
 $Function = &NIM_MODIFY if( ! defined $Function );
 if( ! Shell_NotifyIcon( $Function, $pNotifyIconData ) )
 {
  print "
Error!
";
  PrintError();
 }
}