Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F3686574
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
79 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/fencing/commands.c b/fencing/commands.c
index 54ec1b1882..487aab4893 100644
--- a/fencing/commands.c
+++ b/fencing/commands.c
@@ -1,1472 +1,1474 @@
/*
* Copyright (C) 2009 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/utsname.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/cluster/internal.h>
#include <crm/common/mainloop.h>
#include <crm/stonith-ng.h>
#include <crm/fencing/internal.h>
#include <crm/common/xml.h>
#include <internal.h>
GHashTable *device_list = NULL;
GHashTable *topology = NULL;
GList *cmd_list = NULL;
static int active_children = 0;
static gboolean stonith_device_dispatch(gpointer user_data);
static void st_child_done(GPid pid, gint status, gpointer user_data);
static int
get_action_timeout(stonith_device_t *device, const char *action, int default_timeout)
{
char buffer[512] = { 0, };
char *value = NULL;
CRM_CHECK(action != NULL, return default_timeout);
if (!device->params) {
return default_timeout;
}
snprintf(buffer, sizeof(buffer) - 1, "pcmk_%s_timeout", action);
value = g_hash_table_lookup(device->params, buffer);
if (!value) {
return default_timeout;
}
return atoi(value);
}
static void free_async_command(async_command_t *cmd)
{
if (!cmd) {
return;
}
cmd_list = g_list_remove(cmd_list, cmd);
g_list_free(cmd->device_list);
free(cmd->device);
free(cmd->action);
free(cmd->victim);
free(cmd->remote);
free(cmd->client);
free(cmd->client_name);
free(cmd->origin);
free(cmd->op);
free(cmd);
}
static async_command_t *create_async_command(xmlNode *msg)
{
async_command_t *cmd = NULL;
xmlNode *op = get_xpath_object("//@"F_STONITH_ACTION, msg, LOG_ERR);
const char *action = crm_element_value(op, F_STONITH_ACTION);
CRM_CHECK(action != NULL, crm_log_xml_warn(msg, "NoAction"); return NULL);
crm_log_xml_trace(msg, "Command");
cmd = calloc(1, sizeof(async_command_t));
crm_element_value_int(msg, F_STONITH_CALLID, &(cmd->id));
crm_element_value_int(msg, F_STONITH_CALLOPTS, &(cmd->options));
crm_element_value_int(msg, F_STONITH_TIMEOUT, &(cmd->default_timeout));
cmd->timeout = cmd->default_timeout;
cmd->origin = crm_element_value_copy(msg, F_ORIG);
cmd->remote = crm_element_value_copy(msg, F_STONITH_REMOTE);
cmd->client = crm_element_value_copy(msg, F_STONITH_CLIENTID);
cmd->client_name = crm_element_value_copy(msg, F_STONITH_CLIENTNAME);
cmd->op = crm_element_value_copy(msg, F_STONITH_OPERATION);
cmd->action = strdup(action);
cmd->victim = crm_element_value_copy(op, F_STONITH_TARGET);
cmd->mode = crm_element_value_copy(op, F_STONITH_MODE);
cmd->device = crm_element_value_copy(op, F_STONITH_DEVICE);
cmd->done = st_child_done;
CRM_CHECK(cmd->op != NULL, crm_log_xml_warn(msg, "NoOp"); free_async_command(cmd); return NULL);
CRM_CHECK(cmd->client != NULL, crm_log_xml_warn(msg, "NoClient"));
cmd_list = g_list_append(cmd_list, cmd);
return cmd;
}
static int stonith_manual_ack(xmlNode *msg, remote_fencing_op_t *op)
{
async_command_t *cmd = create_async_command(msg);
xmlNode *dev = get_xpath_object("//@"F_STONITH_TARGET, msg, LOG_ERR);
if(cmd == NULL) {
return -EINVAL;
}
cmd->device = strdup("manual_ack");
cmd->remote = strdup(op->id);
crm_notice("Injecting manual confirmation that %s is safely off/down",
crm_element_value(dev, F_STONITH_TARGET));
st_child_done(0, 0, cmd);
return pcmk_ok;
}
static gboolean stonith_device_execute(stonith_device_t *device)
{
int rc = 0;
int exec_rc = 0;
async_command_t *cmd = NULL;
CRM_CHECK(device != NULL, return FALSE);
if(device->active_pid) {
crm_trace("%s is still active with pid %u", device->id, device->active_pid);
return TRUE;
}
if(device->pending_ops) {
GList *first = device->pending_ops;
device->pending_ops = g_list_remove_link(device->pending_ops, first);
cmd = first->data;
g_list_free_1(first);
}
if(cmd == NULL) {
crm_trace("Nothing further to do for %s", device->id);
return TRUE;
}
exec_rc = run_stonith_agent(device->agent, cmd->action, cmd->victim,
device->params, device->aliases, &rc, NULL, cmd);
if(exec_rc > 0) {
crm_debug("Operation %s%s%s on %s now running with pid=%d, timeout=%dms",
cmd->action, cmd->victim?" for node ":"", cmd->victim?cmd->victim:"",
device->id, exec_rc, cmd->timeout);
device->active_pid = exec_rc;
} else {
crm_warn("Operation %s%s%s on %s failed (%d/%d)",
cmd->action, cmd->victim?" for node ":"", cmd->victim?cmd->victim:"",
device->id, exec_rc, rc);
st_child_done(0, rc<0?rc:exec_rc, cmd);
}
return TRUE;
}
static gboolean stonith_device_dispatch(gpointer user_data)
{
return stonith_device_execute(user_data);
}
static void schedule_stonith_command(async_command_t *cmd, stonith_device_t *device)
{
CRM_CHECK(cmd != NULL, return);
CRM_CHECK(device != NULL, return);
if (cmd->device) {
free(cmd->device);
}
cmd->device = strdup(device->id);
cmd->timeout = get_action_timeout(device, cmd->action, cmd->default_timeout);
crm_debug("Scheduling %s on %s for %s (timeout=%dms)", cmd->action, device->id,
cmd->remote?cmd->remote:cmd->client, cmd->timeout);
device->pending_ops = g_list_append(device->pending_ops, cmd);
mainloop_set_trigger(device->work);
}
void free_device(gpointer data)
{
GListPtr gIter = NULL;
stonith_device_t *device = data;
g_hash_table_destroy(device->params);
g_hash_table_destroy(device->aliases);
for(gIter = device->pending_ops; gIter != NULL; gIter = gIter->next) {
async_command_t *cmd = gIter->data;
crm_warn("Removal of device '%s' purged operation %s", device->id, cmd->action);
st_child_done(0, -ENODEV, cmd);
free_async_command(cmd);
}
g_list_free(device->pending_ops);
g_list_free_full(device->targets, free);
free(device->namespace);
free(device->agent);
free(device->id);
free(device);
}
static GHashTable *build_port_aliases(const char *hostmap, GListPtr *targets)
{
char *name = NULL;
int last = 0, lpc = 0, max = 0, added = 0;
GHashTable *aliases = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
if(hostmap == NULL) {
return aliases;
}
max = strlen(hostmap);
for(; lpc <= max; lpc++) {
switch(hostmap[lpc]) {
/* Assignment chars */
case '=':
case ':':
if(lpc > last) {
free(name);
name = calloc(1, 1 + lpc - last);
memcpy(name, hostmap + last, lpc - last);
}
last = lpc + 1;
break;
/* Delimeter chars */
/* case ',': Potentially used to specify multiple ports */
case 0:
case ';':
case ' ':
case '\t':
if(name) {
char *value = NULL;
value = calloc(1, 1 + lpc - last);
memcpy(value, hostmap + last, lpc - last);
crm_debug("Adding alias '%s'='%s'", name, value);
g_hash_table_replace(aliases, name, value);
if(targets) {
*targets = g_list_append(*targets, strdup(value));
}
value=NULL;
name=NULL;
added++;
} else if(lpc > last) {
crm_debug("Parse error at offset %d near '%s'", lpc-last, hostmap+last);
}
last = lpc + 1;
break;
}
if(hostmap[lpc] == 0) {
break;
}
}
if(added == 0) {
crm_info("No host mappings detected in '%s'", hostmap);
}
free(name);
return aliases;
}
static void parse_host_line(const char *line, GListPtr *output)
{
int lpc = 0;
int max = 0;
int last = 0;
if(line) {
max = strlen(line);
} else {
return;
}
/* Check for any complaints about additional parameters that the device doesn't understand */
if(strstr(line, "invalid") || strstr(line, "variable")) {
crm_debug("Skipping: %s", line);
return;
}
crm_trace("Processing: %s", line);
/* Skip initial whitespace */
for(lpc = 0; lpc <= max && isspace(line[lpc]); lpc++) {
last = lpc+1;
}
/* Now the actual content */
for(lpc = 0; lpc <= max; lpc++) {
gboolean a_space = isspace(line[lpc]);
if(a_space && lpc < max && isspace(line[lpc+1])) {
/* fast-forward to the end of the spaces */
} else if(a_space || line[lpc] == ',' || line[lpc] == 0) {
int rc = 1;
char *entry = NULL;
if(lpc != last) {
entry = calloc(1, 1 + lpc - last);
rc = sscanf(line+last, "%[a-zA-Z0-9_-.]", entry);
}
if(entry == NULL) {
/* Skip */
} else if(rc != 1) {
crm_warn("Could not parse (%d %d): %s", last, lpc, line+last);
} else if(safe_str_neq(entry, "on") && safe_str_neq(entry, "off")) {
crm_trace("Adding '%s'", entry);
*output = g_list_append(*output, entry);
entry = NULL;
}
free(entry);
last = lpc + 1;
}
}
}
static GListPtr parse_host_list(const char *hosts)
{
int lpc = 0;
int max = 0;
int last = 0;
GListPtr output = NULL;
if(hosts == NULL) {
return output;
}
max = strlen(hosts);
for(lpc = 0; lpc <= max; lpc++) {
if(hosts[lpc] == '\n' || hosts[lpc] == 0) {
char *line = NULL;
line = calloc(1, 2 + lpc - last);
snprintf(line, 1 + lpc - last, "%s", hosts+last);
parse_host_line(line, &output);
free(line);
last = lpc + 1;
}
}
return output;
}
static stonith_device_t *build_device_from_xml(xmlNode *msg)
{
xmlNode *dev = get_xpath_object("//"F_STONITH_DEVICE, msg, LOG_ERR);
stonith_device_t *device = NULL;
device = calloc(1, sizeof(stonith_device_t));
device->id = crm_element_value_copy(dev, XML_ATTR_ID);
device->agent = crm_element_value_copy(dev, "agent");
device->namespace = crm_element_value_copy(dev, "namespace");
device->params = xml2list(dev);
device->work = mainloop_add_trigger(G_PRIORITY_HIGH, stonith_device_dispatch, device);
/* TODO: Hook up priority */
return device;
}
static const char *
target_list_type(stonith_device_t *dev)
{
const char *check_type = NULL;
check_type = g_hash_table_lookup(dev->params, STONITH_ATTR_HOSTCHECK);
if(check_type == NULL) {
if(g_hash_table_lookup(dev->params, STONITH_ATTR_HOSTLIST)) {
check_type = "static-list";
} else if(g_hash_table_lookup(dev->params, STONITH_ATTR_HOSTMAP)) {
check_type = "static-list";
} else {
check_type = "dynamic-list";
}
}
return check_type;
}
static void
update_dynamic_list(stonith_device_t *dev)
{
time_t now = time(NULL);
/* Host/alias must be in the list output to be eligable to be fenced
*
* Will cause problems if down'd nodes aren't listed or (for virtual nodes)
* if the guest is still listed despite being moved to another machine
*/
if(dev->targets_age < 0) {
crm_trace("Port list queries disabled for %s", dev->id);
} else if(dev->targets == NULL || dev->targets_age + 60 < now) {
char *output = NULL;
int rc = pcmk_ok;
int exec_rc = pcmk_ok;
if(dev->active_pid != 0) {
crm_notice("Port list query can not execute because device is busy, using cache: %s",
dev->targets ? "YES" : "NO");
return;
}
exec_rc = run_stonith_agent(dev->agent, "list", NULL, dev->params, NULL, &rc, &output, NULL);
if(rc != 0 && dev->active_pid == 0) {
/* This device probably only supports a single
* connection, which appears to already be in use,
* likely involved in a montior or (less likely)
* metadata operation.
*
* Avoid disabling port list queries in the hope that
* the op would succeed next time
*/
crm_info("Couldn't query ports for %s. Call failed with rc=%d and active_pid=%d: %s",
dev->agent, rc, dev->active_pid, output);
} else if(exec_rc < 0 || rc != 0) {
crm_notice("Disabling port list queries for %s (%d/%d): %s",
dev->id, exec_rc, rc, output);
dev->targets_age = -1;
/* Fall back to status */
g_hash_table_replace(dev->params, strdup(STONITH_ATTR_HOSTCHECK), strdup("status"));
g_list_free_full(dev->targets, free);
dev->targets = NULL;
} else {
crm_info("Refreshing port list for %s", dev->id);
g_list_free_full(dev->targets, free);
dev->targets = parse_host_list(output);
dev->targets_age = now;
}
free(output);
}
}
int stonith_device_register(xmlNode *msg, const char **desc)
{
const char *value = NULL;
stonith_device_t *device = build_device_from_xml(msg);
value = g_hash_table_lookup(device->params, STONITH_ATTR_HOSTLIST);
if(value) {
device->targets = parse_host_list(value);
}
value = g_hash_table_lookup(device->params, STONITH_ATTR_HOSTMAP);
device->aliases = build_port_aliases(value, &(device->targets));
value = target_list_type(device);
if (safe_str_eq(value, "dynamic-list")) {
/* set the dynamic list during the register to guarantee we have
* targets cached */
update_dynamic_list(device);
}
g_hash_table_replace(device_list, device->id, device);
crm_notice("Added '%s' to the device list (%d active devices)", device->id, g_hash_table_size(device_list));
if(desc) {
*desc = device->id;
}
return pcmk_ok;
}
static int stonith_device_remove(xmlNode *msg, const char **desc)
{
xmlNode *dev = get_xpath_object("//"F_STONITH_DEVICE, msg, LOG_ERR);
const char *id = crm_element_value(dev, XML_ATTR_ID);
if(g_hash_table_remove(device_list, id)) {
crm_info("Removed '%s' from the device list (%d active devices)",
id, g_hash_table_size(device_list));
} else {
crm_info("Device '%s' not found (%d active devices)",
id, g_hash_table_size(device_list));
}
if(desc) {
*desc = id;
}
return pcmk_ok;
}
static int count_active_levels(stonith_topology_t *tp)
{
int lpc = 0;
int count = 0;
for(lpc = 0; lpc < ST_LEVEL_MAX; lpc++) {
if(tp->levels[lpc] != NULL) {
count++;
}
}
return count;
}
void free_topology_entry(gpointer data)
{
stonith_topology_t *tp = data;
int lpc = 0;
for(lpc = 0; lpc < ST_LEVEL_MAX; lpc++) {
if(tp->levels[lpc] != NULL) {
g_list_free_full(tp->levels[lpc], free);
}
}
free(tp->node);
free(tp);
}
int stonith_level_register(xmlNode *msg, char **desc)
{
int id = 0;
int rc = pcmk_ok;
xmlNode *child = NULL;
xmlNode *level = get_xpath_object("//"F_STONITH_LEVEL, msg, LOG_ERR);
const char *node = crm_element_value(level, F_STONITH_TARGET);
stonith_topology_t *tp = g_hash_table_lookup(topology, node);
crm_element_value_int(level, XML_ATTR_ID, &id);
if(desc) {
*desc = g_strdup_printf("%s[%d]", node, id);
}
if(id <= 0 || id >= ST_LEVEL_MAX) {
return -EINVAL;
}
if(tp == NULL) {
tp = calloc(1, sizeof(stonith_topology_t));
tp->node = strdup(node);
g_hash_table_replace(topology, tp->node, tp);
crm_trace("Added %s to the topology (%d active entries)", node, g_hash_table_size(topology));
}
if(tp->levels[id] != NULL) {
crm_info("Adding to the existing %s[%d] topology entry (%d active entries)", node, id, count_active_levels(tp));
}
for (child = __xml_first_child(level); child != NULL; child = __xml_next(child)) {
const char *device = ID(child);
crm_trace("Adding device '%s' for %s (%d)", device, node, id);
tp->levels[id] = g_list_append(tp->levels[id], strdup(device));
}
crm_info("Node %s has %d active fencing levels", node, count_active_levels(tp));
return rc;
}
int stonith_level_remove(xmlNode *msg, char **desc)
{
int id = 0;
xmlNode *level = get_xpath_object("//"F_STONITH_LEVEL, msg, LOG_ERR);
const char *node = crm_element_value(level, F_STONITH_TARGET);
stonith_topology_t *tp = g_hash_table_lookup(topology, node);
if(desc) {
*desc = g_strdup_printf("%s[%d]", node, id);
}
crm_element_value_int(level, XML_ATTR_ID, &id);
if(tp == NULL) {
crm_info("Node %s not found (%d active entries)",
node, g_hash_table_size(topology));
return pcmk_ok;
} else if(id < 0 || id >= ST_LEVEL_MAX) {
return -EINVAL;
}
if(id == 0 && g_hash_table_remove(topology, node)) {
crm_info("Removed all %s related entries from the topology (%d active entries)",
node, g_hash_table_size(topology));
} else if(id > 0 && tp->levels[id] != NULL) {
g_list_free_full(tp->levels[id], free);
tp->levels[id] = NULL;
crm_info("Removed entry '%d' from %s's topology (%d active entries remaining)",
id, node, count_active_levels(tp));
}
return pcmk_ok;
}
static gboolean string_in_list(GListPtr list, const char *item)
{
int lpc = 0;
int max = g_list_length(list);
for(lpc = 0; lpc < max; lpc ++) {
const char *value = g_list_nth_data(list, lpc);
if(safe_str_eq(item, value)) {
return TRUE;
}
}
return FALSE;
}
static int stonith_device_action(xmlNode *msg, char **output)
{
int rc = pcmk_ok;
xmlNode *dev = get_xpath_object("//"F_STONITH_DEVICE, msg, LOG_ERR);
const char *id = crm_element_value(dev, F_STONITH_DEVICE);
async_command_t *cmd = NULL;
stonith_device_t *device = NULL;
if(id) {
crm_trace("Looking for '%s'", id);
device = g_hash_table_lookup(device_list, id);
}
if(device) {
cmd = create_async_command(msg);
if(cmd == NULL) {
free_device(device);
return -EPROTO;
}
schedule_stonith_command(cmd, device);
rc = -EINPROGRESS;
} else {
crm_info("Device %s not found", id?id:"<none>");
rc = -ENODEV;
}
return rc;
}
static gboolean can_fence_host_with_device(stonith_device_t *dev, const char *host)
{
gboolean can = FALSE;
const char *alias = host;
const char *check_type = NULL;
if(dev == NULL) {
return FALSE;
} else if(host == NULL) {
return TRUE;
}
if(g_hash_table_lookup(dev->aliases, host)) {
alias = g_hash_table_lookup(dev->aliases, host);
}
check_type = target_list_type(dev);
if(safe_str_eq(check_type, "none")) {
can = TRUE;
} else if(safe_str_eq(check_type, "static-list")) {
/* Presence in the hostmap is sufficient
* Only use if all hosts on which the device can be active can always fence all listed hosts
*/
if(string_in_list(dev->targets, host)) {
can = TRUE;
} else if(g_hash_table_lookup(dev->params, STONITH_ATTR_HOSTMAP)
&& g_hash_table_lookup(dev->aliases, host)) {
can = TRUE;
}
} else if(safe_str_eq(check_type, "dynamic-list")) {
update_dynamic_list(dev);
if(string_in_list(dev->targets, alias)) {
can = TRUE;
}
} else if(safe_str_eq(check_type, "status")) {
int rc = 0;
int exec_rc = 0;
/* Run the status operation for the device/target combination
* Will cause problems if the device doesn't return 2 for down'd nodes or
* (for virtual nodes) if the device doesn't return 1 for guests that
* have been moved to another host
*/
exec_rc = run_stonith_agent(
dev->agent, "status", host, dev->params, dev->aliases, &rc, NULL, NULL);
if(exec_rc != 0) {
crm_err("Could not invoke %s: rc=%d", dev->id, exec_rc);
} else if(rc == 1 /* unkown */) {
crm_trace("Host %s is not known by %s", host, dev->id);
} else if(rc == 0 /* active */ || rc == 2 /* inactive */) {
can = TRUE;
} else {
crm_notice("Unkown result when testing if %s can fence %s: rc=%d", dev->id, host, rc);
}
} else {
crm_err("Unknown check type: %s", check_type);
}
if(safe_str_eq(host, alias)) {
crm_info("%s can%s fence %s: %s", dev->id, can?"":" not", host, check_type);
} else {
crm_info("%s can%s fence %s (aka. '%s'): %s", dev->id, can?"":" not", host, alias, check_type);
}
return can;
}
struct device_search_s
{
const char *host;
GListPtr capable;
};
static void search_devices(
gpointer key, gpointer value, gpointer user_data)
{
stonith_device_t *dev = value;
struct device_search_s *search = user_data;
if(can_fence_host_with_device(dev, search->host)) {
search->capable = g_list_append(search->capable, value);
}
}
static int stonith_query(xmlNode *msg, xmlNode **list)
{
struct device_search_s search;
int available_devices = 0;
const char *action = NULL;
xmlNode *dev = get_xpath_object("//@"F_STONITH_TARGET, msg, LOG_DEBUG_3);
search.host = NULL;
search.capable = NULL;
if(dev) {
const char *device = crm_element_value(dev, F_STONITH_DEVICE);
search.host = crm_element_value(dev, F_STONITH_TARGET);
if(device && safe_str_eq(device, "manual_ack")) {
/* No query necessary */
if(list) {
*list = NULL;
}
return pcmk_ok;
}
action = crm_element_value(dev, F_STONITH_ACTION);
}
crm_log_xml_debug(msg, "Query");
g_hash_table_foreach(device_list, search_devices, &search);
available_devices = g_list_length(search.capable);
if(search.host) {
crm_debug("Found %d matching devices for '%s'",
available_devices, search.host);
} else {
crm_debug("%d devices installed", available_devices);
}
/* Pack the results into data */
if(list) {
GListPtr lpc = NULL;
*list = create_xml_node(NULL, __FUNCTION__);
crm_xml_add(*list, F_STONITH_TARGET, search.host);
crm_xml_add_int(*list, "st-available-devices", available_devices);
for(lpc = search.capable; lpc != NULL; lpc = lpc->next) {
stonith_device_t *device = (stonith_device_t*)lpc->data;
int action_specific_timeout = get_action_timeout(device, action, 0);
dev = create_xml_node(*list, F_STONITH_DEVICE);
crm_xml_add(dev, XML_ATTR_ID, device->id);
crm_xml_add(dev, "namespace", device->namespace);
crm_xml_add(dev, "agent", device->agent);
if (action_specific_timeout) {
crm_xml_add_int(dev, F_STONITH_ACTION_TIMEOUT, action_specific_timeout);
}
if(search.host == NULL) {
xmlNode *attrs = create_xml_node(dev, XML_TAG_ATTRS);
g_hash_table_foreach(device->params, hash2field, attrs);
}
}
}
g_list_free(search.capable);
return available_devices;
}
static void log_operation(async_command_t *cmd, int rc, int pid, const char *next, const char *output)
{
if(rc == 0) {
next = NULL;
}
if(cmd->victim != NULL) {
do_crm_log(rc==0?LOG_NOTICE:LOG_ERR,
"Operation '%s' [%d] (call %d from %s) for host '%s' with device '%s' returned: %d%s%s",
cmd->action, pid, cmd->id, cmd->client, cmd->victim, cmd->device, rc,
next?". Trying: ":"", next?next:"");
} else {
do_crm_log_unlikely(rc==0?LOG_DEBUG:LOG_NOTICE,
"Operation '%s' [%d] for device '%s' returned: %d%s%s",
cmd->action, pid, cmd->device, rc, next?". Trying: ":"", next?next:"");
}
if(output) {
/* Logging the whole string confuses syslog when the string is xml */
char *local_copy = strdup(output);
int lpc = 0, last = 0, more = strlen(local_copy);
for(lpc = 0; lpc < more; lpc++) {
if(local_copy[lpc] == '\n' || local_copy[lpc] == 0) {
local_copy[lpc] = 0;
do_crm_log(rc==0?LOG_INFO:LOG_WARNING, "%s: %s",
cmd->device, local_copy+last);
last = lpc+1;
}
}
crm_debug("%s: %s (total %d bytes)", cmd->device, local_copy+last, more);
free(local_copy);
}
}
static void
stonith_send_async_reply(async_command_t *cmd, const char *output, int rc, GPid pid)
{
xmlNode *reply = NULL;
gboolean bcast = TRUE;
reply = stonith_construct_async_reply(cmd, output, NULL, rc);
if(safe_str_eq(cmd->action, "metadata")) {
/* Too verbose to log */
bcast = FALSE;
output = NULL;
crm_trace("Directed reply: %s op", cmd->action);
} else if(crm_str_eq(cmd->action, "monitor", TRUE) ||
crm_str_eq(cmd->action, "list", TRUE) ||
crm_str_eq(cmd->action, "status", TRUE)) {
crm_trace("Directed reply: %s op", cmd->action);
bcast = FALSE;
} else if(safe_str_eq(cmd->mode, "slave")) {
crm_trace("Directed reply: Complex op with %s", cmd->device);
bcast = FALSE;
}
log_operation(cmd, rc, pid, NULL, output);
crm_log_xml_trace(reply, "Reply");
if(bcast && !stand_alone) {
/* Send reply as T_STONITH_NOTIFY so everyone does notifications
* Potentially limit to unsucessful operations to the originator?
*/
crm_trace("Broadcast reply");
crm_xml_add(reply, F_STONITH_OPERATION, T_STONITH_NOTIFY);
send_cluster_message(NULL, crm_msg_stonith_ng, reply, FALSE);
} else if(cmd->origin) {
crm_trace("Directed reply to %s", cmd->origin);
send_cluster_message(cmd->origin, crm_msg_stonith_ng, reply, FALSE);
} else {
crm_trace("Directed local %ssync reply to %s", (cmd->options & st_opt_sync_call)?"":"a-", cmd->client);
do_local_reply(reply, cmd->client, cmd->options & st_opt_sync_call, FALSE);
}
if(stand_alone) {
/* Do notification with a clean data object */
xmlNode *notify_data = create_xml_node(NULL, T_STONITH_NOTIFY_FENCE);
crm_xml_add_int(notify_data, F_STONITH_RC, rc);
crm_xml_add(notify_data, F_STONITH_TARGET, cmd->victim);
crm_xml_add(notify_data, F_STONITH_OPERATION, cmd->op);
crm_xml_add(notify_data, F_STONITH_DELEGATE, cmd->device);
crm_xml_add(notify_data, F_STONITH_REMOTE, cmd->remote);
crm_xml_add(notify_data, F_STONITH_ORIGIN, cmd->client);
do_stonith_notify(0, T_STONITH_NOTIFY_FENCE, rc, notify_data, NULL);
}
free_xml(reply);
}
static void cancel_stonith_command(async_command_t *cmd)
{
stonith_device_t *device;
CRM_CHECK(cmd != NULL, return);
if (!cmd->device) {
return;
}
device = g_hash_table_lookup(device_list, cmd->device);
if (device) {
crm_trace("Cancel scheduled %s on %s", cmd->action, device->id);
device->pending_ops = g_list_remove(device->pending_ops, cmd);
}
}
#define READ_MAX 500
static void st_child_done(GPid pid, gint status, gpointer user_data)
{
int rc = -pcmk_err_generic;
int len = 0;
int more = 0;
char *output = NULL;
stonith_device_t *device = NULL;
async_command_t *cmd = user_data;
GListPtr gIter = NULL;
GListPtr gIterNext = NULL;
CRM_CHECK(cmd != NULL, return);
if(cmd->timer_sigterm > 0) {
g_source_remove(cmd->timer_sigterm);
}
if(cmd->timer_sigkill > 0) {
g_source_remove(cmd->timer_sigkill);
}
if(WIFSIGNALED(status)) {
int signo = WTERMSIG(status);
if(signo == SIGTERM || signo == SIGKILL) {
rc = -ETIME;
} else {
rc = -ECONNABORTED;
}
crm_notice("Child process %d performing action '%s' with '%s' terminated with signal %d",
pid, cmd->action, cmd->device, signo);
} else if(WIFEXITED(status)) {
rc = WEXITSTATUS(status);
crm_debug("Child process %d performing action '%s' with '%s' exited with rc %d",
pid, cmd->action, cmd->device, rc);
}
active_children--;
/* The device is ready to do something else now */
device = g_hash_table_lookup(device_list, cmd->device);
if(device) {
device->active_pid = 0;
mainloop_set_trigger(device->work);
}
do {
char buffer[READ_MAX];
errno = 0;
if(cmd->fd_stdout > 0) {
memset(&buffer, 0, READ_MAX);
more = read(cmd->fd_stdout, buffer, READ_MAX-1);
crm_trace("Got %d more bytes: %s", more, buffer);
}
if(more > 0) {
output = realloc(output, len + more + 1);
sprintf(output+len, "%s", buffer);
len += more;
}
} while (more == (READ_MAX-1) || (more < 0 && errno == EINTR));
if(cmd->fd_stdout) {
close(cmd->fd_stdout);
cmd->fd_stdout = 0;
}
crm_trace("Operation on %s completed with rc=%d (%d remaining)",
cmd->device, rc, g_list_length(cmd->device_next));
if(rc != 0 && cmd->device_next) {
stonith_device_t *dev = cmd->device_next->data;
log_operation(cmd, rc, pid, dev->id, output);
cmd->device_next = cmd->device_next->next;
schedule_stonith_command(cmd, dev);
/* Prevent cmd from being freed */
cmd = NULL;
goto done;
}
if(rc > 0) {
rc = -pcmk_err_generic;
}
stonith_send_async_reply(cmd, output, rc, pid);
if(rc != 0) {
goto done;
}
/* Check to see if any operations are scheduled to do the exact
* same thing that just completed. If so, rather than
* performing the same fencing operation twice, return the result
* of this operation for all pending commands it matches. */
for (gIter = cmd_list; gIter != NULL; gIter = gIterNext) {
async_command_t *cmd_other = gIter->data;
gIterNext = gIter->next;
if(cmd == cmd_other) {
continue;
}
/* A pending scheduled command matches the command that just finished if.
* 1. The client connections are different.
* 2. The node victim is the same.
* 3. The fencing action is the same.
* 4. The device scheduled to execute the action is the same.
*/
if(safe_str_eq(cmd->client, cmd_other->client) ||
safe_str_neq(cmd->victim, cmd_other->victim) ||
safe_str_neq(cmd->action, cmd_other->action) ||
safe_str_neq(cmd->device, cmd_other->device)) {
continue;
}
crm_notice("Merging stonith action %s for node %s originating from client %s with identical stonith request from client %s",
cmd_other->action,
cmd_other->victim,
cmd_other->client,
cmd->client);
cmd_list = g_list_remove_link(cmd_list, gIter);
stonith_send_async_reply(cmd_other, output, rc, pid);
cancel_stonith_command(cmd_other);
free_async_command(cmd_other);
g_list_free_1(gIter);
}
done:
free_async_command(cmd);
free(output);
}
static gint sort_device_priority(gconstpointer a, gconstpointer b)
{
const stonith_device_t *dev_a = a;
const stonith_device_t *dev_b = a;
if(dev_a->priority > dev_b->priority) {
return -1;
} else if(dev_a->priority < dev_b->priority) {
return 1;
}
return 0;
}
static int stonith_fence(xmlNode *msg)
{
int options = 0;
const char *device_id = NULL;
stonith_device_t *device = NULL;
async_command_t *cmd = create_async_command(msg);
xmlNode *dev = get_xpath_object("//@"F_STONITH_TARGET, msg, LOG_ERR);
if(cmd == NULL) {
return -EPROTO;
}
device_id = crm_element_value(dev, F_STONITH_DEVICE);
if(device_id) {
device = g_hash_table_lookup(device_list, device_id);
if(device == NULL) {
crm_err("Requested device '%s' is not available", device_id);
}
} else {
struct device_search_s search;
search.capable = NULL;
search.host = crm_element_value(dev, F_STONITH_TARGET);
crm_element_value_int(msg, F_STONITH_CALLOPTS, &options);
if(options & st_opt_cs_nodeid) {
int nodeid = crm_atoi(search.host, NULL);
crm_node_t *node = crm_get_peer(nodeid, NULL);
if(node) {
search.host = node->uname;
}
}
g_hash_table_foreach(device_list, search_devices, &search);
crm_info("Found %d matching devices for '%s'", g_list_length(search.capable), search.host);
if(g_list_length(search.capable) > 0) {
/* Order based on priority */
search.capable = g_list_sort(search.capable, sort_device_priority);
device = search.capable->data;
if(g_list_length(search.capable) > 1) {
cmd->device_list = search.capable;
cmd->device_next = cmd->device_list->next;
} else {
g_list_free(search.capable);
}
}
}
if(device) {
schedule_stonith_command(cmd, device);
return -EINPROGRESS;
}
free_async_command(cmd);
return -EHOSTUNREACH;
}
xmlNode *stonith_construct_reply(xmlNode *request, char *output, xmlNode *data, int rc)
{
int lpc = 0;
xmlNode *reply = NULL;
const char *name = NULL;
const char *value = NULL;
const char *names[] = {
F_STONITH_OPERATION,
F_STONITH_CALLID,
F_STONITH_CLIENTID,
F_STONITH_CLIENTNAME,
F_STONITH_REMOTE,
F_STONITH_CALLOPTS
};
crm_trace("Creating a basic reply");
reply = create_xml_node(NULL, T_STONITH_REPLY);
crm_xml_add(reply, "st_origin", __FUNCTION__);
crm_xml_add(reply, F_TYPE, T_STONITH_NG);
crm_xml_add(reply, "st_output", output);
crm_xml_add_int(reply, F_STONITH_RC, rc);
CRM_CHECK(request != NULL, crm_warn("Can't create a sane reply"); return reply);
for(lpc = 0; lpc < DIMOF(names); lpc++) {
name = names[lpc];
value = crm_element_value(request, name);
crm_xml_add(reply, name, value);
}
if(data != NULL) {
crm_trace("Attaching reply output");
add_message_xml(reply, F_STONITH_CALLDATA, data);
}
return reply;
}
xmlNode *stonith_construct_async_reply(async_command_t *cmd, const char *output, xmlNode *data, int rc)
{
xmlNode *reply = NULL;
crm_trace("Creating a basic reply");
reply = create_xml_node(NULL, T_STONITH_REPLY);
crm_xml_add(reply, "st_origin", __FUNCTION__);
crm_xml_add(reply, F_TYPE, T_STONITH_NG);
crm_xml_add(reply, F_STONITH_OPERATION, cmd->op);
crm_xml_add(reply, F_STONITH_DEVICE, cmd->device);
crm_xml_add(reply, F_STONITH_REMOTE, cmd->remote);
crm_xml_add(reply, F_STONITH_CLIENTID, cmd->client);
crm_xml_add(reply, F_STONITH_CLIENTNAME, cmd->client_name);
crm_xml_add(reply, F_STONITH_TARGET, cmd->victim);
crm_xml_add(reply, F_STONITH_ACTION, cmd->op);
+ crm_xml_add(reply, F_STONITH_ORIGIN, cmd->origin);
crm_xml_add_int(reply, F_STONITH_CALLID, cmd->id);
crm_xml_add_int(reply, F_STONITH_CALLOPTS, cmd->options);
+
crm_xml_add_int(reply, F_STONITH_RC, rc);
crm_xml_add(reply, "st_output", output);
if(data != NULL) {
crm_info("Attaching reply output");
add_message_xml(reply, F_STONITH_CALLDATA, data);
}
return reply;
}
void
stonith_command(stonith_client_t *client, uint32_t id, uint32_t flags, xmlNode *request, const char *remote)
{
int call_options = 0;
int rc = -EOPNOTSUPP;
gboolean is_reply = FALSE;
gboolean always_reply = FALSE;
xmlNode *reply = NULL;
xmlNode *data = NULL;
char *output = NULL;
const char *op = crm_element_value(request, F_STONITH_OPERATION);
const char *client_id = crm_element_value(request, F_STONITH_CLIENTID);
crm_element_value_int(request, F_STONITH_CALLOPTS, &call_options);
if(get_xpath_object("//"T_STONITH_REPLY, request, LOG_DEBUG_3)) {
is_reply = TRUE;
}
crm_debug("Processing %s%s from %s (%16x)", op, is_reply?" reply":"",
client?client->name:remote, call_options);
if(is_set(call_options, st_opt_sync_call)) {
CRM_ASSERT(client->request_id == id);
}
if(crm_str_eq(op, CRM_OP_REGISTER, TRUE)) {
xmlNode *reply = create_xml_node(NULL, "reply");
crm_xml_add(reply, F_STONITH_OPERATION, CRM_OP_REGISTER);
crm_xml_add(reply, F_STONITH_CLIENTID, client->id);
crm_ipcs_send(client->channel, id, reply, FALSE);
client->request_id = 0;
free_xml(reply);
return;
} else if(crm_str_eq(op, STONITH_OP_EXEC, TRUE)) {
rc = stonith_device_action(request, &output);
} else if (crm_str_eq(op, STONITH_OP_TIMEOUT_UPDATE, TRUE)) {
const char *call_id = crm_element_value(request, F_STONITH_CALLID);
const char *client_id = crm_element_value(request, F_STONITH_CLIENTID);
int op_timeout = 0;
crm_element_value_int(request, F_STONITH_TIMEOUT, &op_timeout);
do_stonith_async_timeout_update(client_id, call_id, op_timeout);
return;
} else if(is_reply && crm_str_eq(op, STONITH_OP_QUERY, TRUE)) {
process_remote_stonith_query(request);
return;
} else if(crm_str_eq(op, STONITH_OP_QUERY, TRUE)) {
create_remote_stonith_op(client_id, request, TRUE); /* Record it for the future notification */
rc = stonith_query(request, &data);
always_reply = TRUE;
if(!data) {
return;
}
} else if(is_reply && crm_str_eq(op, T_STONITH_NOTIFY, TRUE)) {
process_remote_stonith_exec(request);
return;
} else if(is_reply && crm_str_eq(op, STONITH_OP_FENCE, TRUE)) {
/* Reply to a complex fencing op */
process_remote_stonith_exec(request);
return;
} else if(crm_str_eq(op, T_STONITH_NOTIFY, TRUE)) {
const char *flag_name = NULL;
flag_name = crm_element_value(request, F_STONITH_NOTIFY_ACTIVATE);
if(flag_name) {
crm_debug("Setting %s callbacks for %s (%s): ON",
flag_name, client->name, client->id);
client->flags |= get_stonith_flag(flag_name);
}
flag_name = crm_element_value(request, F_STONITH_NOTIFY_DEACTIVATE);
if(flag_name) {
crm_debug("Setting %s callbacks for %s (%s): off",
flag_name, client->name, client->id);
client->flags |= get_stonith_flag(flag_name);
}
if(flags & crm_ipc_client_response) {
crm_ipcs_send_ack(client->channel, id, "ack", __FUNCTION__, __LINE__);
client->request_id = 0;
}
return;
/* } else if(is_reply && crm_str_eq(op, STONITH_OP_FENCE, TRUE)) { */
/* process_remote_stonith_exec(request); */
/* return; */
} else if(is_reply == FALSE && crm_str_eq(op, STONITH_OP_RELAY, TRUE)) {
if(initiate_remote_stonith_op(NULL, request, FALSE) != NULL) {
rc = -EINPROGRESS;
}
} else if(is_reply == FALSE && crm_str_eq(op, STONITH_OP_FENCE, TRUE)) {
if(remote || stand_alone) {
rc = stonith_fence(request);
} else if(call_options & st_opt_manual_ack) {
remote_fencing_op_t *rop = initiate_remote_stonith_op(client, request, TRUE);
rc = stonith_manual_ack(request, rop);
} else {
const char *alternate_host = NULL;
xmlNode *dev = get_xpath_object("//@"F_STONITH_TARGET, request, LOG_TRACE);
const char *target = crm_element_value_copy(dev, F_STONITH_TARGET);
if(g_hash_table_lookup(topology, target) && safe_str_eq(target, stonith_our_uname)) {
GHashTableIter gIter;
crm_node_t *entry = NULL;
int membership = crm_proc_plugin | crm_proc_heartbeat | crm_proc_cpg;
g_hash_table_iter_init(&gIter, crm_peer_cache);
while (g_hash_table_iter_next(&gIter, NULL, (void **)&entry)) {
crm_trace("Checking for %s.%d != %s",
entry->uname, entry->id, target);
if(entry->uname
&& (entry->processes & membership)
&& safe_str_neq(entry->uname, target)) {
alternate_host = entry->uname;
break;
}
}
if(alternate_host == NULL) {
crm_err("No alternate host available to handle complex self fencing request");
g_hash_table_iter_init(&gIter, crm_peer_cache);
while (g_hash_table_iter_next(&gIter, NULL, (void **)&entry)) {
crm_notice("Peer[%d] %s", entry->id, entry->uname);
}
}
}
if(alternate_host) {
crm_notice("Forwarding complex self fencing request to peer %s", alternate_host);
crm_xml_add(request, F_STONITH_OPERATION, STONITH_OP_RELAY);
crm_xml_add(request, F_STONITH_CLIENTID, client->id);
send_cluster_message(alternate_host, crm_msg_stonith_ng, request, FALSE);
rc = -EINPROGRESS;
} else if(initiate_remote_stonith_op(client, request, FALSE) != NULL) {
rc = -EINPROGRESS;
}
}
} else if (crm_str_eq(op, STONITH_OP_FENCE_HISTORY, TRUE)) {
rc = stonith_fence_history(request, &data);
always_reply = TRUE;
} else if(crm_str_eq(op, CRM_OP_REGISTER, TRUE)) {
return;
} else if(crm_str_eq(op, STONITH_OP_DEVICE_ADD, TRUE)) {
const char *id = NULL;
xmlNode *notify_data = create_xml_node(NULL, op);
rc = stonith_device_register(request, &id);
crm_xml_add(notify_data, F_STONITH_DEVICE, id);
crm_xml_add_int(notify_data, F_STONITH_ACTIVE, g_hash_table_size(device_list));
do_stonith_notify(call_options, op, rc, notify_data, NULL);
free_xml(notify_data);
} else if(crm_str_eq(op, STONITH_OP_DEVICE_DEL, TRUE)) {
const char *id = NULL;
xmlNode *notify_data = create_xml_node(NULL, op);
rc = stonith_device_remove(request, &id);
crm_xml_add(notify_data, F_STONITH_DEVICE, id);
crm_xml_add_int(notify_data, F_STONITH_ACTIVE, g_hash_table_size(device_list));
do_stonith_notify(call_options, op, rc, notify_data, NULL);
free_xml(notify_data);
} else if(crm_str_eq(op, STONITH_OP_LEVEL_ADD, TRUE)) {
char *id = NULL;
xmlNode *notify_data = create_xml_node(NULL, op);
rc = stonith_level_register(request, &id);
crm_xml_add(notify_data, F_STONITH_DEVICE, id);
crm_xml_add_int(notify_data, F_STONITH_ACTIVE, g_hash_table_size(topology));
do_stonith_notify(call_options, op, rc, notify_data, NULL);
free_xml(notify_data);
} else if(crm_str_eq(op, STONITH_OP_LEVEL_DEL, TRUE)) {
char *id = NULL;
xmlNode *notify_data = create_xml_node(NULL, op);
rc = stonith_level_remove(request, &id);
crm_xml_add(notify_data, F_STONITH_DEVICE, id);
crm_xml_add_int(notify_data, F_STONITH_ACTIVE, g_hash_table_size(topology));
do_stonith_notify(call_options, op, rc, notify_data, NULL);
free_xml(notify_data);
} else if(crm_str_eq(op, STONITH_OP_CONFIRM, TRUE)) {
async_command_t *cmd = create_async_command(request);
xmlNode *reply = stonith_construct_async_reply(cmd, NULL, NULL, 0);
crm_xml_add(reply, F_STONITH_OPERATION, T_STONITH_NOTIFY);
crm_notice("Broadcasting manual fencing confirmation for node %s", cmd->victim);
send_cluster_message(NULL, crm_msg_stonith_ng, reply, FALSE);
free_async_command(cmd);
free_xml(reply);
} else {
crm_err("Unknown %s%s from %s", op, is_reply?" reply":"",
client?client->name:remote);
crm_log_xml_warn(request, "UnknownOp");
}
do_crm_log_unlikely(rc>0?LOG_DEBUG:LOG_INFO,"Processed %s%s from %s: %s (%d)", op, is_reply?" reply":"",
client?client->name:remote, rc>0?"":pcmk_strerror(rc), rc);
if(is_reply || rc == -EINPROGRESS) {
/* Nothing (yet) */
} else if(remote) {
reply = stonith_construct_reply(request, output, data, rc);
send_cluster_message(remote, crm_msg_stonith_ng, reply, FALSE);
free_xml(reply);
} else if(rc <= pcmk_ok || always_reply) {
reply = stonith_construct_reply(request, output, data, rc);
do_local_reply(reply, client_id, call_options & st_opt_sync_call, remote!=NULL);
free_xml(reply);
}
free(output);
free_xml(data);
}
diff --git a/fencing/remote.c b/fencing/remote.c
index 6c83e0fe13..532537614f 100644
--- a/fencing/remote.c
+++ b/fencing/remote.c
@@ -1,792 +1,792 @@
/*
* Copyright (C) 2009 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/utsname.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/cluster/internal.h>
#include <crm/stonith-ng.h>
#include <crm/fencing/internal.h>
#include <crm/common/xml.h>
#include <crm/common/util.h>
#include <internal.h>
typedef struct st_query_result_s
{
char *host;
int devices;
GListPtr device_list;
GHashTable *custom_action_timeouts;
} st_query_result_t;
GHashTable *remote_op_list = NULL;
void call_remote_stonith(remote_fencing_op_t *op, st_query_result_t *peer);
extern xmlNode *stonith_create_op(
int call_id, const char *token, const char *op, xmlNode *data, int call_options);
static void free_remote_query(gpointer data)
{
if(data) {
st_query_result_t *query = data;
crm_trace("Free'ing query result from %s", query->host);
free(query->host);
g_hash_table_destroy(query->custom_action_timeouts);
free(query);
}
}
static void free_remote_op(gpointer data)
{
remote_fencing_op_t *op = data;
crm_trace("Free'ing op %s for %s", op->id, op->target);
crm_log_xml_debug(op->request, "Destroying");
free(op->id);
free(op->action);
free(op->target);
free(op->client_id);
free(op->client_name);
free(op->originator);
if(op->query_timer) {
g_source_remove(op->query_timer);
}
if(op->op_timer) {
g_source_remove(op->op_timer);
}
if(op->query_results) {
g_list_free_full(op->query_results, free_remote_query);
}
if(op->request) {
free_xml(op->request);
op->request = NULL;
}
free(op);
}
static void remote_op_done(remote_fencing_op_t *op, xmlNode *data, int rc)
{
xmlNode *reply = NULL;
xmlNode *local_data = NULL;
xmlNode *notify_data = NULL;
op->completed = time(NULL);
if(op->query_timer) {
g_source_remove(op->query_timer);
op->query_timer = 0;
}
if(op->op_timer) {
g_source_remove(op->op_timer);
op->op_timer = 0;
}
if(data == NULL) {
data = create_xml_node(NULL, "remote-op");
local_data = data;
} else {
op->delegate = crm_element_value_copy(data, F_ORIG);
}
crm_xml_add_int(data, "state", op->state);
crm_xml_add(data, F_STONITH_TARGET, op->target);
crm_xml_add(data, F_STONITH_OPERATION, op->action);
if(op->request != NULL) {
reply = stonith_construct_reply(op->request, NULL, data, rc);
crm_xml_add(reply, F_STONITH_DELEGATE, op->delegate);
do_crm_log(rc==pcmk_ok?LOG_NOTICE:LOG_ERR,
"Operation %s of %s by %s for %s[%s]: %s",
op->action, op->target, op->delegate?op->delegate:"<no-one>",
op->originator, op->client_id, pcmk_strerror(rc));
} else {
crm_err("Already sent notifications for '%s of %s by %s' (op=%s, for=%s, state=%d): %s",
op->action, op->target, op->delegate, op->id, op->client_id, op->state,
pcmk_strerror(rc));
return;
}
if(reply) {
do_local_reply(reply, op->client_id, op->call_options & st_opt_sync_call, FALSE);
}
/* Do notification with a clean data object */
notify_data = create_xml_node(NULL, T_STONITH_NOTIFY_FENCE);
crm_xml_add_int(notify_data, "state", op->state);
crm_xml_add_int(notify_data, F_STONITH_RC, rc);
crm_xml_add(notify_data, F_STONITH_TARGET, op->target);
crm_xml_add(notify_data, F_STONITH_OPERATION, op->action);
crm_xml_add(notify_data, F_STONITH_DELEGATE, op->delegate);
crm_xml_add(notify_data, F_STONITH_REMOTE, op->id);
crm_xml_add(notify_data, F_STONITH_ORIGIN, op->originator);
crm_xml_add(notify_data, F_STONITH_CLIENTID, op->client_id);
crm_xml_add(notify_data, F_STONITH_CLIENTNAME, op->client_name);
do_stonith_notify(0, T_STONITH_NOTIFY_FENCE, rc, notify_data, NULL);
free_xml(notify_data);
free_xml(local_data);
free_xml(reply);
/* Free non-essential parts of the record
* Keep the record around so we can query the history
*/
if(op->query_results) {
g_list_free_full(op->query_results, free_remote_query);
op->query_results = NULL;
}
if(op->request) {
free_xml(op->request);
op->request = NULL;
}
}
static gboolean remote_op_timeout(gpointer userdata)
{
remote_fencing_op_t *op = userdata;
op->query_timer = 0;
if(op->state == st_done) {
crm_debug("Action %s (%s) for %s already completed", op->action, op->id, op->target);
return FALSE;
}
crm_debug("Action %s (%s) for %s timed out", op->action, op->id, op->target);
remote_op_done(op, NULL, -ETIME);
op->state = st_failed;
return FALSE;
}
static gboolean remote_op_query_timeout(gpointer data)
{
remote_fencing_op_t *op = data;
op->query_timer = 0;
if(op->state == st_done) {
crm_debug("Operation %s for %s already completed", op->id, op->target);
} else if(op->state == st_exec) {
crm_debug("Operation %s for %s already in progress", op->id, op->target);
} else if(op->query_results) {
crm_debug("Query %s for %s complete: %d", op->id, op->target, op->state);
call_remote_stonith(op, NULL);
} else {
if(op->op_timer) {
g_source_remove(op->op_timer);
op->op_timer = 0;
}
remote_op_timeout(op);
}
return FALSE;
}
static int stonith_topology_next(remote_fencing_op_t *op)
{
stonith_topology_t *tp = NULL;
if(op->target) {
/* Queries don't have a target set */
tp = g_hash_table_lookup(topology, op->target);
}
if(tp == NULL) {
return pcmk_ok;
}
set_bit(op->call_options, st_opt_topology);
do {
op->level++;
} while(op->level < ST_LEVEL_MAX && tp->levels[op->level] == NULL);
if(op->level < ST_LEVEL_MAX) {
crm_trace("Attempting fencing level %d for %s (%d devices)", op->level, op->target, g_list_length(tp->levels[op->level]));
op->devices = tp->levels[op->level];
return pcmk_ok;
}
crm_notice("All fencing options for %s failed", op->target);
return -EINVAL;
}
void *create_remote_stonith_op(const char *client, xmlNode *request, gboolean peer)
{
remote_fencing_op_t *op = NULL;
xmlNode *dev = get_xpath_object("//@"F_STONITH_TARGET, request, LOG_TRACE);
if(remote_op_list == NULL) {
remote_op_list = g_hash_table_new_full(
crm_str_hash, g_str_equal, NULL, free_remote_op);
}
if(peer && dev) {
const char *peer_id = crm_element_value(dev, F_STONITH_REMOTE);
CRM_CHECK(peer_id != NULL, return NULL);
op = g_hash_table_lookup(remote_op_list, peer_id);
if(op) {
crm_debug("%s already exists", peer_id);
return op;
}
}
op = calloc(1, sizeof(remote_fencing_op_t));
crm_element_value_int(request, F_STONITH_TIMEOUT, (int*)&(op->base_timeout));
if(peer && dev) {
op->id = crm_element_value_copy(dev, F_STONITH_REMOTE);
crm_trace("Recorded new stonith op: %s", op->id);
} else {
op->id = crm_generate_uuid();
crm_trace("Generated new stonith op: %s", op->id);
}
g_hash_table_replace(remote_op_list, op->id, op);
CRM_LOG_ASSERT(g_hash_table_lookup(remote_op_list, op->id) != NULL);
op->state = st_query;
op->action = crm_element_value_copy(dev, F_STONITH_ACTION);
- op->originator = crm_element_value_copy(dev, F_STONITH_OWNER);
+ op->originator = crm_element_value_copy(dev, F_STONITH_ORIGIN);
if(op->originator == NULL) {
/* Local request */
op->originator = strdup(stonith_our_uname);
}
if(client) {
op->client_id = strdup(client);
}
op->client_name = crm_element_value_copy(request, F_STONITH_CLIENTNAME);
op->target = crm_element_value_copy(dev, F_STONITH_TARGET);
op->request = copy_xml(request); /* TODO: Figure out how to avoid this */
crm_element_value_int(request, F_STONITH_CALLOPTS, (int*)&(op->call_options));
if(op->call_options & st_opt_cs_nodeid) {
int nodeid = crm_atoi(op->target, NULL);
crm_node_t *node = crm_get_peer(nodeid, NULL);
/* Ensure the conversion only happens once */
op->call_options &= ~st_opt_cs_nodeid;
if(node) {
free(op->target);
op->target = strdup(node->uname);
}
}
if(stonith_topology_next(op) != pcmk_ok) {
op->state = st_failed;
}
return op;
}
remote_fencing_op_t *initiate_remote_stonith_op(stonith_client_t *client, xmlNode *request, gboolean manual_ack)
{
xmlNode *query = NULL;
const char *client_id = NULL;
remote_fencing_op_t *op = NULL;
if(client) {
client_id = client->id;
} else {
client_id = crm_element_value(request, F_STONITH_CLIENTID);
}
CRM_LOG_ASSERT(client_id != NULL);
op = create_remote_stonith_op(client_id, request, FALSE);
query = stonith_create_op(0, op->id, STONITH_OP_QUERY, NULL, 0);
if(!manual_ack) {
op->query_timer = g_timeout_add(100*op->base_timeout, remote_op_query_timeout, op);
} else {
crm_xml_add(query, F_STONITH_DEVICE, "manual_ack");
}
crm_xml_add(query, F_STONITH_REMOTE, op->id);
crm_xml_add(query, F_STONITH_TARGET, op->target);
crm_xml_add(query, F_STONITH_ACTION, op->action);
- crm_xml_add(query, F_STONITH_OWNER, op->originator);
+ crm_xml_add(query, F_STONITH_ORIGIN, op->originator);
crm_xml_add(query, F_STONITH_CLIENTID, op->client_id);
crm_xml_add(query, F_STONITH_CLIENTNAME, op->client_name);
crm_xml_add_int(query, F_STONITH_TIMEOUT, op->base_timeout);
crm_info("Initiating remote operation %s for %s: %s", op->action, op->target, op->id);
CRM_CHECK(op->action, return NULL);
send_cluster_message(NULL, crm_msg_stonith_ng, query, FALSE);
free_xml(query);
return op;
}
static gint sort_strings(gconstpointer a, gconstpointer b)
{
return strcmp(a, b);
}
static st_query_result_t *stonith_choose_peer(remote_fencing_op_t *op)
{
GListPtr iter = NULL;
do {
if(op->devices) {
crm_trace("Checking for someone to fence %s with %s", op->target, (char*)op->devices->data);
} else {
crm_trace("Checking for someone to fence %s", op->target);
}
for(iter = op->query_results; iter != NULL; iter = iter->next) {
st_query_result_t *peer = iter->data;
if(is_set(op->call_options, st_opt_topology)) {
/* Do they have the next device of the current fencing level? */
GListPtr match = NULL;
if(op->devices) {
match = g_list_find_custom(peer->device_list, op->devices->data, sort_strings);
}
if(match) {
crm_trace("Removing %s from %s (%d remaining)", (char*)match->data, peer->host, g_list_length(peer->device_list));
peer->device_list = g_list_remove(peer->device_list, match->data);
return peer;
}
} else if(peer && peer->devices > 0) {
/* No topology: Use the current best peer */
crm_trace("Simple fencing");
return peer;
}
}
/* Try the next fencing level if there is one */
} while(is_set(op->call_options, st_opt_topology)
&& stonith_topology_next(op) == pcmk_ok);
if(op->devices) {
crm_trace("Couldn't find anyone to fence %s with %s", op->target, (char*)op->devices->data);
} else {
crm_trace("Couldn't find anyone to fence %s", op->target);
}
return NULL;
}
static int
get_device_timeout(st_query_result_t *peer, const char *device, int default_timeout)
{
gpointer res;
if (!peer || !device) {
return default_timeout;
}
res = g_hash_table_lookup(peer->custom_action_timeouts, device);
return res ? GPOINTER_TO_INT(res) : default_timeout;
}
static int
get_op_total_timeout(remote_fencing_op_t *op, st_query_result_t *chosen_peer, int default_timeout)
{
stonith_topology_t *tp = g_hash_table_lookup(topology, op->target);
int total_timeout = 0;
if (is_set(op->call_options, st_opt_topology) && tp) {
int i;
GListPtr device_list = NULL;
GListPtr iter = NULL;
/* Yep, this looks scary, nested loops all over the place.
* Here is what is going on.
* Loop1: Iterate through fencing levels.
* Loop2: If a fencing level has devices, loop through each device
* Loop3: For each device in a fencing level, see what peer owns it
* and what that peer has reported the timeout is for the device.
*/
for (i = 0; i < ST_LEVEL_MAX; i++) {
if (!tp->levels[i]) {
continue;
}
for (device_list = tp->levels[i]; device_list; device_list = device_list->next) {
for(iter = op->query_results; iter != NULL; iter = iter->next) {
st_query_result_t *peer = iter->data;
if (g_list_find_custom(peer->device_list, device_list->data, sort_strings)) {
total_timeout += get_device_timeout(chosen_peer, device_list->data, default_timeout);
break;
}
} /* End Loop3: match device with peer that owns device, find device's timeout period */
} /* End Loop2: iterate through devices at a specific level */
} /*End Loop1: iterate through fencing levels */
} else if (chosen_peer) {
GListPtr cur = NULL;
for (cur = chosen_peer->device_list; cur; cur = cur->next) {
total_timeout += get_device_timeout(chosen_peer, cur->data, default_timeout);
}
} else {
total_timeout = default_timeout;
}
return total_timeout ? total_timeout : default_timeout;
}
static void
report_timeout_period(remote_fencing_op_t *op, int op_timeout)
{
xmlNode *update = NULL;
const char *client_node = NULL;
const char *client_id = NULL;
const char *call_id = NULL;
if (op->call_options & st_opt_sync_call) {
/* There is no reason to report the timeout for a syncronous call. It
* is impossible to use the reported timeout to do anything when the client
* is blocking for the response. This update is only important for
* async calls that require a callback to report the results in. */
return;
} else if (!op->request) {
return;
}
client_node = crm_element_value(op->request, F_STONITH_CLIENTNODE);
call_id = crm_element_value(op->request, F_STONITH_CALLID);
client_id = crm_element_value(op->request, F_STONITH_CLIENTID);
if (!client_node || !call_id || !client_id) {
return;
}
if (safe_str_eq(client_node, stonith_our_uname)) {
/* The client is connected to this node, send the update direclty to them */
do_stonith_async_timeout_update(client_id, call_id, op_timeout);
return;
}
/* The client is connected to another node, relay this update to them */
update = stonith_create_op(0, op->id, STONITH_OP_TIMEOUT_UPDATE, NULL, 0);
crm_xml_add(update, F_STONITH_REMOTE, op->id);
crm_xml_add(update, F_STONITH_CLIENTID, client_id);
crm_xml_add(update, F_STONITH_CALLID, call_id);
crm_xml_add_int(update, F_STONITH_TIMEOUT, op_timeout);
send_cluster_message(client_node, crm_msg_stonith_ng, update, FALSE);
free_xml(update);
}
void call_remote_stonith(remote_fencing_op_t *op, st_query_result_t *peer)
{
const char *device = NULL;
int timeout = op->base_timeout;
if(peer == NULL && !is_set(op->call_options, st_opt_topology)) {
peer = stonith_choose_peer(op);
}
if(!op->op_timer) {
int op_timeout = get_op_total_timeout(op, peer, op->base_timeout);
op->op_timer = g_timeout_add((1200 * op_timeout), remote_op_timeout, op);
report_timeout_period(op, op_timeout);
crm_info("Total remote op timeout set to %d for fencing of node %s", op_timeout, op->target);
}
if(is_set(op->call_options, st_opt_topology)) {
/* Ignore any preference, they might not have the device we need */
/* When using topology, the stonith_choose_peer function pops off
* the peer from the op's query results. Make sure to calculate
* the op_timeout before calling this function when topology is in use */
peer = stonith_choose_peer(op);
device = op->devices->data;
timeout = get_device_timeout(peer, device, timeout);
}
if(peer) {
xmlNode *query = stonith_create_op(0, op->id, STONITH_OP_FENCE, NULL, 0);
crm_xml_add(query, F_STONITH_REMOTE, op->id);
crm_xml_add(query, F_STONITH_TARGET, op->target);
crm_xml_add(query, F_STONITH_ACTION, op->action);
- crm_xml_add(query, F_STONITH_OWNER, op->originator);
+ crm_xml_add(query, F_STONITH_ORIGIN, op->originator);
crm_xml_add(query, F_STONITH_CLIENTID, op->client_id);
crm_xml_add(query, F_STONITH_CLIENTNAME, op->client_name);
crm_xml_add_int(query, F_STONITH_TIMEOUT, timeout);
if(device) {
crm_info("Requesting that %s perform op %s %s with %s", peer->host, op->action, op->target, device);
crm_xml_add(query, F_STONITH_DEVICE, device);
crm_xml_add(query, F_STONITH_MODE, "slave");
} else {
crm_info("Requesting that %s perform op %s %s", peer->host, op->action, op->target);
crm_xml_add(query, F_STONITH_MODE, "smart");
}
op->state = st_exec;
send_cluster_message(peer->host, crm_msg_stonith_ng, query, FALSE);
free_xml(query);
return;
} else if(op->query_timer == 0) {
/* We've exhausted all available peers */
crm_info("No remaining peers capable of terminating %s", op->target);
remote_op_timeout(op);
} else if(device) {
crm_info("Waiting for additional peers capable of terminating %s with %s", op->target, device);
} else {
crm_info("Waiting for additional peers capable of terminating %s", op->target);
}
free_remote_query(peer);
}
static gint sort_peers(gconstpointer a, gconstpointer b)
{
const st_query_result_t *peer_a = a;
const st_query_result_t *peer_b = a;
if(peer_a->devices > peer_b->devices) {
return -1;
} else if(peer_a->devices > peer_b->devices) {
return 1;
}
return 0;
}
int process_remote_stonith_query(xmlNode *msg)
{
int devices = 0;
const char *id = NULL;
const char *host = NULL;
remote_fencing_op_t *op = NULL;
st_query_result_t *result = NULL;
xmlNode *dev = get_xpath_object("//@"F_STONITH_REMOTE, msg, LOG_ERR);
xmlNode *child = NULL;
CRM_CHECK(dev != NULL, return -EPROTO);
id = crm_element_value(dev, F_STONITH_REMOTE);
CRM_CHECK(id != NULL, return -EPROTO);
dev = get_xpath_object("//@st-available-devices", msg, LOG_ERR);
CRM_CHECK(dev != NULL, return -EPROTO);
crm_element_value_int(dev, "st-available-devices", &devices);
op = g_hash_table_lookup(remote_op_list, id);
if(op == NULL) {
crm_debug("Unknown or expired remote op: %s", id);
return -EOPNOTSUPP;
}
op->replies++;
host = crm_element_value(msg, F_ORIG);
if(devices <= 0) {
/* If we're doing 'known' then we might need to fire anyway */
crm_trace("Query result from %s (%d devices)", host, devices);
return pcmk_ok;
} else if(op->call_options & st_opt_allow_suicide) {
crm_trace("Allowing %s to potentialy fence itself", op->target);
} else if(safe_str_eq(host, op->target)) {
crm_info("Ignoring reply from %s, hosts are not permitted to commit suicide", op->target);
return pcmk_ok;
}
crm_debug("Query result from %s (%d devices)", host, devices);
result = calloc(1, sizeof(st_query_result_t));
result->host = strdup(host);
result->devices = devices;
result->custom_action_timeouts = g_hash_table_new_full(
crm_str_hash, g_str_equal, free, NULL);
for (child = __xml_first_child(dev); child != NULL; child = __xml_next(child)) {
const char *device = ID(child);
int action_timeout = 0;
if(device) {
result->device_list = g_list_prepend(result->device_list, strdup(device));
crm_element_value_int(child, F_STONITH_ACTION_TIMEOUT, &action_timeout);
if (action_timeout) {
crm_trace("Peer %s with device %s returned action timeout %d",
result->host, device, action_timeout);
g_hash_table_insert(result->custom_action_timeouts,
strdup(device),
GINT_TO_POINTER(action_timeout));
}
}
}
CRM_CHECK(devices == g_list_length(result->device_list),
crm_err("Mis-match: Query claimed to have %d devices but %d found", devices, g_list_length(result->device_list)));
op->query_results = g_list_insert_sorted(op->query_results, result, sort_peers);
if(op->state == st_query && is_set(op->call_options, st_opt_all_replies) == FALSE) {
call_remote_stonith(op, result);
} else if(op->state == st_done) {
crm_info("Discarding query result from %s (%d devices): Operation is in state %d",
result->host, result->devices, op->state);
}
return pcmk_ok;
}
int process_remote_stonith_exec(xmlNode *msg)
{
int rc = 0;
const char *id = NULL;
remote_fencing_op_t *op = NULL;
xmlNode *dev = get_xpath_object("//@"F_STONITH_REMOTE, msg, LOG_ERR);
CRM_CHECK(dev != NULL, return -EPROTO);
id = crm_element_value(dev, F_STONITH_REMOTE);
CRM_CHECK(id != NULL, return -EPROTO);
dev = get_xpath_object("//@"F_STONITH_RC, msg, LOG_ERR);
CRM_CHECK(dev != NULL, return -EPROTO);
crm_element_value_int(dev, F_STONITH_RC, &rc);
if(remote_op_list) {
op = g_hash_table_lookup(remote_op_list, id);
}
if(op == NULL && rc == pcmk_ok) {
/* Record successful fencing operations */
const char *client_id = crm_element_value(msg, F_STONITH_CLIENTID);
op = create_remote_stonith_op(client_id, msg, TRUE);
}
if(op == NULL) {
/* Could be for an event that began before we started */
/* TODO: Record the op for later querying */
crm_info("Unknown or expired remote op: %s", id);
return -EOPNOTSUPP;
}
if(is_set(op->call_options, st_opt_topology)) {
const char *device = crm_element_value(msg, F_STONITH_DEVICE);
crm_notice("Call to %s for %s on behalf of %s: %s (%d)", device, op->target, op->originator, rc == pcmk_ok?"passed":"failed", rc);
if(safe_str_eq(op->originator, stonith_our_uname)) {
if(op->state == st_done) {
remote_op_done(op, msg, rc);
return rc;
} else if(rc == pcmk_ok && op->devices) {
/* Success, are there any more? */
op->devices = op->devices->next;
}
if(op->devices == NULL) {
crm_trace("Broadcasting completion of complex fencing op for %s", op->target);
send_cluster_message(NULL, crm_msg_stonith_ng, msg, FALSE);
op->state = st_done;
return rc;
}
} else {
op->state = st_done;
remote_op_done(op, msg, rc);
}
} else if(rc == pcmk_ok && op->devices == NULL) {
crm_trace("All done for %s", op->target);
op->state = st_done;
remote_op_done(op, msg, rc);
return rc;
}
/* Retry on failure or execute the rest of the topology */
crm_trace("Next for %s (rc was %d)", op->target, rc);
call_remote_stonith(op, NULL);
return rc;
}
int stonith_fence_history(xmlNode *msg, xmlNode **output)
{
int rc = 0;
const char *target = NULL;
xmlNode *dev = get_xpath_object("//@"F_STONITH_TARGET, msg, LOG_TRACE);
if(dev) {
int options = 0;
target = crm_element_value(dev, F_STONITH_TARGET);
crm_element_value_int(msg, F_STONITH_CALLOPTS, &options);
if(target && (options & st_opt_cs_nodeid)) {
int nodeid = crm_atoi(target, NULL);
crm_node_t *node = crm_get_peer(nodeid, NULL);
if(node) {
target = node->uname;
}
}
}
*output = create_xml_node(NULL, F_STONITH_HISTORY_LIST);
if (remote_op_list) {
GHashTableIter iter;
remote_fencing_op_t *op = NULL;
g_hash_table_iter_init(&iter, remote_op_list);
while(g_hash_table_iter_next(&iter, NULL, (void**)&op)) {
xmlNode *entry = NULL;
if (target && strcmp(op->target, target) != 0) {
continue;
}
rc = 0;
entry = create_xml_node(*output, STONITH_OP_EXEC);
crm_xml_add(entry, F_STONITH_TARGET, op->target);
crm_xml_add(entry, F_STONITH_ACTION, op->action);
crm_xml_add(entry, F_STONITH_ORIGIN, op->originator);
crm_xml_add(entry, F_STONITH_DELEGATE, op->delegate);
crm_xml_add_int(entry, F_STONITH_DATE, op->completed);
crm_xml_add_int(entry, F_STONITH_STATE, op->state);
}
}
return rc;
}
diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h
index e998e86bc5..445b2a5f1c 100644
--- a/include/crm/fencing/internal.h
+++ b/include/crm/fencing/internal.h
@@ -1,130 +1,133 @@
/*
* Copyright (C) 2011 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef STONITH_NG_INTERNAL__H
# define STONITH_NG_INTERNAL__H
# include <crm/common/ipc.h>
# include <crm/common/xml.h>
typedef struct async_command_s {
int id;
int pid;
int fd_stdout;
int options;
int default_timeout;
int timeout;
char *op;
char *origin;
char *client;
char *client_name;
char *remote;
char *victim;
char *action;
char *device;
char *mode;
GListPtr device_list;
GListPtr device_next;
void (*done)(GPid pid, gint status, gpointer user_data);
guint timer_sigterm;
guint timer_sigkill;
} async_command_t;
int run_stonith_agent(const char *agent, const char *action, const char *victim,
GHashTable * dev_hash, GHashTable * port_map, int *agent_result,
char **output, async_command_t * track);
gboolean is_redhat_agent(const char *agent);
xmlNode *create_level_registration_xml(const char *node, int level,
stonith_key_value_t * device_list);
xmlNode *create_device_registration_xml(const char *id, const char *namespace, const char *agent,
stonith_key_value_t * params);
#define ST_LEVEL_MAX 10
#define F_STONITH_CLIENTID "st_clientid"
#define F_STONITH_CALLOPTS "st_callopt"
#define F_STONITH_CALLID "st_callid"
#define F_STONITH_CALLDATA "st_calldata"
#define F_STONITH_OPERATION "st_op"
#define F_STONITH_TARGET "st_target"
#define F_STONITH_REMOTE "st_remote_op"
#define F_STONITH_RC "st_rc"
/*! Timeout period per a device execution */
#define F_STONITH_TIMEOUT "st_timeout"
/*! Action specific timeout period returned in query of fencing devices. */
#define F_STONITH_ACTION_TIMEOUT "st_action_timeout"
#define F_STONITH_CALLBACK_TOKEN "st_async_id"
#define F_STONITH_CLIENTNAME "st_clientname"
#define F_STONITH_CLIENTNODE "st_clientnode"
#define F_STONITH_NOTIFY_TYPE "st_notify_type"
#define F_STONITH_NOTIFY_ACTIVATE "st_notify_activate"
#define F_STONITH_NOTIFY_DEACTIVATE "st_notify_deactivate"
#define F_STONITH_DELEGATE "st_delegate"
+/*! The node initiating the stonith operation. If an operation
+ * is relayed, this is the last node the operation lands on. When
+ * in standalone mode, origin is the client's id that originated the
+ * operation. */
#define F_STONITH_ORIGIN "st_origin"
#define F_STONITH_HISTORY_LIST "st_history"
#define F_STONITH_DATE "st_date"
#define F_STONITH_STATE "st_state"
#define F_STONITH_LEVEL "st_level"
-#define F_STONITH_OWNER "st_owner"
#define F_STONITH_ACTIVE "st_active"
#define F_STONITH_DEVICE "st_device_id"
#define F_STONITH_ACTION "st_device_action"
#define F_STONITH_MODE "st_mode"
#define T_STONITH_NG "stonith-ng"
#define T_STONITH_REPLY "st-reply"
/*! For async operations, an event from the server containing
* the total amount of time the server is allowing for the operation
* to take place is returned to the client. */
#define T_STONITH_TIMEOUT_VALUE "st-async-timeout-value"
#define T_STONITH_NOTIFY "st_notify"
#define STONITH_ATTR_ARGMAP "pcmk_arg_map"
#define STONITH_ATTR_HOSTARG "pcmk_host_argument"
#define STONITH_ATTR_HOSTMAP "pcmk_host_map"
#define STONITH_ATTR_HOSTLIST "pcmk_host_list"
#define STONITH_ATTR_HOSTCHECK "pcmk_host_check"
#define STONITH_ATTR_ACTION_OP "action"
#define STONITH_OP_EXEC "st_execute"
#define STONITH_OP_TIMEOUT_UPDATE "st_timeout_update"
#define STONITH_OP_QUERY "st_query"
#define STONITH_OP_FENCE "st_fence"
#define STONITH_OP_RELAY "st_relay"
#define STONITH_OP_CONFIRM "st_confirm"
#define STONITH_OP_DEVICE_ADD "st_device_register"
#define STONITH_OP_DEVICE_DEL "st_device_remove"
#define STONITH_OP_DEVICE_METADATA "st_device_metadata"
#define STONITH_OP_FENCE_HISTORY "st_fence_history"
#define STONITH_OP_LEVEL_ADD "st_level_add"
#define STONITH_OP_LEVEL_DEL "st_level_remove"
#define stonith_channel "st_command"
#define stonith_channel_callback "st_callback"
#endif
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Apr 21, 12:33 PM (1 d, 6 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1661296
Default Alt Text
(79 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment