Использование менеджера высокой готовности

Обзор «сторожевых таймеров» менеджера высокой готовности и его дублера

Оглавление:

Общие сведения
Иерархическая структура менеджера высокой готовности
Объекты
Условия
Действия
Реакции на ошибки выполнения действий
Поэтапное восстановление
Состояние менеджера высокой готовности
Пример файловой системы /proc/ham
API менеджера высокой готовности
Функции подключения/отключения
Функции присоединения/отсоединения
Функции для работы с объектами
Функции для работы с условиями
Типы условий
Флаги условий
Функции для работы с действиями
Функции для работы с дескрипторами
Пример клиента
Запуск и остановка менеджера высокой готовности
Остановка менеджера высокой готовности
Функции управления
Управление детализацией информации
Публикация условий, самостоятельно обнаруживаемых компонентами
Публикация изменений состояния
Публикация других условий
Подписка на условия, самостоятельно публикуемые компонентами
Реагирование на изменения состояния
Реагирование на конкретные опубликованные условия

Общие сведения

Менеджер высокой готовности (HAM) ― отказоустойчивый механизм наблюдения за процессами и службами системы («умный сторож»), который способен поэтапно восстанавливать системные службы и процессы после отказов, сбоев и недопустимого ухудшения качества работы. HAM входит в состав комплекта высокой готовности, который информирует компоненты системы о важных событиях с помощью обычного механизма публикации и подписки. Комплект высокой готовности автоматически интегрируется в собственный сетевой механизм (Qnet), и наблюдает за сетью так же, как за локальной системой.

HAM играет роль коммуникатора, с помощью которого компоненты получают и публикуют информацию о состоянии системы — отдельного узла или нескольких узлов, связанных между собой через Qnet. Менеджер высокой готовности следит за конкретными процессами, а также управляет реакцией системы на сбои компонентов и действиями по их восстановлению. Кроме того, менеджер высокой готовности позволяет внешним детекторам информировать систему о важных событиях и задавать действия, которые выполняются при их наступлении.

Во многих системах высокой готовности необходимо локализовывать и защищать единые точки отказа (англ. Single Points Of Failure, SPOFs). Поскольку менеджер высокой готовности хранит информацию о состоянии системы и обеспечивает основные средства ее восстановления, он сам не должен становиться единой точкой отказа ни при каких условиях.

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

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

Иерархическая структура менеджера высокой готовности

Менеджер высокой готовности состоит из трех основных компонентов:

Объекты

Объекты — это основные элементы, за которыми осуществляется наблюдение в системе. Фактически каждый объект представляет собой процесс с уникальным идентификатором (pid) и символьным именем, по которому к нему можно обращаться. Имена объектов уникальны в пределах системы. Поскольку на текущий момент администратор связан с конкретным узлом, имена объектов должны быть уникальными в пределах узла. Как мы увидим позже, уникальные имена объектов очень похожи на имена файлов в иерархической файловой системе.

Существуют следующие типы объектов:

Cамостоятельно присоединяемые объекты
К этой категории относятся процессы, которые самостоятельно подключаются к HAM с помощью функции ham_attach_self() и отключаются от него с помощью функции ham_detach_self(). Самостоятельно присоединяемые объекты компилируются с библиотекой HAM API, а наблюдение за ними выполняется между вызовами функций ham_attach_self() и ham_detach_self().

Самостоятельно присоединяемые объекты также могут информировать HAM о своей работоспособности с помощью контрольных сигналов. Механизм контрольных сигналов позволяет наблюдать за процессами, в которых невозможно обнаруживать сбои, поскольку эти процессы не находятся в сеансе 1, не порождены процессами, за которыми ведется наблюдение, и т.д.

Самостоятельно присоединяемые объекты сами задают моменты начала и окончания наблюдения, а также условия, на которые они намерены реагировать. Иными словами, процесс сам «составляет завещание» на случай своей «смерти».
Объекты, присоединяемые извне
К этой категории относятся традиционные системные процессы (например, демоны и службы), которые выполняют ответственные функции и за которыми ведется наблюдение. Механизм присоединения извне позволяет одному процессу получать уведомления о сбоях в другом процессе без его ведома.
Глобальный объект
Глобальный объект фактически представляет собой шаблон, которому соответствует любой объект системы и который позволяет задавать реакцию на связанное с ним событие. Термин «глобальный» относится к множеству наблюдаемых объектов в системе. Глобальный объект позволяет создавать команды типа «выполнить действие, если любой процесс завершился или не отправил контрольный сигнал». Глобальный объект невозможно добавлять и удалять; на него можно только ссылаться. Добавление/удаление условий и действий из глобального объекта осуществляется стандартным образом.

Note: Чтобы получить дескриптор глобального объекта, следует вызвать функцию ham_entity_handle(), передав NULL в качестве аргумента ename.

Как правило, для создания образов процессов, которые аварийно завершаются из-за выполнения недопустимых операций, используется утилита dumper. Она отправляет уведомления об авариях HAM, а система также информирует его о завершении любых процессов, относящихся к сеансу 1, в том числе демонов, которые отключились от своего управляющего терминала с помощью функции procmgr_daemon().

Если процесс вызывает функцию daemon(), создается новый процесс, который заменяет его и становится лидером сеанса. Если HAM ранее наблюдал за исходным процессом, он автоматически продолжает наблюдение за новым процессом.

Условия

Условия характеризуют состояние объектов. Ниже приведены примеры условий:

CONDDEATH
Завершение объекта (смерть).
CONDABNORMALDEATH
Аварийное завершение объекта. Это условие инициируется, если при завершении объекта был сгенерирован файл дампа (дополнительную информацию см. в описании утилиты dumper).
CONDDETACH
Отсоединение контролируемого объекта. HAM прекращает наблюдение за ним.
CONDATTACH
Присоединение объекта, для которого ранее был создан шаблон (какой-либо процесс подписался на события, связанные с этим объектом). HAM начинает наблюдение за этим объектом.
CONDHBEATMISSEDHIGH
Объект не отправил контрольный сигнал с высоким уровнем важности.
CONDHBEATMISSEDLOW
Объект не отправил контрольный сигнал с низким уровнем важности.
CONDRESTART
Объект был перезапущен. Это условие инициируется после успешного перезапуска объекта.
CONDRAISE
HAM получил извне уведомление о наступлении условия. Подписчики могут связывать действия с такими внешними условиями.
CONDSTATE
Объект сообщил HAM об изменении своего состояния. Подписчики могут связывать действия с такими изменениями.
CONDANY
Это условие произвольного типа, которое позволяет связывать определенные действия с одним из нескольких условий.

Менеджер высокой готовности автоматически обнаруживает и/или инициирует (публикует) вышеперечисленные условия, кроме CONDSTATE, CONDRAISE и CONDANY. Условия CONDSTATE и CONDRAISE передаются HAM внешними детекторами. Подписчики могут назначать любому условию последовательность действий, которая выполняется при его возникновении. Условия CONDSTATE и CONDRAISE обеспечивают возможности фильтрации, которые позволяют подписчикам выборочно связывать действия с конкретными условиями в зависимости от опубликованной информации.

