【QNX】高可用性框架(4):使用HAM

Ø 基本介绍

高可用性管理器(HAM)为监视进程和服务提供了一种机制。它的目标是提供一个弹性管理器(或“智能看门狗”),在系统服务或进程失败、没有响应或提供不可接受级别的服务时,可以执行多级恢复。HA框架(包括HAM)使用简单的发布/订阅机制在系统中对感兴趣的组件传递系统事件。通过自动集成到本机网络机制(QNET),该框架可透明地将本地监视机制扩展到网络。

HAM充当了一个管道,通过它,系统的其余部分可以获取和传递关于整个系统状态的信息。系统可以是单个节点,也可以是通过QNET连接的节点集合。HAM可以监视特定的进程,并且可以在特定组件失败并需要恢复时控制系统的行为。HAM还允许外部检测器向系统报告感兴趣的事件,并可以将操作与这些事件的发生关联起来。

在许多HA系统中,必须仔细识别和处理单点故障(SPOFs)。由于HAM维护了关于系统健康状况的信息,并提供基本的恢复框架,所以HAM本身绝不能成为SPOF。

作为一个自我监控的管理者,HAM对内部故障有很强的弹性。无论出于何种原因,如果HAM本身被异常地停止,它可以立即并完全地重建它自己的状态。一个被称为“守护者”的镜像进程时刻准备着,等待着接管“HAM”的角色。由于所有状态信息都保存在共享内存中,所以监护者可以假定原始HAM在失败之前处于完全相同的状态。

但是如果”守护者“非正常终止会发生什么呢? ”守护者“(现在是新的HAM)在代替原来的HAM之前又为自己创造一个新的守护者。因此,从实践上讲,如果一个没有那么另一个也就不可能存在。

由于HAM/Guardian对彼此进行监视,所以任何一方的失败都可以完全恢复。停掉HAM的唯一方法是明确地指示它终止守护,然后终止它自己。

Ø HAM层次结构

HAM有三个主要的组成部分:

  1. 实体Entity

  2. 条件Condition

  3. 行动Action

1. 实体

实体是系统中观察/监测的基本单位。本质上,实体就是一个进程(pid)。作为进程,所有实体都是由它们的pid唯一标识的。与每个实体相关联的是一个符号名称,可以用来引用特定的实体。同样,与实体关联的名称在整个系统中是惟一的。管理器当前与节点关联,因此唯一性规则适用于节点。稍后我们将看到,这种唯一性要求与分层文件系统中使用的命名方案非常相似。

基本的实体类型包括:

(1)Self-attached实体

这些是显式选择为HA-aware的进程。这些进程使用ham_attach_self()和ham_detach_self()函数来连接和断开与HAM的连接。Self-attached进程是根据HAM API库编译的,监视的生存期是从ham_attach_self()调用到ham_detach_self()调用的时间。

Self-attached实体也可以选择将心跳发送到一个HAM,然后它将监控心跳是否失败。由于系统上的任意进程对于失败并不一定是“可跟踪的”(例如,它们不在会话1中,也不是子进程中,等等),所以您可以使用这个心跳机制来监视这些进程。

Self-attached(自依附实体)可以自行决定在其生命周期中的什么时候需要监视,需要对什么条件采取行动,以及何时需要停止监视。换句话说,在这种情况下,一个进程会说,“如果我死了,就照我说的做。”

(2)外部连接的实体(Externally attached entities)

这些是系统中正被监视的普通进程。这些进程可以是任意的守护进程或服务提供者,它们的健康状况被认为是重要的。这个方法对于某些情况很有用,例如进程A说“告诉我进程B什么时候死”,但是进程B根本不需要知道这个。

(3)全局实体(Global entity)

全局实体实际上只是可以匹配任何实体的占位符。它可用于关联将在检测到与系统上的任何实体相关的关注的事件时触发的操作。全局指的是系统中被监控的一组实体。这允许我们说“当任何进程死亡或当任何进程丢失心跳时,执行以下操作”之类的话。全局实体永远不会被添加或删除,只会被引用。可以像往常一样向全局实体添加/删除条件,也可以从任何条件中添加/删除操作。

要获得全局实体的句柄,请调用ham_entity_handle(),为ename参数传递NULL

转储进程(dumper process)通常用于获取由于执行任何非法操作而异常终止的进程的核心映像。HAM从dumper那里收到这样的终止通知。此外,HAM还从系统中收到终止会话1中任何进程的通知。这包括调用procmgr_daemon()的守护进程,从而将它们从控制终端中分离出来。

如果进程调用daemon(),则创建一个新进程并替换原来的进程,成为会话的领导者。如果HAM正在监视原始进程,那么它将自动切换到监视新进程。

2. 条件

