fence-virt/server/pm-fence.c
Lon Hohberger 71ee23d555 Clean up build and comments.
Signed-off-by: Lon Hohberger <lon@users.sourceforge.net>
2012-02-07 10:47:12 -05:00

695 lines
15 KiB
C

/*
Copyright Red Hat, Inc. 2006
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, or (at your option) any
later version.
This program 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 program; see the file COPYING. If not, write to the
Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
MA 02139, USA.
*/
#include <stdio.h>
#include <netdb.h>
#include <errno.h>
#include <syslog.h>
#include <simpleconfig.h>
#include <static_map.h>
#include <server_plugin.h>
#include <crm/cib.h>
#include <crm/pengine/status.h>
/* Local includes */
#include "xvm.h"
#include "debug.h"
#define BACKEND_NAME "pm-fence"
#define VERSION "0.1"
#define MAGIC 0x1e0d197a
#define ATTR_NAME_PREFIX "force_stop-"
#define ATTR_VALUE "true"
#define READ_CIB_RETRY 30
enum rsc_status {
RS_STARTED = 1,
RS_STOPPED,
RS_UNDEFINED,
RS_GETERROR
};
struct pf_info {
int magic;
cib_t *cib;
unsigned int loglevel;
};
cib_t **cib = NULL;
pe_working_set_t data_set;
#define VALIDATE(arg) \
do { \
if (!arg || ((struct pf_info *)arg)->magic != MAGIC) { \
errno = EINVAL; \
return -1; \
} \
} while(0)
static void
free_dataset(void)
{
dbg_printf(5, "%s\n", __FUNCTION__);
if (!data_set.input)
return;
free_xml(data_set.input);
data_set.input = NULL;
cleanup_calculations(&data_set);
memset(&data_set, 0, sizeof(pe_working_set_t));
}
static void
disconnect_cib(void)
{
dbg_printf(5, "%s\n", __FUNCTION__);
if (*cib) {
(*cib)->cmds->signoff(*cib);
cib_delete(*cib);
*cib = NULL;
}
free_dataset();
}
static gboolean
connect_cib(void)
{
enum cib_errors rc = cib_ok;
int i;
dbg_printf(5, "%s\n", __FUNCTION__);
if (*cib)
return TRUE;
memset(&data_set, 0, sizeof(pe_working_set_t));
*cib = cib_new();
if (!*cib) {
syslog(LOG_NOTICE, "cib connection initialization failed\n");
printf("cib connection initialization failed\n");
return FALSE;
}
for (i = 1; i <= 20; i++) {
if (i) sleep(1);
dbg_printf(4, "%s: connect to cib attempt: %d\n", __FUNCTION__, i);
rc = (*cib)->cmds->signon(*cib, crm_system_name, cib_command);
if (rc == cib_ok)
break;
}
if (rc != cib_ok) {
syslog(LOG_NOTICE,
"failed to signon to cib: %s\n", cib_error2string(rc));
printf("failed to signon to cib: %s\n", cib_error2string(rc));
disconnect_cib();
return FALSE;
}
dbg_printf(3, "%s: succeed at connect to cib\n", __FUNCTION__);
return TRUE;
}
static gboolean
get_dataset(void)
{
xmlNode *current_cib;
unsigned int loglevel;
dbg_printf(5, "%s\n", __FUNCTION__);
free_dataset();
current_cib = get_cib_copy(*cib);
if (!current_cib)
return FALSE;
set_working_set_defaults(&data_set);
data_set.input = current_cib;
data_set.now = new_ha_date(TRUE);
/* log output of the level below LOG_ERR is deterred */
loglevel = get_crm_log_level();
set_crm_log_level(LOG_ERR);
cluster_status(&data_set);
set_crm_log_level(loglevel);
return TRUE;
}
static enum rsc_status
get_rsc_status(const char *rid, char **node, char **uuid)
{
GListPtr gIter = NULL, gIter2 = NULL;
resource_t *rsc;
dbg_printf(5, "%s: Resource %s\n", __FUNCTION__, rid);
if (!rid || connect_cib() == FALSE)
return RS_GETERROR;
if (get_dataset() == FALSE) {
disconnect_cib();
if (connect_cib() == FALSE || get_dataset() == FALSE)
return RS_GETERROR;
}
/* find out from RUNNING resources */
gIter = data_set.nodes;
for(; gIter; gIter = gIter->next) {
node_t *node2 = (node_t*)gIter->data;
gIter2 = node2->details->running_rsc;
for(; gIter2; gIter2 = gIter2->next) {
resource_t *rsc2 = (resource_t*)gIter2->data;
dbg_printf(3, "%s: started resource [%s]\n",
__FUNCTION__, rsc2->id);
if (safe_str_eq(rid, rsc2->id)) {
if (node && !*node) {
*node = crm_strdup(node2->details->uname);
*uuid = crm_strdup(node2->details->id);
dbg_printf(3, "%s: started node [%s(%s)]\n",
__FUNCTION__, *node, *uuid);
}
return RS_STARTED;
}
}
}
/* find out from ALL resources */
rsc = pe_find_resource(data_set.resources, rid);
if (rsc) {
dbg_printf(3, "%s: stopped resource [%s]\n", __FUNCTION__, rsc->id);
return RS_STOPPED;
}
return RS_UNDEFINED;
}
/*
* The cluster node attribute is updated for RA which controls a virtual machine.
*/
static gboolean
update_status_attr(char cmd, const char *rid,
const char *node, const char *uuid, gboolean confirm)
{
char *name = g_strdup_printf("%s%s", ATTR_NAME_PREFIX, rid);
char *value;
gboolean ret = FALSE;
dbg_printf(5, "%s\n", __FUNCTION__);
switch (cmd) {
case 'U':
value = ATTR_VALUE;
break;
case 'D':
value = NULL;
break;
default:
goto out;
}
dbg_printf(1, "%s: Update attribute %s=%s for %s\n",
__FUNCTION__, name, value, node);
ret = attrd_lazy_update(cmd, node,
name, value, XML_CIB_TAG_STATUS, NULL, NULL);
if (confirm == FALSE)
goto out;
if (ret == TRUE) {
enum cib_errors rc;
int i;
ret = FALSE; value = NULL;
for (i = 1; i <= READ_CIB_RETRY; i++) {
dbg_printf(4, "%s: waiting..[%d]\n", __FUNCTION__, i);
sleep(1);
#ifdef PM_1_0
rc = read_attr(*cib, XML_CIB_TAG_STATUS,
uuid, NULL, NULL, name, &value, FALSE);
#else
rc = read_attr(*cib, XML_CIB_TAG_STATUS,
uuid, NULL, NULL, NULL, name, &value, FALSE);
#endif
dbg_printf(3, "%s: cmd=%c, rc=%d, value=%s\n",
__FUNCTION__, cmd, rc, value);
if (rc == cib_ok) {
if (cmd == 'U' && !g_strcmp0(value, ATTR_VALUE)) {
ret = TRUE;
break;
}
} else if (rc == cib_NOTEXISTS) {
if (cmd == 'D') {
ret = TRUE;
break;
}
} else {
break;
}
crm_free(value);
}
crm_free(value);
}
out:
crm_free(name);
return ret;
}
/*
* ref. pacemaker/tools/crm_resource.c
*/
static enum cib_errors
find_meta_attr(const char *rid, const char *name, char **id)
{
char *xpath;
xmlNode *xml = NULL;
const char *p;
enum cib_errors rc;
dbg_printf(5, "%s\n", __FUNCTION__);
xpath = g_strdup_printf("%s/*[@id=\"%s\"]/%s/nvpair[@name=\"%s\"]",
get_object_path("resources"), rid, XML_TAG_META_SETS, name);
dbg_printf(3, "%s: query=%s\n", __FUNCTION__, xpath);
rc = (*cib)->cmds->query(*cib, xpath, &xml,
cib_sync_call|cib_scope_local|cib_xpath);
if (rc != cib_ok) {
if (rc != cib_NOTEXISTS) {
syslog(LOG_NOTICE, "failed to query to cib: %s\n",
cib_error2string(rc));
printf("failed to query to cib: %s\n",
cib_error2string(rc));
}
crm_free(xpath);
return rc;
}
crm_log_xml_debug(xml, "Match");
p = crm_element_value(xml, XML_ATTR_ID);
if (p)
*id = crm_strdup(p);
crm_free(xpath);
free_xml(xml);
return rc;
}
/*
* ref. pacemaker/tools/crm_resource.c
*/
static gboolean
set_rsc_role(const char *rid, const char *value)
{
resource_t *rsc;
char *id = NULL;
xmlNode *top = NULL, *obj = NULL;
enum cib_errors rc;
const char *name = XML_RSC_ATTR_TARGET_ROLE;
dbg_printf(5, "%s\n", __FUNCTION__);
rsc = pe_find_resource(data_set.resources, rid);
if (!rsc)
return FALSE;
rc = find_meta_attr(rid, name, &id);
if (rc == cib_ok) {
dbg_printf(3, "%s: Found a match for name=%s: id=%s\n",
__FUNCTION__, name, id);
} else if (rc == cib_NOTEXISTS) {
char *set;
set = crm_concat(rid, XML_TAG_META_SETS, '-');
id = crm_concat(set, name, '-');
top = create_xml_node(NULL, crm_element_name(rsc->xml));
crm_xml_add(top, XML_ATTR_ID, rid);
obj = create_xml_node(top, XML_TAG_META_SETS);
crm_xml_add(obj, XML_ATTR_ID, set);
crm_free(set);
} else {
return FALSE;
}
obj = create_xml_node(obj, XML_CIB_TAG_NVPAIR);
if (!top)
top = obj;
crm_xml_add(obj, XML_ATTR_ID, id);
crm_xml_add(obj, XML_NVPAIR_ATTR_NAME, name);
crm_xml_add(obj, XML_NVPAIR_ATTR_VALUE, value);
dbg_printf(1, "%s: Update meta-attr %s=%s for %s\n",
__FUNCTION__, name, value, rid);
crm_log_xml_debug(top, "Update");
rc = (*cib)->cmds->modify(*cib, XML_CIB_TAG_RESOURCES, top, cib_sync_call);
if (rc != cib_ok) {
syslog(LOG_NOTICE,
"failed to modify to cib: %s\n", cib_error2string(rc));
printf("failed to modify to cib: %s\n", cib_error2string(rc));
}
free_xml(top);
crm_free(id);
return rc == cib_ok ? TRUE : FALSE;
}
static gboolean
start_resource(const char *rid)
{
gboolean updated_cib = FALSE;
int i = 0;
dbg_printf(5, "%s\n", __FUNCTION__);
if (!rid)
return FALSE;
printf("Starting domain %s(resource)\n", rid);
check:
if (i >= READ_CIB_RETRY)
return FALSE;
switch (get_rsc_status(rid, NULL, NULL)) {
case RS_STARTED:
dbg_printf(2, "%s: Resource %s started\n", __FUNCTION__, rid);
return TRUE;
case RS_STOPPED:
if (updated_cib == FALSE) {
if (set_rsc_role(rid, RSC_ROLE_STARTED_S) == FALSE)
return FALSE;
updated_cib = TRUE;
} else {
i++;
}
dbg_printf(4, "%s: waiting..[%d]\n", __FUNCTION__, i);
sleep(1);
goto check;
default:
return FALSE;
}
}
static gboolean
stop_resource(const char *rid)
{
char *node = NULL, *uuid = NULL;
gboolean updated_cib = FALSE;
gboolean ret = FALSE;
int i = 0;
dbg_printf(5, "%s\n", __FUNCTION__);
if (!rid)
return FALSE;
printf("Destroying domain %s(resource)\n", rid);
check:
if (i >= READ_CIB_RETRY)
goto rollback;
switch (get_rsc_status(rid, &node, &uuid)) {
case RS_STARTED:
if (updated_cib == FALSE) {
if (update_status_attr('U', rid, node, uuid, TRUE) == FALSE)
goto out;
if (set_rsc_role(rid, RSC_ROLE_STOPPED_S) == FALSE)
goto rollback;
updated_cib = TRUE;
} else {
i++;
}
dbg_printf(4, "%s: waiting..[%d]\n", __FUNCTION__, i);
sleep(1);
goto check;
case RS_STOPPED:
dbg_printf(2, "%s: Resource %s stopped\n", __FUNCTION__, rid);
if (updated_cib == FALSE)
ret = TRUE;
else
ret = update_status_attr('D', rid, node, uuid, TRUE);
goto out;
default:
goto out;
}
rollback:
update_status_attr('D', rid, node, uuid, FALSE);
out:
if (node) crm_free(node);
if (uuid) crm_free(uuid);
return ret;
}
static int
char2level(const char *str)
{
dbg_printf(5, "%s\n", __FUNCTION__);
if (!str)
return 0;
if (safe_str_eq(str, "emerg")) return LOG_EMERG;
else if (safe_str_eq(str, "alert")) return LOG_ALERT;
else if (safe_str_eq(str, "crit")) return LOG_CRIT;
else if (safe_str_eq(str, "err") ||
safe_str_eq(str, "error")) return LOG_ERR;
else if (safe_str_eq(str, "warning") ||
safe_str_eq(str, "warn")) return LOG_WARNING;
else if (safe_str_eq(str, "notice")) return LOG_NOTICE;
else if (safe_str_eq(str, "info")) return LOG_INFO;
else if (safe_str_eq(str, "debug")) return LOG_DEBUG;
else if (safe_str_eq(str, "debug2")) return LOG_DEBUG + 1;
else if (safe_str_eq(str, "debug3")) return LOG_DEBUG + 2;
else if (safe_str_eq(str, "debug4")) return LOG_DEBUG + 3;
else if (safe_str_eq(str, "debug5")) return LOG_DEBUG + 4;
else if (safe_str_eq(str, "debug6")) return LOG_DEBUG + 5;
return 0;
}
static void
reset_lib_log(unsigned int level)
{
dbg_printf(5, "%s\n", __FUNCTION__);
cl_log_set_entity(BACKEND_NAME);
set_crm_log_level(level);
}
static int
pf_null(const char *rid, void *priv)
{
dbg_printf(5, "%s: Resource %s\n", __FUNCTION__, rid);
printf("NULL operation: returning failure\n");
return 1;
}
static int
pf_off(const char *rid, const char *src, uint32_t seqno, void *priv)
{
struct pf_info *info = (struct pf_info *)priv;
int ret;
dbg_printf(5, "%s: Resource %s\n", __FUNCTION__, rid);
VALIDATE(info);
reset_lib_log(info->loglevel);
cib = &info->cib;
ret = stop_resource(rid) == TRUE ? 0 : 1;
free_dataset();
return ret;
}
static int
pf_on(const char *rid, const char *src, uint32_t seqno, void *priv)
{
struct pf_info *info = (struct pf_info *)priv;
int ret;
dbg_printf(5, "%s: Resource %s\n", __FUNCTION__, rid);
VALIDATE(info);
reset_lib_log(info->loglevel);
cib = &info->cib;
ret = start_resource(rid) == TRUE ? 0 : 1;
free_dataset();
return ret;
}
static int
pf_devstatus(void *priv)
{
dbg_printf(5, "%s\n", __FUNCTION__);
if (priv)
return 0;
return 1;
}
static int
pf_status(const char *rid, void *priv)
{
struct pf_info *info = (struct pf_info *)priv;
enum rsc_status rstat;
dbg_printf(5, "%s: Resource %s\n", __FUNCTION__, rid);
VALIDATE(info);
reset_lib_log(info->loglevel);
cib = &info->cib;
rstat = get_rsc_status(rid, NULL, NULL);
dbg_printf(3, "%s: get_rsc_status [%d]\n", __FUNCTION__, rstat);
free_dataset();
switch (rstat) {
case RS_STARTED:
return RESP_SUCCESS;
case RS_STOPPED:
return RESP_OFF;
case RS_UNDEFINED:
case RS_GETERROR:
default:
return RESP_FAIL;
}
}
static int
pf_reboot(const char *rid, const char *src, uint32_t seqno, void *priv)
{
struct pf_info *info = (struct pf_info *)priv;
int ret = 1;
dbg_printf(5, "%s: Resource %s\n", __FUNCTION__, rid);
VALIDATE(info);
reset_lib_log(info->loglevel);
cib = &info->cib;
if (stop_resource(rid) == TRUE)
ret = start_resource(rid) == TRUE ? 0 : ret;
free_dataset();
return ret;
}
/*
* Not implemented, because it is not called from the STONITH plug-in.
*/
static int
pf_hostlist(hostlist_callback callback, void *arg, void *priv)
{
struct pf_info *info = (struct pf_info *)priv;
dbg_printf(5, "%s\n", __FUNCTION__);
VALIDATE(info);
return 1;
}
static int
pf_init(backend_context_t *c, config_object_t *conf)
{
struct pf_info *info = NULL;
int level = 0;
char value[256];
char key[32];
dbg_printf(5, "%s\n", __FUNCTION__);
#ifdef _MODULE
if (sc_get(conf, "fence_virtd/@debug", value, sizeof(value)) == 0)
dset(atoi(value));
#endif
sprintf(key, "backends/%s/@pmlib_loglevel", BACKEND_NAME);
if (sc_get(conf, key, value, sizeof(value)) == 0) {
level = char2level(value);
crm_log_init(BACKEND_NAME, level, FALSE, FALSE, 0, NULL);
cl_log_enable_stdout(TRUE);
}
info = malloc(sizeof(*info));
if (!info)
return -1;
memset(info, 0, sizeof(*info));
info->magic = MAGIC;
info->loglevel = level;
*c = (void *)info;
return 0;
}
static int
pf_shutdown(backend_context_t c)
{
struct pf_info *info = (struct pf_info *)c;
dbg_printf(5, "%s\n", __FUNCTION__);
VALIDATE(info);
reset_lib_log(info->loglevel);
cib = &info->cib;
disconnect_cib();
free(info);
return 0;
}
static fence_callbacks_t pf_callbacks = {
.null = pf_null,
.off = pf_off,
.on = pf_on,
.reboot = pf_reboot,
.status = pf_status,
.devstatus = pf_devstatus,
.hostlist = pf_hostlist
};
static backend_plugin_t pf_plugin = {
.name = BACKEND_NAME,
.version = VERSION,
.callbacks = &pf_callbacks,
.init = pf_init,
.cleanup = pf_shutdown,
};
#ifdef _MODULE
double
BACKEND_VER_SYM(void)
{
return PLUGIN_VERSION_BACKEND;
}
const backend_plugin_t *
BACKEND_INFO_SYM(void)
{
return &pf_plugin;
}
#else
static void __attribute__((constructor))
pf_register_plugin(void)
{
plugin_reg_backend(&pf_plugin);
}
#endif