Условия также имеют символьные имена, уникальные в пределах объекта.


Note: HAM обладает расширяемой архитектурой. Он самостоятельно обнаруживает ряд условий; также существует механизм инициации условий, который позволяет другим компонентам уведомлять HAM о важных событиях, происходящих в системе. При разработке отказоустойчивой системы можно можно гибко настраивать эти условия, а также изучать исходный код HAM и добавлять в него функции обнаружения других условий (например, нехватки оперативной памяти или дискового пространства, высокой нагрузки на ЦП и др.)

Действия

Действия связаны с условиями: одно условие может включать в себя несколько действий, которые выполняются при его инициировании. Порядок выполнения действий соответствует порядку их добавления в условие (FIFO). Если несколько условий наступают одновременно, они инициируются в произвольном порядке. Условия типа HCONDINDEPENDENT выполняются в отдельном потоке параллельно с другими условиями (см. раздел Функции для работы с условиями).

API менеджера высокой готовности включает в себя функции для выполнения различных действий:

Действие Описание
ham_action_restart() Перезапуск объекта.
ham_action_execute() Выполнение произвольной команды (например, запуск процесса).
ham_action_notify_pulse() Оповещение процесса о возникновении соответствующего условия в форме импульса, значение которого задается процессом-адресатом. Импульсы можно отправлять по сети, указывая спецификатор соответствующего удаленного узла.
ham_action_notify_signal() Оповещение процесса о возникновении соответствующего условия в форме сигнала реального времени, значение которого задается процессом-адресатом. Сигналы можно отправлять по сети, указывая спецификатор соответствующего удаленного узла.
ham_action_notify_pulse_node() Эта функция идентична описанной выше функции ham_action_notify_pulse(), однако в ней можно указывать полное имя узла вместо его идентификатора.
ham_action_notify_signal_node() Эта функция идентична описанной выше функции ham_action_notify_signal() однако в ней можно указывать полное имя узла вместо его идентификатора.
ham_action_waitfor() Добавление задержки между двумя действиями в последовательности. С помощью этой функции также можно ожидать создания определенных имен в пространстве имен.
ham_action_heartbeat_healthy() Сброс механизма контрольных сигналов для объекта, который перестал отправлять их и инициировал событие пропуска контрольных сигналов, но к настоящему моменту восстановил работоспособность.
ham_action_log() Регистрация настраиваемого информационного сообщения в журнале действий HAM.

Действия также имеют символьные имена, уникальные в пределах условия.


Note: Расширяемая архитектура HAM позволяет разработчику при необходимости создавать собственные функции действий.

Реакции на ошибки выполнения действий

На случай, если при выполнении определенного действия последовательности происходит ошибка, можно задавать альтернативную последовательность действий для ее исправления. Альтернативное действие называется реакцией на ошибку выполнения действия, с которым оно связано. В качестве реакций на ошибки можно выполнять обычные действия, за исключением ham_action_restart() и ham_action_heartbeat_healthy(). Ниже перечислены функции реакций на ошибки:

Действие Описание
ham_action_fail_execute() Выполнение произвольной команды (например, запуск процесса).
ham_action_fail_notify_pulse() Оповещение процесса о возникновении соответствующего условия в форме импульса, значение которого задается процессом-адресатом. Импульсы можно отправлять по сети, указывая спецификатор соответствующего удаленного узла.
ham_action_fail_notify_signal() Оповещение процесса о возникновении соответствующего условия в форме сигнала реального времени, значение которого задается процессом-адресатом. Сигналы можно отправлять по сети, указывая спецификатор соответствующего удаленного узла.
ham_action_fail_notify_pulse_node() Эта функция идентична описанной выше функции ham_action_fail_notify_pulse(), однако в ней можно указывать полное имя узла вместо его идентификатора.
ham_action_fail_notify_signal_node() Эта функция идентична описанной выше функции ham_action_fail_notify_signal(), однако в ней можно указывать полное имя узла вместо его идентификатора.
ham_action_fail_waitfor() Добавление задержки между двумя действиями в последовательности. С помощью этой функции также можно ожидать создания определенных имен в пространстве имен.
ham_action_fail_log() Регистрация настраиваемого информационного сообщения в журнале действий менеджера высокой готовности.

Поэтапное восстановление

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

Допустим, что пользователь запустил файловую систему NFS ( fs-nfs3) и смонтировал несколько каталогов из различных источников. Можно дать HAM команду перезапускать процесс fs-nfs3 после сбоев и затем заново монтировать эти каталоги. Если во время работы fs-nfs3 какие-либо каталоги размонтируются, их повторное монтирование удаляется из списка действий.

Приведем еще один пример: если в менеджере сетевого ввода/вывода io-pkt-* произошел сбой, мы можем дать менеджеру высокой готовности команду перезапустить его, а затем загрузить необходимые сетевые драйверы и несколько дополнительных компонентов, которым сетевые службы необходимы для работы.

Состояние менеджера высокой готовности

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

Менеджер высокой готовности публикует свое текущее состояние в виде файловой системы для чтения, которая позволяет произвольным процессам считывать его, например, с помощью команды:

ls /proc/ham

HAM публикует в файловой системе не только свое состояние, но и статистику и сведения о каждом ее элементе (объект/условие/действие) в файле .info, который находится в соответствующем подкаталоге каталога /proc/ham.

Пример файловой системы /proc/ham

Рассмотрим простой пример, в котором HAM наблюдает за демоном inetd и перезапускает его после сбоев:

# ls -al /proc/ham total 2 -r-------- 1 root root 175 Aug 30 23:05 .info dr-x------ 1 root root 1 Aug 30 23:06 inetd

Файл .info верхнего уровня содержит информацию о HAM, дублере, объектах и других компонентах системы:

# cat /proc/ham/.info Ham Pid : 10993674 Guardian Pid : 10997782 Ham Failures : 0 Guardian Failures : 0 Num Entities : 1 Num Conditions : 1 Num Actions : 1

Здесь inetd — единственный контролируемый объект; он представлен в виде подкаталога каталога /proc/ham:

# ls -al /proc/ham/inetd total 2 -r-------- 1 root root 173 Aug 30 23:06 .info dr-x------ 1 root root 1 Aug 30 23:06 death # cat /proc/ham/inetd/.info Path : inetd Entity Pid : 11014167 Num conditions : 1 Entity type : ATTACHED Stats: Created : 2001/08/30 23:04:49:930148650 Num Restarts : 0

Как видно, файл .info содержит информацию и статистику по объекту inetd. Эта информация генерируется динамически и включает в себя актуальные сведения о каждом объекте.

С объектом inetd связано единственное условие (death или завершение), которое возникает при завершении работы объекта.

# ls -al /proc/ham/inetd/death total 2 -r-------- 1 root root 126 Aug 30 23:07 .info -r-------- 1 root root 108 Aug 30 23:07 restart # cat /proc/ham/inetd/death/.info Path : inetd/death Entity Pid : 11014167 Num Actions : 1 Condition ReArm : ON Condition type : CONDDEATH