条件与实体相关联, 条件表示实体的状态。以下是一些条件的例子:

  • CONDDEATH
    The entity has died.

  • CONDABNORMALDEATH
    The entity has died an abnormal death. This condition is triggered whenever an entity dies by a mechanism that results in the generation of a core file (see dumper in the Utilities Reference for details).

  • CONDDETACH
    The entity that was being monitored is detaching. This ends HAM's monitoring of that entity.

  • CONDATTACH
    An entity for whom a place holder was previously created (someone has subscribed to events relating to this entity), has joined the system. This is also the start of the monitoring of the entity by a HAM.

  • CONDHBEATMISSEDHIGH
    The entity missed sending a heartbeat message specified for a condition of “high” severity.

  • CONDHBEATMISSEDLOW
    The entity missed sending a heartbeat message specified for a condition of “low” severity.

  • CONDRESTART
    The entity was restarted. This condition is true after the entity is successfully restarted.

  • CONDRAISE
    An externally detected condition is reported to a HAM. Subscribers can associate actions with these externally detected conditions.

  • CONDSTATE
    An entity reports a state transition to a HAM. Subscribers can associate actions with specific state transitions.

  • CONDANY
    This condition type matches any condition type. It can be used to associate the same actions with one of many conditions.

除了CONDSTATE、CONDRAISE和CONDANY之外,上述条件都是由HAM自动检测和/或触发的(即HAM是条件的发布者)。外部探测器将约束条件和约束条件发布到HAM上。对于所有条件,订阅者都可以与触发条件时将按顺序执行的操作列表相关联。CONDSTATE和CONDRAISE条件都提供过滤功能,因此订阅者可以根据发布的信息有选择地将操作与单个条件关联起来。

条件还与符号名称相关联,符号名称在实体中也必须是惟一的。

HAM体系结构是可扩展的。HAM会自动检测到几种情况。另外,通过使用条件引发机制,系统中的其他组件可以将系统中有趣的事件通知HAM。这些条件可以完全定制。另外,通过研究源代码,可以将探测其他条件的能力添加到HAM中(例如,低内存、高CPU利用率、低磁盘空间等),以适应HA应用程序。

3. 动作

动作与条件相关联。一个条件可以包含多个操作。只要相应的条件为真,就会执行这些操作。条件中的操作按FIFO顺序执行(它们被添加到条件中的顺序)。为真的多个条件以任意顺序同时触发。指定为HCONDINDEPENDENT的条件将与其他条件并行地在单独的执行线程中执行。(参见本章条件函数一节。)

HAM API包含了几种不同类型动作的函数:

  • ham_action_restart()
    This action restarts the entity.

  • ham_action_execute()
    Executes an arbitrary command (e.g. to start a process).

  • ham_action_notify_pulse()
    Notifies some process that this condition has occurred. This notification is sent using a specific pulse with a value specified by the process that wished to receive this notify message. Pulses can be delivered to remote nodes, by specifying the appropriate node specifier.

  • ham_action_notify_signal()
    Notifies some process that this condition has occurred. This notification is sent using a specific realtime signal with a value specified by the process that wished to receive this notify message. Signals can be delivered to remote nodes, by specifying the appropriate node specifier.

  • ham_action_notify_pulse_node()
    This is the same as the ham_action_notify_pulse() described above, except that the node name specified for the recipient of the pulse can be given using the fully qualified node name instead of the node identifier.

  • ham_action_notify_signal_node()
    This is the same as the ham_action_notify_signal() described above, except that the node name specified for the recipient of the signal can be given using the fully qualified node name instead of the node identifier.

  • ham_action_waitfor()
    This action lets you insert delays between consecutive actions in a sequence. You can also wait for certain names to appear in the namespace.

  • ham_action_heartbeat_healthy()
    Resets the heartbeat mechanism for an entity that had previously missed sending heartbeats, and had triggered a missed heartbeat condition, but has now recovered.

  • ham_action_log()
    This allows one to insert a customizable verbosity message into the activity log maintained by a HAM.

操作还与符号名称相关联,符号名称在特定条件下是惟一的

同样,HAM体系结构是可扩展的,因此您可以根据需要添加自己的操作函数。

行动失败的行动(Action Fail actions)

