diff --git a/crm/admin/crm_attribute.c b/crm/admin/crm_attribute.c new file mode 100644 index 0000000000..a8a47095db --- /dev/null +++ b/crm/admin/crm_attribute.c @@ -0,0 +1,389 @@ +/* $Id: crm_attribute.c,v 1.1 2005/09/12 11:04:22 andrew Exp $ */ + +/* + * Copyright (C) 2004 Andrew Beekhof + * + * 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.1 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#ifdef HAVE_GETOPT_H +# include +#endif +#include +void usage(const char *cmd, int exit_status); + +gboolean BE_QUIET = FALSE; +gboolean DO_WRITE = TRUE; +gboolean DO_DELETE = FALSE; +char *dest_node = NULL; +const char *type = NULL; +const char *dest_uname = NULL; +char *set_name = NULL; +char *attr_id = NULL; +char *attr_name = NULL; +const char *attr_value = NULL; +const char *crm_system_name = "crm_master"; + +#define OPTARGS "V?GDQU:u:s:n:v:l:t:i:" + +int +main(int argc, char **argv) +{ + gboolean is_done = FALSE; + cib_t * the_cib = NULL; + enum cib_errors rc = cib_ok; + + int argerr = 0; + int flag; + +#ifdef HAVE_GETOPT_H + int option_index = 0; + static struct option long_options[] = { + /* Top-level Options */ + {"verbose", 0, 0, 'V'}, + {"help", 0, 0, '?'}, + {"quiet", 0, 0, 'Q'}, + {"get-value", 0, 0, 'G'}, + {"delete-attr", 0, 0, 'D'}, + {"node-uname", 1, 0, 'U'}, + {"node-uuid", 1, 0, 'u'}, + {"set-name", 1, 0, 's'}, + {"attr-name", 1, 0, 'n'}, + {"attr-value", 1, 0, 'v'}, + {"lifetime", 1, 0, 'l'}, + {"type", 1, 0, 't'}, + + {0, 0, 0, 0} + }; +#endif + + crm_system_name = basename(argv[0]); + crm_log_init(crm_system_name); + crm_log_level = LOG_ERR; + cl_log_enable_stderr(TRUE); + + if(argc < 2) { + usage(crm_system_name, LSB_EXIT_EINVAL); + } + + while (1) { +#ifdef HAVE_GETOPT_H + flag = getopt_long(argc, argv, OPTARGS, + long_options, &option_index); +#else + flag = getopt(argc, argv, OPTARGS); +#endif + if (flag == -1) + break; + + switch(flag) { + case 'V': + cl_log_enable_stderr(TRUE); + alter_debug(DEBUG_INC); + break; + case '?': + usage(crm_system_name, LSB_EXIT_OK); + break; + case 'G': + DO_WRITE = FALSE; + break; + case 'Q': + BE_QUIET = TRUE; + break; + case 'D': + DO_DELETE = TRUE; + break; + case 'U': + crm_debug_2("Option %c => %s", flag, optarg); + dest_uname = optarg; + break; + case 'u': + crm_debug_2("Option %c => %s", flag, optarg); + dest_node = crm_strdup(optarg); + break; + case 's': + crm_debug_2("Option %c => %s", flag, optarg); + set_name = crm_strdup(optarg); + break; + case 'l': + crm_debug_2("Option %c => %s", flag, optarg); + type = optarg; + break; + case 't': + crm_debug_2("Option %c => %s", flag, optarg); + type = optarg; + break; + case 'n': + crm_debug_2("Option %c => %s", flag, optarg); + attr_name = crm_strdup(optarg); + if(attr_id == NULL) { + attr_id = crm_strdup(optarg); + } + break; + case 'i': + crm_debug_2("Option %c => %s", flag, optarg); + attr_id = crm_strdup(optarg); + break; + case 'v': + crm_debug_2("Option %c => %s", flag, optarg); + attr_value = optarg; + break; + default: + printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag); + ++argerr; + break; + } + } + + if (optind < argc) { + printf("non-option ARGV-elements: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + } + + if (optind > argc) { + ++argerr; + } + + if (argerr) { + usage(crm_system_name, LSB_EXIT_GENERIC); + } + + the_cib = cib_new(); + rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command); + + if(rc != cib_ok) { + crm_err("Error signing on to the CIB service: %s", + cib_error2string(rc)); + return rc; + } + + if(dest_node == NULL && dest_uname != NULL) { + rc = query_node_uuid(the_cib, dest_uname, &dest_node); + if(rc != cib_ok) { + crm_err("Could not map uname=%s to a UUID: %s", + dest_uname, cib_error2string(rc)); + return rc; + } else { + crm_info("Mapped %s to %s", dest_uname, crm_str(dest_node)); + } + } + + if(safe_str_eq(crm_system_name, "crm_master")) { + int len = 0; + char *rsc = NULL; + + dest_node = getenv("OCF_RESKEY_target_uuid"); + if(dest_node == NULL) { + crm_err("Please specify a value for -U or -u"); + return 1; + } + if(safe_str_eq(type, "reboot")) { + type = XML_CIB_TAG_STATUS; + } else { + type = XML_CIB_TAG_NODES; + } + rsc = getenv("OCF_RESOURCE_INSTANCE"); + + CRM_DEV_ASSERT(rsc != NULL); + CRM_DEV_ASSERT(dest_node != NULL); + + len = 8 + strlen(rsc); + crm_malloc0(attr_name, len); + sprintf(attr_name, "master-%s", rsc); + + len = 2 + strlen(attr_name) + strlen(dest_node); + crm_malloc0(attr_id, len); + sprintf(attr_id, "%s-%s", attr_name, dest_node); + + len = 8 + strlen(dest_node); + crm_malloc0(set_name, len); + sprintf(set_name, "master-%s", dest_node); + + } else if(safe_str_eq(crm_system_name, "crm_standby")) { + if(dest_node == NULL) { + crm_err("Please specify a value for -U or -u"); + fprintf(stderr,"Please specify a value for -U or -u\n"); + return 1; + + } else if(DO_DELETE) { + rc = delete_standby( + the_cib, dest_node, type, attr_value); + + } else if(DO_WRITE) { + rc = set_standby(the_cib, dest_node, type, attr_value); + + } else { + char *read_value = NULL; + rc = query_standby( + the_cib, dest_node, type, &read_value); + + if(BE_QUIET == FALSE) { + fprintf(stdout, "%s%s %s%s value=%s\n", + attr_id?"id=":"", attr_id?attr_id:"", + attr_name?"name=":"", attr_name?attr_name:"", + read_value?read_value:"(null)"); + + } else if(read_value != NULL) { + fprintf(stdout, "%s\n", read_value); + } + } + is_done = TRUE; + + } else if(type == NULL && dest_node == NULL) { + type = XML_CIB_TAG_CRMCONFIG; + + } else if (type == NULL) { + crm_err("Please specify a value for -t"); + fprintf(stderr,"Please specify a value for -t\n"); + return 1; + } + + if(is_done) { + + } else if(DO_DELETE) { + rc = delete_attr(the_cib, type, dest_node, set_name, + attr_id, attr_name, attr_value); + + } else if(DO_WRITE) { + CRM_DEV_ASSERT(type != NULL); + CRM_DEV_ASSERT(attr_name != NULL); + CRM_DEV_ASSERT(attr_value != NULL); + + rc = update_attr(the_cib, type, dest_node, set_name, + attr_id, attr_name, attr_value); + + } else { + char *read_value = NULL; + rc = read_attr(the_cib, type, dest_node, set_name, + attr_id, attr_name, &read_value); + crm_info("Read %s=%s %s%s", + attr_name, crm_str(read_value), + set_name?"in ":"", set_name?set_name:""); + + if(BE_QUIET == FALSE) { + fprintf(stdout, "%s%s %s%s value=%s\n", + attr_id?"id=":"", attr_id?attr_id:"", + attr_name?"name=":"", attr_name?attr_name:"", + read_value?read_value:"(null)"); + + } else if(read_value != NULL) { + fprintf(stdout, "%s\n", read_value); + } + } + the_cib->cmds->signoff(the_cib); + if(rc != cib_ok) { + crm_err("Error performing operation: %s", + cib_error2string(rc)); + } + return rc; +} + + +void +usage(const char *cmd, int exit_status) +{ + FILE *stream; + + stream = exit_status ? stderr : stdout; + if(safe_str_eq(cmd, "crm_master")) { + fprintf(stream, "usage: %s [-?VQ] -(D|G|v) [-l]\n", cmd); + + } else if(safe_str_eq(cmd, "crm_standby")) { + fprintf(stream, "usage: %s [-?V] -(u|U) -(D|G|v) [-l]\n", cmd); + + } else { + fprintf(stream, "usage: %s [-?V] -(D|G|v) [options]\n", cmd); + } + + fprintf(stream, "Options\n"); + fprintf(stream, "\t--%s (-%c)\t: this help message\n", "help", '?'); + fprintf(stream, "\t--%s (-%c)\t: " + "turn on debug info. additional instances increase verbosity\n", + "verbose", 'V'); + fprintf(stream, "\t--%s (-%c)\t: Print only the value on stdout" + " (use with -G)\n", "quiet", 'Q'); + fprintf(stream, "\t--%s (-%c)\t: " + "Retrieve rather than set the attribute\n", "get-value", 'G'); + fprintf(stream, "\t--%s (-%c)\t: " + "Delete rather than set the attribute\n", "delete-attr", 'D'); + fprintf(stream, "\t--%s (-%c) \t: " + "Value to use (ignored with -G)\n", "attr-value", 'v'); + + if(safe_str_eq(cmd, "crm_master")) { + fprintf(stream, "\t--%s (-%c) \t: " + "How long the preference lasts (reboot|forever)\n", + "lifetime", 'l'); + exit(exit_status); + } else if(safe_str_eq(cmd, "crm_standby")) { + fprintf(stream, "\t--%s (-%c) \t: " + "UUID of the node to change\n", "node-uuid", 'u'); + fprintf(stream, "\t--%s (-%c) \t: " + "uname of the node to change\n", "node-uname", 'U'); + fprintf(stream, "\t--%s (-%c) \t: " + "How long the preference lasts (reboot|forever)\n" + "\t If a forever value exists, it is ALWAYS used by the CRM\n" + "\t instead of any reboot value\n", "lifetime", 'l'); + exit(exit_status); + } + + fprintf(stream, "\t--%s (-%c) \t: " + "UUID of the node to change\n", "node-uuid", 'u'); + fprintf(stream, "\t--%s (-%c) \t: " + "uname of the node to change\n", "node-uname", 'U'); + fprintf(stream, "\t--%s (-%c) \t: " + "Set of attributes in which to read/write the attribute\n", + "set-name", 's'); + fprintf(stream, "\t--%s (-%c) \t: " + "Attribute to set\n", "attr-name", 'n'); + fprintf(stream, "\t--%s (-%c) \t: " + "Which section of the CIB to set the attribute: (%s|%s|%s)\n", + "type", 't', + XML_CIB_TAG_NODES, XML_CIB_TAG_STATUS, XML_CIB_TAG_CRMCONFIG); + fprintf(stream, "\t -t=%s options: -(U|u) -n [-s]\n", XML_CIB_TAG_NODES); + fprintf(stream, "\t -t=%s options: -(U|u) -n [-s]\n", XML_CIB_TAG_STATUS); + fprintf(stream, "\t -t=%s options: -n [-s]\n", XML_CIB_TAG_CRMCONFIG); + fflush(stream); + + exit(exit_status); +} diff --git a/crm/pengine/master.c b/crm/pengine/master.c new file mode 100644 index 0000000000..353ddbd507 --- /dev/null +++ b/crm/pengine/master.c @@ -0,0 +1,417 @@ +/* $Id: master.c,v 1.1 2005/09/12 11:04:22 andrew Exp $ */ +/* + * Copyright (C) 2004 Andrew Beekhof + * + * 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.1 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include +#include +#include + +extern void clone_create_notifications( + resource_t *rsc, action_t *action, action_t *action_complete, + pe_working_set_t *data_set); + +typedef struct clone_variant_data_s +{ + resource_t *self; + + int clone_max; + int clone_max_node; + + int active_clones; + int max_nodes; + + gboolean interleave; + gboolean ordered; + + gboolean notify_confirm; + + GListPtr child_list; /* resource_t* */ + +} clone_variant_data_t; + +#define NO_MASTER_PREFS 0 + +#define get_clone_variant_data(data, rsc) \ + CRM_ASSERT(rsc->variant == pe_master); \ + data = (clone_variant_data_t *)rsc->variant_opaque; + +void master_unpack(resource_t *rsc, pe_working_set_t *data_set) +{ + add_hash_param(rsc->parameters, "stateful", XML_BOOLEAN_TRUE); + clone_unpack(rsc, data_set); +} + +static void +child_promoting_constraints( + clone_variant_data_t *clone_data, enum pe_ordering type, + resource_t *child, resource_t *last, pe_working_set_t *data_set) +{ +/* if(clone_data->ordered */ +/* || clone_data->self->restart_type == pe_restart_restart) { */ +/* type = pe_ordering_manditory; */ +/* } */ + if(child == NULL) { + if(clone_data->ordered && last != NULL) { + crm_debug_4("Ordered version (last node)"); + /* last child promote before promoted started */ + custom_action_order( + last, promote_key(last), NULL, + clone_data->self, promoted_key(clone_data->self), NULL, + type, data_set); + } + + } else if(clone_data->ordered) { + crm_debug_4("Ordered version"); + if(last == NULL) { + /* global promote before first child promote */ + last = clone_data->self; + + } /* else: child/child relative promote */ + + order_start_start(last, child, type); + custom_action_order( + last, promote_key(last), NULL, + child, promote_key(child), NULL, + type, data_set); + + } else { + crm_debug_4("Un-ordered version"); + + /* child promote before global promoted */ + custom_action_order( + child, promote_key(child), NULL, + clone_data->self, promoted_key(clone_data->self), NULL, + type, data_set); + + /* global promote before child promote */ + custom_action_order( + clone_data->self, promote_key(clone_data->self), NULL, + child, promote_key(child), NULL, + type, data_set); + + } +} + +static void +child_demoting_constraints( + clone_variant_data_t *clone_data, enum pe_ordering type, + resource_t *child, resource_t *last, pe_working_set_t *data_set) +{ +/* if(clone_data->ordered */ +/* || clone_data->self->restart_type == pe_restart_restart) { */ +/* type = pe_ordering_manditory; */ +/* } */ + + if(child == NULL) { + if(clone_data->ordered && last != NULL) { + crm_debug_4("Ordered version (last node)"); + /* global demote before first child demote */ + custom_action_order( + clone_data->self, demote_key(clone_data->self), NULL, + last, demote_key(last), NULL, + pe_ordering_manditory, data_set); + } + + } else if(clone_data->ordered && last != NULL) { + crm_debug_4("Ordered version"); + + /* child/child relative demote */ + custom_action_order(child, demote_key(child), NULL, + last, demote_key(last), NULL, + type, data_set); + + } else if(clone_data->ordered) { + crm_debug_4("Ordered version (1st node)"); + /* first child stop before global stopped */ + custom_action_order( + child, demote_key(child), NULL, + clone_data->self, demoted_key(clone_data->self), NULL, + type, data_set); + + } else { + crm_debug_4("Un-ordered version"); + + /* child demote before global demoted */ + custom_action_order( + child, demote_key(child), NULL, + clone_data->self, demoted_key(clone_data->self), NULL, + type, data_set); + + /* global demote before child demote */ + custom_action_order( + clone_data->self, demote_key(clone_data->self), NULL, + child, demote_key(child), NULL, + type, data_set); + } +} + + +static void +master_update_pseudo_status( + resource_t *child, gboolean *demoting, gboolean *promoting) +{ + CRM_ASSERT(demoting != NULL); + CRM_ASSERT(promoting != NULL); + + slist_iter( + action, action_t, child->actions, lpc, + + if(*promoting && *demoting) { + return; + + } else if(action->optional) { + continue; + + } else if(safe_str_eq(CRMD_ACTION_DEMOTE, action->task)) { + *demoting = TRUE; + + } else if(safe_str_eq(CRMD_ACTION_PROMOTE, action->task)) { + *promoting = TRUE; + } + ); + +} + +void master_create_actions(resource_t *rsc, pe_working_set_t *data_set) +{ + int len = 0; + node_t *chosen = NULL; + char *attr_name = NULL; + const char *attr_value = NULL; + + action_t *action = NULL; + action_t *action_complete = NULL; + gboolean any_promoting = FALSE; + gboolean any_demoting = FALSE; + resource_t *last_promote_rsc = NULL; + resource_t *last_demote_rsc = NULL; + const char *master_max_s = + get_rsc_param(rsc, XML_RSC_ATTR_MASTER_MAX); + const char *master_node_max_s = + get_rsc_param(rsc, XML_RSC_ATTR_MASTER_NODEMAX); + + int promoted = 0; + int max_nodes = 0; + int master_max = crm_atoi(master_max_s, "1"); + int master_node_max = crm_atoi(master_node_max_s, "1"); + + clone_variant_data_t *clone_data = NULL; + get_clone_variant_data(clone_data, rsc); + + /* how many can we have? */ + if(master_max > clone_data->max_nodes * clone_data->clone_max_node) { + master_max = clone_data->max_nodes * clone_data->clone_max_node; + crm_info("Limited to %d masters (potential slaves)",master_max); + } + if(master_max > max_nodes * master_node_max) { + master_max = clone_data->max_nodes * master_node_max; + crm_info("Limited to %d masters (available nodes)", master_max); + } + + /* + * assign priority + */ + + slist_iter( + child_rsc, resource_t, clone_data->child_list, lpc, + + CRM_DEV_ASSERT(child_rsc->color != NULL); + chosen = child_rsc->color->details->chosen_node; + + switch(child_rsc->next_role) { + case RSC_ROLE_STARTED: + if(NO_MASTER_PREFS) { + child_rsc->priority = + clone_data->clone_max - lpc; + break; + } + + child_rsc->priority = -1; + + CRM_DEV_ASSERT(chosen != NULL); + + len = 8 + strlen(child_rsc->id); + crm_malloc0(attr_name, len); + sprintf(attr_name, "master-%s", child_rsc->id); + + attr_value = g_hash_table_lookup( + chosen->details->attrs, attr_name); + + crm_err("%s=%s for %s", attr_name, + crm_str(attr_value), + chosen->details->uname); + + if(attr_value != NULL) { + child_rsc->priority = char2score( + attr_value); + } + crm_free(attr_name); + break; + case RSC_ROLE_SLAVE: + child_rsc->priority = -1; + break; + case RSC_ROLE_STOPPED: + child_rsc->priority = -2; + break; + default: + CRM_DEV_ASSERT(FALSE/* unhandled */); + } + ); + + /* sort based on the new "promote" priority */ + clone_data->child_list = g_list_sort( + clone_data->child_list, sort_rsc_priority); + + /* mark the first N as masters */ + slist_iter( + child_rsc, resource_t, clone_data->child_list, lpc, + switch(child_rsc->next_role) { + case RSC_ROLE_STARTED: + if(child_rsc->priority > 0 && master_max > lpc){ + crm_info("Promoting %s", child_rsc->id); + child_rsc->next_role = RSC_ROLE_MASTER; + promoted++; + + } else { + crm_info("Demoting %s", child_rsc->id); + child_rsc->next_role = RSC_ROLE_SLAVE; + } + break; + + case RSC_ROLE_SLAVE: + if(child_rsc->priority < 0 ||master_max <= lpc){ + pe_warn("Cannot promote %s (slave)", + child_rsc->id); + lpc--; + } + break; + + case RSC_ROLE_STOPPED: + if(child_rsc->priority < 0 ||master_max <= lpc){ + pe_warn("Cannot promote %s (stopping)", + child_rsc->id); + lpc--; + } + break; + default: + CRM_DEV_ASSERT(FALSE/* unhandled */); + } + ); + crm_info("Promoted %d (of %d) slaves to master", promoted, master_max); + + /* create actions as normal */ + clone_create_actions(rsc, data_set); + + slist_iter( + child_rsc, resource_t, clone_data->child_list, lpc, + gboolean child_promoting = FALSE; + gboolean child_demoting = FALSE; + + master_update_pseudo_status( + child_rsc, &child_demoting, &child_promoting); + + any_demoting = any_demoting || child_demoting; + any_promoting = any_promoting || child_promoting; + ); + + /* promote */ + action = promote_action(clone_data->self, NULL, !any_promoting); + action_complete = custom_action( + clone_data->self, promoted_key(rsc), + CRMD_ACTION_PROMOTED, NULL, !any_promoting, data_set); + + action->pseudo = TRUE; + action_complete->pseudo = TRUE; + + child_promoting_constraints(clone_data, pe_ordering_optional, + NULL, last_promote_rsc, data_set); + + clone_create_notifications(rsc, action, action_complete, data_set); + + + /* demote */ + action = demote_action(clone_data->self, NULL, !any_demoting); + action_complete = custom_action( + clone_data->self, demoted_key(rsc), + CRMD_ACTION_DEMOTED, NULL, !any_demoting, data_set); + + action->pseudo = TRUE; + action_complete->pseudo = TRUE; + + child_demoting_constraints(clone_data, pe_ordering_optional, + NULL, last_demote_rsc, data_set); + + clone_create_notifications(rsc, action, action_complete, data_set); +} + +void +master_internal_constraints(resource_t *rsc, pe_working_set_t *data_set) +{ + resource_t *last_rsc = NULL; + clone_variant_data_t *clone_data = NULL; + get_clone_variant_data(clone_data, rsc); + + clone_internal_constraints(rsc, data_set); + + /* global demoted before start */ + custom_action_order( + clone_data->self, demoted_key(clone_data->self), NULL, + clone_data->self, start_key(clone_data->self), NULL, + pe_ordering_optional, data_set); + + /* global started before promote */ + custom_action_order( + clone_data->self, started_key(clone_data->self), NULL, + clone_data->self, promote_key(clone_data->self), NULL, + pe_ordering_optional, data_set); + + /* global demoted before stop */ + custom_action_order( + clone_data->self, demoted_key(clone_data->self), NULL, + clone_data->self, stop_key(clone_data->self), NULL, + pe_ordering_optional, data_set); + + /* global demote before demoted */ + custom_action_order( + clone_data->self, demote_key(clone_data->self), NULL, + clone_data->self, demoted_key(clone_data->self), NULL, + pe_ordering_optional, data_set); + + slist_iter( + child_rsc, resource_t, clone_data->child_list, lpc, + + /* child demote before promote */ + custom_action_order( + child_rsc, demote_key(child_rsc), NULL, + child_rsc, promote_key(child_rsc), NULL, + pe_ordering_restart, data_set); + + child_promoting_constraints(clone_data, pe_ordering_optional, + child_rsc, last_rsc, data_set); + + child_demoting_constraints(clone_data, pe_ordering_optional, + child_rsc, last_rsc, data_set); + + last_rsc = child_rsc; + + ); + +} + diff --git a/lib/crm/cib/cib_attrs.c b/lib/crm/cib/cib_attrs.c new file mode 100644 index 0000000000..9d764eb25e --- /dev/null +++ b/lib/crm/cib/cib_attrs.c @@ -0,0 +1,426 @@ +/* $Id: cib_attrs.c,v 1.1 2005/09/12 11:04:22 andrew Exp $ */ + +/* + * Copyright (C) 2004 Andrew Beekhof + * + * 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.1 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + + +enum cib_errors +update_attr(cib_t *the_cib, + const char *section, const char *node_uuid, const char *set_name, + const char *attr_id, const char *attr_name, const char *attr_value) +{ + const char *tag = NULL; + + enum cib_errors rc = cib_ok; + crm_data_t *xml_top = NULL; + crm_data_t *xml_obj = NULL; + crm_data_t *fragment = NULL; + + if(attr_id == NULL) { + attr_id = attr_name; + } + if(attr_name == NULL) { + attr_name = attr_id; + } + + CRM_ASSERT(attr_id != NULL); + CRM_ASSERT(attr_name != NULL); + + if(safe_str_eq(section, XML_CIB_TAG_CRMCONFIG)) { + tag = NULL; + + } else if(safe_str_eq(section, XML_CIB_TAG_NODES)) { + tag = XML_CIB_TAG_NODE; + + } else if(safe_str_eq(section, XML_CIB_TAG_STATUS)) { + tag = XML_CIB_TAG_STATE; + + } else { + return cib_NOSECTION; + } + + crm_debug("Creating %s/%s", section, tag); + if(tag != NULL) { + xml_obj = create_xml_node(NULL, tag); + crm_xml_add(xml_obj, XML_ATTR_ID, node_uuid); + xml_top = xml_obj; + } + + if(set_name != NULL) { + xml_obj = create_xml_node(xml_obj, XML_TAG_ATTR_SETS); + if(xml_top == NULL) { + xml_top = xml_obj; + } + crm_xml_add(xml_obj, XML_ATTR_ID, set_name); + xml_obj = create_xml_node(xml_obj, XML_TAG_ATTRS); + } + xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR); + if(xml_top == NULL) { + xml_top = xml_obj; + } + crm_xml_add(xml_obj, XML_ATTR_ID, attr_id); + crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, attr_name); + crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, attr_value); + + fragment = create_cib_fragment(xml_top, NULL); + crm_log_xml_debug_2(xml_top, "Update"); + crm_log_xml_debug(fragment, "Update Fragment"); + + free_xml(xml_top); + + rc = the_cib->cmds->modify(the_cib, section, fragment, NULL, + cib_sync_call|cib_quorum_override); + if(rc != cib_ok) { + crm_err("Error setting %s=%s (section=%s, set=%s): %s", + attr_name, attr_value, section, crm_str(set_name), + cib_error2string(rc)); + } + + free_xml(fragment); + return rc; +} + +enum cib_errors +read_attr(cib_t *the_cib, + const char *section, const char *node_uuid, const char *set_name, + const char *attr_id, const char *attr_name, char **attr_value) +{ + const char *tag = NULL; + enum cib_errors rc = cib_ok; + crm_data_t *a_node = NULL; + crm_data_t *xml_obj = NULL; + crm_data_t *xml_next = NULL; + crm_data_t *fragment = NULL; + + CRM_ASSERT(attr_value != NULL); + *attr_value = NULL; + + crm_debug("Searching for attribute %s (section=%s, node=%s, set=%s)", + attr_name, section, crm_str(node_uuid), crm_str(set_name)); + + if(safe_str_eq(section, XML_CIB_TAG_CRMCONFIG)) { + tag = NULL; + + } else if(safe_str_eq(section, XML_CIB_TAG_NODES)) { + tag = XML_CIB_TAG_NODE; + + } else if(safe_str_eq(section, XML_CIB_TAG_STATUS)) { + tag = XML_CIB_TAG_STATE; + + } else { + return cib_NOSECTION; + } + + rc = the_cib->cmds->query( + the_cib, section, &fragment, cib_sync_call); + + if(rc != cib_ok) { + crm_err("Query failed for attribute %s (section=%s, node=%s, set=%s): %s", + attr_name, section, crm_str(set_name), crm_str(node_uuid), + cib_error2string(rc)); + return rc; + } + + a_node = find_xml_node(fragment, XML_TAG_CIB, TRUE); + xml_obj = get_object_root(section, a_node); + CRM_ASSERT(xml_obj != NULL); + crm_log_xml_debug_2(xml_obj, "Result section"); + + if(tag != NULL) { + xml_next = find_entity(xml_obj, tag, node_uuid); + if(xml_next == NULL) { + crm_debug("%s=%s not found in %s", tag, node_uuid, + crm_element_name(xml_obj)); + return cib_NOTEXISTS; + } + xml_obj = xml_next; + } + if(set_name != NULL) { + xml_next = find_entity(xml_obj, XML_TAG_ATTR_SETS, set_name); + if(xml_next == NULL) { + crm_debug("%s=%s object not found in %s", + XML_TAG_ATTR_SETS, set_name, + crm_element_name(xml_obj)); + return cib_NOTEXISTS; + } + xml_obj = xml_next; + + xml_next = find_xml_node(xml_obj, XML_TAG_ATTRS, TRUE); + if(xml_next == NULL) { + crm_debug("%s object not found in %s", + XML_TAG_ATTRS, crm_element_name(xml_obj)); + return cib_NOTEXISTS; + } + xml_obj = xml_next; + } + + xml_next = NULL; + xml_child_iter( + xml_obj, a_child, XML_CIB_TAG_NVPAIR, + const char *name = crm_element_value( + a_child, XML_NVPAIR_ATTR_NAME); + + if(attr_id != NULL + && safe_str_neq(attr_id, ID(a_child))) { + continue; + + } else if(attr_name != NULL + && safe_str_neq(attr_name, name)) { + continue; + } + xml_next = a_child; + break; + ); + + if(xml_next == NULL) { + crm_debug("<%s id=%s name=%s/> not found in %s", + XML_CIB_TAG_NVPAIR, attr_id, attr_name, + crm_element_name(xml_obj)); + return cib_NOTEXISTS; + } + xml_obj = xml_next; + + if(crm_element_value(xml_obj, XML_NVPAIR_ATTR_VALUE) != NULL) { + *attr_value = crm_element_value_copy( + xml_obj, XML_NVPAIR_ATTR_VALUE); + } + + free_xml(fragment); + return cib_ok; +} + + +enum cib_errors +delete_attr(cib_t *the_cib, + const char *section, const char *node_uuid, const char *set_name, + const char *attr_id, const char *attr_name, const char *attr_value) +{ + char *tmp = NULL; + enum cib_errors rc = cib_ok; + crm_data_t *xml_obj = NULL; + + rc = read_attr(the_cib, section, node_uuid, set_name, + attr_id, attr_name, &tmp); + + if(rc != cib_ok) { + return rc; + + } else if(attr_value != NULL + && safe_str_neq(attr_value, tmp)) { + crm_free(tmp); + return cib_NOTEXISTS; + } + crm_free(tmp); + + xml_obj = create_xml_node(NULL, XML_CIB_TAG_NVPAIR); + crm_xml_add(xml_obj, XML_ATTR_ID, attr_id); + crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, attr_name); + crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, attr_value); + + rc = the_cib->cmds->delete( + the_cib, section, xml_obj, NULL, + cib_sync_call|cib_quorum_override); + + free_xml(xml_obj); + return rc; +} + +enum cib_errors +query_node_uuid(cib_t *the_cib, const char *uname, char **uuid) +{ + enum cib_errors rc = cib_ok; + crm_data_t *xml_obj = NULL; + crm_data_t *fragment = NULL; + const char *child_name = NULL; + + CRM_ASSERT(uname != NULL); + CRM_ASSERT(uuid != NULL); + + rc = the_cib->cmds->query( + the_cib, XML_CIB_TAG_NODES, &fragment, cib_sync_call); + if(rc != cib_ok) { + return rc; + } + + xml_obj = find_xml_node(fragment, XML_TAG_CIB, TRUE); + xml_obj = get_object_root(XML_CIB_TAG_NODES, xml_obj); + CRM_ASSERT(xml_obj != NULL); + crm_log_xml_debug(xml_obj, "Result section"); + + rc = cib_NOTEXISTS; + *uuid = NULL; + + xml_child_iter( + xml_obj, a_child, XML_CIB_TAG_NODE, + child_name = crm_element_value(a_child, XML_ATTR_UNAME); + + if(safe_str_eq(uname, child_name)) { + child_name = ID(a_child); + if(child_name != NULL) { + *uuid = crm_strdup(child_name); + rc = cib_ok; + } + break; + } + ); + free_xml(fragment); + return rc; +} + +enum cib_errors +query_node_uname(cib_t *the_cib, const char *uuid, char **uname) +{ + enum cib_errors rc = cib_ok; + crm_data_t *xml_obj = NULL; + crm_data_t *fragment = NULL; + const char *child_name = NULL; + + CRM_ASSERT(uname != NULL); + CRM_ASSERT(uuid != NULL); + + rc = the_cib->cmds->query( + the_cib, XML_CIB_TAG_NODES, &fragment, cib_sync_call); + if(rc != cib_ok) { + return rc; + } + + xml_obj = find_xml_node(fragment, XML_TAG_CIB, TRUE); + xml_obj = get_object_root(XML_CIB_TAG_NODES, xml_obj); + CRM_ASSERT(xml_obj != NULL); + crm_log_xml_debug_2(xml_obj, "Result section"); + + rc = cib_NOTEXISTS; + *uname = NULL; + + xml_child_iter( + xml_obj, a_child, XML_CIB_TAG_NODE, + child_name = ID(a_child); + + if(safe_str_eq(uuid, child_name)) { + child_name = crm_element_value(a_child, XML_ATTR_UNAME); + if(child_name != NULL) { + *uname = crm_strdup(child_name); + rc = cib_ok; + } + break; + } + ); + free_xml(fragment); + return rc; +} + +#define standby_common char *attr_id = NULL; \ + char *set_name = NULL; \ + const char *attr_name = "standby"; \ + const char *type = XML_CIB_TAG_NODES; \ + \ + if(safe_str_eq(scope, "reboot") \ + || safe_str_eq(scope, XML_CIB_TAG_STATUS)) { \ + type = XML_CIB_TAG_STATUS; \ + } \ + \ + CRM_DEV_ASSERT(uuid != NULL); \ + CRM_DEV_ASSERT(standby_value != NULL); \ + \ + crm_malloc0(attr_id, 2 + strlen(attr_name) + strlen(uuid)); \ + sprintf(attr_id, "%s-%s", attr_name, uuid); \ + \ + crm_malloc0(set_name, 2 + strlen(attr_name) + strlen(uuid)); \ + sprintf(set_name, "%s-%s", attr_name, uuid); \ + + +enum cib_errors +query_standby(cib_t *the_cib, const char *uuid, const char *scope, + char **standby_value) +{ + enum cib_errors rc = cib_ok; + standby_common; + + if(scope != NULL) { + rc = read_attr(the_cib, type, uuid, set_name, + attr_id, attr_name, standby_value); + } else { + rc = read_attr(the_cib, XML_CIB_TAG_NODES, uuid, set_name, + attr_id, attr_name, standby_value); + if(rc == cib_NOTEXISTS) { + crm_info("No standby value found with lifetime=forever," + " checking lifetime=reboot."); + rc = read_attr(the_cib, XML_CIB_TAG_STATUS, uuid, set_name, + attr_id, attr_name, standby_value); + } + } + + crm_free(attr_id); + crm_free(set_name); + return rc; +} + +enum cib_errors +set_standby(cib_t *the_cib, const char *uuid, const char *scope, + const char *standby_value) +{ + enum cib_errors rc = cib_ok; + standby_common; + + rc = update_attr(the_cib, type, uuid, set_name, + attr_id, attr_name, standby_value); + + crm_free(attr_id); + crm_free(set_name); + + return rc; +} + +enum cib_errors +delete_standby(cib_t *the_cib, const char *uuid, const char *scope, + const char *standby_value) +{ + enum cib_errors rc = cib_ok; + + standby_common; + + rc = delete_attr(the_cib, type, uuid, set_name, + attr_id, attr_name, standby_value); + + crm_free(attr_id); + crm_free(set_name); + + return rc; +} +