С этим условием связано единственное действие — restart (перезапуск). Каждое действие отображается в виде файла в каталоге соответствующего условия. Указанный файл включает в себя сведения о действии, которое выполняется при наступлении этого условия.

# cat /proc/ham/inetd/death/restart Path : inetd/death/restart Entity Pid : 11014167 Action ReArm : ON Restart Line : /usr/sbin/inetd -D


Note: Если inetd не является самостоятельно присоединившимся объектом, следует передать ему ключ -D, чтобы «демонизировать» его посредством функции procmgr_daemon(), а не daemon(). Менеджер высокой готовности получает сообщения о завершении только тех объектов, которые самостоятельно присоединились к нему, завершились аварийно либо выполнялись в сеансе 1; функция daemon() не помещает вызывающий ее процесс в этот сеанс.

Если объект inetd присоединился самостоятельно, можно не передавать ему ключ -D, поскольку HAM начинает автоматически контролировать новый процесс, который создается функцией daemon().


Когда процесс inetd завершается, выполняются все действия, связанные с этим условием:

# slay inetd # cat /proc/ham/inetd/.info Path : inetd Entity Pid : 11071511 <- new pid of entity Num conditions : 1 Entity type : ATTACHED Stats: Created : 2001/08/30 23:04:49:930148650 Last Death : 2001/08/30 23:10:31:889820814 Restarted : 2001/08/30 23:10:31:904818519 Num Restarts : 1

Как видно, статистика по объекту inetd обновилась.

Аналогично, если завершается сам HAM, его место занимает дублер, который создает для себя нового дублера.

# cat /proc/ham/.info Ham Pid : 10993674 <----- менеджер высокой готовности Guardian Pid : 10997782 <----- дублер (Guardian) Ham Failures : 0 Guardian Failures : 0 Num Entities : 1 Num Conditions : 1 Num Actions : 1 ... Уничтожение менеджера высокой готовности .... # /bin/kill -9 10993674 <---- имитация ошибки ... повторное считывание статистики ... # cat /proc/ham/.info Ham Pid : 10997782 <----- новый менеджер высокой готовности Guardian Pid : 11124746 <----- дублер (Guardian) Ham Failures : 1 Guardian Failures : 0 Num Entities : 1 Num Conditions : 1 Num Actions : 1

Как видно, прежний дублер заменил собой HAM и создал нового дублера (Guardian). Все объекты и условия сохранились, а наблюдение продолжилось в обычном режиме. HAM и дублер игнорируют все сигналы, которые допустимо игнорировать.

API менеджера высокой готовности

HAM предоставляет API с функциями для взаимодействия с ним и выполнения следующих действий:

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

Функции подключения/отключения

Библиотека API обеспечивает только одно подключение к HAM. Она работает в многопоточной среде и объединяет множество подключений одного или нескольких потоков в единое подключение к HAM с использованием счетчика ссылок.

Ниже перечислены основные функции подключения к менеджеру высокой готовности:

/* Основные функции подключения возвращают признак успешного выполнения (0) или ошибки (-1 и переменную errno) */
int ham_connect( unsigned flags );
int ham_connect_nd( int nd,
unsigned flags );
int ham_connect_node( const char *nodename,
unsigned flags );
int ham_disconnect( unsigned flags );
int ham_disconnect_nd( int nd,
unsigned flags );
int ham_disconnect_node( const char *nodename,
unsigned flags );

Эти функции открывают и закрывают подключения к HAM. При первом вызове функция ham_connect*() открывает файловый дескриптор fd, а при последующих вызовах инкрементирует счетчик ссылок.

Аналогичным образом функция ham_disconnect() декрементирует счетчик ссылок до нуля, а вызов, обнуляющий счетчик ссылок, закрывает файловый дескриптор fd. Эти функции возвращают -1 при возникновении ошибки и 0 при успешном выполнении. Аналогично функция ham_disconnect*() декрементирует счетчик ссылок до нуля, а вызов, обнуляющий счетчик ссылок, закрывает файловый дескриптор. Эти функции возвращают -1 (и присваивают значение переменной errno) при возникновении ошибки и 0 при успешном выполнении.

В многопоточной среде в каждый момент времени открыто только одно подключение к HAM, даже если функции ham_connect*() / ham_disconnect*() выполняются несколькими потоками.

Функции ham_*_nd() и ham_*_node() открывают подключение к удаленному HAM по протоколу Qnet. В параметре nd указывается идентификатор удаленного узла в момент вызова функции. Поскольку идентификаторы узлов могут изменяться с течением времени, необходимо запрашивать идентификатор нужного узла непосредственно перед вызовом функции. В качестве альтернативы можно указывать полное имя узла (англ. Fully Qualified Node Name, FQNN) в параметре nodename. Значение ND_LOCAL_NODE параметра nd (константа, определенная в файле <sys/netmgr.h>) и значение NULL параметра nodename (либо пустая строка) эквивалентны друг другу и указывают на текущий узел (их применение аналогично непосредственному вызову функции ham_connect() или ham_disconnect()).

Вызовы функций ham_connect(), ham_connect_nd() и ham_connect_node() можно чередовать друг с другом; необходимо лишь следить за тем, чтобы количество вызовов функций подключения совпадало с количеством вызовов функций отключения для каждого конкретного (локального или сетевого) соединения с HAM (файлового дескриптора fd) перед его закрытием.

Функции присоединения/отсоединения

Самостоятельное присоединение объектов

ham_entity_t * ham_attach_self( char *ename,
uint64_t hp,
int hpdl,
int hpdh,
unsigned flags );
int ham_detach_self( ham_entity_t *ehdl,
unsigned flags );

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

Аргумент ename содержит символьное имя объекта, которое должно быть уникальным в пределах системы (среди всех контролируемых объектов в момент вызова функции).

Аргумент hp содержит длительность периода отправки контрольных сигналов в наносекундах. Передача контрольных сигналов применяется для наблюдения за работоспособностью объекта. Работоспособность — это состояние объекта, в котором он выполняет возложенные на него задачи. Нарушение готовности компонента часто проявляется не в его завершении, а в отсутствии реакции на запросы или прекращении выполнения каких-либо действий. Можно настроить отправку компонентом контрольных сигналов через заданные промежутки времени; пропуск указанного количества отправок инициирует условие отсутствия контрольных сигналов.

Аргументы hpdl и hpdh содержат количество пропущенных контрольных сигналов, при которых возникают условия heartbeatmissedlow и heartbeatmissedhigh. Библиотека API регистрирует этот запрос и создает поток, который администрирует подключение к HAM. При аварийном завершении объекта это подключение закрывается и HAM обнаруживает сбой, поскольку перед завершением объект не вызвал функцию ham_detach_self().

Если HAM аварийно завершается (что крайне маловероятно) и его место занимает дублер, подключение к прежнему HAM становится недействительным; дублер отправляет всем самостоятельно присоединившимся объектам запрос на повторное присоединение, а упомянутый выше дополнительный поток выполняет его в прозрачном режиме.