当操作列表中的一个操作失败时,可以指定执行另一个操作列表,以从给定操作的失败中恢复。这些操作被称为action_fail操作,并与每个单独的操作相关联。除了ham_action_restart()和ham_action_heartbeat_healthy()之外,action_fail操作实际上是一组相同的执行操作。以下是动作失败的动作清单:

  • ham_action_fail_execute()
    Executes an arbitrary command (e.g. to start a process).

  • ham_action_fail_notify_pulse()
    Notifies some process that this condition has occurred. This notification is sent using a specific pulse with a value specified by the process that wished to receive this notify message. Pulses can be delivered to remote nodes by specifying the appropriate node specifier.

  • ham_action_fail_notify_signal()
    Notifies some process that this condition has occurred. This notification is sent using a specific realtime signal with a value specified by the process that wished to receive this notify message. Signals can be delivered to remote nodes by specifying the appropriate node specifier.

  • ham_action_fail_notify_pulse_node()
    This is the same as the ham_action_fail_notify_pulse() described above, except that the node name specified for the recipient of the pulse can be given using the fully qualified node name instead of the node identifier.

  • ham_action_fail_notify_signal_node()
    This is the same as the ham_action_fail_notify_signal() described above, except that the node name specified for the recipient of the signal can be given using the fully qualified node name instead of the node identifier.

  • ham_action_fail_waitfor()
    This action lets you insert delays between consecutive actions in a sequence. You can also wait for certain names to appear in the namespace.

  • ham_action_fail_log()
    This allows one to insert a customizable verbosity message into the activity log maintained by a HAM.

4. 多级恢复

这种完整的机制允许我们以多阶段的方式执行单个服务或流程故障的恢复。

例如,假设您启动了fs-nfs2 (NFS文件系统),然后挂载了来自多个源的几个目录。您可以指示HAM在出现故障时重新启动fs-nfs2,并且在重新启动NFS进程后根据需要重新安装适当的目录。如果在fs-nfs2的生命周期中卸载了某些目录,则可以从要执行的操作集中删除那些特定的操作。

再举一个例子,假设io管理器(network I/O manager)死了。我们可以让一个HAM重新启动它,并加载适当的网络驱动程序(为了正常工作,可能还需要一些其他的服务)。

Ø HAM状态

实际上,HAM的内部状态类似于分层的文件系统,其中实体类似于目录,与这些实体关联的条件类似于子目录,而这些条件中的操作类似于此树结构的叶节点。

HAM还将此状态表示为/proc/ ham下的只读文件系统。因此,任意进程都可以查看当前状态(例如,可以执行ls /proc/ham)。

除了将状态视图表示为文件系统之外,对于每一项(实体/条件/动作),HAM还可以在/proc/ ham下的HAM文件系统的每一层的相应.info文件中显示与之相关的统计信息和信息。

示例:

在/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和Guardian的信息,以及系统中实体和其他对象的概述:

# 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实体只与它关联一个条件(即死亡),该条件在实体死亡时触发。

# 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

类似地,与此死亡条件关联的操作只有一个:重启机制。条件下的每个操作都以文件的形式出现在适当的条件目录下。该文件包含有关触发条件时将执行的操作的详细信息。

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

如果inetd不是一个自附加的实体,那么需要为它指定-D选项,通过调用procmgr_daemon()来强制inetd进行守护,而不是调用daemon()。HAM只能看到来自自附加实体的死亡消息、非正常终止的进程以及在会话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本身被终止,那么Guardian将接管这个新的HAM,并为自己创建一个Guardian。

# cat /proc/ham/.info
Ham Pid            : 10993674  <----- This is the HAM
Guardian Pid       : 10997782  <----- This is the Guardian
Ham Failures       : 0
Guardian Failures  : 0
Num Entities       : 1
Num Conditions     : 1
Num Actions        : 1

... Kill the ham ....

# /bin/kill -9 10993674        <---- Simulate failure

... re-read the stats ...

# cat /proc/ham/.info  
Ham Pid            : 10997782  <----- This is the new HAM
Guardian Pid       : 11124746  <----- This is the Guardian
Ham Failures       : 1
Guardian Failures  : 0
Num Entities       : 1
Num Conditions     : 1
Num Actions        : 1

正如你所看到的,旧的守护者现在变成了新的HAM,一个新的守护者被创造出来。一切实体和条件保持不变;监测工作照常进行。HAM和监护者忽略了所有他们可以忽略的信号。

Ø HAM API

HAM提供了一个API供您使用,以便与之交互。这个API提供了一个函数集合:

  1. 连接到一个HAM并断开连接

  2. 将实体/条件/操作添加到(或移除它们从)当前监视的事物集中。

API是以一个可以链接的库实现的。这个库是线程安全的,也是取消安全的。

1. 连接/断开函数

HAM API库只维护一个到HAM的连接。该库本身是线程安全的,并且多个连接(来自不同的线程)或同一个线程在同一个到HAM的单个连接上进行多路复用。这个库会维护参考计数。

以下是基本的连接功能:

/* Basic connect functions
   return success (0) or failure (-1, with errno set) */

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*()将引用计数递减到0,计数为0时关闭fd。如果错误,则返回-1并设置errno,如果成功则返回0。

在多线程情况下,在任何给定时间内都只存在一个到给定HAM的开放连接,即使多个线程执行ham_connect()/ham_disconnect()调用也是如此。