Если подключение к HAM уже открыто, функция ham_attach_self() использует его и инкрементирует счетчик открытых клиентом подключений. Если клиент объявил о намерении отправлять HAM контрольные сигналы с заданным периодом, он обязан делать это с помощью функции ham_heartbeat().

Библиотека также проверяет уникальность имени, указанного в аргументе ename. Если такого имени не существует, запрос передается HAM, который также проверяет его во избежание конфликтов при создании новых объектов. Функция ham_attach_self() возвращает обычный дескриптор, который является непрозрачным указателем и может использоваться процессом для последующего отсоединения от HAM, а также добавления условий и действий, как показано далее.

Функция ham_detach_self() закрывает подключение к HAM, который прекращает наблюдение за самостоятельно подключившимся объектом и отменяет дополнительный поток. Функция ham_detach_self() принимает в качестве аргумента дескриптор, возвращаемый функцией ham_attach_self().

Фрагмент кода с вызовами функций самостоятельного присоединения/отсоединения

В следующем фрагменте кода используются функции ham_attach() | detach_self():

...
ham_entity_t *ehdl; /* дескриптор объекта */
int status;
/* подключение к менеджеру высокой готовности с периодом контрольного сигнала 5 секунд, именем
* объекта "client1", без использования флагов. Также указаны параметры hpdh = 4 и hpdh = 8 */
ehdl = ham_attach_self( "client1", 5000000000, 4, 8, 0 );
if ( ehdl == NULL )
{
printf( "Could not attach to Ham\n" );
exit( -1 );
}
/* Отсоединение от менеджера высокой готовности с помощью исходного дескриптора */
status = ham_detach_self( ehdl, 0 );
...

Присоединение/отсоединение любых других объектов

ham_entity_t * ham_attach( char *ename,
int nd,
pid_t pid,
char *line,
unsigned flags );
ham_entity_t * ham_attach_node( char *ename,
const char *nodename,
pid_t pid,
char *line,
unsigned flags );
int ham_detach( ham_entity_t *ehdl,
unsigned flags );
int ham_detach_name( int nd,
char *ename,
unsigned flags );
int ham_detach_name_node( const char *nodename,
char *ename,
unsigned flags );

Функции attach/detach/detach-name очень похожи на рассмотренные ранее функции *_self(), но дают менеджеру высокой готовности команду контролировать другой процесс.

Этот механизм позволяет наблюдать за любыми существующими объектами, которые не скомпилированы с библиотекой API HAM, при этом наблюдение может выполняться без их ведома.

Вызов функции ham_attach() позволяет:

Если мы предполагаем, что объект не выполняется, мы указываем -1 в качестве параметра pid в вызове ham_attach(). В этом случае объект запускается с помощью команды, указанной в строке line. Если значение pid больше 0, команда line игнорируется и менеджер высокой готовности присоединяет объект с указанным pid. Значение параметра ename должно быть уникальным среди объектов, зарегистрированных в момент вызова функции.

Спецификатор nd функций ham_attach() и ham_detach_name() и спецификатор nodename функций ham_attach_node() и ham_detach_name_node() используются для обращения к удаленному HAM через протокол Qnet. В параметре nd указывается идентификатор удаленного узла в момент вызова функции. Поскольку идентификаторы узлов могут изменяться со временем, необходимо запрашивать идентификатор нужного узла непосредственно перед вызовом функции. В качестве альтернативы можно указывать полное имя узла в параметре nodename. Значение ND_LOCAL_NODE параметра nd (константа, определенная в файле <sys/netmgr.h>) и значение NULL параметра nodename (либо пустая строка) эквивалентны друг другу и указывают на текущий узел.

Функции ham_detach*() прекращают наблюдение за конкретным объектом. Вызов функции ham_detach() принимает в качестве аргумента исходный дескриптор, возвращаемый функцией ham_attach(). В функции ham_detach_name() вместо дескриптора используется имя объекта.

С помощью дескриптора также можно добавлять условия в объект (см. далее).

Фрагмент кода с вызовами функций присоединения/отсоединения других объектов

...
ham_entity_t *ehdl;
int status;
ehdl = ham_attach( "inetd", 0, -1, "/usr/sbin/inetd -D", 0 );
/* процесс inetd запущен и работает под наблюдением */
...
status = ham_detach( ehdl, 0 );
...

Разумеется, присоединение и отсоединение от менеджера высокой готовности не обязательно выполнять в одном потоке:

...
ham_entity_t *ehdl;
int status;
/* запуск процесса inetd и наблюдения за ним */
ehdl = ham_attach( "inetd", 0, -1, "/usr/sbin/inetd -D", 0 );
...
/* отсоединение от менеджера высокой готовности с продолжением наблюдения */
exit( 0 );

Отсоединение inetd:

...
int status;
/* прекращение наблюдения за inetd. */
status = ham_detach_name( 0, "inetd", 0 );
...
exit( 0 );

Если бы процесс inetd уже выполнялся (например, с pid 105328676), код присоединения/отсоединения было бы можно написать следующим образом:

ham_entity_t *ehdl;
int status;
ehdl = ham_attach( "inetd", 0, 105328676, NULL, 0 );
...
status = ham_detach( ehdl, 0 );
/* status = ham_detach_name(0, "inetd",0); */
...
exit( 0 );

Функции ham_attach() и ham_detach() создают подключение к HAM, если оно не было создано ранее; это делается только для того, чтобы облегчить их использование.

Подключения к HAM существуют только между вызовами функций присоединения и отсоединения; перед любыми последующими обращениями к HAM необходимо вызывать функцию ham_connect().

При отправке большого количества последовательных запросов HAM рекомендуется:

  1. Вызывать функцию ham_connect() перед первым запросом.
  2. Вызывать функцию ham_disconnect() после последнего запроса.

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

Функции для работы с объектами

Функции ham_attach_*() обычно используются, если объект уже выполняется либо будет запущен HAM; наблюдение начинается с момента их вызова. API менеджера высокой готовности также включает в себя две функции для создания шаблонов объектов, которые еще не выполняются, но могут быть запущены позднее. Этот механизм позволяет процессам подписываться на интересующие события, не дожидаясь, когда объект будет создан публикатором (другим объектом или HAM).

ham_entity_t * ham_entity( const char *ename,
int nd,
unsigned flags );
ham_entity_t * ham_entity_node( const char *ename,
const char *nodename,
unsigned flags );

Эти функции создают шаблоны объектов с именем ename на узле с идентификатором nd или именем nodename. В созданные шаблоны можно добавлять условия и действия для соответствующих объектов. При последующем вызове функции ham_attach*() с именем шаблона в параметре ename выполняется поиск шаблона с соответствующим идентификатором процесса, после чего начинается обычное наблюдение за объектом.

Функции для работы с условиями

ham_condition_t * ham_condition( ham_entity_t *ehdl,
int type,
const char *cname,
unsigned flags );
int ham_condition_remove( ham_condition_t *chdl,
unsigned flags );

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

Этот механизм позволяет объединять действия в группы для последующего удаления и управления.

Поскольку условия связаны с объектами, для добавления условий необходимо получать дескриптор объекта. Функции ham_condition*() возвращают дескриптор условия — непрозрачный указатель, который можно использовать для добавления и удаления действий из условия.