ham_nd()和ham_node()版本的函数调用用于跨QNET打开到远程HAM的连接。传递给函数的nd是在进行调用时引用远程主机的节点标识符。由于节点标识符是瞬时值,因此必须在调用之前获得节点标识符。另一个选项是使用主机的完全限定节点名(FQNN),并将其作为nodename参数传递。ND_LOCAL_NODE (sys/netmgr.h中定义的常量)的nd或NULL(或空字符串)的节点名是等效的,并引用当前节点。(这也与直接调用ham_connect()或ham_disconnect()相同)。

对ham_connect()、ham_connect_nd()和ham_connect_node()的调用可以自由组合,只要连接调用的数量等于连接(fd)关闭之前到特定(本地或远程)HAM的每个连接的断开调用的数量。

2. 附加/分离函数

对于Self-attached实体

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);

您可以使用这两个函数将进程作为自附加实体附加/分离到HAM。

ename参数表示这个实体的符号名称,它需要在系统中是唯一的(在发出调用时对所有受监视的实体)。

hp参数表示心跳周期的时间值(以纳秒为单位)。心跳可以用来确保监测实体的“活力”。活性是描述组件的有用进展的属性。在许多情况下,系统组件的可用性受到损害,并不是因为组件必然死亡,而是因为它没有响应或没有取得任何进展。心跳机制允许您指定组件将在给定的时间间隔发出心跳,如果它错过了一定数量的心跳,则将构成心跳错过的条件。

hpdlhpdh代表在心跳频率低和心跳频率高的情况被触发之前可以错过的心跳次数。HAM API库将此请求注册到一个HAM,并创建一个线程来保持与HAM的连接处于打开状态。如果实体异常终止,则关闭到HAM的连接,HAM将知道这是一个异常终止(因为ham_detach_self()没有首先被调用)。

另一方面,如果一个HAM不正常地失败了(极不可能),Guardian接手成为新的HAM,那么与原来的HAM的联系就会失去新意。在这种情况下,Guardian通知所有自我依附的实体重新依附。上面提到的额外线程透明地处理这个重新连接。

如果到HAM的连接已经打开,那么ham_attach_self()使用相同的连接,但是增加这个客户机打开的连接的引用计数。指示它将在某个时间段发出心跳的客户机必须调用ham_heartbeat()来实际将心跳传输到HAM。

库还验证调用方提供的ename是否惟一。如果它还不存在,那么这个请求将被转发给一个HAM, HAM也会再次检查它,以避免在创建新实体时出现任何竞争条件。ham_attach_self()返回一个通用句柄,稍后可以使用它将进程与HAM分离。注意,这个句柄是一个不透明的指针,它还用于添加如下所示的条件和操作。

ham_detach_self()函数用于关闭到HAM的连接。调用之后,HAM将不再作为一个自我依附的实体来监控这个进程。额外的线程被取消。Ham_detach_self函数将ham_attach_self()返回的句柄作为参数。

使用Self-attach/detach 调用的例子:

...
ham_entity_t *ehdl; /* The entity Handle */
int status;

/* 
 connects to a HAM with a heartbeat of 5 seconds
 and an entity name of "client1", and no flags
 it also specifies hpdh = 4, and hpdh = 8
*/

ehdl = ham_attach_self("client1", 5000000000, 4, 8, 0);
if (ehdl == NULL) {
  printf("Could not attach to Ham\n");
  exit(-1);
}
/* Detach from a HAM using the original handle */
status = ham_detach_self(ehdl,0);
...

对于其他attaching/detaching实体

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()函数非常相似,只是在这里,调用进程要求一个HAM监视一个不同的进程。

这种机制允许对已经存在的实体进行任意监控,并且不会根据HAM API库进行编译。事实上,被监视的实体甚至不需要知道它们正在被监视。

你可以使用ham_attach()调用:

启动一个实体并继续监视它

或者:

开始监视已经运行的实体。

在ham_attach()调用中,如果pid是-1,那么我们就假设实体没有运行。实体使用line(参数)作为它的启动命令行来启动。但是如果pid大于0,则忽略这一行,并将给定的pid作为一个实体附加上去。同样,ename需要在当前注册的所有实体中是唯一的。