Типы условий

Можно указывать следующие типы условий:

CONDDEATH
Завершение объекта.
CONDABNORMALDEATH
Аварийное завершение объекта. Это условие возникает, если при завершении объекта был сгенерирован файл дампа (дополнительную информацию см. в описании утилиты dumper).
CONDDETACH
Отсоединение контролируемого объекта. HAM прекращает наблюдение за ним.
CONDATTACH
Присоединение объекта, для которого ранее был создан шаблон (какой-либо процесс подписался на события, связанные с этим объектом). HAM начинает наблюдать за этим объектом.
CONDHBEATMISSEDHIGH
Объект не отправил контрольный сигнал с высоким уровнем важности.
CONDHBEATMISSEDLOW
Объект не отправил контрольный сигнал с низким уровнем важности.
CONDRESTART
Объект перезапущен. Это условие инициируется после успешного перезапуска объекта.
CONDANY
Это условие подходит ко всем типам условий; с его помощью можно связывать одни и те же действия с одним из множества условий.

Условия CONDATTACH, CONDDETACH и CONDRESTART инициируются HAM при присоединении, отсоединении и перезапуске объектов соответственно. Условия CONDHBEATMISSEDHIGH и CONDHBEATMISSEDLOW инициируются HAM, когда он обнаруживает пропущенные контрольные сигналы от объектов, уведомивших его о намерении отправлять их.

Условие CONDDEATH инициируется при завершении объекта. Условие CONDABNORMALDEATH инициируется только при аварийном завершении объекта, однак одновременно с ним также инициируется условие CONDDEATH.

Условие detach (отсоединение) позволяет выполнять действия при корректном отсоединении наблюдаемого объекта от HAM. После отсоединения наблюдение за объектом прекращается. С помощью этого условия можно уведомлять клиентов о том, что HAM больше не публикует информацию об отсоединившемся объекте.

Условие restart (перезапуск) автоматически создается и инициируется HAM при завершении и перезапуске объекта.

Флаги условий

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

Если у условия установлены флаги HCONDINDEPENDENT и HCONDNOWAIT, флаг HCONDNOWAIT имеет приоритет; все действия этого условия выполняются в одном потоке с действиями всех остальных условий с флагом HCONDNOWAIT, поскольку этим условиям уже гарантированы минимальные задержки.

Условие, у которого не установлен ни один из флагов HCONDNOWAIT и HCONDINDEPENDENT, обрабатывается вместе с другими условиями (HCONDOTHER) в порядке наступления (FIFO).

Резюмируем:

  1. Все инициированные условия (CONDDEATH, CONDDETACH и др.) с флагом HCONDNOWAIT выполняются в одном потоке в порядке наступления (FIFO).

  2. Каждое условие с флагом HCONDINDEPENDENT (но без флага HCONDNOWAIT) выполняется в отдельном потоке.

  3. Все остальные условия выполняются в порядке наступления (FIFO) в одном общем потоке.

Таким образом, максимальное количество потоков обработки условий составляет:

(количество условий HCONDINDEPENDENT) + 2,

где один поток обрабатывает все условия с флагом HCONDNOWAIT, а другой поток — все остальные условия.

Все действия конкретного условия выполняются в порядке очереди (FIFO) независимо от состояния флагов HCONDNOWAIT и HCONDINDEPENDENT.

Функции для работы с действиями

/* операции действий */
ham_action_t * ham_action_restart( ham_condition_t *chdl,
const char *aname,
const char *path,
unsigned flags );
ham_action_t * ham_action_execute( ham_condition_t *chdl,
const char *aname,
const char *path,
unsigned flags );
ham_action_t * ham_action_waitfor( ham_condition_t *chdl,
const char *aname,
const char *path,
int delay,
unsigned flags );
ham_action_t * ham_action_notify_pulse( ham_condition_t *chdl,
const char *aname,
int nd,
int topid,
int chid,
int pulsecode,
int value,
unsigned flags );
ham_action_t * ham_action_notify_signal( ham_condition_t *chdl,
const char *aname,
int nd,
pid_t topid,
int signum,
int code,
int value,
unsigned flags );
ham_action_t * ham_action_notify_pulse_node( ham_condition_t *chdl,
const char *aname,
const char *nodename,
int topid,
int chid,
int pulsecode,
int value,
unsigned flags );
ham_action_t * ham_action_notify_signal_node( ham_condition_t *chdl,
const char *aname,
const char *nodename,
pid_t topid,
int signum,
int code,
int value,
unsigned flags );
ham_action_t * ham_action_heartbeat_healthy( ham_condition_t *chdl,
const char *aname,
unsigned flags );
ham_action_t * ham_action_log( ham_condition_t *chdl,
const char *aname,
const char *msg,
unsigned attachprefix,
int verbosity,
unsigned flags );
/* удаление действия */
int ham_action_remove( ham_action_t *ahdl,
unsigned flags );

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

ham_action_restart()
Перезапускает объект при наступлении условия death (завершение). Это означает, что объект завершился; действие restart (перезапуск) перезапускает его и сохраняет новый идентификатор процесса (pid) объекта.

Note: Действия restart можно связывать только с условиями death, каждое из которых может содержать только одно действие restart в каждый момент времени. Это гарантирует, что объект перезапускается однократно и только после завершения (к типу death относятся условия CONDDEATH в CONDABNORMALDEATH).

ham_action_execute()
Выполняет произвольную командную строку. При наступлении условия осуществляется обход и последовательное выполнение списка действий.

Командная строка выполняется в соответствии с указанными в ней параметрами; она должна содержать ПОЛНЫЙ путь к исполняемому файлу и все требуемые параметры. HAM передает командную строку команде spawn(), которая создает новый процесс, выполняющий команду.

Действия execute (выполнение) удобно использовать для поэтапного восстановления объекта. Например, если процесс fs-nfs3 перезапускается после завершения, функция ham_action_execute() позволяет повторно монтировать все необходимые каталоги.

Действие execute с флагом HACTIONDONOW выполняется немедленно. Этот механизм также удобно использовать для поэтапного создания и запуска объекта.

Следует иметь в виду, что флаг HACTIONDONOW игнорируется для действий waitfor, поэтому для добавления задержек в последовательность действий с флагом HACTIONDONOW необходимо вставлять их в клиентскую программу между вызовами ham_action*().
ham_action_waitfor()
Функция ham_action_waitfor() вставляет задержки между последовательно выполняемыми действиями, если это разрешено соответствующим условием (см. раздел Функции для работы с условиями в настоящей главе). Длительность задержки кратна 100 мс.

С помощью вызова ham_action_waitfor() можно ожидать появления в пространстве имен определенного имени, указанного в аргументе path. Если аргумент path не задан (NULL), функция выполняет задержку на delay мс, а если задан — ожидает delay мс или создания имени path в пространстве имен (в зависимости от того, что произойдет раньше). Следует иметь в виду, что если путевое имя указан, длительность задержки равна ближайшему кратному 100 мс с округлением в большую сторону. При задержке, равной 0, действие waitfor фактически не выполняется, а аргумент pathname игнорируется.
ham_action_notify_pulse()
ham_action_notify_signal()
Функция ham_action_notify_pulse() отправляет соответствующий импульс в канал nd/pid/chid (идентификатор узла / идентификатор процесса / идентификатор канала).

Функция action_notify_signal() отправляет соответствующий сигнал реального времени со значением процессу pid, который запрашивает его.

После перезапуска объекта можно сохранять как его действия, так и условия (т.е. повторно инициировать их), добавляя флаг HREARMAFTERRESTART в аргумент флагов вызова ham_condition() или соответствующего действия с помощью операции ИЛИ.

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

Если условие не отмечено как реактивируемое, все его действия автоматически удаляются, поскольку не могут существовать отдельно от него.

Возможность сохранения условий и действий объекта зависит от способа его перезапуска. Если объект не перезапускается с помощью действия ACTIONRESTART (оно не используется или при его выполнении произошла ошибка), объект удаляется вместе со всеми условиями и действиями.
ham_action_notify_pulse_node()
Эта функция идентична описанной выше функции ham_action_notify_pulse(), однако в ней можно указывать полное имя узла вместо его идентификатора (nd).
ham_action_notify_signal_node()
Эта функция идентична описанной выше функции ham_action_notify_signal(), однако в ней можно указывать полное имя узла вместо его идентификатора (nd).

Action fail functions

/* Операции реагирования на ошибки выполнения действий */
int ham_action_fail_execute( ham_action_t *ahdl,
const char *aname,
const char *path,
unsigned flags );
int ham_action_fail_waitfor( ham_action_t *ahdl,
const char *aname,
const char *path,
int delay,
unsigned flags );
int ham_action_fail_notify_pulse( ham_action_t *ahdl,
const char *aname,
int nd,
int topid,
int chid,
int pulsecode,
int value,
unsigned flags );
int ham_action_fail_notify_signal( ham_action_t *ahdl,
const char *aname,
int nd,
pid_t topid,
int signum,
int code,
int value,
unsigned flags );
int ham_action_fail_notify_pulse_node( ham_action_t *ahdl,
const char *aname,
const char *nodename,
int topid,
int chid,
int pulsecode,
int value,
unsigned flags );
int ham_action_fail_notify_signal_node( ham_action_t *ahdl,
const char *aname,
const char *nodename,
pid_t topid,
int signum,
int code,
int value,
unsigned flags );
int ham_action_fail_log( ham_action_t *ahdl,
const char *aname,
const char *message,
unsigned attachprefix,
int verbosity,
unsigned flags );
/* удаление операции реагирования на ошибку выполнения действия */
int ham_action_fail_remove( ham_action_t *ahdl,
const char *aname,
unsigned flags );

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

Пример наблюдения за процессом inetd

В следующем фрагменте кода показан запуск наблюдения за процессом inetd:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/netmgr.h>
#include <fcntl.h>
#include <ha/ham.h>
int main( int argc, char *argv[] )
{
int status;
char *inetdpath;
ham_entity_t *ehdl;
ham_condition_t *chdl;
ham_action_t *ahdl;
int inetdpid;
inetdpath = strdup( "/usr/sbin/inetd -D" );
inetdpid = -1;
ham_connect( 0 );
ehdl = ham_attach( "inetd", ND_LOCAL_NODE, inetdpid, inetdpath, 0 );
if ( ehdl != NULL )
{
chdl = ham_condition( ehdl, CONDDEATH, "death", HREARMAFTERRESTART );
if ( chdl != NULL )
{
ahdl = ham_action_restart( chdl, "restart", inetdpath, HREARMAFTERRESTART );
if ( ahdl == NULL )
printf("add action failed\n");
} else
printf( "add condition failed\n" );
} else
printf( "add entity failed\n" );
ham_disconnect( 0 );
exit( 0 );
}

Пример наблюдения за процессом fs-nfs3

В следующем фрагменте кода показан запуск наблюдения за процессом fs-nfs3:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/netmgr.h>
#include <fcntl.h>
#include <ha/ham.h>
int main( int argc, char *argv[] )
{
int status;
ham_entity_t *ehdl;
ham_condition_t *chdl;
ham_action_t *ahdl;
char *fsnfspath;
int fsnfs3pid;
fsnfspath = strdup( "/usr/sbin/fs-nfs3" );
fsnfs3pid = -1;
ham_connect( 0 );
ehdl = ham_attach( "Fs-nfs3", ND_LOCAL_NODE, fsnfs3pid, fsnfspath, 0 );
if ( ehdl != NULL )
{
chdl = ham_condition( ehdl, CONDDEATH, "Death", HREARMAFTERRESTART );
if ( chdl != NULL )
{
ahdl = ham_action_restart( chdl, "Restart", fsnfspath, HREARMAFTERRESTART );
if ( ahdl == NULL )
printf("add action failed\n");
else {
ahdl = ham_action_waitfor( chdl, "Delay1", NULL, 2000, HREARMAFTERRESTART );
if ( ahdl == NULL )
printf( "add action failed\n" );
ahdl = ham_action_execute( chdl, "MountDir1", "/bin/mount -t nfs a.b.c.d:/dir1 /dir1",
HREARMAFTERRESTART | HACTIONDONOW );
if ( ahdl == NULL )
printf( "add action failed\n" );
ahdl = ham_action_waitfor( chdl, "Delay2", NULL, 2000, HREARMAFTERRESTART );
if ( ahdl == NULL )
printf( "add action failed\n" );
ahdl = ham_action_execute( chdl, "Mountdir2", "/bin/mount -t nfs a.b.c.d:/dir2 /dir2",
HREARMAFTERRESTART | HACTIONDONOW );
if ( ahdl == NULL )
printf( "add action failed\n" );
}
} else
printf( "add condition failed\n" );
} else
printf( "add entity failed\n" );
ham_disconnect( 0 );
exit( 0 );
}

Функции для работы с дескрипторами

/* получение/освобождение дескрипторов */
ham_entity_t * ham_entity_handle( int nd,
const char *ename,
unsigned flags );
ham_condition_t * ham_condition_handle( int nd,
const char *ename,
const char *cname,
unsigned flags );
ham_action_t * ham_action_handle( int nd,
const char *ename,
const char *cname,
const char *aname,
unsigned flags );
ham_entity_t * ham_entity_handle_node( const char *nodename,
const char *ename,
unsigned flags );
ham_condition_t * ham_condition_handle_node( const char * nodename,
const char *ename,
const char *cname,
unsigned flags );
ham_action_t * ham_action_handle_node( const char * nodename,
const char *ename,
const char *cname,
const char *aname,
unsigned flags );
int ham_entity_handle_free( ham_entity_t *ehdl );
int ham_condition_handle_free( ham_condition_t *chdl );
int ham_action_handle_free( ham_action_t *ahdl );

Функции для работы с дескрипторами позволяют получать и освобождать дескрипторы по имени объекта, условия и действия. С помощью полученных дескрипторов можно добавлять и удалять условия и действия. Аналогично другим функциям, у функций для работы с дескрипторами существуют версии типа *_node(), которые позволяют обращаться к HAM по сети с указанием полного имени узла (FQNN).