ham_attach()和ham_detach_name()中的nd说明符,以及ham_attach_node()和ham_detach_name_node()中调用的节点名说明符,用于跨Qnet引用远程HAM。传递给函数的nd是在进行调用时引用远程主机的节点标识符。由于节点标识符是瞬时值,因此必须在调用之前获得节点标识符。另一个选项是使用主机的完全限定节点名(FQNN),并将其作为nodename参数传递。ND_LOCAL_NODE的nd (sys/netmgr.h中定义的常量或NULL(或空字符串)的节点名是等效的,并指向当前节点。

ham_detach*()函数停止监视给定的实体。ham_detach()调用接受ham_attach()返回的原始句柄作为参数。您还可以调用ham_detach_name(),它使用实体的名称而不是句柄。

注意,稍后还可以使用实体句柄向实体添加条件(如下所述)。

使用 attach/detach 调用的例子:

...
ham_entity_t *ehdl;
int status;
ehdl = ham_attach("inetd", 0, -1, "/usr/sbin/inetd -D", 0);
/* inetd is started, running and monitored now */
... 
...
status = ham_detach(ehdl,0);
...
...

当然,附加和分离不必由同一个调用者执行:

...
ham_entity_t *ehdl;
int status;
/* starts and begins monitoring inetd */
ehdl = ham_attach("inetd", 0, -1, "/usr/sbin/inetd -D", 0);
...
...
/* disconnect from Ham (monitoring still continues) */
exit(0);

分离inetd:

...
int status;
/* stops monitoring 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的连接仅在attach/detach调用期间保持;对HAM的任何后续请求之前都必须执行相应的ham_connect()调用。

向一个HAM执行请求序列的最好方法是:

  1. 在第一个请求之前调用ham_connect()。

  2. 在最后一个请求之后调用ham_disconnect()。

这是最有效的方法,因为它保证了打开到HAM的连接总是相同的。

3. 实体函数

ham_attach_()函数通常在一个实体已经运行或将由一个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);

这些函数在节点标识符nd或节点名给出的节点名所描述的对应节点上创建具有指定ename的名称的实体位置占位符。创建后,这些占位符可用于向关联实体添加条件和操作。当随后的ham_attach*()调用引用相同的ename时,它将使用适当的进程ID填充实体占位符。从那时起,实体将被正常监控。

4. 条件函数

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*()函数返回一个不透明的指针,该指针是一个条件句柄,稍后可以使用它删除一个条件或向条件添加操作。

4.1 条件类型

你可以为类型指定以下任意值:

  • CONDDEATH

The entity has died.

  • CONDABNORMALDEATH

The entity has died an abnormal death. This condition is triggered whenever an entity dies by a mechanism that results in the generation of a core file (see dumper in the Utilities Reference for details).

  • CONDDETACH

The entity that was being monitored is detaching. This ends HAM's monitoring of that entity.

  • CONDATTACH

An entity for whom a place holder was previously created (someone has subscribed to events relating to this entity), has joined the system. This is also the start of the monitoring of the entity by a HAM.

  • CONDHBEATMISSEDHIGH

The entity missed sending a heartbeat message specified for a condition of “high” severity.

  • CONDHBEATMISSEDLOW

The entity missed sending a heartbeat message specified for a condition of “low” severity.

  • CONDRESTART

The entity was restarted. This condition is true after the entity is successfully restarted.

  • CONDANY

This condition type matches any condition type. It can be used to associate the same actions with one of many conditions.

当实体分别附加、分离或重新启动时,HAM将触发CONDATTACH、CONDDETACH和CONDRESTART条件。当HAM检测到丢失的心跳条件时,就会在内部触发CONDHBEATMISSEDHIGH和CONDHBEATMISSEDLOW条件,这是在实体表示它们最初的心跳意图时定义的。CONDDEATH在实体死亡时触发。CONDABNORMALDEATH只有在非正常死亡发生时才会触发,但这种非正常死亡也会触发CONDDEATH条件。

当被监视的实体正确地从HAM中分离时,您可以使用detach条件来执行一些操作。在此之后,HAM将不再监视实体。实际上,当HAM不能再提供关于分离实体的任何更多信息时,您可以使用这个来“通知”感兴趣的客户端。

如果一个实体死亡并重新启动,则由一个HAM自动断言并触发重新启动条件。

4.2 条件标志

HCONDNOWAIT

保证在此条件下操作列表中不存在“waitfor”语句。标记HCONDNOWAIT的所有条件都是在单独的线程中处理的,因此不会以任何方式由于其他条件中的操作而延迟。

HCONDINDEPENDENT

如果设置了此标志,则在此条件下的所有操作将在单独的线程中执行。这使您可以将延迟插入到条件中,而不会在其他条件中引起任何延迟。

如果一个条件同时被HCONDINDEPENDENT和HCONDNOWAIT标记,那么HCONDNOWAIT是优先的,并且这个条件中的所有操作都是在与所有其他条件相同的线程中执行的,这些条件也被标记为HCONDNOWAIT。这是因为所有的HCONDNOWAIT条件已经保证有最小的延迟。

如果一个条件既不标记HCONDNOWAIT也不标记HCONDINDEPENDENT,那么它将被视为另一个条件,这意味着它将在所有为真的条件中按FIFO顺序执行。

总结:

每当一个条件(例如CONDDEATH、CONDDETACH等)发生时,所有标记了HCONDNOWAIT的条件都会在一个线程中以FIFO顺序执行。

所有标记了HCONDINDEPENDENT的条件(但不是HCONDNOWAIT)都在单独的线程中执行。

所有其他条件都是在一个线程中按FIFO顺序执行的。

这限制了所有线程的最大数量:

(HCONDINDEPENDENT conditions的数量)+ 2

也就是说,一个用于标记HCONDNOWAIT的所有条件,另一个用于标记HCONDNOWAIT的所有其他条件。

此外,在一个条件中,所有的操作也都是按FIFO顺序执行的。这是正确的,不管条件是依赖的还是依赖的。

5. 动作函数

5.1 动作函数(Action Functions)

/* action operations             */
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);
/* remove an action              */
int ham_action_remove(ham_action_t *ahdl, unsigned flags);