Пример клиента

Ниже приведен пример клиента, который получает от HAM уведомления о важных событиях в виде импульсов и сигналов. Клиент регистрирует механизм уведомления о завершении или отсоединении процесса inetd в виде импульса и завершении процесса fs-nfs3 в виде сигнала.

В этом примере также показана проблема задержки уведомлений и ее решение с помощью условия HCONDINDEPENDENT.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/neutrino.h>
#include <sys/iomsg.h>
#include <sys/netmgr.h>
#include <signal.h>
#include <ha/ham.h>
#define PCODEINETDDEATH (_PULSE_CODE_MINAVAIL + 1)
#define PCODEINETDDETACH (_PULSE_CODE_MINAVAIL + 2)
#define PCODENFSDELAYED (_PULSE_CODE_MINAVAIL + 3)
#define PCODEINETDRESTART1 (_PULSE_CODE_MINAVAIL + 4)
#define PCODEINETDRESTART2 (_PULSE_CODE_MINAVAIL + 5)
#define MYSIG (SIGRTMIN + 1)
int fsnfs_value;
/* Обработчик сигналов для обработки уведомлений о завершении процесса fs-nfs3 */
void MySigHandler( int signo, siginfo_t *info, void *extra )
{
printf( "Received signal %d, with code = %d, value %d\n", signo, info->si_code, info->si_value.sival_int );
if ( info->si_value.sival_int == fsnfs_value )
printf( "FS-nfs3 died, this is the notify signal\n" );
return;
}
int main( int argc, char *argv[] )
{
int chid, coid, rcvid;
struct _pulse pulse;
pid_t pid;
int status, value;
ham_entity_t *ehdl;
ham_condition_t *chdl;
ham_action_t *ahdl;
struct sigaction sa;
int scode, svalue;
/* для приема импульсов необходимо создать канал */
chid = ChannelCreate( 0 );
/* для доставки импульсов необходимо подключиться к этому каналу */
coid = ConnectAttach( 0, 0, chid, _NTO_SIDE_CHANNEL, 0 );
/* заполнение структуры события для импульса */
pid = getpid();
value = 13;
ham_connect( 0 );
/* считаем, что объект с именем "inetd" уже существует */
chdl = ham_condition_handle( ND_LOCAL_NODE, "inetd","death", 0 );
ahdl = ham_action_notify_pulse( chdl, "notifypulsedeath", ND_LOCAL_NODE, pid, chid,
PCODEINETDDEATH, value, HREARMAFTERRESTART );
ham_action_handle_free( ahdl );
ham_condition_handle_free( chdl );
ehdl = ham_entity_handle( ND_LOCAL_NODE, "inetd", 0 );
chdl = ham_condition( ehdl, CONDDETACH, "detach", HREARMAFTERRESTART );
ahdl = ham_action_notify_pulse( chdl, "notifypulsedetach", ND_LOCAL_NODE, pid, chid,
PCODEINETDDETACH, value, HREARMAFTERRESTART );
ham_action_handle_free( ahdl );
ham_condition_handle_free( chdl );
ham_entity_handle_free( ehdl );
fsnfs_value = 18; /* значение, которое будет использоваться как признак завершения fs-nfs */
scode = 0;
svalue = fsnfs_value;
sa.sa_sigaction = MySigHandler;
sigemptyset( &sa.sa_mask );
sa.sa_flags = SA_SIGINFO;
sigaction( MYSIG, &sa, NULL );
/* Считаем, что объект с именем "Fs-nfs3" уже существует. Имя "Fs-nfs3" используется в качестве
* символьного идентификатора объекта fs-nfs3. Имя объекта может быть любым, однако рекомендуется
* использовать легкочитаемые и осмысленные имена. */
ehdl = ham_entity_handle( ND_LOCAL_NODE, "Fs-nfs3", 0 );
/* Добавление нового "независимого" условия. Его уведомления/действия защищены от задержек "waitfor"
* в потоках, которые выполняют другие последовательности действий */
chdl = ham_condition( ehdl, CONDDEATH, "DeathSep", HCONDINDEPENDENT | HREARMAFTERRESTART );
ahdl = ham_action_notify_signal( chdl, "notifysignaldeath", ND_LOCAL_NODE, pid, MYSIG, scode,
svalue, HREARMAFTERRESTART );
ham_action_handle_free( ahdl );
ham_condition_handle_free( chdl );
ham_entity_handle_free( ehdl );
chdl = ham_condition_handle( ND_LOCAL_NODE, "Fs-nfs3","Death",0 );
/* Это действие добавляется в условие без флага HCONDNOWAIT. Условие может включать в себя
* последовательность действий с произвольными задержками и ожиданиями, которые препятствуют
* доставке уведомлений. */
ahdl = ham_action_notify_pulse( chdl, "delayednfsdeathpulse", ND_LOCAL_NODE, pid, chid,
PCODENFSDELAYED, value, HREARMAFTERRESTART );
ham_action_handle_free( ahdl );
ham_condition_handle_free( chdl );
ehdl = ham_entity_handle( ND_LOCAL_NODE, "inetd", 0 );
/* Мы указываем, что это условие является независимым */
chdl = ham_condition( ehdl, CONDRESTART, "restart", HREARMAFTERRESTART | HCONDINDEPENDENT );
ahdl = ham_action_notify_pulse( chdl, "notifyrestart_imm", ND_LOCAL_NODE, pid, chid,
PCODEINETDRESTART1, value, HREARMAFTERRESTART );
ham_action_handle_free( ahdl );
ahdl = ham_action_waitfor( chdl, "delay", NULL, 6532, HREARMAFTERRESTART );
ham_action_handle_free( ahdl );
ahdl = ham_action_notify_pulse( chdl, "notifyrestart_delayed", ND_LOCAL_NODE, pid, chid,
PCODEINETDRESTART2, value, HREARMAFTERRESTART );
ham_action_handle_free( ahdl );
ham_condition_handle_free( chdl );
ham_entity_handle_free( ehdl );
while ( 1 )
{
rcvid = MsgReceivePulse( chid, &pulse, sizeof( pulse ), NULL );
if ( rcvid < 0 )
{
if ( errno != EINTR )
exit( -1 );
} else {
switch ( pulse.code )
{
case PCODEINETDDEATH:
printf( "Inetd Death Pulse\n" );
break;
case PCODENFSDELAYED:
printf( "Fs-nfs3 died: this is the possibly delayed pulse\n" );
break;
case PCODEINETDDETACH:
printf( "Inetd detached, so quitting\n" );
goto the_end;
case PCODEINETDRESTART1:
printf( "Inetd Restart Pulse: Immediate\n" );
break;
case PCODEINETDRESTART2:
printf( "Inetd Restart Pulse: Delayed\n" );
break;
}
}
}
/* Мы больше не ждем информацию о процессе inetd, поскольку знаем, что он завершен. Мы
* продолжаем получать информацию о завершении процесса fs-nfs3, поскольку не удалили эти
* действия. Если мы завершим работу сейчас, при следующем выполнении этих действий произойдет
* ошибка (поскольку получателя уведомлений больше не существует), после чего они будут
* автоматически удалены и уничтожены. */
the_end:
ham_disconnect( 0 );
exit( 0 );
}