如前所述,HAM目前支持几种不同类型的操作函数,但是请注意,您可以添加自己的操作函数来适应特定的HA应用程序。

  • ham_action_restart()

在发生死亡情况时为实体提供重新启动机制。这意味着所涉实体已经结束;重新启动操作将重新启动实体,并跟踪实体现在关联的新pid。

注意:重新启动操作只能与死亡条件相关联。在所有类型死亡的情况下,在任何时候都只能有一个重启动作。这确保了只有在实体终止时才重新启动它,而且只有一次。(death类型情况包括CONDDEATH和CONDABNORMALDEATH)。

  • ham_action_execute()

在条件为真时可以执行任意命令。命令可以是任何可执行的命令行。当条件为true时,将按顺序遍历和执行操作列表执行。

这将按照参数中指定的命令行执行。命令行必须包含可执行文件的完整路径以及要传递给它的所有参数。命令行依次由HAM传递到spawn命令,以创建将执行该命令的新进程。

在需要设置多级恢复时,您会发现execute操作非常有用。例如,如果fs-nfs2挂起并重新启动,ham_action_execute()函数允许您在fs-nfs2重新启动后重新挂起所需的任何目录。

通过设置HACTIONDONOW标志,可以立即执行操作。同样,这对于在多个阶段创建实体的启动情况也非常有用。

注意,HACTIONDONOW在等待动作时将被忽略。因此,为了将延迟插入到标记为HACTIONDONOW的操作序列中,您需要将延迟插入到客户端程序中(在调用ham_action*()之间)。

  • ham_action_waitfor()

给定条件下的操作序列将按FIFO顺序执行,您可以使用ham_action_waitfor()将延迟插入到执行序列中(只要条件允许—请参阅本章的条件函数一节)。指定的延迟是100毫秒的倍数。

ham_action_waitfor()调用将路径组件作为参数,该组件可用于等待特定名称出现在名称空间中。如果path为空,则等待的正是延迟毫秒。但是,如果指定了path,则等待延迟msecs或直到path出现在名称空间(以较早出现的名称为准)。注意,指定路径名时的延迟是100毫秒的整数倍。

如果指定了路径名,则延迟将是最近100毫秒的整数倍,进行舍入。0延迟即表示了禁用了waitfor,使得指定路径名变得冗余。

  • ham_action_notify_pulse()
  • ham_action_notify_signal()

ham_action_notify_pulse()函数将适当的脉冲发送给给定的nd/pid/chid。

Ham_action_notify_signal()向请求它的pid发送一个适当的带有值的实时信号。

如果实体重新启动,则操作可以在重新启动期间保持。类似地,条件也可以设置为在实体重新启动后保持(即可以重新加载它们)。您可以通过在ham_condition()调用或相应的操作语句的flags参数中执行HREARMAFTERRESTART来实现这一点。

如果在重新启动实体时,条件持续存在,则将检查每个单独的操作,以确定它是否也持续存在。不需要重新加载的动作只执行一次并删除。任何失败的动作也需要被移除,即使它们被设置为重新加载。

如果一个条件没有被标记为rearmed,那么该条件下的所有操作都将被自动删除,因为这些操作只与该条件相关联,如果条件不再存在,则不能保留这些操作。

重启期间条件和动作的持久性取决于实体本身的重启。因此,如果实体没有重新启动(即没有ACTIONRESTART或ACTIONRESTART由于某种原因失败),那么实体将被删除,同时删除的还有与实体关联的所有条件和操作。

  • ham_action_notify_pulse_node()

这与上面的ham_action_notify_pulse()相同,不同之处是可以使用完全限定的节点名而不是节点标识符(nd)来指定为脉冲的接收者指定的节点名。

  • ham_action_notify_signal_node()

这与上面的ham_action_notify_signal()相同,不同之处是,可以使用完全限定的节点名而不是节点标识符(nd)来指定信号接收方的节点名。

5.2 动作失败函数

/* action fail operations          */
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);

/* remove an action fail operation */
int ham_action_fail_remove(ham_action_t *ahdl, const char *aname,  unsigned flags);

这些操作用于关联将在条件中的操作失败时执行的操作列表。这些函数与前一节中描述的相应操作函数相似,主要区别在于第一个参数,在这些函数中,第一个参数是操作句柄(与条件句柄相反)。

监视inetd的例子:

以下代码演示了如果监视inetd进程

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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-nfs2的例子:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    int status;
    ham_entity_t *ehdl;
    ham_condition_t *chdl;
    ham_action_t *ahdl;
    char *fsnfspath;
    int fsnfs2pid;

    fsnfspath = strdup("/usr/sbin/fs-nfs2");
    fsnfs2pid = -1;

    ham_connect(0);
    ehdl = ham_attach("Fs-nfs2", ND_LOCAL_NODE, fsnfs2pid, 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);
}

5.3 句柄操作函数

/* Get/Free handles    */
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);

使用句柄函数根据实体、条件和操作名称获取/释放句柄。稍后,您可以使用这些句柄来添加或删除条件和操作。对于所有其他函数,使用完全限定节点名(FQNN),使用_node()变体来引用不一定是本地的HAM。

客户端示例代码

下面是一个客户端示例,它通过脉冲和信号从一个HAM获取关于重要事件的通知。它在inetd死亡或分离的事件中注册一个脉冲通知方案。它还为fs-nfs2的死亡注册了一个信号通知机制。

这个示例还演示了延迟通知是如何发生的,并展示了如何使用HCONDINDEPENDENT条件克服这个问题。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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;
/* Signal handler to handle the death notify of fs-nfs2 */
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-nfs2 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;
  int value;
  ham_entity_t *ehdl;
  ham_condition_t *chdl;
  ham_action_t *ahdl;
  struct sigaction sa;
  int scode;
  int svalue;

  /* we need a channel to receive the pulse notification on */
  chid = ChannelCreate( 0 ); 

  /* and we need a connection to that channel for the pulse to be
     delivered on */
  coid = ConnectAttach( 0, 0, chid, _NTO_SIDE_CHANNEL, 0 );

  /* fill in the event structure for a pulse */
  pid = getpid();
  value = 13;
  ham_connect(0);
  /* Assumes there is already an entity by the name "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; /* value we expect when fs-nfs dies */
  scode = 0;
  svalue = fsnfs_value; 
  sa.sa_sigaction = MySigHandler;
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = SA_SIGINFO;
  sigaction(MYSIG, &sa, NULL);

  /*
   Assumes there is an entity by the name "Fs-nfs2".
   We use "Fs-nfs2" to symbolically represent the entity
   fs-nfs2. Any name can be used to represent the
   entity, but it's best to use a readable and meaningful name.
  */
  ehdl = ham_entity_handle(ND_LOCAL_NODE, "Fs-nfs2", 0);

  /*
   Add a new condition, which will be an "independent" condition.
   This means that notifications/actions inside this condition
   are not affected by "waitfor" delays in other action
   sequence threads
  */
  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-nfs2","Death",0);
  /*
   This action is added to a condition that does not
   have an HCONDNOWAIT. Since we are unaware what the condition
   already contains, we might end up getting a delayed notification
   since the action sequence might have "arbitrary" delays and
   "waits" in it.
  */
  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);

  /* We force this condition to be independent of all others. */
  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-nfs2 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;
              }
    }
  }
  /*
   At this point we are no longer waiting for the
   information about inetd, since we know that it
   has exited.
   We will still continue to obtain information about the
   death of fs-nfs2, since we did not remove those actions.
   If we exit now, the next time those actions are executed
   they will fail (notifications fail if the receiver does not
   exist anymore), and they will automatically get removed and
   cleaned up.
  */
the_end:
  ham_disconnect(0);
  exit(0);
}

请注意,HAM API有一定的限制:

实体、条件和操作的名称(ename、cname和aname)不能包含“/”字符。

所有名称都受_POSIX_PATH_MAX施加的长度限制(如中定义的那样)。由于名称显示在名称空间中,名称的有效长度是路径组件名称的最大长度。换句话说,实体/条件/操作名称(包括/proc/ham前缀)的组合长度不能超过_POSIX_PATH_MAX。

6. 启动/停止HAM

6.1 启动HAM

通过再命令行中运行ham 实用程序来启动HAM

ham

ham实用程序有以下命令行参数

-?|h : Display usage message

-d : Disable internal verbosity.

-f : Log verbose output to a file (default is stderr).

-t none|relative|absolute|shortabs: Specify the timestamping method. The default is relative.

-v : Set verbosity level — extra -v's increase verbosity.

-Vn : Set verbosity level — use a number to specify the level (e.g. -V3).

HAM启动时,同时它的Guardian进程也一并启动。

启动时,必须附带完整路径或者将ham的路径设置到PATH变量中。Start Stop Ham时,需要以Root权限执行

6.2 停止HAM

要停止HAM,您必须使用ham_stop()函数或hamctrl实用程序。这是停止Ham的唯一正确(也是唯一保证)的方法。