Note: Следует иметь в виду, что в API HAM существует ряд ограничений:

  • Имена объектов, условий и действий (ename, cname и aname) не должны содержать символ /.

  • Длина имен не может быть больше константы POSIX_PATH_MAX, заданной в файле <limits.h>. Поскольку имена создаются в пространстве имен, фактическая длина имени равна длине пути, частью которого оно является. Другими словами, _суммарная длина имени в в формате «объект/условие/действие» в совокупности с префиксом /proc/ham не должна превышать значения _POSIX_PATH_MAX.

Запуск и остановка менеджера высокой готовности

HAM запускается командой ham в командной строке:

ham

Утилита ham имеет следующие параметры:

-?|h
Вывод справочной информации об использовании утилиты.
-d
Отключение внутреннего режима вывода подробной информации.
-f
Вывод подробной информации в файл (по умолчанию stderr).
-t, none|relative|absolute|shortabs
Тип используемых меток времени. По умолчанию применяется тип relative (относительное время).
-v
Задание уровня детализации — чем больше ключей -v, тем подробнее информация.
-V, n
Задание уровня детализации в числовом виде (например, -V 3).

При запуске HAM создает своего дублера.


Note: Необходимо указывать полный путь к утилите ham в команде запуска менеджера высокой готовности или включать его в переменную PATH.

Только пользователь root имеет право на запуск и остановку HAM.


Остановка менеджера высокой готовности

Чтобы остановить HAM, необходимо воспользоваться функцией ham_stop() или утилитой hamctrl. Других корректных и гарантированных способов остановки HAM не существует.

Функция ham_stop() и утилита hamctrl дают HAM команду завершить работу. HAM сначала завершает дублера, а затем завершается сам. Чтобы остановить HAM с помощью командной строки, следует ввести команду:

hamctrl -stop

Для остановки удаленного HAM применяется ключ -node:

hamctrl -node "nodename" -stop

Для остановки HAM в программе с помощью API применяются следующие функции:

/* завершение работы */
int ham_stop( void );
int ham_stop_nd( int nd );
int ham_stop_node( const char *nodename );

Функции управления

Следующие функции позволяют управлять настройками объектов, условий и действий.

/* Операции управления */
int ham_entity_control( ham_entity_t *ehdl,
int command,
unsigned flags );
int ham_condition_control( ham_condition_t *chdl,
int command,
unsigned flags );
int ham_action_control( ham_action_t *ahdl,
int command,
unsigned flags );

Существуют следующие операции (команды):

HENABLE
Включить элемент.
HDISABLE
Отключить элемент.
HADDFLAGS
Установить флаг.
HREMOVEFLAGS
Снять флаг.
HSETFLAGS
Задать значение флага.
HGETFLAGS
Считать флаг.

Функции «включения» и «отключения» временно отображают/скрывают объект, условие или действие.

Скрытый объект не удаляется, но наблюдение за его условиями не осуществляется. Аналогично, скрытые условия не иниицируются, а скрытые действия не выполняются. По умолчанию операции включения и отключения не рекурсивны, хотя условия отключенного объекта не инициируются, а действия отключенного условия не выполняются.

Тонкие различия между рекурсивным выполнением операций управления см. в описаниях следующих функций API:

С помощью команд addflags, removeflags, setflags и getflags можно считывать и изменять флаги объектов, условий и действий. Дополнительную информацию см. в описании функций ham_*_control_*().

Управление детализацией информации

Функция ham_verbose() позволяет программно считывать и задавать (уменьшать или увеличивать) уровень детализации информации:

int ham_verbose( const char *nodename,
int op,
int value );

Для интерактивного управления детализацией информации можно использовать утилиту hamctrl:

hamctrl -verbose /* increase verbosity */ hamctrl +verbose /* decrease verbosity */ hamctrl =verbose /* get current verbosity */

Для работы с удаленным HAM следует использовать утилиту hamctrl с ключом -node:

hamctrl -node "nodename" -verbose /* increase verbosity */ hamctrl -node "nodename" +verbose /* decrease verbosity */ hamctrl -node "nodename" =verbose /* get current verbosity */

где nodename — действующее имя удаленного или локального узла.

Публикация условий, самостоятельно обнаруживаемых компонентами

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

На текущий момент существуют два принципиальных метода публикации данных в HAM, на основе которых можно создавать более сложные механизмы обмена информацией.

Публикация изменений состояния

Объект может передавать информацию об изменениях своего состояния HAM, который сохраняет ее. HAM не интерпретирует состояния и не проверяет корректность их изменения, но может генерировать события при переходе из одного состояния в другое.

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

Для публикации изменения состояния можно использовать указанную ниже функцию. Поскольку HAM интересует только новое состояние объекта, он получает информацию только о нем. Затем HAM инициирует событие изменения состояния, на которое могут подписываться другие компоненты системы при помощи функции ham_condition_state(), описанной далее.

/* публикация изменения состояния */
int ham_entity_condition_state( ham_entity_t *ehdl,
unsigned tostate,
unsigned flags );

Публикация других условий

Компоненты системы также могут публиковать условия, которые они обнаруживают самостоятельно, с помощью функции API HAM ham_entity_condition_raise(). Компонент, который публикует условие, может указывать его тип, класс и важность, что позволяет другим компонентам гибко фильтровать условия, на которые они подписываются. В результате этого вызова HAM генерирует событие инициации условия, на которое другие компоненты могут подписываться с помощью функции ham_condition_raise(), описанной далее.

/* публикация самостоятельно обнаруживаемых условий */
int ham_entity_condition_raise( ham_entity_t *ehdl,
unsigned rtype,
unsigned rclass,
unsigned severity,
unsigned flags );

Подписка на условия, самостоятельно публикуемые компонентами

Компоненты могут подписываться на события, которые публикуют другие компоненты, с помощью следующих функций API менеджера высокой готовности:

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

Реагирование на изменения состояния

Когда объект публикует изменение своего состояния, инициируется условие, которое зависит от прежнего и нового состояний. Подписчики указывают интересующие состояния в параметрах fromstate (исходное состояние) и tostate (новое состояние) функции API HAM.

Более подробную информацию см. в описании функции ham_condition_state().

ham_condition_t * ham_condition_state( ham_entity_t *ehdl,
const char *cname,
unsigned fromstate,
unsigned tostate,
unsigned flags );

Реагирование на конкретные опубликованные условия

Подписчики могут реагировать на конкретные условия, инициируемые объектами, указывая их с помощью параметров функции ham_condition_raise().

Более подробную информацию см. в описании функции ham_condition_raise().

ham_condition_t * ham_condition_raise( ham_entity_t *ehdl,
const char *cname,
unsigned rtype,
unsigned rclass,
unsigned rseverity,
unsigned flags);




Предыдущий раздел: Менеджер высокой готовности (HAM)