ham_stop()函数或hamctrl实用程序指示HAM终止。而HAM首先指示Guardian终止,然后终止自身。要从命令行停止HAM,需使用hamctrl实用工具:

hamctrl -stop

要停止远程HAM,使用hamctrl实用程序的-node选项:

hamctrl -node "nodename" -stop

要使用API以编程方式停止HAM,请使用以下函数:

/* terminate                     */
int ham_stop(void);
int ham_stop_nd(int nd);
int ham_stop_node(const char *nodename);

7. HAM控制函数

目前提供了以下函数来允许对当前配置的实体、条件和操作进行控制。

/* control operations                           */
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                 /* enable item          */
HDISABLE                /* disable item         */
HADDFLAGS               /* add flag             */
HREMOVEFLAGS            /* remove flag          */
HSETFLAGS               /* set flag to specific */
HGETFLAGS               /* get flag             */

“启用”和“禁用”命令可用于临时取消/隐藏实体、条件或操作。隐藏的实体不会被移除,但是不会被监控任何情况。类似地,隐藏的条件将永远不会被触发,而隐藏的操作将不会被执行。默认情况下,enable和disable操作不会进行递归操作(尽管禁用一个实体将防止触发它下面的任何条件,而禁用一个条件将防止执行其中的操作)。

要了解控制函数递归操作的细微差别,请参考以下API描述:

ham_entity_control ()

ham_condition_control ()

ham_action_control ()

“addflags”“removeflags”“setflags”“getflags”命令可用于获取或修改与任何实体、条件或操作相关联的标志。有关更多细节,请参考ham_control()函数的API描述。

8. 冗余控制

您可以使用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,请使用带有-node选项的hamctrl实用程序:

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

其中nodename是表示远程(或本地)节点的有效名称。

9. 发布自主检测到的条件(Publishing autonomously detected conditions)

系统上的实体或其他组件可以发布它们认为HAM感兴趣的条件,而HAM又可以将这些条件传递给系统中表示感兴趣并订阅了它们的其他组件。这允许能够检测错误条件或潜在错误条件的任意系统组件向HAM报告这些情况,从而通知其他组件开始纠正过程和/或采取预防措施。

目前有两种不同的方式向HAM发布信息。这两种机制都设计得足够通用,允许客户机使用它们构建更复杂的信息交换机制。

9.1 发布状态转换(Publish state transitions)

一个实体可以向一个HAM报告它的状态转换。HAM维护每个实体的当前状态(由实体报告)。HAM不解释状态值本身的含义,也不尝试验证状态转换,但可以根据从一个状态到另一个状态的转换生成事件。

组件可以发布它们希望外部世界知道的转换。这些状态不一定表示应用程序内部用于决策制定的特定状态。

下面的函数可用于通知HAM状态转换。因为HAM只对转换中的下一个状态感兴趣,所以这是传递给HAM的唯一信息。HAM然后使用下面描述的ham_condition_state() API调用在内部触发一个条件状态更改事件,其他组件可以订阅该事件。

int ham_entity_condition_state(ham_entity_t *ehdl, unsigned tostate, 
    unsigned flags);

9.2 发布其他条件

除了以上所述,系统上的组件还可以使用ham_entity_condition_raise() API调用来发布自主检测到的条件。引发条件的组件还可以指定其选择的类型、类和严重程度,从而允许订阅者进一步细粒度地过滤出要订阅的特定条件。这个调用会导致HAM在内部触发条件提升事件,其他组件可以使用下面描述的ham_condition_raise() API调用来订阅这个事件。

/* publish autonomously detected condition */
int ham_entity_condition_raise(ham_entity_t *ehdl, unsigned rtype, 
    unsigned rclass, unsigned severity, unsigned flags);

9.3 订阅自动发布条件(Subscribing to autonomously published conditions)

订阅者可以使用以下API调用来表达他们对其他组件发布的事件的兴趣:

ham_condition_state ()

ham_condition_raise ()

这些调用类似于ham_condition() API调用,并返回一个条件句柄,但允许订阅者自定义他们感兴趣的几个可能发布的条件中的哪一个。

9.4 基于状态转换的触发(Trigger based on state transition)

当一个实体发布状态转换时,基于转换中涉及的两个状态(from状态和to状态),将为该实体提出状态转换条件。订阅者通过在API调用中为from state和to state参数指定值来指示他们感兴趣的状态。

有关详细信息,请参阅ham_condition_state()的API参考文档。

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

9.5 基于特定发布条件触发(Trigger based on specific published condition)

订阅者可以使用ham_condition_raise()来表示对实体引发的条件的兴趣,并将其作为调用的参数来指示他们所感兴趣的条件种类。

有关更多信息,请参阅ham_condition_raise()的API文档。

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

你可能感兴趣的:(【QNX】高可用性框架(4):使用HAM)