diff --git a/lib/crm/pengine/Makefile.am b/lib/crm/pengine/Makefile.am new file mode 100644 index 0000000000..15329128ee --- /dev/null +++ b/lib/crm/pengine/Makefile.am @@ -0,0 +1,88 @@ +# +# 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 +# of the License, 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; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +MAINTAINERCLEANFILES = Makefile.in + + + +INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ + -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \ + -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \ + -I$(top_builddir) -I$(top_srcdir) + +hadir = $(sysconfdir)/ha.d +halibdir = $(libdir)/@HB_PKG@ +commmoddir = $(halibdir)/modules/comm +havarlibdir = $(localstatedir)/lib/@HB_PKG@ +pe_varlibdir = $(HA_VARLIBDIR)/$(HB_PKG)/pengine + +# sockets with path +crmdir = $(havarlibdir)/crm +apigid = @HA_APIGID@ +crmuid = @HA_CCMUID@ + + +COMMONLIBS = $(CRM_DEBUG_LIBS) \ + $(top_builddir)/lib/clplumbing/libplumb.la \ + $(top_builddir)/lib/crm/common/libcrmcommon.la \ + $(GLIBLIB) \ + $(CURSESLIBS) \ + $(LIBRT) + +LIBRT = @LIBRT@ +AM_CFLAGS = @CFLAGS@ $(CRM_DEBUG_FLAGS) + +## libraries +lib_LTLIBRARIES = libpe_rules.la libpe_status.la libpengine.la + +## binary progs +halib_PROGRAMS = ptest + +## SOURCES + +rule_files = rules.c common.c +status_files = status.c unpack.c utils.c complex.c native.c group.c clone.c + +libpe_rules_la_LDFLAGS = -version-info 1:0:0 +libpe_rules_la_SOURCES = $(rule_files) + +libpe_status_la_LDFLAGS = -version-info 1:0:0 +libpe_status_la_SOURCES = $(rule_files) $(status_files) + +libpengine_la_LDFLAGS = -version-info 3:0:0 +# -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version +libpengine_la_SOURCES = pengine.c allocate.c \ + native_allocate.c group_allocate.c \ + clone_allocate.c master.c graph.c + +noinst_HEADERS = utils.h allocate.h pengine.h + +ptest_SOURCES = ptest.c + +ptest_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' +ptest_LDADD = $(COMMONLIBS) \ + libpe_status.la \ + libpengine.la \ + $(top_builddir)/lib/crm/cib/libcib.la \ + $(top_builddir)/lib/crm/transition/libtransitioner.la + +clean-generic: + rm -f *.log *.debug *~ + +install-exec-local: + +uninstall-local: diff --git a/lib/crm/pengine/clone.c b/lib/crm/pengine/clone.c new file mode 100644 index 0000000000..3a18e83ba2 --- /dev/null +++ b/lib/crm/pengine/clone.c @@ -0,0 +1,312 @@ +/* $Id: clone.c,v 1.1 2006/05/31 14:59:12 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 + +void clone_create_notifications( + resource_t *rsc, action_t *action, action_t *action_complete, + pe_working_set_t *data_set); + +extern gboolean rsc_colocation_new( + const char *id, enum con_strength strength, + resource_t *rsc_lh, resource_t *rsc_rh, + const char *state_lh, const char *state_rh); + +typedef struct clone_variant_data_s +{ + resource_t *self; + + int clone_max; + int clone_node_max; + + int active_clones; + int max_nodes; + + gboolean interleave; + gboolean ordered; + + crm_data_t *xml_obj_child; + + gboolean notify_confirm; + + GListPtr child_list; /* resource_t* */ + +} clone_variant_data_t; + +void child_stopping_constraints( + clone_variant_data_t *clone_data, enum pe_ordering type, + resource_t *child, resource_t *last, pe_working_set_t *data_set); + +void child_starting_constraints( + clone_variant_data_t *clone_data, enum pe_ordering type, + resource_t *child, resource_t *last, pe_working_set_t *data_set); + + +#define get_clone_variant_data(data, rsc) \ + CRM_ASSERT(rsc->variant == pe_clone || rsc->variant == pe_master); \ + data = (clone_variant_data_t *)rsc->variant_opaque; + + +static gboolean +create_child_clone(resource_t *rsc, int sub_id, pe_working_set_t *data_set) +{ + char *inc_num = NULL; + char *inc_max = NULL; + resource_t *child_rsc = NULL; + crm_data_t * child_copy = NULL; + clone_variant_data_t *clone_data = NULL; + get_clone_variant_data(clone_data, rsc); + + CRM_CHECK(clone_data->xml_obj_child != NULL, return FALSE); + + inc_num = crm_itoa(sub_id); + inc_max = crm_itoa(clone_data->clone_max); + + child_copy = copy_xml(clone_data->xml_obj_child); + + crm_xml_add(child_copy, XML_RSC_ATTR_INCARNATION, inc_num); + + if(common_unpack(child_copy, &child_rsc, + rsc, data_set) == FALSE) { + pe_err("Failed unpacking resource %s", + crm_element_value(child_copy, XML_ATTR_ID)); + return FALSE; + } +/* child_rsc->parent = clone_data->self; */ + + crm_debug_3("Setting clone attributes for: %s", child_rsc->id); + clone_data->child_list = g_list_append( + clone_data->child_list, child_rsc); + + add_hash_param(child_rsc->meta, XML_RSC_ATTR_INCARNATION_MAX, inc_max); + + print_resource(LOG_DEBUG_3, "Added", child_rsc, FALSE); + + crm_free(inc_num); + crm_free(inc_max); + + return TRUE; +} + +gboolean master_unpack(resource_t *rsc, pe_working_set_t *data_set) +{ + add_hash_param(rsc->parameters, crm_meta_name("stateful"), + XML_BOOLEAN_TRUE); + return clone_unpack(rsc, data_set); +} + +gboolean clone_unpack(resource_t *rsc, pe_working_set_t *data_set) +{ + int lpc = 0; + crm_data_t *xml_tmp = NULL; + crm_data_t *xml_self = NULL; + crm_data_t *xml_obj = rsc->xml; + clone_variant_data_t *clone_data = NULL; + resource_t *self = NULL; + + const char *ordered = g_hash_table_lookup( + rsc->meta, XML_RSC_ATTR_ORDERED); + const char *interleave = g_hash_table_lookup( + rsc->meta, XML_RSC_ATTR_INTERLEAVE); + const char *max_clones = g_hash_table_lookup( + rsc->meta, XML_RSC_ATTR_INCARNATION_MAX); + const char *max_clones_node = g_hash_table_lookup( + rsc->meta, XML_RSC_ATTR_INCARNATION_NODEMAX); + + crm_debug_3("Processing resource %s...", rsc->id); + + crm_malloc0(clone_data, sizeof(clone_variant_data_t)); + rsc->variant_opaque = clone_data; + clone_data->child_list = NULL; + clone_data->interleave = FALSE; + clone_data->ordered = FALSE; + + clone_data->active_clones = 0; + clone_data->xml_obj_child = NULL; + clone_data->clone_node_max = crm_parse_int(max_clones_node,"1"); + + clone_data->clone_max = crm_parse_int(max_clones, "-1"); + if(clone_data->clone_max < 0) { + clone_data->clone_max = g_list_length(data_set->nodes); + } + if(crm_is_true(interleave)) { + clone_data->interleave = TRUE; + } + if(crm_is_true(ordered)) { + clone_data->ordered = TRUE; + } + + clone_data->xml_obj_child = find_xml_node( + xml_obj, XML_CIB_TAG_GROUP, FALSE); + + if(clone_data->xml_obj_child == NULL) { + clone_data->xml_obj_child = find_xml_node( + xml_obj, XML_CIB_TAG_RESOURCE, TRUE); + } + + if(clone_data->xml_obj_child == NULL) { + pe_config_err("%s has nothing to clone", rsc->id); + return FALSE; + } + + xml_self = copy_xml(rsc->xml); + /* this is a bit of a hack - but simplifies everything else */ + ha_msg_mod(xml_self, F_XML_TAGNAME, XML_CIB_TAG_RESOURCE); +/* set_id(xml_self, "self", -1); */ + xml_tmp = find_xml_node(xml_obj, "operations", FALSE); + if(xml_tmp != NULL) { + add_node_copy(xml_self, xml_tmp); + } + + if(common_unpack(xml_self, &self, NULL, data_set)) { + clone_data->self = self; + + } else { + crm_log_xml_err(xml_self, "Couldnt unpack dummy child"); + clone_data->self = self; + return FALSE; + } + + clone_data->notify_confirm = clone_data->self->notify; + + for(lpc = 0; lpc < clone_data->clone_max; lpc++) { + create_child_clone(rsc, lpc, data_set); + } + + crm_debug_3("Added %d children to resource %s...", + clone_data->clone_max, rsc->id); + return TRUE; +} + +resource_t * +clone_find_child(resource_t *rsc, const char *id) +{ + clone_variant_data_t *clone_data = NULL; + get_clone_variant_data(clone_data, rsc); + return pe_find_resource(clone_data->child_list, id); +} + +GListPtr clone_children(resource_t *rsc) +{ + clone_variant_data_t *clone_data = NULL; + get_clone_variant_data(clone_data, rsc); + return clone_data->child_list; +} + +gboolean clone_active(resource_t *rsc, gboolean all) +{ + clone_variant_data_t *clone_data = NULL; + get_clone_variant_data(clone_data, rsc); + + slist_iter( + child_rsc, resource_t, clone_data->child_list, lpc, + gboolean child_active = child_rsc->fns->active(child_rsc, all); + if(all == FALSE && child_active) { + return TRUE; + } else if(all && child_active == FALSE) { + return FALSE; + } + ); + if(all) { + return TRUE; + } else { + return FALSE; + } +} + +void clone_print( + resource_t *rsc, const char *pre_text, long options, void *print_data) +{ + const char *child_text = NULL; + clone_variant_data_t *clone_data = NULL; + get_clone_variant_data(clone_data, rsc); + if(pre_text != NULL) { + child_text = " "; + } else { + child_text = " "; + } + + if(rsc->variant == pe_master) { + status_print("%sMaster/Slave Set: %s", + pre_text?pre_text:"", clone_data->self->id); + + } else { + status_print("%sClone Set: %s", + pre_text?pre_text:"", clone_data->self->id); + } + + if(options & pe_print_html) { + status_print("\n
    \n"); + + } else if((options & pe_print_log) == 0) { + status_print("\n"); + } + + slist_iter( + child_rsc, resource_t, clone_data->child_list, lpc, + + if(options & pe_print_html) { + status_print("
  • \n"); + } + child_rsc->fns->print( + child_rsc, child_text, options, print_data); + if(options & pe_print_html) { + status_print("
  • \n"); + } + ); + + if(options & pe_print_html) { + status_print("
\n"); + } +} + +void clone_free(resource_t *rsc) +{ + clone_variant_data_t *clone_data = NULL; + get_clone_variant_data(clone_data, rsc); + + crm_debug_3("Freeing %s", rsc->id); + + slist_iter( + child_rsc, resource_t, clone_data->child_list, lpc, + + crm_debug_3("Freeing child %s", child_rsc->id); + free_xml(child_rsc->xml); + child_rsc->fns->free(child_rsc); + ); + + crm_debug_3("Freeing child list"); + pe_free_shallow_adv(clone_data->child_list, FALSE); + + if(clone_data->self) { + free_xml(clone_data->self->xml); + clone_data->self->fns->free(clone_data->self); + } + common_free(rsc); +} + +enum rsc_role_e +clone_resource_state(resource_t *rsc) +{ + return RSC_ROLE_UNKNOWN; +} diff --git a/lib/crm/pengine/common.c b/lib/crm/pengine/common.c new file mode 100644 index 0000000000..cddf0e5bf5 --- /dev/null +++ b/lib/crm/pengine/common.c @@ -0,0 +1,297 @@ +/* $Id: common.c,v 1.1 2006/05/31 14:59:12 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 + +gboolean was_processing_error = FALSE; +gboolean was_processing_warning = FALSE; +gboolean was_config_error = FALSE; +gboolean was_config_warning = FALSE; + +const char * +fail2text(enum action_fail_response fail) +{ + const char *result = ""; + switch(fail) + { + case action_fail_ignore: + result = "ignore"; + break; + case action_fail_block: + result = "block"; + break; + case action_fail_recover: + result = "recover"; + break; + case action_fail_migrate: + result = "migrate"; + break; + case action_fail_fence: + result = "fence"; + break; + } + return result; +} + + + +enum action_tasks +text2task(const char *task) +{ + if(safe_str_eq(task, CRMD_ACTION_STOP)) { + return stop_rsc; + } else if(safe_str_eq(task, CRMD_ACTION_STOPPED)) { + return stopped_rsc; + } else if(safe_str_eq(task, CRMD_ACTION_START)) { + return start_rsc; + } else if(safe_str_eq(task, CRMD_ACTION_STARTED)) { + return started_rsc; + } else if(safe_str_eq(task, CRM_OP_SHUTDOWN)) { + return shutdown_crm; + } else if(safe_str_eq(task, CRM_OP_FENCE)) { + return stonith_node; + } else if(safe_str_eq(task, CRMD_ACTION_MON)) { + return monitor_rsc; + } else if(safe_str_eq(task, CRMD_ACTION_NOTIFY)) { + return action_notify; + } else if(safe_str_eq(task, CRMD_ACTION_NOTIFIED)) { + return action_notified; + } else if(safe_str_eq(task, CRMD_ACTION_PROMOTE)) { + return action_promote; + } else if(safe_str_eq(task, CRMD_ACTION_DEMOTE)) { + return action_demote; + } else if(safe_str_eq(task, CRMD_ACTION_PROMOTED)) { + return action_promoted; + } else if(safe_str_eq(task, CRMD_ACTION_DEMOTED)) { + return action_demoted; + } else if(safe_str_eq(task, CRMD_ACTION_CANCEL)) { + return no_action; + } else if(safe_str_eq(task, CRMD_ACTION_DELETE)) { + return no_action; + } else if(safe_str_eq(task, CRMD_ACTION_STATUS)) { + return no_action; + } else if(safe_str_eq(task, CRM_OP_PROBED)) { + return no_action; + } else if(safe_str_eq(task, CRM_OP_LRM_REFRESH)) { + return no_action; + } + pe_err("Unsupported action: %s", task); + return no_action; +} + + +const char * +task2text(enum action_tasks task) +{ + const char *result = ""; + switch(task) + { + case no_action: + result = "no_action"; + break; + case stop_rsc: + result = CRMD_ACTION_STOP; + break; + case stopped_rsc: + result = CRMD_ACTION_STOPPED; + break; + case start_rsc: + result = CRMD_ACTION_START; + break; + case started_rsc: + result = CRMD_ACTION_STARTED; + break; + case shutdown_crm: + result = CRM_OP_SHUTDOWN; + break; + case stonith_node: + result = CRM_OP_FENCE; + break; + case monitor_rsc: + result = CRMD_ACTION_MON; + break; + case action_notify: + result = CRMD_ACTION_NOTIFY; + break; + case action_notified: + result = CRMD_ACTION_NOTIFIED; + break; + case action_promote: + result = CRMD_ACTION_PROMOTE; + break; + case action_promoted: + result = CRMD_ACTION_PROMOTED; + break; + case action_demote: + result = CRMD_ACTION_DEMOTE; + break; + case action_demoted: + result = CRMD_ACTION_DEMOTED; + break; + } + + return result; +} + +const char * +role2text(enum rsc_role_e role) +{ + CRM_CHECK(role >= RSC_ROLE_UNKNOWN, return RSC_ROLE_UNKNOWN_S); + CRM_CHECK(role < RSC_ROLE_MAX, return RSC_ROLE_UNKNOWN_S); + switch(role) { + case RSC_ROLE_UNKNOWN: + return RSC_ROLE_UNKNOWN_S; + case RSC_ROLE_STOPPED: + return RSC_ROLE_STOPPED_S; + case RSC_ROLE_STARTED: + return RSC_ROLE_STARTED_S; + case RSC_ROLE_SLAVE: + return RSC_ROLE_SLAVE_S; + case RSC_ROLE_MASTER: + return RSC_ROLE_MASTER_S; + } + return RSC_ROLE_UNKNOWN_S; +} + +enum rsc_role_e +text2role(const char *role) +{ + if(safe_str_eq(role, RSC_ROLE_STOPPED_S)) { + return RSC_ROLE_STOPPED; + } else if(safe_str_eq(role, RSC_ROLE_STARTED_S)) { + return RSC_ROLE_STARTED; + } else if(safe_str_eq(role, RSC_ROLE_SLAVE_S)) { + return RSC_ROLE_SLAVE; + } else if(safe_str_eq(role, RSC_ROLE_MASTER_S)) { + return RSC_ROLE_MASTER; + } else if(safe_str_eq(role, RSC_ROLE_UNKNOWN_S)) { + return RSC_ROLE_UNKNOWN; + } + crm_err("Unknown role: %s", role); + return RSC_ROLE_UNKNOWN; +} + +int +merge_weights(int w1, int w2) +{ + int result = w1 + w2; + + if(w1 <= -INFINITY || w2 <= -INFINITY) { + if(w1 >= INFINITY || w2 >= INFINITY) { + crm_debug_2("-INFINITY + INFINITY == -INFINITY"); + } + return -INFINITY; + + } else if(w1 >= INFINITY || w2 >= INFINITY) { + return INFINITY; + } + + /* detect wrap-around */ + if(result > 0) { + if(w1 <= 0 && w2 < 0) { + result = -INFINITY; + } + + } else if(w1 > 0 && w2 > 0) { + result = INFINITY; + } + + /* detect +/- INFINITY */ + if(result >= INFINITY) { + result = INFINITY; + + } else if(result <= -INFINITY) { + result = -INFINITY; + } + + crm_debug_5("%d + %d = %d", w1, w2, result); + return result; +} + + +int +char2score(const char *score) +{ + int score_f = 0; + + if(score == NULL) { + + } else if(safe_str_eq(score, MINUS_INFINITY_S)) { + score_f = -INFINITY; + + } else if(safe_str_eq(score, INFINITY_S)) { + score_f = INFINITY; + + } else if(safe_str_eq(score, "+"INFINITY_S)) { + score_f = INFINITY; + + } else { + score_f = crm_parse_int(score, NULL); + if(score_f > 0 && score_f > INFINITY) { + score_f = INFINITY; + + } else if(score_f < 0 && score_f < -INFINITY) { + score_f = -INFINITY; + } + } + + return score_f; +} + + +char * +score2char(int score) +{ + + if(score >= INFINITY) { + return crm_strdup("+"INFINITY_S); + + } else if(score <= -INFINITY) { + return crm_strdup("-"INFINITY_S); + } + return crm_itoa(score); +} + + +void +add_hash_param(GHashTable *hash, const char *name, const char *value) +{ + CRM_CHECK(hash != NULL, return); + + crm_debug_3("adding: name=%s value=%s", crm_str(name), crm_str(value)); + if(name == NULL || value == NULL) { + return; + + } else if(safe_str_eq(value, "#default")) { + return; + + } else if(g_hash_table_lookup(hash, name) == NULL) { + g_hash_table_insert(hash, crm_strdup(name), crm_strdup(value)); + } +} + diff --git a/lib/crm/pengine/common.h b/lib/crm/pengine/common.h new file mode 100644 index 0000000000..22e54bb338 --- /dev/null +++ b/lib/crm/pengine/common.h @@ -0,0 +1,150 @@ +/* $Id: common.h,v 1.1 2006/05/31 14:59:12 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 + */ +#ifndef PE_COMMON__H +#define PE_COMMON__H + +/* + * The man pages for both curses and ncurses suggest inclusion of "curses.h". + * We believe the following to be acceptable and portable. + */ + +#if defined(HAVE_LIBNCURSES) || defined(HAVE_LIBCURSES) +#if defined(HAVE_NCURSES_H) && !defined(HAVE_INCOMPATIBLE_PRINTW) +# include +# define CURSES_ENABLED 1 +#elif defined(HAVE_NCURSES_NCURSES_H) && !defined(HAVE_INCOMPATIBLE_PRINTW) +# include +# define CURSES_ENABLED 1 +#elif defined(HAVE_CURSES_H) && !defined(HAVE_INCOMPATIBLE_PRINTW) +# include +# define CURSES_ENABLED 1 +#elif defined(HAVE_CURSES_CURSES_H) && !defined(HAVE_INCOMPATIBLE_PRINTW) +# include +# define CURSES_ENABLED 1 +#else +# define CURSES_ENABLED 0 +#endif +#else +# define CURSES_ENABLED 0 +#endif + +extern gboolean was_processing_error; +extern gboolean was_processing_warning; +extern gboolean was_config_error; +extern gboolean was_config_warning; +extern unsigned int pengine_input_loglevel; + +/* order is significant here + * items listed in order of accending severeness + * more severe actions take precedent over lower ones + */ +enum action_fail_response { + action_fail_ignore, + action_fail_recover, + action_fail_migrate, + action_fail_block, +/* action_fail_stop, */ + action_fail_fence +}; + +enum action_tasks { + no_action, + monitor_rsc, + stop_rsc, + stopped_rsc, + start_rsc, + started_rsc, + action_notify, + action_notified, + action_promote, + action_promoted, + action_demote, + action_demoted, + shutdown_crm, + stonith_node +}; + +enum rsc_recovery_type { + recovery_stop_start, + recovery_stop_only, + recovery_block +}; + +enum rsc_start_requirement { + rsc_req_nothing, + rsc_req_quorum, + rsc_req_stonith +}; + +enum rsc_role_e { + RSC_ROLE_UNKNOWN, + RSC_ROLE_STOPPED, + RSC_ROLE_STARTED, + RSC_ROLE_SLAVE, + RSC_ROLE_MASTER, +}; +#define RSC_ROLE_MAX RSC_ROLE_MASTER+1 + +#define RSC_ROLE_UNKNOWN_S "Unknown" +#define RSC_ROLE_STOPPED_S "Stopped" +#define RSC_ROLE_STARTED_S "Started" +#define RSC_ROLE_SLAVE_S "Slave" +#define RSC_ROLE_MASTER_S "Master" + +enum pe_print_options { + + pe_print_log = 0x0001, + pe_print_html = 0x0002, + pe_print_ncurses = 0x0004, + pe_print_printf = 0x0008, + pe_print_dev = 0x0010, + pe_print_details = 0x0020, + pe_print_max_details = 0x0040, + pe_print_rsconly = 0x0080, +}; + + +extern int merge_weights(int w1, int w2); + +extern const char *task2text(enum action_tasks task); +extern enum action_tasks text2task(const char *task); + +extern enum rsc_role_e text2role(const char *role); +extern const char *role2text(enum rsc_role_e role); + +extern const char *fail2text(enum action_fail_response fail); + +extern int char2score(const char *score); +extern char *score2char(int score); + +extern void add_hash_param(GHashTable *hash, const char *name, const char *value); + + +/* Helper macros to avoid NULL pointers */ +#define safe_val3(def, t,u,v) (t?t->u?t->u->v:def:def) +#define safe_val5(def, t,u,v,w,x) (t?t->u?t->u->v?t->u->v->w?t->u->v->w->x:def:def:def:def) + +#define pe_err(fmt...) { was_processing_error = TRUE; was_config_error = TRUE; crm_err(fmt); } +#define pe_warn(fmt...) { was_processing_warning = TRUE; was_config_warning = TRUE; crm_warn(fmt); } +#define pe_proc_err(fmt...) { was_processing_error = TRUE; crm_err(fmt); } +#define pe_proc_warn(fmt...) { was_processing_warning = TRUE; crm_warn(fmt); } +#define pe_config_err(fmt...) { was_config_error = TRUE; crm_err(fmt); } +#define pe_config_warn(fmt...) { was_config_warning = TRUE; crm_warn(fmt); } + +#endif diff --git a/lib/crm/pengine/group.c b/lib/crm/pengine/group.c new file mode 100644 index 0000000000..585639067d --- /dev/null +++ b/lib/crm/pengine/group.c @@ -0,0 +1,269 @@ +/* $Id: group.c,v 1.1 2006/05/31 14:59:12 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 + +extern gboolean rsc_colocation_new( + const char *id, enum con_strength strength, + resource_t *rsc_lh, resource_t *rsc_rh, + const char *state_lh, const char *state_rh); + + +typedef struct group_variant_data_s +{ + int num_children; + GListPtr child_list; /* resource_t* */ + resource_t *self; + resource_t *first_child; + resource_t *last_child; + + gboolean colocated; + gboolean ordered; + + gboolean child_starting; + gboolean child_stopping; + +} group_variant_data_t; + + +#define get_group_variant_data(data, rsc) \ + CRM_ASSERT(rsc != NULL); \ + CRM_ASSERT(rsc->variant == pe_group); \ + CRM_ASSERT(rsc->variant_opaque != NULL); \ + data = (group_variant_data_t *)rsc->variant_opaque; \ + +void group_assign_color(resource_t *rsc, color_t *group_color); + +gboolean group_unpack(resource_t *rsc, pe_working_set_t *data_set) +{ + resource_t *self = NULL; + crm_data_t *xml_obj = rsc->xml; + crm_data_t *xml_self = copy_xml(rsc->xml); + group_variant_data_t *group_data = NULL; + const char *group_ordered = g_hash_table_lookup( + rsc->meta, XML_RSC_ATTR_ORDERED); + const char *group_colocated = g_hash_table_lookup( + rsc->meta, "collocated"); + const char *clone_id = NULL; + + crm_debug_3("Processing resource %s...", rsc->id); +/* rsc->id = "dummy_group_rsc_id"; */ + + crm_malloc0(group_data, sizeof(group_variant_data_t)); + group_data->num_children = 0; + group_data->self = NULL; + group_data->child_list = NULL; + group_data->first_child = NULL; + group_data->last_child = NULL; + rsc->variant_opaque = group_data; + + group_data->ordered = TRUE; + group_data->colocated = TRUE; + + if(group_ordered != NULL) { + cl_str_to_boolean(group_ordered, &(group_data->ordered)); + } + if(group_colocated != NULL) { + cl_str_to_boolean(group_colocated, &(group_data->colocated)); + } + + /* this is a bit of a hack - but simplifies everything else */ + ha_msg_mod(xml_self, F_XML_TAGNAME, XML_CIB_TAG_RESOURCE); +/* set_id(xml_self, "self", -1); */ + + if(common_unpack(xml_self, &self, NULL, data_set)) { + group_data->self = self; + self->restart_type = pe_restart_restart; + + } else { + crm_log_xml_err(xml_self, "Couldnt unpack dummy child"); + return FALSE; + } + + clone_id = crm_element_value(rsc->xml, XML_RSC_ATTR_INCARNATION); + + xml_child_iter_filter( + xml_obj, xml_native_rsc, XML_CIB_TAG_RESOURCE, + + resource_t *new_rsc = NULL; + crm_xml_add(xml_native_rsc, XML_RSC_ATTR_INCARNATION, clone_id); + if(common_unpack(xml_native_rsc, &new_rsc, + rsc, data_set) == FALSE) { + pe_err("Failed unpacking resource %s", + crm_element_value(xml_obj, XML_ATTR_ID)); + if(new_rsc != NULL && new_rsc->fns != NULL) { + new_rsc->fns->free(new_rsc); + } + } + + group_data->num_children++; + group_data->child_list = g_list_append( + group_data->child_list, new_rsc); + + if(group_data->first_child == NULL) { + group_data->first_child = new_rsc; + + } else if(group_data->colocated) { + rsc_colocation_new( + "pe_group_internal_colo", pecs_must, + group_data->first_child, new_rsc, + NULL, NULL); + } + group_data->last_child = new_rsc; + print_resource(LOG_DEBUG_3, "Added", new_rsc, FALSE); + ); + + if(group_data->num_children == 0) { + pe_config_err("Group %s did not have any children", rsc->id); + return FALSE; + } + + crm_debug_3("Added %d children to resource %s...", + group_data->num_children, rsc->id); + + return TRUE; +} + + +resource_t * +group_find_child(resource_t *rsc, const char *id) +{ + group_variant_data_t *group_data = NULL; + get_group_variant_data(group_data, rsc); + return pe_find_resource(group_data->child_list, id); +} + +GListPtr group_children(resource_t *rsc) +{ + group_variant_data_t *group_data = NULL; + get_group_variant_data(group_data, rsc); + return group_data->child_list; +} + +gboolean group_active(resource_t *rsc, gboolean all) +{ + group_variant_data_t *group_data = NULL; + get_group_variant_data(group_data, rsc); + + slist_iter( + child_rsc, resource_t, group_data->child_list, lpc, + gboolean child_active = child_rsc->fns->active(child_rsc, all); + if(all == FALSE && child_active) { + return TRUE; + } else if(child_active == FALSE) { + return FALSE; + } + ); + if(all) { + return TRUE; + } else { + return FALSE; + } +} + +void group_print( + resource_t *rsc, const char *pre_text, long options, void *print_data) +{ + const char *child_text = NULL; + group_variant_data_t *group_data = NULL; + get_group_variant_data(group_data, rsc); + if(pre_text != NULL) { + child_text = " "; + } else { + child_text = " "; + } + + status_print("%sResource Group: %s", + pre_text?pre_text:"", rsc->id); + + if(options & pe_print_html) { + status_print("\n
    \n"); + + } else if((options & pe_print_log) == 0) { + status_print("\n"); + } + + slist_iter( + child_rsc, resource_t, group_data->child_list, lpc, + + if(options & pe_print_html) { + status_print("
  • \n"); + } + child_rsc->fns->print( + child_rsc, child_text, options, print_data); + if(options & pe_print_html) { + status_print("
  • \n"); + } + ); + + if(options & pe_print_html) { + status_print("
\n"); + } +} + +void group_free(resource_t *rsc) +{ + group_variant_data_t *group_data = NULL; + CRM_CHECK(rsc != NULL, return); + get_group_variant_data(group_data, rsc); + + crm_debug_3("Freeing %s", rsc->id); + + slist_iter( + child_rsc, resource_t, group_data->child_list, lpc, + + crm_debug_3("Freeing child %s", child_rsc->id); + child_rsc->fns->free(child_rsc); + ); + + crm_debug_3("Freeing child list"); + pe_free_shallow_adv(group_data->child_list, FALSE); + + if(group_data->self != NULL) { + free_xml(group_data->self->xml); + group_data->self->fns->free(group_data->self); + } + + common_free(rsc); +} + +enum rsc_role_e +group_resource_state(resource_t *rsc) +{ + enum rsc_role_e group_role = RSC_ROLE_UNKNOWN; + group_variant_data_t *group_data = NULL; + get_group_variant_data(group_data, rsc); + + slist_iter( + child_rsc, resource_t, group_data->child_list, lpc, + + if(child_rsc->next_role > group_role) { + group_role = rsc->next_role; + } + if(child_rsc->failed) { + rsc->failed = TRUE; + } + ); + return group_role; +} diff --git a/lib/crm/pengine/native.c b/lib/crm/pengine/native.c new file mode 100644 index 0000000000..8b0146a2dd --- /dev/null +++ b/lib/crm/pengine/native.c @@ -0,0 +1,467 @@ +/* $Id: native.c,v 1.1 2006/05/31 14:59:12 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 + +#define DELETE_THEN_REFRESH 1 + +typedef struct native_variant_data_s +{ +/* GListPtr allowed_nodes; /\* node_t* *\/ */ + +} native_variant_data_t; + +gboolean DeleteRsc(resource_t *rsc, node_t *node, pe_working_set_t *data_set); + +#define get_native_variant_data(data, rsc) \ + CRM_ASSERT(rsc->variant == pe_native); \ + CRM_ASSERT(rsc->variant_opaque != NULL); \ + data = (native_variant_data_t *)rsc->variant_opaque; + +void +native_add_running(resource_t *rsc, node_t *node, pe_working_set_t *data_set) +{ + CRM_CHECK(node != NULL, return); + + slist_iter( + a_node, node_t, rsc->running_on, lpc, + CRM_CHECK(a_node != NULL, return); + if(safe_str_eq(a_node->details->id, node->details->id)) { + return; + } + ); + + rsc->running_on = g_list_append(rsc->running_on, node); + if(rsc->variant == pe_native) { + node->details->running_rsc = g_list_append( + node->details->running_rsc, rsc); + } + + if(rsc->variant != pe_native) { + } else if(rsc->is_managed == FALSE) { + crm_info("resource %s isnt managed", rsc->id); + rsc2node_new( + "not_managed_default", rsc, INFINITY, node, data_set); + return; + +#if 0 + } else if(rsc->failed) { + crm_info("Skipping resource stickiness for failed resource %s", + rsc->id); +#endif + } else if(rsc->stickiness > 0 || rsc->stickiness < 0) { + rsc2node_new("stickiness", rsc, rsc->stickiness, node,data_set); + crm_debug("Resource %s: preferring current location (%s/%s)", + rsc->id, node->details->uname, node->details->id); + } + + if(rsc->variant == pe_native && g_list_length(rsc->running_on) > 1) { + const char *type = crm_element_value(rsc->xml, XML_ATTR_TYPE); + const char *class = crm_element_value( + rsc->xml, XML_AGENT_ATTR_CLASS); + + + /* these are errors because hardly any gets it right + * at the moment and this way the might notice + */ + pe_err("Resource %s::%s:%s appears to be active on %d nodes.", + class, type, rsc->id, g_list_length(rsc->running_on)); + cl_log(LOG_ERR, "See %s for more information.", + HAURL("v2/faq/resource_too_active")); + + if(rsc->recovery_type == recovery_stop_only) { + pe_free_shallow_adv(rsc->allowed_nodes, TRUE); + rsc->allowed_nodes = node_list_dup( + data_set->nodes, FALSE, FALSE); + slist_iter( + node, node_t, rsc->allowed_nodes, lpc, + node->weight = -INFINITY; + ); + + } else if(rsc->recovery_type == recovery_block) { + rsc->is_managed = FALSE; + } + + } else { + crm_debug_2("Resource %s is active on: %s", + rsc->id, node->details->uname); + } + + if(rsc->parent != NULL) { + native_add_running(rsc->parent, node, data_set); + } + +} + + +gboolean native_unpack(resource_t *rsc, pe_working_set_t *data_set) +{ + native_variant_data_t *native_data = NULL; + + crm_debug_3("Processing resource %s...", rsc->id); + + crm_malloc0(native_data, sizeof(native_variant_data_t)); + + rsc->allowed_nodes = NULL; + rsc->running_on = NULL; + + rsc->variant_opaque = native_data; + return TRUE; +} + + +resource_t * +native_find_child(resource_t *rsc, const char *id) +{ + return NULL; +} + +GListPtr native_children(resource_t *rsc) +{ + return NULL; +} + +static void +hash_copy_field(gpointer key, gpointer value, gpointer user_data) +{ + const char *name = key; + const char *s_value = value; + + GHashTable *hash_copy = user_data; + g_hash_table_insert(hash_copy, crm_strdup(name), crm_strdup(s_value)); +} + +char * +native_parameter( + resource_t *rsc, node_t *node, gboolean create, const char *name, + pe_working_set_t *data_set) +{ + char *value_copy = NULL; + const char *value = NULL; + GHashTable *hash = rsc->parameters; + GHashTable *local_hash = NULL; + + CRM_CHECK(rsc != NULL, return NULL); + CRM_CHECK(name != NULL && strlen(name) != 0, return NULL); + + crm_debug_2("Looking up %s in %s", name, rsc->id); + + if(create) { + if(node != NULL) { + crm_debug_2("Creating hash with node %s", + node->details->uname); + } else { + crm_debug_2("Creating default hash"); + } + + local_hash = g_hash_table_new_full( + g_str_hash, g_str_equal, + g_hash_destroy_str, g_hash_destroy_str); + + g_hash_table_foreach( + rsc->parameters, hash_copy_field, local_hash); + unpack_instance_attributes( + rsc->xml, XML_TAG_ATTR_SETS, + node?node->details->attrs:NULL, + local_hash, NULL, data_set->now); + + hash = local_hash; + } + + value = g_hash_table_lookup(hash, name); + if(value == NULL) { + /* try meta attributes instead */ + value = g_hash_table_lookup(rsc->meta, name); + } + + if(value != NULL) { + value_copy = crm_strdup(value); + } + if(local_hash != NULL) { + g_hash_table_destroy(local_hash); + } + return value_copy; +} + +gboolean native_active(resource_t *rsc, gboolean all) +{ + slist_iter( + a_node, node_t, rsc->running_on, lpc, + + if(a_node->details->online == FALSE) { + crm_debug("Resource %s: node %s is offline", + rsc->id, a_node->details->uname); + } else if(a_node->details->unclean) { + crm_debug("Resource %s: node %s is unclean", + rsc->id, a_node->details->uname); + } else { + crm_debug("Resource %s active on %s", + rsc->id, a_node->details->uname); + return TRUE; + } + ); + + return FALSE; +} + +struct print_data_s +{ + long options; + void *print_data; +}; + +static void native_print_attr(gpointer key, gpointer value, gpointer user_data) +{ + long options = ((struct print_data_s*)user_data)->options; + void *print_data = ((struct print_data_s*)user_data)->print_data; + status_print("Option: %s = %s\n", (char*)key, (char*)value); +} + +void +native_print( + resource_t *rsc, const char *pre_text, long options, void *print_data) +{ + node_t *node = NULL; + const char *prov = NULL; + const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); + + if(safe_str_eq(class, "ocf")) { + prov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); + } + + if(rsc->running_on != NULL) { + node = rsc->running_on->data; + } + + if(options & pe_print_html) { + if(rsc->is_managed == FALSE) { + status_print(""); + + } else if(rsc->failed) { + status_print(""); + + } else if(rsc->variant == pe_native + && g_list_length(rsc->running_on) == 0) { + status_print(""); + + } else if(g_list_length(rsc->running_on) > 1) { + status_print(""); + + } else { + status_print(""); + } + } + + if((options & pe_print_rsconly) || g_list_length(rsc->running_on) > 1) { + const char *desc = NULL; + desc = crm_element_value(rsc->xml, XML_ATTR_DESC); + status_print("%s%s\t(%s%s%s:%s)%s%s", + pre_text?pre_text:"", rsc->id, + prov?prov:"", prov?"::":"", + class, crm_element_value(rsc->xml, XML_ATTR_TYPE), + desc?": ":"", desc?desc:""); + + } else { + status_print("%s%s\t(%s%s%s:%s):\t%s %s%s%s", + pre_text?pre_text:"", rsc->id, + prov?prov:"", prov?"::":"", + class, crm_element_value(rsc->xml, XML_ATTR_TYPE), + (rsc->variant!=pe_native)?"":role2text(rsc->role), + (rsc->variant!=pe_native)?"":node!=NULL?node->details->uname:"", + rsc->is_managed?"":" (unmanaged)", rsc->failed?" FAILED":""); + +#if CURSES_ENABLED + if(options & pe_print_ncurses) { + move(-1, 0); + } +#endif + } + + if(options & pe_print_html) { + status_print(" "); + } + + if((options & pe_print_rsconly)) { + + } else if(g_list_length(rsc->running_on) > 1) { + if(options & pe_print_html) { + status_print("
    \n"); + } else if((options & pe_print_printf) + || (options & pe_print_ncurses)) { + status_print("["); + } + + slist_iter(node, node_t, rsc->running_on, lpc, + if(options & pe_print_html) { + status_print("
  • \n%s", + node->details->uname); + + } else if((options & pe_print_printf) + || (options & pe_print_ncurses)) { + status_print("\t%s", node->details->uname); + + } else if((options & pe_print_log)) { + status_print("\t%d : %s", + lpc, node->details->uname); + + } else { + status_print("%s", node->details->uname); + } + if(options & pe_print_html) { + status_print("
  • \n"); + + } + ); + + if(options & pe_print_html) { + status_print("
\n"); + } else if((options & pe_print_printf) + || (options & pe_print_ncurses)) { + status_print(" ]"); + } + } + + if(options & pe_print_html) { + status_print("
\n"); + } else if((options & pe_print_printf) || (options & pe_print_ncurses)) { + status_print("\n"); + } + + if(options & pe_print_details) { + struct print_data_s pdata; + pdata.options = options; + pdata.print_data = print_data; + g_hash_table_foreach(rsc->parameters, native_print_attr, &pdata); + } + + if(options & pe_print_dev) { + status_print("%s\t(%s%svariant=%s, priority=%f)", + pre_text, rsc->provisional?"provisional, ":"", + rsc->runnable?"":"non-startable, ", + crm_element_name(rsc->xml), + (double)rsc->priority); + + status_print("%s\t%d candidate colors, %d allowed nodes," + " %d rsc_cons", + pre_text, g_list_length(rsc->candidate_colors), + g_list_length(rsc->allowed_nodes), + g_list_length(rsc->rsc_cons)); + } + + if(options & pe_print_max_details) { + status_print("%s\t=== Actions.\n", pre_text); + slist_iter( + action, action_t, rsc->actions, lpc, + log_action(LOG_DEBUG_4, "\trsc action: ", action, FALSE); + ); + + status_print("%s\t=== Colors\n", pre_text); + slist_iter( + color, color_t, rsc->candidate_colors, lpc, + print_color("\t", color, FALSE) + ); + + status_print("%s\t=== Allowed Nodes\n", pre_text); + slist_iter( + node, node_t, rsc->allowed_nodes, lpc, + print_node("\t", node, FALSE); + ); + } +} + +void native_free(resource_t *rsc) +{ + crm_debug_4("Freeing Allowed Nodes"); + crm_free(rsc->color); + common_free(rsc); +} + + +enum rsc_role_e +native_resource_state(resource_t *rsc) +{ + if(rsc->next_role != RSC_ROLE_UNKNOWN) { + return rsc->next_role; + } + if(rsc->role != RSC_ROLE_UNKNOWN) { + return rsc->role; + } + + return RSC_ROLE_STOPPED; +} + +gboolean +DeleteRsc(resource_t *rsc, node_t *node, pe_working_set_t *data_set) +{ + action_t *delete = NULL; + action_t *refresh = NULL; + + char *stop = NULL; + char *start = NULL; + + if(rsc->failed) { + crm_debug_2("Resource %s not deleted from %s: failed", + rsc->id, node->details->uname); + return FALSE; + + } else if(node == NULL) { + crm_debug_2("Resource %s not deleted: NULL node", rsc->id); + return FALSE; + + } else if(node->details->unclean || node->details->online == FALSE) { + crm_debug_2("Resource %s not deleted from %s: unrunnable", + rsc->id, node->details->uname); + return FALSE; + } + + stop = stop_key(rsc); + start = start_key(rsc); + + crm_notice("Removing %s from %s", + rsc->id, node->details->uname); + + delete = delete_action(rsc, node); + + custom_action_order( + rsc, stop, NULL, rsc, NULL, delete, + pe_ordering_optional, data_set); + + custom_action_order( + rsc, NULL, delete, rsc, start, NULL, + pe_ordering_manditory, data_set); + +#if DELETE_THEN_REFRESH + refresh = custom_action( + NULL, crm_strdup(CRM_OP_LRM_REFRESH), CRM_OP_LRM_REFRESH, + node, FALSE, TRUE, data_set); + add_hash_param(refresh->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); + + custom_action_order( + rsc, NULL, delete, NULL, NULL, refresh, + pe_ordering_optional, data_set); +#endif + + + return TRUE; +} diff --git a/lib/crm/pengine/rules.c b/lib/crm/pengine/rules.c new file mode 100644 index 0000000000..2fe699a01c --- /dev/null +++ b/lib/crm/pengine/rules.c @@ -0,0 +1,611 @@ +/* $Id: rules.c,v 1.1 2006/05/31 14:59:12 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 + +ha_time_t *parse_xml_duration(ha_time_t *start, crm_data_t *duration_spec); + +gboolean test_date_expression(crm_data_t *time_expr, ha_time_t *now); +gboolean cron_range_satisfied(ha_time_t *now, crm_data_t *cron_spec); +gboolean test_attr_expression( + crm_data_t *expr, GHashTable *hash, ha_time_t *now); +gboolean test_role_expression( + crm_data_t *expr, enum rsc_role_e role, ha_time_t *now); + +gboolean +test_ruleset(crm_data_t *ruleset, GHashTable *node_hash, ha_time_t *now) +{ + gboolean ruleset_default = TRUE; + xml_child_iter_filter( + ruleset, rule, XML_TAG_RULE, + + ruleset_default = FALSE; + if(test_rule(rule, node_hash, RSC_ROLE_UNKNOWN, now)) { + return TRUE; + } + ); + + return ruleset_default; +} + +gboolean +test_rule(crm_data_t *rule, GHashTable *node_hash, enum rsc_role_e role, + ha_time_t *now) +{ + gboolean test = TRUE; + gboolean passed = TRUE; + gboolean do_and = TRUE; + + const char *value = crm_element_value(rule, "boolean_op"); + if(safe_str_eq(value, "or")) { + do_and = FALSE; + passed = FALSE; + } + + crm_debug_2("Testing rule %s", ID(rule)); + xml_child_iter( + rule, expr, + test = test_expression(expr, node_hash, role, now); + + if(test && do_and == FALSE) { + crm_debug_3("Expression %s/%s passed", + ID(rule), ID(expr)); + return TRUE; + + } else if(test == FALSE && do_and) { + crm_debug_3("Expression %s/%s failed", + ID(rule), ID(expr)); + return FALSE; + } + ); + + crm_debug_2("Rule %s %s", ID(rule), passed?"passed":"failed"); + return passed; +} + +gboolean +test_expression(crm_data_t *expr, GHashTable *node_hash, enum rsc_role_e role, + ha_time_t *now) +{ + gboolean accept = FALSE; + const char *uname = NULL; + + switch(find_expression_type(expr)) { + case nested_rule: + accept = test_rule(expr, node_hash, role, now); + break; + case attr_expr: + case loc_expr: + /* these expressions can never succeed if there is + * no node to compare with + */ + if(node_hash != NULL) { + accept = test_attr_expression(expr, node_hash, now); + } + break; + + case time_expr: + accept = test_date_expression(expr, now); + break; + + case role_expr: + accept = test_role_expression(expr, role, now); + break; + + default: + CRM_CHECK(FALSE /* bad type */, return FALSE); + accept = FALSE; + } + if(node_hash) { + uname = g_hash_table_lookup(node_hash, "#uname"); + } + + crm_debug_2("Expression %s %s on %s", + ID(expr), accept?"passed":"failed", + uname?uname:"all ndoes"); + return accept; +} + +enum expression_type +find_expression_type(crm_data_t *expr) +{ + const char *tag = NULL; + const char *attr = NULL; + attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE); + tag = crm_element_name(expr); + + if(safe_str_eq(tag, "date_expression")) { + return time_expr; + + } else if(safe_str_eq(tag, XML_TAG_RULE)) { + return nested_rule; + + } else if(safe_str_neq(tag, "expression")) { + return not_expr; + + } else if(safe_str_eq(attr, "#uname") || safe_str_eq(attr, "#id")) { + return loc_expr; + + } else if(safe_str_eq(attr, "#role")) { + return role_expr; + } + + return attr_expr; +} + +gboolean +test_role_expression( + crm_data_t *expr, enum rsc_role_e role, ha_time_t *now) +{ + gboolean accept = FALSE; + const char *op = NULL; + const char *value = NULL; + + if(role == RSC_ROLE_UNKNOWN) { + return accept; + } + + value = crm_element_value(expr, XML_EXPR_ATTR_VALUE); + op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION); + + if(safe_str_eq(op, "defined")) { + if(role > RSC_ROLE_STARTED) { + accept = TRUE; + } + + } else if(safe_str_eq(op, "not_defined")) { + if(role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) { + accept = TRUE; + } + + } else if(safe_str_eq(op, "eq")) { + if(text2role(value) == role) { + accept = TRUE; + } + + } else if(safe_str_eq(op, "ne")) { + /* we will only test "ne" wtih master/slave roles style */ + if(role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) { + accept = FALSE; + + } else if(text2role(value) != role) { + accept = TRUE; + } + } + return accept; +} + +gboolean +test_attr_expression(crm_data_t *expr, GHashTable *hash, ha_time_t *now) +{ + gboolean accept = FALSE; + int cmp = 0; + const char *h_val = NULL; + + const char *op = NULL; + const char *type = NULL; + const char *attr = NULL; + const char *value = NULL; + + attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE); + op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION); + value = crm_element_value(expr, XML_EXPR_ATTR_VALUE); + type = crm_element_value(expr, XML_EXPR_ATTR_TYPE); + + if(attr == NULL || op == NULL) { + pe_err("Invlaid attribute or operation in expression" + " (\'%s\' \'%s\' \'%s\')", + crm_str(attr), crm_str(op), crm_str(value)); + return FALSE; + } + + if(hash != NULL) { + h_val = (const char*)g_hash_table_lookup(hash, attr); + } + + if(value != NULL && h_val != NULL) { + if(type == NULL || (safe_str_eq(type, "string"))) { + cmp = strcmp(h_val, value); + + } else if(safe_str_eq(type, "number")) { + int h_val_f = crm_parse_int(h_val, NULL); + int value_f = crm_parse_int(value, NULL); + + if(h_val_f < value_f) { + cmp = -1; + } else if(h_val_f > value_f) { + cmp = 1; + } else { + cmp = 0; + } + + } else if(safe_str_eq(type, "version")) { + cmp = compare_version(h_val, value); + + } + + } else if(value == NULL && h_val == NULL) { + cmp = 0; + } else if(value == NULL) { + cmp = 1; + } else { + cmp = -1; + } + + if(safe_str_eq(op, "defined")) { + if(h_val != NULL) { accept = TRUE; } + + } else if(safe_str_eq(op, "not_defined")) { + if(h_val == NULL) { accept = TRUE; } + + } else if(safe_str_eq(op, "eq")) { + if((h_val == value) || cmp == 0) { + accept = TRUE; + } + + } else if(safe_str_eq(op, "ne")) { + if((h_val == NULL && value != NULL) + || (h_val != NULL && value == NULL) + || cmp != 0) { + accept = TRUE; + } + + } else if(value == NULL || h_val == NULL) { + /* the comparision is meaningless from this point on */ + accept = FALSE; + + } else if(safe_str_eq(op, "lt")) { + if(cmp < 0) { accept = TRUE; } + + } else if(safe_str_eq(op, "lte")) { + if(cmp <= 0) { accept = TRUE; } + + } else if(safe_str_eq(op, "gt")) { + if(cmp > 0) { accept = TRUE; } + + } else if(safe_str_eq(op, "gte")) { + if(cmp >= 0) { accept = TRUE; } + } + + return accept; +} + +/* As per the nethack rules: + * + * moon period = 29.53058 days ~= 30, year = 365.2422 days + * days moon phase advances on first day of year compared to preceding year + * = 365.2422 - 12*29.53058 ~= 11 + * years in Metonic cycle (time until same phases fall on the same days of + * the month) = 18.6 ~= 19 + * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30 + * (29 as initial condition) + * current phase in days = first day phase + days elapsed in year + * 6 moons ~= 177 days + * 177 ~= 8 reported phases * 22 + * + 11/22 for rounding + * + * 0-7, with 0: new, 4: full + */ + +static int +phase_of_the_moon(ha_time_t *now) +{ + int epact, diy, goldn; + + diy = now->yeardays; + goldn = (now->years % 19) + 1; + epact = (11 * goldn + 18) % 30; + if ((epact == 25 && goldn > 11) || epact == 24) + epact++; + + return( (((((diy + epact) * 6) + 11) % 177) / 22) & 7 ); +} + +#define cron_check(xml_field, time_field) \ + value = crm_element_value(cron_spec, xml_field); \ + if(value != NULL) { \ + gboolean pass = TRUE; \ + decodeNVpair(value, '-', &value_low, &value_high); \ + if(value_low == NULL) { \ + value_low = crm_strdup(value); \ + } \ + value_low_i = crm_parse_int(value_low, "0"); \ + value_high_i = crm_parse_int(value_high, "-1"); \ + if(value_low_i > time_field) { \ + pass = FALSE; \ + } else if(value_high_i < 0) { \ + } else if(value_high_i < time_field) { \ + pass = FALSE; \ + } \ + crm_free(value_low); \ + crm_free(value_high); \ + if(pass == FALSE) { \ + crm_debug("Condition '%s' in %s: failed", value, xml_field); \ + return pass; \ + } \ + crm_debug("Condition '%s' in %s: passed", value, xml_field); \ + } + +gboolean +cron_range_satisfied(ha_time_t *now, crm_data_t *cron_spec) +{ + const char *value = NULL; + char *value_low = NULL; + char *value_high = NULL; + + int value_low_i = 0; + int value_high_i = 0; + + cron_check("seconds", now->seconds); + cron_check("minutes", now->minutes); + cron_check("hours", now->hours); + cron_check("monthdays", now->days); + cron_check("weekdays", now->weekdays); + cron_check("yeardays", now->yeardays); + cron_check("weeks", now->weeks); + cron_check("months", now->months); + cron_check("years", now->years); + cron_check("weekyears", now->weekyears); + cron_check("moon", phase_of_the_moon(now)); + + return TRUE; +} + +#define update_field(xml_field, time_fn) \ + value = crm_element_value(duration_spec, xml_field); \ + if(value != NULL) { \ + int value_i = crm_parse_int(value, "0"); \ + time_fn(end, value_i); \ + } + +ha_time_t * +parse_xml_duration(ha_time_t *start, crm_data_t *duration_spec) +{ + ha_time_t *end = NULL; + const char *value = NULL; + + end = new_ha_date(FALSE); + ha_set_time(end, start, TRUE); + + update_field("years", add_years); + update_field("months", add_months); + update_field("weeks", add_weeks); + update_field("days", add_days); + update_field("hours", add_hours); + update_field("minutes", add_minutes); + update_field("seconds", add_seconds); + + return end; +} + + +gboolean +test_date_expression(crm_data_t *time_expr, ha_time_t *now) +{ + ha_time_t *start = NULL; + ha_time_t *end = NULL; + const char *value = NULL; + char *value_copy = NULL; + char *value_copy_start = NULL; + const char *op = crm_element_value(time_expr, "operation"); + + crm_data_t *duration_spec = NULL; + crm_data_t *date_spec = NULL; + + gboolean passed = FALSE; + + crm_debug_2("Testing expression: %s", ID(time_expr)); + + duration_spec = cl_get_struct(time_expr, "duration"); + date_spec = cl_get_struct(time_expr, "date_spec"); + + value = crm_element_value(time_expr, "start"); + if(value != NULL) { + value_copy = crm_strdup(value); + value_copy_start = value_copy; + start = parse_date(&value_copy); + crm_free(value_copy_start); + } + value = crm_element_value(time_expr, "end"); + if(value != NULL) { + value_copy = crm_strdup(value); + value_copy_start = value_copy; + end = parse_date(&value_copy); + crm_free(value_copy_start); + } + + if(start != NULL && end == NULL) { + end = parse_xml_duration(start, duration_spec); + } + if(op == NULL) { + op = "in_range"; + } + + if(safe_str_eq(op, "date_spec") || safe_str_eq(op, "in_range")) { + if(start != NULL && compare_date(start, now) > 0) { + passed = FALSE; + } else if(end != NULL && compare_date(end, now) < 0) { + passed = FALSE; + } else if(safe_str_eq(op, "in_range")) { + passed = TRUE; + } else { + passed = cron_range_satisfied(now, date_spec); + } + + } else if(safe_str_eq(op, "gt") && compare_date(start, now) < 0) { + passed = TRUE; + + + } else if(safe_str_eq(op, "lt") && compare_date(end, now) > 0) { + passed = TRUE; + + } else if(safe_str_eq(op, "eq") && compare_date(start, now) == 0) { + passed = TRUE; + + } else if(safe_str_eq(op, "neq") && compare_date(start, now) != 0) { + passed = TRUE; + } + + free_ha_date(start); + free_ha_date(end); + return passed; +} + + +typedef struct sorted_set_s +{ + const char *name; + const char *special_name; + int score; + crm_data_t *attr_set; + GHashTable *node_hash; + GHashTable *hash; + ha_time_t *now; +} sorted_set_t; + +static gint +sort_pairs(gconstpointer a, gconstpointer b) +{ + const sorted_set_t *pair_a = a; + const sorted_set_t *pair_b = b; + + if(a == NULL && b == NULL) { + return 0; + } else if(a == NULL) { + return 1; + } else if(b == NULL) { + return -1; + } + + if(safe_str_eq(pair_a->name, pair_a->special_name)) { + return -1; + + } else if(safe_str_eq(pair_b->name, pair_a->special_name)) { + return 1; + } + + if(pair_a->score < pair_b->score) { + return 1; + } else if(pair_a->score > pair_b->score) { + return -1; + } + return 0; +} + + +static void +populate_hash(crm_data_t *nvpair_list, GHashTable *hash) +{ + const char *name = NULL; + const char *value = NULL; + xml_child_iter_filter( + nvpair_list, an_attr, XML_CIB_TAG_NVPAIR, + + name = crm_element_value(an_attr, XML_NVPAIR_ATTR_NAME); + + crm_debug_4("Setting attribute: %s", name); + value = crm_element_value( + an_attr, XML_NVPAIR_ATTR_VALUE); + + if(name == NULL || value == NULL) { + return; + + } else if(safe_str_eq(value, "#default")) { + return; + + } else if(g_hash_table_lookup(hash, name) == NULL) { + g_hash_table_insert( + hash, crm_strdup(name), crm_strdup(value)); + } + ); +} + +static void +unpack_attr_set(gpointer data, gpointer user_data) +{ + sorted_set_t *pair = data; + sorted_set_t *unpack_data = user_data; + crm_data_t *attributes = NULL; + + if(test_ruleset(pair->attr_set, + unpack_data->node_hash, unpack_data->now) == FALSE) { + return; + } + + crm_debug_3("Adding attributes from %s", pair->name); + attributes = cl_get_struct(pair->attr_set, XML_TAG_ATTRS); + populate_hash(attributes, unpack_data->hash); +} + +static void +free_pair(gpointer data, gpointer user_data) +{ + sorted_set_t *pair = data; + crm_free(pair); +} + +void +unpack_instance_attributes( + crm_data_t *xml_obj, const char *set_name, GHashTable *node_hash, + GHashTable *hash, const char *always_first, ha_time_t *now) +{ + GListPtr sorted = NULL; + const char *score = NULL; + sorted_set_t *pair = NULL; + + if(xml_obj == NULL) { + crm_debug_4("No instance attributes"); + return; + } + + crm_debug_4("Checking for attributes"); + xml_child_iter_filter( + xml_obj, attr_set, set_name, + + pair = NULL; + crm_malloc0(pair, sizeof(sorted_set_t)); + pair->name = ID(attr_set); + pair->special_name = always_first; + pair->attr_set = attr_set; + score = crm_element_value(attr_set, XML_RULE_ATTR_SCORE); + pair->score = char2score(score); + + sorted = g_list_prepend(sorted, pair); + + ); + + if(pair != NULL) { + pair->hash = hash; + pair->node_hash = node_hash; + pair->now = now; + } + + sorted = g_list_sort(sorted, sort_pairs); + g_list_foreach(sorted, unpack_attr_set, pair); + g_list_foreach(sorted, free_pair, NULL); + g_list_free(sorted); +} + diff --git a/lib/crm/pengine/rules.h b/lib/crm/pengine/rules.h new file mode 100644 index 0000000000..bd717d5e8e --- /dev/null +++ b/lib/crm/pengine/rules.h @@ -0,0 +1,50 @@ +/* $Id: rules.h,v 1.1 2006/05/31 14:59:12 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 + */ +#ifndef PENGINE_RULES__H +#define PENGINE_RULES__H + +#include +#include +#include + +enum expression_type { + not_expr, + nested_rule, + attr_expr, + loc_expr, + role_expr, + time_expr +}; + +extern enum expression_type find_expression_type(crm_data_t *expr); + +extern gboolean test_ruleset( + crm_data_t *ruleset, GHashTable *node_hash, ha_time_t *now); + +extern gboolean test_rule(crm_data_t *rule, GHashTable *node_hash, + enum rsc_role_e role, ha_time_t *now); + +extern gboolean test_expression(crm_data_t *expr, GHashTable *node_hash, + enum rsc_role_e role, ha_time_t *now); + +extern void unpack_instance_attributes( + crm_data_t *xml_obj, const char *set_name, GHashTable *node_hash, + GHashTable *hash, const char *always_first, ha_time_t *now); + +#endif diff --git a/lib/crm/pengine/status.c b/lib/crm/pengine/status.c new file mode 100644 index 0000000000..f6a4585bf5 --- /dev/null +++ b/lib/crm/pengine/status.c @@ -0,0 +1,239 @@ +/* $Id: status.c,v 1.1 2006/05/31 14:59:12 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 + +crm_data_t * do_calculations( + pe_working_set_t *data_set, crm_data_t *xml_input, ha_time_t *now); + +unsigned int pengine_input_loglevel = LOG_INFO; + +#define PE_WORKING_DIR HA_VARLIBDIR"/heartbeat/pengine" + +#define MEMCHECK_STAGE_0 0 + +#define check_and_exit(stage) cleanup_calculations(data_set); \ + crm_mem_stats(NULL); \ + crm_err("Exiting: stage %d", stage); \ + exit(1); + + +/* + * Unpack everything + * At the end you'll have: + * - A list of nodes + * - A list of resources (each with any dependencies on other resources) + * - A list of constraints between resources and nodes + * - A list of constraints between start/stop actions + * - A list of nodes that need to be stonith'd + * - A list of nodes that need to be shutdown + * - A list of the possible stop/start actions (without dependencies) + */ +gboolean +cluster_status(pe_working_set_t *data_set) +{ +/* int lpc; */ + crm_data_t * config = get_object_root( + XML_CIB_TAG_CRMCONFIG, data_set->input); + crm_data_t * cib_nodes = get_object_root( + XML_CIB_TAG_NODES, data_set->input); + crm_data_t * cib_resources = get_object_root( + XML_CIB_TAG_RESOURCES, data_set->input); + crm_data_t * cib_status = get_object_root( + XML_CIB_TAG_STATUS, data_set->input); + const char *value = crm_element_value( + data_set->input, XML_ATTR_HAVE_QUORUM); + + crm_debug_3("Beginning unpack"); + + /* reset remaining global variables */ + + if(data_set->input == NULL) { + return FALSE; + } + + if(data_set->input != NULL + && crm_element_value(data_set->input, XML_ATTR_DC_UUID) != NULL) { + /* this should always be present */ + data_set->dc_uuid = crm_element_value_copy( + data_set->input, XML_ATTR_DC_UUID); + } + + data_set->no_color = create_color(data_set, NULL, NULL); + + unpack_config(config, data_set); + + if(value != NULL) { + cl_str_to_boolean(value, &data_set->have_quorum); + } + + if(data_set->have_quorum == FALSE + && data_set->no_quorum_policy != no_quorum_ignore) { + crm_warn("We do not have quorum" + " - fencing and resource management disabled"); + } + + unpack_nodes(cib_nodes, data_set); + unpack_resources(cib_resources, data_set); + unpack_status(cib_status, data_set); + + return TRUE; +} + +void +cleanup_calculations(pe_working_set_t *data_set) +{ + GListPtr iterator = NULL; + + if(data_set == NULL) { + return; + } + + if(data_set->config_hash != NULL) { + g_hash_table_destroy(data_set->config_hash); + } + + crm_free(data_set->dc_uuid); + crm_free(data_set->transition_idle_timeout); + + crm_debug_3("deleting order cons"); + pe_free_ordering(data_set->ordering_constraints); + + crm_debug_3("deleting actions"); + pe_free_actions(data_set->actions); + + crm_debug_3("deleting resources"); + pe_free_resources(data_set->resources); + + crm_debug_3("deleting nodes"); + pe_free_nodes(data_set->nodes); + + crm_debug_3("deleting colors"); + pe_free_colors(data_set->colors); + + crm_debug_3("deleting node cons"); + iterator = data_set->placement_constraints; + while(iterator) { + pe_free_rsc_to_node(iterator->data); + iterator = iterator->next; + } + if(data_set->placement_constraints != NULL) { + g_list_free(data_set->placement_constraints); + } + free_xml(data_set->graph); + free_ha_date(data_set->now); + free_xml(data_set->input); + data_set->stonith_action = NULL; +} + + +void +set_working_set_defaults(pe_working_set_t *data_set) +{ + data_set->input = NULL; + data_set->now = NULL; + data_set->graph = NULL; + + data_set->transition_idle_timeout = crm_strdup("60s"); + data_set->dc_uuid = NULL; + data_set->dc_node = NULL; + data_set->have_quorum = FALSE; + data_set->stonith_enabled = FALSE; + data_set->stonith_action = NULL; + data_set->symmetric_cluster = TRUE; + data_set->is_managed_default = TRUE; + data_set->no_quorum_policy = no_quorum_freeze; + + data_set->remove_after_stop = FALSE; + data_set->stop_action_orphans = TRUE; + data_set->stop_rsc_orphans = TRUE; + + data_set->config_hash = NULL; + data_set->nodes = NULL; + data_set->resources = NULL; + data_set->ordering_constraints = NULL; + data_set->placement_constraints = NULL; + + data_set->no_color = NULL; + data_set->colors = NULL; + data_set->actions = NULL; + + data_set->num_synapse = 0; + data_set->max_valid_nodes = 0; + data_set->order_id = 1; + data_set->action_id = 1; + data_set->color_id = 0; + + data_set->default_resource_stickiness = 0; + data_set->default_resource_fail_stickiness = 0; +} + + +resource_t * +pe_find_resource(GListPtr rsc_list, const char *id) +{ + unsigned lpc = 0; + resource_t *rsc = NULL; + resource_t *child_rsc = NULL; + + if(id == NULL) { + return NULL; + } + + crm_debug_4("Looking for %s in %d objects", id, g_list_length(rsc_list)); + for(lpc = 0; lpc < g_list_length(rsc_list); lpc++) { + rsc = g_list_nth_data(rsc_list, lpc); + if(rsc == NULL) { + } else if(safe_str_eq(rsc->id, id)){ + crm_debug_4("Found a match for %s", id); + return rsc; + + } else if(safe_str_eq(rsc->long_name, id)) { + crm_debug_4("Found a match for %s", id); + return rsc; + } + } + for(lpc = 0; lpc < g_list_length(rsc_list); lpc++) { + rsc = g_list_nth_data(rsc_list, lpc); + + child_rsc = rsc->fns->find_child(rsc, id); + if(child_rsc != NULL) { + crm_debug_4("Found a match for %s in %s", + id, rsc->id); + + return child_rsc; + } + } + crm_debug_2("No match for %s", id); + return NULL; +} diff --git a/lib/crm/pengine/status.h b/lib/crm/pengine/status.h new file mode 100644 index 0000000000..3ac447b309 --- /dev/null +++ b/lib/crm/pengine/status.h @@ -0,0 +1,175 @@ +/* $Id: status.h,v 1.1 2006/05/31 14:59:12 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 + */ +#ifndef PENGINE_STATUS__H +#define PENGINE_STATUS__H + +#include +#include +#include + +typedef struct node_s node_t; +typedef struct color_s color_t; +typedef struct resource_s resource_t; + +typedef enum no_quorum_policy_e { + no_quorum_freeze, + no_quorum_stop, + no_quorum_ignore +} no_quorum_policy_t; + +enum node_type { + node_ping, + node_member +}; + +enum pe_restart { + pe_restart_restart, + pe_restart_ignore +}; + +typedef struct pe_working_set_s +{ + crm_data_t *input; + ha_time_t *now; + + /* options extracted from the input */ + char *transition_idle_timeout; + char *dc_uuid; + node_t *dc_node; + gboolean have_quorum; + gboolean stonith_enabled; + const char *stonith_action; + gboolean symmetric_cluster; + gboolean is_managed_default; + + gboolean remove_after_stop; + gboolean stop_rsc_orphans; + gboolean stop_action_orphans; + + int default_resource_stickiness; + int default_resource_fail_stickiness; + no_quorum_policy_t no_quorum_policy; + + GHashTable *config_hash; + + /* intermediate steps */ + color_t *no_color; + + GListPtr nodes; + GListPtr resources; + GListPtr placement_constraints; + GListPtr ordering_constraints; + + GListPtr colors; + GListPtr actions; + + /* stats */ + int num_synapse; + int max_valid_nodes; + int order_id; + int action_id; + int color_id; + + /* final output */ + crm_data_t *graph; + +} pe_working_set_t; + +struct node_shared_s { + const char *id; + const char *uname; + gboolean online; + gboolean standby; + gboolean unclean; + gboolean shutdown; + gboolean expected_up; + gboolean is_dc; + int num_resources; + GListPtr running_rsc; /* resource_t* */ + + GHashTable *attrs; /* char* => char* */ + enum node_type type; +}; + +struct node_s { + int weight; + gboolean fixed; + struct node_shared_s *details; +}; + +#include + +struct resource_s { + char *id; + char *long_name; + crm_data_t *xml; + crm_data_t *ops_xml; + + resource_t *parent; + void *variant_opaque; + enum pe_obj_types variant; + resource_object_functions_t *fns; + resource_alloc_functions_t *cmds; + + enum rsc_recovery_type recovery_type; + enum pe_restart restart_type; + + int priority; + int stickiness; + int fail_stickiness; + int effective_priority; + + gboolean notify; + gboolean is_managed; + gboolean starting; + gboolean stopping; + gboolean runnable; + gboolean provisional; + gboolean globally_unique; + + gboolean failed; + gboolean start_pending; + + gboolean orphan; + + GListPtr candidate_colors; /* color_t* */ + GListPtr rsc_cons; /* rsc_colocation_t* */ + GListPtr rsc_location; /* rsc_to_node_t* */ + GListPtr actions; /* action_t* */ + + color_t *color; + GListPtr colors; /* color_t* */ + GListPtr running_on; /* node_t* */ + GListPtr known_on; /* node_t* */ + GListPtr allowed_nodes; /* node_t* */ + + enum rsc_role_e role; + enum rsc_role_e next_role; + + GHashTable *meta; + GHashTable *parameters; +}; + + +gboolean cluster_status(pe_working_set_t *data_set); +extern void set_working_set_defaults(pe_working_set_t *data_set); +extern void cleanup_calculations(pe_working_set_t *data_set); +extern resource_t *pe_find_resource(GListPtr rsc_list, const char *id_rh); + +#endif diff --git a/lib/crm/pengine/unpack.c b/lib/crm/pengine/unpack.c new file mode 100644 index 0000000000..05e3650817 --- /dev/null +++ b/lib/crm/pengine/unpack.c @@ -0,0 +1,2086 @@ +/* $Id: unpack.c,v 1.1 2006/05/31 14:59:12 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 /* for ONLINESTATUS */ + +#include +#include +#include + +gint sort_op_by_callid(gconstpointer a, gconstpointer b); + +gboolean unpack_rsc_to_attr(crm_data_t *xml_obj, pe_working_set_t *data_set); + +gboolean unpack_rsc_to_node(crm_data_t *xml_obj, pe_working_set_t *data_set); + +gboolean unpack_rsc_order(crm_data_t *xml_obj, pe_working_set_t *data_set); + +gboolean unpack_rsc_colocation(crm_data_t *xml_obj, pe_working_set_t *data_set); + +gboolean unpack_rsc_location(crm_data_t *xml_obj, pe_working_set_t *data_set); + +gboolean unpack_lrm_resources( + node_t *node, crm_data_t * lrm_state, pe_working_set_t *data_set); + +gboolean add_node_attrs( + crm_data_t * attrs, node_t *node, pe_working_set_t *data_set); + +gboolean unpack_rsc_op( + resource_t *rsc, node_t *node, crm_data_t *xml_op, + int *max_call_id, enum action_fail_response *failed, pe_working_set_t *data_set); + +gboolean determine_online_status( + crm_data_t * node_state, node_t *this_node, pe_working_set_t *data_set); + +gboolean rsc_colocation_new( + const char *id, enum con_strength strength, + resource_t *rsc_lh, resource_t *rsc_rh, + const char *state_lh, const char *state_rh); + +gboolean create_ordering( + const char *id, enum con_strength strength, + resource_t *rsc_lh, resource_t *rsc_rh, pe_working_set_t *data_set); + +const char *param_value( + GHashTable *hash, crm_data_t * parent, const char *name); + +rsc_to_node_t *generate_location_rule( + resource_t *rsc, crm_data_t *location_rule, pe_working_set_t *data_set); + +#define get_cluster_pref(pref) value = g_hash_table_lookup(config_hash, pref); \ + if(value == NULL) { \ + pe_config_warn("No value specified for cluster preference: %s", pref); \ + } + + +gboolean +unpack_config(crm_data_t * config, pe_working_set_t *data_set) +{ + const char *name = NULL; + const char *value = NULL; + GHashTable *config_hash = g_hash_table_new_full( + g_str_hash,g_str_equal, g_hash_destroy_str,g_hash_destroy_str); + + data_set->config_hash = config_hash; + + unpack_instance_attributes( + config, XML_CIB_TAG_PROPSET, NULL, config_hash, + CIB_OPTIONS_FIRST, data_set->now); + +#if CRM_DEPRECATED_SINCE_2_0_1 + xml_child_iter_filter( + config, a_child, XML_CIB_TAG_NVPAIR, + + name = crm_element_value(a_child, XML_NVPAIR_ATTR_NAME); + + value = crm_element_value(a_child, XML_NVPAIR_ATTR_VALUE); + if(g_hash_table_lookup(config_hash, name) == NULL) { + g_hash_table_insert( + config_hash,crm_strdup(name),crm_strdup(value)); + } + pe_config_err("Creating directly" + "beneath has been depreciated since" + " 2.0.1", ID(a_child), name); + ); +#else + xml_child_iter_filter( + config, a_child, XML_CIB_TAG_NVPAIR, + + name = crm_element_value(a_child, XML_NVPAIR_ATTR_NAME); + pe_config_err("Creating directly" + "beneath has been depreciated since" + " 2.0.1 and is now disabled", ID(a_child), name); + ); +#endif + + get_cluster_pref("transition_idle_timeout"); + if(value != NULL) { + long tmp = crm_get_msec(value); + if(tmp > 0) { + crm_free(data_set->transition_idle_timeout); + data_set->transition_idle_timeout = crm_strdup(value); + } else { + crm_err("Invalid value for transition_idle_timeout: %s", + value); + } + } + + crm_debug("%s set to: %s", + "transition_idle_timeout", data_set->transition_idle_timeout); + + get_cluster_pref("default_"XML_RSC_ATTR_STICKINESS); + data_set->default_resource_stickiness = char2score(value); + crm_info("Default stickiness: %d", + data_set->default_resource_stickiness); + + get_cluster_pref("default_"XML_RSC_ATTR_FAIL_STICKINESS); + data_set->default_resource_fail_stickiness = char2score(value); + crm_info("Default failure stickiness: %d", + data_set->default_resource_fail_stickiness); + + get_cluster_pref("stonith_enabled"); + if(value != NULL) { + cl_str_to_boolean(value, &data_set->stonith_enabled); + } + crm_info("STONITH of failed nodes is %s", + data_set->stonith_enabled?"enabled":"disabled"); + + get_cluster_pref("stonith_action"); + if(value == NULL || safe_str_neq(value, "poweroff")) { + value = "reboot"; + } + data_set->stonith_action = value; + crm_info("STONITH will %s nodes", data_set->stonith_action); + + get_cluster_pref("symmetric_cluster"); + if(value != NULL) { + cl_str_to_boolean(value, &data_set->symmetric_cluster); + } + if(data_set->symmetric_cluster) { + crm_info("Cluster is symmetric" + " - resources can run anywhere by default"); + } + + get_cluster_pref("no_quorum_policy"); + if(safe_str_eq(value, "ignore")) { + data_set->no_quorum_policy = no_quorum_ignore; + + } else if(safe_str_eq(value, "freeze")) { + data_set->no_quorum_policy = no_quorum_freeze; + + } else { + data_set->no_quorum_policy = no_quorum_stop; + } + + switch (data_set->no_quorum_policy) { + case no_quorum_freeze: + crm_info("On loss of CCM Quorum: Freeze resources"); + break; + case no_quorum_stop: + crm_info("On loss of CCM Quorum: Stop ALL resources"); + break; + case no_quorum_ignore: + crm_notice("On loss of CCM Quorum: Ignore"); + break; + } + + get_cluster_pref("stop_orphan_resources"); + if(value != NULL) { + cl_str_to_boolean(value, &data_set->stop_rsc_orphans); + } + crm_info("Orphan resources are %s", + data_set->stop_rsc_orphans?"stopped":"ignored"); + + get_cluster_pref("stop_orphan_actions"); + if(value != NULL) { + cl_str_to_boolean(value, &data_set->stop_action_orphans); + } + crm_info("Orphan resource actions are %s", + data_set->stop_action_orphans?"stopped":"ignored"); + + get_cluster_pref("remove_after_stop"); + if(value != NULL) { + cl_str_to_boolean(value, &data_set->remove_after_stop); + } + crm_info("Stopped resources are removed from the status section: %s", + data_set->remove_after_stop?"true":"false"); + + get_cluster_pref("is_managed_default"); + if(value != NULL) { + cl_str_to_boolean(value, &data_set->is_managed_default); + } + crm_info("By default resources are %smanaged", + data_set->is_managed_default?"":"not "); + + return TRUE; +} + +gboolean +unpack_nodes(crm_data_t * xml_nodes, pe_working_set_t *data_set) +{ + node_t *new_node = NULL; + const char *id = NULL; + const char *uname = NULL; + const char *type = NULL; + + crm_debug_2("Begining unpack... %s", + xml_nodes?crm_element_name(xml_nodes):""); + xml_child_iter_filter( + xml_nodes, xml_obj, XML_CIB_TAG_NODE, + + new_node = NULL; + + id = crm_element_value(xml_obj, XML_ATTR_ID); + uname = crm_element_value(xml_obj, XML_ATTR_UNAME); + type = crm_element_value(xml_obj, XML_ATTR_TYPE); + crm_debug_3("Processing node %s/%s", uname, id); + + if(id == NULL) { + pe_config_err("Must specify id tag in "); + continue; + } + if(type == NULL) { + pe_config_err("Must specify type tag in "); + continue; + } + crm_malloc0(new_node, sizeof(node_t)); + if(new_node == NULL) { + return FALSE; + } + + new_node->weight = 0; + new_node->fixed = FALSE; + crm_malloc0(new_node->details, + sizeof(struct node_shared_s)); + + if(new_node->details == NULL) { + crm_free(new_node); + return FALSE; + } + + crm_debug_3("Creaing node for entry %s/%s", uname, id); + new_node->details->id = id; + new_node->details->uname = uname; + new_node->details->type = node_ping; + new_node->details->online = FALSE; + new_node->details->shutdown = FALSE; + new_node->details->running_rsc = NULL; + new_node->details->attrs = g_hash_table_new_full( + g_str_hash, g_str_equal, + g_hash_destroy_str, g_hash_destroy_str); + +/* if(data_set->have_quorum == FALSE */ +/* && data_set->no_quorum_policy == no_quorum_stop) { */ +/* /\* start shutting resources down *\/ */ +/* new_node->weight = -INFINITY; */ +/* } */ + + if(data_set->stonith_enabled) { + /* all nodes are unclean until we've seen their + * status entry + */ + new_node->details->unclean = TRUE; + } else { + /* blind faith... */ + new_node->details->unclean = FALSE; + } + + + if(type == NULL + || safe_str_eq(type, "member") + || safe_str_eq(type, NORMALNODE)) { + new_node->details->type = node_member; + } + + add_node_attrs(xml_obj, new_node, data_set); + + if(crm_is_true(g_hash_table_lookup( + new_node->details->attrs, "standby"))) { + crm_info("Node %s is in standby-mode", + new_node->details->uname); + new_node->weight = -INFINITY; + new_node->details->standby = TRUE; + } + + data_set->nodes = g_list_append(data_set->nodes, new_node); + crm_debug_3("Done with node %s", + crm_element_value(xml_obj, XML_ATTR_UNAME)); + + crm_action_debug_3(print_node("Added", new_node, FALSE)); + ); + +/* data_set->nodes = g_list_sort(data_set->nodes, sort_node_weight); */ + + return TRUE; +} + +gboolean +unpack_resources(crm_data_t * xml_resources, pe_working_set_t *data_set) +{ + crm_debug_2("Begining unpack... %s", + xml_resources?crm_element_name(xml_resources):""); + xml_child_iter( + xml_resources, xml_obj, + + resource_t *new_rsc = NULL; + crm_debug_2("Begining unpack... %s", + xml_obj?crm_element_name(xml_obj):""); + if(common_unpack(xml_obj, &new_rsc, NULL, data_set)) { + data_set->resources = g_list_append( + data_set->resources, new_rsc); + + print_resource(LOG_DEBUG_3, "Added", new_rsc, FALSE); + + } else { + pe_config_err("Failed unpacking %s %s", + crm_element_name(xml_obj), + crm_element_value(xml_obj, XML_ATTR_ID)); + if(new_rsc != NULL && new_rsc->fns != NULL) { + new_rsc->fns->free(new_rsc); + } + } + ); + + data_set->resources = g_list_sort( + data_set->resources, sort_rsc_priority); + + return TRUE; +} + +gboolean +unpack_constraints(crm_data_t * xml_constraints, pe_working_set_t *data_set) +{ + crm_data_t *lifetime = NULL; + crm_debug_2("Begining unpack... %s", + xml_constraints?crm_element_name(xml_constraints):""); + xml_child_iter( + xml_constraints, xml_obj, + + const char *id = crm_element_value(xml_obj, XML_ATTR_ID); + if(id == NULL) { + pe_config_err("Constraint <%s...> must have an id", + crm_element_name(xml_obj)); + continue; + } + + crm_debug_3("Processing constraint %s %s", + crm_element_name(xml_obj),id); + + lifetime = cl_get_struct(xml_obj, "lifetime"); + + if(test_ruleset(lifetime, NULL, data_set->now) == FALSE) { + crm_info("Constraint %s %s is not active", + crm_element_name(xml_obj), id); + + } else if(safe_str_eq(XML_CONS_TAG_RSC_ORDER, + crm_element_name(xml_obj))) { + unpack_rsc_order(xml_obj, data_set); + + } else if(safe_str_eq(XML_CONS_TAG_RSC_DEPEND, + crm_element_name(xml_obj))) { + unpack_rsc_colocation(xml_obj, data_set); + + } else if(safe_str_eq(XML_CONS_TAG_RSC_LOCATION, + crm_element_name(xml_obj))) { + unpack_rsc_location(xml_obj, data_set); + + } else { + pe_err("Unsupported constraint type: %s", + crm_element_name(xml_obj)); + } + ); + + return TRUE; +} + +/* remove nodes that are down, stopping */ +/* create +ve rsc_to_node constraints between resources and the nodes they are running on */ +/* anything else? */ +gboolean +unpack_status(crm_data_t * status, pe_working_set_t *data_set) +{ + const char *id = NULL; + const char *uname = NULL; + + crm_data_t * lrm_rsc = NULL; + crm_data_t * attrs = NULL; + node_t *this_node = NULL; + + crm_debug_3("Begining unpack"); + xml_child_iter_filter( + status, node_state, XML_CIB_TAG_STATE, + + id = crm_element_value(node_state, XML_ATTR_ID); + uname = crm_element_value(node_state, XML_ATTR_UNAME); + attrs = find_xml_node( + node_state, XML_TAG_TRANSIENT_NODEATTRS, FALSE); + + lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); + lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE); + + crm_debug_3("Processing node %s", uname); + this_node = pe_find_node_id(data_set->nodes, id); + + if(uname == NULL) { + /* error */ + continue; + + } else if(this_node == NULL) { + pe_config_warn("Node %s in status section no longer exists", + uname); + continue; + } + + /* Mark the node as provisionally clean + * - at least we have seen it in the current cluster's lifetime + */ + this_node->details->unclean = FALSE; + + crm_debug_3("Adding runtime node attrs"); + add_node_attrs(attrs, this_node, data_set); + + crm_debug_3("determining node state"); + determine_online_status(node_state, this_node, data_set); + + if(this_node->details->online || data_set->stonith_enabled) { + /* offline nodes run no resources... + * unless stonith is enabled in which case we need to + * make sure rsc start events happen after the stonith + */ + crm_debug_3("Processing lrm resource entries"); + unpack_lrm_resources(this_node, lrm_rsc, data_set); + } + ); + + return TRUE; + +} + +static gboolean +determine_online_status_no_fencing(crm_data_t * node_state, node_t *this_node) +{ + gboolean online = FALSE; + const char *join_state = crm_element_value(node_state, + XML_CIB_ATTR_JOINSTATE); + const char *crm_state = crm_element_value(node_state, + XML_CIB_ATTR_CRMDSTATE); + const char *ccm_state = crm_element_value(node_state, + XML_CIB_ATTR_INCCM); + const char *ha_state = crm_element_value(node_state, + XML_CIB_ATTR_HASTATE); + const char *exp_state = crm_element_value(node_state, + XML_CIB_ATTR_EXPSTATE); + + if(!crm_is_true(ccm_state) || safe_str_eq(ha_state,DEADSTATUS)){ + crm_debug_2("Node is down: ha_state=%s, ccm_state=%s", + crm_str(ha_state), crm_str(ccm_state)); + + } else if(!crm_is_true(ccm_state) + || safe_str_eq(ha_state, DEADSTATUS)) { + + } else if(safe_str_neq(join_state, CRMD_JOINSTATE_DOWN) + && safe_str_eq(crm_state, ONLINESTATUS)) { + online = TRUE; + + } else if(this_node->details->expected_up == FALSE) { + crm_debug_2("CRMd is down: ha_state=%s, ccm_state=%s", + crm_str(ha_state), crm_str(ccm_state)); + crm_debug_2("\tcrm_state=%s, join_state=%s, expected=%s", + crm_str(crm_state), crm_str(join_state), + crm_str(exp_state)); + + } else { + /* mark it unclean */ + this_node->details->unclean = TRUE; + + crm_warn("Node %s is partially & un-expectedly down", + this_node->details->uname); + crm_info("\tha_state=%s, ccm_state=%s," + " crm_state=%s, join_state=%s, expected=%s", + crm_str(ha_state), crm_str(ccm_state), + crm_str(crm_state), crm_str(join_state), + crm_str(exp_state)); + } + return online; +} + +static gboolean +determine_online_status_fencing(crm_data_t * node_state, node_t *this_node) +{ + gboolean online = FALSE; + const char *join_state = crm_element_value(node_state, + XML_CIB_ATTR_JOINSTATE); + const char *crm_state = crm_element_value(node_state, + XML_CIB_ATTR_CRMDSTATE); + const char *ccm_state = crm_element_value(node_state, + XML_CIB_ATTR_INCCM); + const char *ha_state = crm_element_value(node_state, + XML_CIB_ATTR_HASTATE); + const char *exp_state = crm_element_value(node_state, + XML_CIB_ATTR_EXPSTATE); + + if(crm_is_true(ccm_state) + && (ha_state == NULL || safe_str_eq(ha_state, ACTIVESTATUS)) + && safe_str_eq(crm_state, ONLINESTATUS) + && safe_str_eq(join_state, CRMD_JOINSTATE_MEMBER)) { + online = TRUE; + + } else if(crm_is_true(ccm_state) == FALSE +/* && safe_str_eq(ha_state, DEADSTATUS) */ + && safe_str_eq(crm_state, OFFLINESTATUS) + && this_node->details->expected_up == FALSE) { + crm_debug("Node %s is down: join_state=%s, expected=%s", + this_node->details->uname, + crm_str(join_state), crm_str(exp_state)); + + } else if(this_node->details->expected_up == FALSE) { + crm_info("Node %s is comming up", this_node->details->uname); + crm_debug("\tha_state=%s, ccm_state=%s," + " crm_state=%s, join_state=%s, expected=%s", + crm_str(ha_state), crm_str(ccm_state), + crm_str(crm_state), crm_str(join_state), + crm_str(exp_state)); + + } else { + /* mark it unclean */ + this_node->details->unclean = TRUE; + + crm_warn("Node %s (%s) is un-expectedly down", + this_node->details->uname, this_node->details->id); + crm_info("\tha_state=%s, ccm_state=%s," + " crm_state=%s, join_state=%s, expected=%s", + crm_str(ha_state), crm_str(ccm_state), + crm_str(crm_state), crm_str(join_state), + crm_str(exp_state)); + } + return online; +} + +gboolean +determine_online_status( + crm_data_t * node_state, node_t *this_node, pe_working_set_t *data_set) +{ + int shutdown = 0; + gboolean online = FALSE; + const char *exp_state = + crm_element_value(node_state, XML_CIB_ATTR_EXPSTATE); + + if(this_node == NULL) { + pe_config_err("No node to check"); + return online; + } + + ha_msg_value_int(node_state, XML_CIB_ATTR_SHUTDOWN, &shutdown); + + this_node->details->expected_up = FALSE; + if(safe_str_eq(exp_state, CRMD_JOINSTATE_MEMBER)) { + this_node->details->expected_up = TRUE; + } + + this_node->details->shutdown = FALSE; + if(shutdown != 0) { + this_node->details->shutdown = TRUE; + this_node->details->expected_up = FALSE; + } + + if(data_set->stonith_enabled == FALSE) { + online = determine_online_status_no_fencing( + node_state, this_node); + + } else { + online = determine_online_status_fencing( + node_state, this_node); + } + + if(online) { + this_node->details->online = TRUE; + + } else { + /* remove node from contention */ + this_node->fixed = TRUE; + this_node->weight = -INFINITY; + } + + if(online && this_node->details->shutdown) { + /* dont run resources here */ + this_node->fixed = TRUE; + this_node->weight = -INFINITY; + } + + if(this_node->details->unclean) { + pe_proc_warn("Node %s is unclean", this_node->details->uname); + + } else if(this_node->details->online) { + crm_info("Node %s is %s", this_node->details->uname, + this_node->details->shutdown?"shutting down":"online"); + + } else { + crm_debug_2("Node %s is offline", this_node->details->uname); + } + + + + return online; +} + +#define set_char(x) last_rsc_id[len] = x; complete = TRUE; + +static void +increment_clone(char *last_rsc_id) +{ + gboolean complete = FALSE; + int len = 0; + + CRM_CHECK(last_rsc_id != NULL, return); + if(last_rsc_id != NULL) { + len = strlen(last_rsc_id); + } + len--; + while(complete == FALSE && len > 0) { + switch (last_rsc_id[len]) { + case 0: + len--; + break; + case '0': + set_char('1'); + break; + case '1': + set_char('2'); + break; + case '2': + set_char('3'); + break; + case '3': + set_char('4'); + break; + case '4': + set_char('5'); + break; + case '5': + set_char('6'); + break; + case '6': + set_char('7'); + break; + case '7': + set_char('8'); + break; + case '8': + set_char('9'); + break; + case '9': + last_rsc_id[len] = '0'; + len--; + break; + default: + crm_err("Unexpected char: %c (%d)", + last_rsc_id[len], len); + break; + } + } +} + +extern gboolean DeleteRsc(resource_t *rsc, node_t *node, pe_working_set_t *data_set); + +static resource_t * +unpack_find_resource( + pe_working_set_t *data_set, node_t *node, const char *rsc_id) +{ + resource_t *rsc = NULL; + gboolean is_duped_clone = FALSE; + char *alt_rsc_id = crm_strdup(rsc_id); + + while(rsc == NULL) { + crm_debug_3("looking for: %s", alt_rsc_id); + rsc = pe_find_resource(data_set->resources, alt_rsc_id); + /* no match */ + if(rsc == NULL) { + crm_debug_3("not found"); + break; + + /* not running anywhere else */ + } else if(rsc->running_on == NULL) { + crm_debug_3("not active yet"); + break; + + /* always unique */ + } else if(rsc->globally_unique) { + crm_debug_3("unique"); + break; + + /* running somewhere already but we dont care + * find another clone instead + */ + } else { + crm_debug_2("find another one"); + rsc = NULL; + is_duped_clone = TRUE; + increment_clone(alt_rsc_id); + } + } + crm_free(alt_rsc_id); + if(is_duped_clone && rsc != NULL) { + crm_info("Internally renamed %s on %s to %s", + rsc_id, node->details->uname, rsc->id); +/* rsc->name = rsc_id; */ + } + return rsc; +} + +static resource_t * +process_orphan_resource(crm_data_t *rsc_entry, node_t *node, pe_working_set_t *data_set) +{ + resource_t *rsc = NULL; + gboolean is_duped_clone = FALSE; + const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); + crm_data_t *xml_rsc = create_xml_node(NULL, XML_CIB_TAG_RESOURCE); + + crm_log_xml_info(rsc_entry, "Orphan resource"); + + pe_config_warn("Nothing known about resource %s running on %s", + rsc_id, node->details->uname); + + if(pe_find_resource(data_set->resources, rsc_id) != NULL) { + is_duped_clone = TRUE; + } + + copy_in_properties(xml_rsc, rsc_entry); + + common_unpack(xml_rsc, &rsc, NULL, data_set); + rsc->orphan = TRUE; + + data_set->resources = g_list_append(data_set->resources, rsc); + + if(data_set->stop_rsc_orphans == FALSE && is_duped_clone == FALSE) { + rsc->is_managed = FALSE; + + } else { + crm_info("Making sure orphan %s is stopped", rsc_id); + + print_resource(LOG_DEBUG_3, "Added orphan", rsc, FALSE); + + CRM_CHECK(rsc != NULL, return NULL); + slist_iter( + any_node, node_t, data_set->nodes, lpc, + rsc2node_new( + "__orphan_dont_run__", rsc, + -INFINITY, any_node, data_set); + ); + } + return rsc; +} + +static gboolean +check_rsc_parameters(resource_t *rsc, node_t *node, crm_data_t *rsc_entry, + pe_working_set_t *data_set) +{ + int attr_lpc = 0; + gboolean force_restart = FALSE; + gboolean delete_resource = FALSE; + + const char *value = NULL; + const char *old_value = NULL; + const char *attr_list[] = { + XML_ATTR_TYPE, + XML_AGENT_ATTR_CLASS, + XML_AGENT_ATTR_PROVIDER + }; + + for(; attr_lpc < DIMOF(attr_list); attr_lpc++) { + value = crm_element_value(rsc->xml, attr_list[attr_lpc]); + old_value = crm_element_value(rsc_entry, attr_list[attr_lpc]); + if(safe_str_eq(value, old_value)) { + continue; + } + + force_restart = TRUE; + crm_notice("Forcing restart of %s on %s, %s changed: %s -> %s", + rsc->id, node->details->uname, attr_list[attr_lpc], + crm_str(old_value), crm_str(value)); + } + if(force_restart) { + /* make sure the restart happens */ + stop_action(rsc, node, FALSE); + rsc->start_pending = TRUE; + delete_resource = TRUE; + } + return delete_resource; +} + +static void +process_rsc_state(resource_t *rsc, node_t *node, + enum action_fail_response on_fail, + pe_working_set_t *data_set) +{ + crm_debug_2("Resource %s is %s on %s", + rsc->id, role2text(rsc->role), + node->details->uname); + + rsc->known_on = g_list_append(rsc->known_on, node); + + if(rsc->role != RSC_ROLE_STOPPED) { + if(on_fail != action_fail_ignore) { + rsc->failed = TRUE; + crm_debug_2("Force stop"); + } + + crm_debug_2("Adding %s to %s", + rsc->id, node->details->uname); + native_add_running(rsc, node, data_set); + + if(on_fail == action_fail_ignore) { + /* nothing to do */ + } else if(node->details->unclean) { + stop_action(rsc, node, FALSE); + + } else if(on_fail == action_fail_fence) { + /* treat it as if it is still running + * but also mark the node as unclean + */ + node->details->unclean = TRUE; + stop_action(rsc, node, FALSE); + + } else if(on_fail == action_fail_block) { + /* is_managed == FALSE will prevent any + * actions being sent for the resource + */ + rsc->is_managed = FALSE; + + } else if(on_fail == action_fail_migrate) { + stop_action(rsc, node, FALSE); + + /* make sure it comes up somewhere else + * or not at all + */ + rsc2node_new("__action_migration_auto__", + rsc, -INFINITY, node, data_set); + + } else { + stop_action(rsc, node, FALSE); + } + + } else { + char *key = stop_key(rsc); + GListPtr possible_matches = find_actions(rsc->actions, key, node); + slist_iter(stop, action_t, possible_matches, lpc, + stop->optional = TRUE; + ); + crm_free(key); + +/* if(rsc->failed == FALSE && node->details->online) { */ +/* delete_resource = TRUE; */ +/* } */ + } +} + +static const char * +get_interval(crm_data_t *xml_op) +{ + const char *interval_s = NULL; + interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL); +#if CRM_DEPRECATED_SINCE_2_0_4 + if(interval_s == NULL) { + crm_data_t *params = NULL; + params = find_xml_node(xml_op, XML_TAG_PARAMS, FALSE); + if(params != NULL) { + interval_s = crm_element_value( + params, XML_LRM_ATTR_INTERVAL); + } + } +#endif + + CRM_CHECK(interval_s != NULL, + crm_err("Invalid rsc op: %s", ID(xml_op)); return "0"); + + return interval_s; +} + +static void +unpack_lrm_rsc_state( + node_t *node, crm_data_t * rsc_entry, pe_working_set_t *data_set) +{ + int fail_count = 0; + char *fail_attr = NULL; + const char *value = NULL; + const char *fail_val = NULL; + gboolean delete_resource = FALSE; + + const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); + + int max_call_id = -1; + GListPtr op_list = NULL; + GListPtr sorted_op_list = NULL; + + enum action_fail_response on_fail = FALSE; + enum rsc_role_e saved_role = RSC_ROLE_UNKNOWN; + + resource_t *rsc = unpack_find_resource(data_set, node, rsc_id); + + crm_debug_3("[%s] Processing %s on %s", + crm_element_name(rsc_entry), rsc_id, node->details->uname); + + if(rsc == NULL) { + rsc = process_orphan_resource(rsc_entry, node, data_set); + } + CRM_ASSERT(rsc != NULL); + + delete_resource = check_rsc_parameters(rsc, node, rsc_entry, data_set); + + /* process failure stickiness */ + fail_count = 0; + fail_attr = crm_concat("fail-count", rsc->id, '-'); + fail_val = g_hash_table_lookup(node->details->attrs, fail_attr); + if(fail_val != NULL) { + crm_debug("%s: %s", fail_attr, fail_val); + fail_count = crm_parse_int(fail_val, "0"); + } + crm_free(fail_attr); + if(fail_count > 0 && rsc->fail_stickiness != 0) { + rsc2node_new("fail_stickiness", rsc, + fail_count * rsc->fail_stickiness, + node, data_set); + crm_debug("Setting failure stickiness for %s on %s: %d", + rsc->id, node->details->uname, + fail_count * rsc->fail_stickiness); + } + + /* process operations */ + max_call_id = -1; + + op_list = NULL; + sorted_op_list = NULL; + + xml_child_iter_filter( + rsc_entry, rsc_op, XML_LRM_TAG_RSC_OP, + op_list = g_list_append(op_list, rsc_op); + ); + + if(op_list != NULL) { + int stop_index = -1; + int start_index = -1; + const char *task = NULL; + const char *status = NULL; + saved_role = rsc->role; + on_fail = action_fail_ignore; + rsc->role = RSC_ROLE_STOPPED; + sorted_op_list = g_list_sort(op_list, sort_op_by_callid); + + slist_iter( + rsc_op, crm_data_t, sorted_op_list, lpc, + task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK); + status = crm_element_value(rsc_op, XML_LRM_ATTR_OPSTATUS); + if(safe_str_eq(task, CRMD_ACTION_STOP) + && safe_str_eq(status, "0")) { + stop_index = lpc; + + } else if(safe_str_eq(task, CRMD_ACTION_START)) { + start_index = lpc; + + } else if(start_index <= stop_index + && safe_str_eq(task, CRMD_ACTION_STATUS)) { + const char *rc = crm_element_value(rsc_op, XML_LRM_ATTR_RC); + if(safe_str_eq(rc, "0") + || safe_str_eq(rc, "8")) { + start_index = lpc; + } + } + + unpack_rsc_op(rsc, node, rsc_op, + &max_call_id, &on_fail, data_set); + ); + + crm_debug_2("%s: Start index %d, stop index = %d", + rsc->id, start_index, stop_index); + slist_iter(rsc_op, crm_data_t, sorted_op_list, lpc, + int interval = 0; + char *key = NULL; + const char *id = ID(rsc_op); + const char *interval_s = NULL; + if(node->details->online == FALSE) { + crm_debug_4("Skipping %s/%s: node is offline", + rsc->id, node->details->uname); + break; + + } else if(start_index < stop_index) { + crm_debug_4("Skipping %s/%s: not active", + rsc->id, node->details->uname); + break; + + } else if(lpc <= start_index) { + crm_debug_4("Skipping %s/%s: old", + id, node->details->uname); + continue; + } + + interval_s = get_interval(rsc_op); + interval = crm_parse_int(interval_s, "0"); + if(interval == 0) { + crm_debug_4("Skipping %s/%s: non-recurring", + id, node->details->uname); + continue; + } + + status = crm_element_value(rsc_op, XML_LRM_ATTR_OPSTATUS); + if(safe_str_eq(status, "-1")) { + crm_debug_4("Skipping %s/%s: status", + id, node->details->uname); + continue; + } + task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK); + /* create the action */ + key = generate_op_key(rsc->id, task, interval); + crm_debug_3("Creating %s/%s", key, node->details->uname); + custom_action(rsc, key, task, node, + TRUE, TRUE, data_set); + ); + + /* no need to free the contents */ + g_list_free(sorted_op_list); + + process_rsc_state(rsc, node, on_fail, data_set); + } + + value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE); + if(value != NULL && safe_str_neq("default", value)) { + enum rsc_role_e req_role = text2role(value); + if(req_role != RSC_ROLE_UNKNOWN && req_role != rsc->next_role){ + crm_debug("%s: Overwriting calculated next role %s" + " with requested next role %s", + rsc->id, role2text(rsc->next_role), + role2text(req_role)); + rsc->next_role = req_role; + } + } + + if(delete_resource) { + DeleteRsc(rsc, node, data_set); + } + + if(saved_role > rsc->role) { + rsc->role = saved_role; + } +} + +gboolean +unpack_lrm_resources(node_t *node, crm_data_t * lrm_rsc_list, pe_working_set_t *data_set) +{ + CRM_CHECK(node != NULL, return FALSE); + + crm_debug_3("Unpacking resources on %s", node->details->uname); + + xml_child_iter_filter( + lrm_rsc_list, rsc_entry, XML_LRM_TAG_RESOURCE, + unpack_lrm_rsc_state(node, rsc_entry, data_set); + ); + + return TRUE; +} + +#define sort_return(an_int) crm_free(a_uuid); crm_free(b_uuid); return an_int + +gint +sort_op_by_callid(gconstpointer a, gconstpointer b) +{ + char *a_uuid = NULL; + char *b_uuid = NULL; + const char *a_task_id = cl_get_string(a, XML_LRM_ATTR_CALLID); + const char *b_task_id = cl_get_string(b, XML_LRM_ATTR_CALLID); + + const char *a_key = cl_get_string(a, XML_ATTR_TRANSITION_MAGIC); + const char *b_key = cl_get_string(b, XML_ATTR_TRANSITION_MAGIC); + + const char *a_xml_id = ID(a); + const char *b_xml_id = ID(b); + + int a_id = -1; + int b_id = -1; + + int a_rc = -1; + int b_rc = -1; + + int a_status = -1; + int b_status = -1; + + int a_call_id = -1; + int b_call_id = -1; + + if(safe_str_eq(a_xml_id, b_xml_id)) { + /* We have duplicate lrm_rsc_op entries in the status + * section which is unliklely to be a good thing + * - we can handle it easily enough, but we need to get + * to the bottom of why its happening. + */ + pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id); + sort_return(0); + } + + CRM_CHECK(a_task_id != NULL && b_task_id != NULL, sort_return(0)); + a_call_id = crm_parse_int(a_task_id, NULL); + b_call_id = crm_parse_int(b_task_id, NULL); + + if(a_call_id == -1 && b_call_id == -1) { + /* both are pending ops so it doesnt matter since + * stops are never pending + */ + sort_return(0); + + } else if(a_call_id >= 0 && a_call_id < b_call_id) { + crm_debug_4("%s (%d) < %s (%d) : call id", + ID(a), a_call_id, ID(b), b_call_id); + sort_return(-1); + + } else if(b_call_id >= 0 && a_call_id > b_call_id) { + crm_debug_4("%s (%d) > %s (%d) : call id", + ID(a), a_call_id, ID(b), b_call_id); + sort_return(1); + } + + crm_debug_5("%s (%d) == %s (%d) : continuing", + ID(a), a_call_id, ID(b), b_call_id); + + /* now process pending ops */ + CRM_CHECK(a_key != NULL && b_key != NULL, sort_return(0)); + CRM_CHECK(decode_transition_magic( + a_key,&a_uuid,&a_id,&a_status, &a_rc), sort_return(0)); + CRM_CHECK(decode_transition_magic( + b_key,&b_uuid,&b_id,&b_status, &b_rc), sort_return(0)); + + /* try and determin the relative age of the operation... + * some pending operations (ie. a start) may have been supuerceeded + * by a subsequent stop + * + * [a|b]_id == -1 means its a shutdown operation and _always_ comes last + */ + if(safe_str_neq(a_uuid, b_uuid) || a_id == b_id) { + /* + * some of the logic in here may be redundant... + * + * if the UUID from the TE doesnt match then one better + * be a pending operation. + * pending operations dont survive between elections and joins + * because we query the LRM directly + */ + + CRM_CHECK(a_call_id == -1 || b_call_id == -1, sort_return(0)); + CRM_CHECK(a_call_id >= 0 || b_call_id >= 0, sort_return(0)); + + if(b_call_id == -1) { + crm_debug_2("%s (%d) < %s (%d) : transition + call id", + ID(a), a_call_id, ID(b), b_call_id); + sort_return(-1); + } + + if(a_call_id == -1) { + crm_debug_2("%s (%d) > %s (%d) : transition + call id", + ID(a), a_call_id, ID(b), b_call_id); + sort_return(1); + } + + } else if((a_id >= 0 && a_id < b_id) || b_id == -1) { + crm_debug_3("%s (%d) < %s (%d) : transition", + ID(a), a_id, ID(b), b_id); + sort_return(-1); + + } else if((b_id >= 0 && a_id > b_id) || a_id == -1) { + crm_debug_3("%s (%d) > %s (%d) : transition", + ID(a), a_id, ID(b), b_id); + sort_return(1); + } + + /* we should never end up here */ + crm_err("%s (%d:%d:%s) ?? %s (%d:%d:%s) : default", + ID(a), a_call_id, a_id, a_uuid, ID(b), b_call_id, b_id, b_uuid); + CRM_CHECK(FALSE, sort_return(0)); +} + +static gboolean +check_action_definition(resource_t *rsc, node_t *active_node, crm_data_t *xml_op, + pe_working_set_t *data_set) +{ + char *key = NULL; + int interval = 0; + const char *interval_s = NULL; + + gboolean did_change = FALSE; + + crm_data_t *pnow = NULL; + GHashTable *local_rsc_params = NULL; + + char *pnow_digest = NULL; + const char *param_digest = NULL; + char *local_param_digest = NULL; + +#if CRM_DEPRECATED_SINCE_2_0_4 + crm_data_t *params = NULL; +#endif + + action_t *action = NULL; + const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); + const char *op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); + + CRM_CHECK(active_node != NULL, return FALSE); + + interval_s = get_interval(xml_op); + interval = crm_parse_int(interval_s, "0"); + key = generate_op_key(rsc->id, task, interval); + + if(interval > 0) { + crm_data_t *op_match = NULL; + + crm_debug_2("Checking parameters for %s %s", key, task); + op_match = find_rsc_op_entry(rsc, key); + + if(op_match == NULL && data_set->stop_action_orphans) { + /* create a cancel action */ + action_t *cancel = NULL; + crm_info("Orphan action will be stopped: %s on %s", + key, active_node->details->uname); + + crm_free(key); + key = generate_op_key(rsc->id, CRMD_ACTION_CANCEL, interval); + + cancel = custom_action( + rsc, key, CRMD_ACTION_CANCEL, active_node, + FALSE, TRUE, data_set); + + add_hash_param(cancel->meta, XML_LRM_ATTR_TASK, task); + add_hash_param(cancel->meta, + XML_LRM_ATTR_INTERVAL, interval_s); + + custom_action_order( + rsc, NULL, cancel, + rsc, stop_key(rsc), NULL, + pe_ordering_optional, data_set); + } + if(op_match == NULL) { + crm_debug("Orphan action detected: %s on %s", + key, active_node->details->uname); + return TRUE; + } + } + + action = custom_action(rsc, key, task, active_node, TRUE, FALSE, data_set); + + local_rsc_params = g_hash_table_new_full( + g_str_hash, g_str_equal, + g_hash_destroy_str, g_hash_destroy_str); + + unpack_instance_attributes( + rsc->xml, XML_TAG_ATTR_SETS, active_node->details->attrs, + local_rsc_params, NULL, data_set->now); + + pnow = create_xml_node(NULL, XML_TAG_PARAMS); + g_hash_table_foreach(action->extra, hash2field, pnow); + g_hash_table_foreach(rsc->parameters, hash2field, pnow); + g_hash_table_foreach(local_rsc_params, hash2field, pnow); + + filter_action_parameters(pnow, op_version); + pnow_digest = calculate_xml_digest(pnow, TRUE); + param_digest = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST); + +#if CRM_DEPRECATED_SINCE_2_0_4 + if(param_digest == NULL) { + params = find_xml_node(xml_op, XML_TAG_PARAMS, TRUE); + } + if(params != NULL) { + crm_data_t *local_params = copy_xml(params); + + crm_warn("Faking parameter digest creation for %s", ID(xml_op)); + filter_action_parameters(local_params, op_version); + xml_remove_prop(local_params, "interval"); + xml_remove_prop(local_params, "timeout"); + crm_log_xml_warn(local_params, "params:used"); + + local_param_digest = calculate_xml_digest(local_params, TRUE); + param_digest = local_param_digest; + + free_xml(local_params); + } +#endif + + if(safe_str_neq(pnow_digest, param_digest)) { +#if CRM_DEPRECATED_SINCE_2_0_4 + if(params) { + crm_data_t *local_params = copy_xml(params); + filter_action_parameters(local_params, op_version); + xml_remove_prop(local_params, "interval"); + xml_remove_prop(local_params, "timeout"); + + free_xml(local_params); + } +#endif + did_change = TRUE; + crm_log_xml_info(pnow, "params:calc"); + crm_warn("Parameters to %s on %s changed: recorded %s vs. calculated %s", + ID(xml_op), active_node->details->uname, + crm_str(param_digest), pnow_digest); + + key = generate_op_key(rsc->id, task, interval); + custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set); + } + + free_xml(pnow); + crm_free(pnow_digest); + crm_free(local_param_digest); + g_hash_table_destroy(local_rsc_params); + + pe_free_action(action); + + return did_change; +} + +gboolean +unpack_rsc_op(resource_t *rsc, node_t *node, crm_data_t *xml_op, + int *max_call_id, enum action_fail_response *on_fail, + pe_working_set_t *data_set) +{ + const char *id = NULL; + const char *task = NULL; + const char *task_id = NULL; + const char *actual_rc = NULL; +/* const char *target_rc = NULL; */ + const char *task_status = NULL; + const char *interval_s = NULL; + const char *op_digest = NULL; + + int interval = 0; + int task_id_i = -1; + int task_status_i = -2; + int actual_rc_i = 0; + + action_t *action = NULL; + gboolean is_probe = FALSE; + gboolean is_stop_action = FALSE; + + CRM_CHECK(rsc != NULL, return FALSE); + CRM_CHECK(node != NULL, return FALSE); + CRM_CHECK(xml_op != NULL, return FALSE); + + id = ID(xml_op); + task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); + task_id = crm_element_value(xml_op, XML_LRM_ATTR_CALLID); + task_status = crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS); + op_digest = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST); + + CRM_CHECK(id != NULL, return FALSE); + CRM_CHECK(task != NULL, return FALSE); + CRM_CHECK(task_status != NULL, return FALSE); + + task_status_i = crm_parse_int(task_status, NULL); + + CRM_CHECK(task_status_i <= LRM_OP_ERROR, return FALSE); + CRM_CHECK(task_status_i >= LRM_OP_PENDING, return FALSE); + + if(safe_str_eq(task, CRMD_ACTION_NOTIFY)) { + /* safe to ignore these */ + return TRUE; + } + + crm_debug_2("Unpacking task %s/%s (call_id=%s, status=%s) on %s (role=%s)", + id, task, task_id, task_status, node->details->uname, + role2text(rsc->role)); + + interval_s = get_interval(xml_op); + interval = crm_parse_int(interval_s, "0"); + + if(interval == 0 && safe_str_eq(task, CRMD_ACTION_STATUS)) { + is_probe = TRUE; + + } else if(interval > 0 && rsc->role < RSC_ROLE_STARTED) { + crm_debug_2("Skipping recurring action %s for stopped resource", id); + return FALSE; + } + + if(rsc->orphan) { + crm_debug_2("Skipping param check for orphan: %s %s", + rsc->id, task); + + } else if(safe_str_eq(task, CRMD_ACTION_STOP)) { + crm_debug_2("Ignoring stop params: %s", id); + + } else if(is_probe || safe_str_eq(task, CRMD_ACTION_START) || interval > 0) { + crm_debug_3("Checking resource definition: %s", rsc->id); + check_action_definition(rsc, node, xml_op, data_set); + } + + if(safe_str_eq(task, CRMD_ACTION_STOP)) { + is_stop_action = TRUE; + } + + if(task_status_i != LRM_OP_PENDING) { + task_id_i = crm_parse_int(task_id, "-1"); + + CRM_CHECK(task_id != NULL, return FALSE); + CRM_CHECK(task_id_i >= 0, return FALSE); + CRM_CHECK(task_id_i > *max_call_id, return FALSE); + } + + if(*max_call_id < task_id_i) { + *max_call_id = task_id_i; + } + + if(node->details->unclean) { + crm_debug_2("Node %s (where %s is running) is unclean." + " Further action depends on the value of %s", + node->details->uname, rsc->id, XML_RSC_ATTR_STOPFAIL); + } + + actual_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC); + CRM_CHECK(actual_rc != NULL, return FALSE); + actual_rc_i = crm_parse_int(actual_rc, NULL); + + if(EXECRA_NOT_RUNNING == actual_rc_i) { + if(is_probe) { + /* treat these like stops */ + is_stop_action = TRUE; + } + if(is_stop_action) { + task_status_i = LRM_OP_DONE; + } else { + CRM_CHECK(task_status_i == LRM_OP_ERROR, + task_status_i = LRM_OP_ERROR); + } + + } else if(EXECRA_RUNNING_MASTER == actual_rc_i) { + if(is_probe + || (rsc->role == RSC_ROLE_MASTER + && safe_str_eq(task, CRMD_ACTION_STATUS))) { + task_status_i = LRM_OP_DONE; + } else { + if(rsc->role != RSC_ROLE_MASTER) { + crm_err("%s reported %s in master mode on %s", + id, rsc->id, + node->details->uname); + } + + CRM_CHECK(task_status_i == LRM_OP_ERROR, + task_status_i = LRM_OP_ERROR); + } + rsc->role = RSC_ROLE_MASTER; + + } else if(EXECRA_FAILED_MASTER == actual_rc_i) { + rsc->role = RSC_ROLE_MASTER; + task_status_i = LRM_OP_ERROR; + + } else if(EXECRA_OK == actual_rc_i + && interval > 0 + && rsc->role == RSC_ROLE_MASTER) { + /* catch status ops that return 0 instead of 8 while they + * are supposed to be in master mode + */ + task_status_i = LRM_OP_ERROR; + } + + if(task_status_i == LRM_OP_ERROR + || task_status_i == LRM_OP_TIMEOUT + || task_status_i == LRM_OP_NOTSUPPORTED) { + action = custom_action(rsc, crm_strdup(id), task, NULL, + TRUE, FALSE, data_set); + if(action->on_fail == action_fail_ignore) { + task_status_i = LRM_OP_DONE; + } + } + + switch(task_status_i) { + case LRM_OP_PENDING: + if(safe_str_eq(task, CRMD_ACTION_START)) { + rsc->start_pending = TRUE; + rsc->role = RSC_ROLE_STARTED; + + } else if(safe_str_eq(task, CRMD_ACTION_PROMOTE)) { + rsc->role = RSC_ROLE_MASTER; + } + break; + + case LRM_OP_DONE: + crm_debug_3("%s/%s completed on %s", + rsc->id, task, node->details->uname); + + if(is_stop_action) { + rsc->role = RSC_ROLE_STOPPED; + + /* clear any previous failure actions */ + *on_fail = action_fail_ignore; + rsc->next_role = RSC_ROLE_UNKNOWN; + + } else if(safe_str_eq(task, CRMD_ACTION_PROMOTE)) { + rsc->role = RSC_ROLE_MASTER; + + } else if(safe_str_eq(task, CRMD_ACTION_DEMOTE)) { + rsc->role = RSC_ROLE_SLAVE; + + } else if(rsc->role < RSC_ROLE_STARTED) { + crm_debug_3("%s active on %s", + rsc->id, node->details->uname); + rsc->role = RSC_ROLE_STARTED; + } + break; + + case LRM_OP_ERROR: + case LRM_OP_TIMEOUT: + case LRM_OP_NOTSUPPORTED: + crm_warn("Processing failed op (%s) for %s on %s", + id, rsc->id, node->details->uname); + + if(*on_fail < action->on_fail) { + *on_fail = action->on_fail; + } + + if(task_status_i == LRM_OP_NOTSUPPORTED + || is_stop_action + || safe_str_eq(task, CRMD_ACTION_START) ) { + crm_warn("Handling failed %s for %s on %s", + task, rsc->id, node->details->uname); + rsc2node_new("dont_run__failed_stopstart", + rsc, -INFINITY, node, data_set); + } + + if(safe_str_eq(task, CRMD_ACTION_PROMOTE)) { + rsc->role = RSC_ROLE_MASTER; + + } else if(safe_str_eq(task, CRMD_ACTION_DEMOTE)) { + rsc->role = RSC_ROLE_MASTER; + + } else if(rsc->role < RSC_ROLE_STARTED) { + rsc->role = RSC_ROLE_STARTED; + } + + crm_debug_2("Resource %s: role=%s, unclean=%s, on_fail=%s, fail_role=%s", + rsc->id, role2text(rsc->role), + node->details->unclean?"true":"false", + fail2text(action->on_fail), + role2text(action->fail_role)); + + if(action->fail_role != RSC_ROLE_STARTED + && rsc->next_role < action->fail_role) { + rsc->next_role = action->fail_role; + } + + if(action->fail_role == RSC_ROLE_STOPPED) { + /* make sure it doesnt come up again */ + pe_free_shallow_adv(rsc->allowed_nodes, TRUE); + rsc->allowed_nodes = node_list_dup( + data_set->nodes, FALSE, FALSE); + slist_iter( + node, node_t, rsc->allowed_nodes, lpc, + node->weight = -INFINITY; + ); + } + + pe_free_action(action); + action = NULL; + break; + case LRM_OP_CANCELLED: + /* do nothing?? */ + pe_err("Dont know what to do for cancelled ops yet"); + break; + } + + crm_debug_3("Resource %s after %s: role=%s", + rsc->id, task, role2text(rsc->role)); + + pe_free_action(action); + + return TRUE; +} + +gboolean +rsc_colocation_new(const char *id, enum con_strength strength, + resource_t *rsc_lh, resource_t *rsc_rh, + const char *state_lh, const char *state_rh) +{ + rsc_colocation_t *new_con = NULL; + rsc_colocation_t *inverted_con = NULL; + + if(rsc_lh == NULL){ + pe_config_err("No resource found for LHS %s", id); + return FALSE; + + } else if(rsc_rh == NULL){ + pe_config_err("No resource found for RHS of %s", id); + return FALSE; + } + + crm_malloc0(new_con, sizeof(rsc_colocation_t)); + if(new_con == NULL) { + return FALSE; + } + if(safe_str_eq(state_lh, CRMD_ACTION_STARTED)) { + state_lh = NULL; + } + if(safe_str_eq(state_rh, CRMD_ACTION_STARTED)) { + state_rh = NULL; + } + + new_con->id = id; + new_con->rsc_lh = rsc_lh; + new_con->rsc_rh = rsc_rh; + new_con->strength = strength; + new_con->state_lh = state_lh; + new_con->state_rh = state_rh; + + inverted_con = invert_constraint(new_con); + + crm_debug_4("Adding constraint %s (%p) to %s", + new_con->id, new_con, rsc_lh->id); + + rsc_lh->rsc_cons = g_list_insert_sorted( + rsc_lh->rsc_cons, new_con, sort_cons_strength); + + crm_debug_4("Adding constraint %s (%p) to %s", + inverted_con->id, inverted_con, rsc_rh->id); + + rsc_rh->rsc_cons = g_list_insert_sorted( + rsc_rh->rsc_cons, inverted_con, sort_cons_strength); + + return TRUE; +} + +/* LHS before RHS */ +gboolean +custom_action_order( + resource_t *lh_rsc, char *lh_action_task, action_t *lh_action, + resource_t *rh_rsc, char *rh_action_task, action_t *rh_action, + enum pe_ordering type, pe_working_set_t *data_set) +{ + order_constraint_t *order = NULL; + + if((lh_action == NULL && lh_rsc == NULL) + || (rh_action == NULL && rh_rsc == NULL)){ + pe_config_err("Invalid inputs lh_rsc=%p, lh_a=%p," + " rh_rsc=%p, rh_a=%p", + lh_rsc, lh_action, rh_rsc, rh_action); + crm_free(lh_action_task); + crm_free(rh_action_task); + return FALSE; + } + + crm_malloc0(order, sizeof(order_constraint_t)); + if(order == NULL) { return FALSE; } + + order->id = data_set->order_id++; + order->type = type; + order->lh_rsc = lh_rsc; + order->rh_rsc = rh_rsc; + order->lh_action = lh_action; + order->rh_action = rh_action; + order->lh_action_task = lh_action_task; + order->rh_action_task = rh_action_task; + + data_set->ordering_constraints = g_list_append( + data_set->ordering_constraints, order); + + if(lh_rsc != NULL && rh_rsc != NULL) { + crm_debug_4("Created ordering constraint %d (%s):" + " %s/%s before %s/%s", + order->id, ordering_type2text(order->type), + lh_rsc->id, lh_action_task, + rh_rsc->id, rh_action_task); + + } else if(lh_rsc != NULL) { + crm_debug_4("Created ordering constraint %d (%s):" + " %s/%s before action %d (%s)", + order->id, ordering_type2text(order->type), + lh_rsc->id, lh_action_task, + rh_action->id, rh_action_task); + + } else if(rh_rsc != NULL) { + crm_debug_4("Created ordering constraint %d (%s):" + " action %d (%s) before %s/%s", + order->id, ordering_type2text(order->type), + lh_action->id, lh_action_task, + rh_rsc->id, rh_action_task); + + } else { + crm_debug_4("Created ordering constraint %d (%s):" + " action %d (%s) before action %d (%s)", + order->id, ordering_type2text(order->type), + lh_action->id, lh_action_task, + rh_action->id, rh_action_task); + } + + return TRUE; +} + +gboolean +unpack_rsc_colocation(crm_data_t * xml_obj, pe_working_set_t *data_set) +{ + enum con_strength strength_e = pecs_ignore; + + const char *id = crm_element_value(xml_obj, XML_ATTR_ID); + const char *id_rh = crm_element_value(xml_obj, XML_CONS_ATTR_TO); + const char *id_lh = crm_element_value(xml_obj, XML_CONS_ATTR_FROM); + const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); + const char *state_lh = crm_element_value(xml_obj, XML_RULE_ATTR_FROMSTATE); + const char *state_rh = crm_element_value(xml_obj, XML_RULE_ATTR_TOSTATE); + + resource_t *rsc_lh = pe_find_resource(data_set->resources, id_lh); + resource_t *rsc_rh = pe_find_resource(data_set->resources, id_rh); + + if(rsc_lh == NULL) { + pe_config_err("No resource (con=%s, rsc=%s)", id, id_lh); + return FALSE; + + } else if(rsc_rh == NULL) { + pe_config_err("No resource (con=%s, rsc=%s)", id, id_rh); + return FALSE; + } + + /* the docs indicate that only +/- INFINITY are allowed, + * but no-one ever reads the docs so all positive values will + * count as "must" and negative values as "must not" + */ + if(score == NULL || score[0] != '-') { + strength_e = pecs_must; + } else { + strength_e = pecs_must_not; + } + return rsc_colocation_new(id, strength_e, rsc_lh, rsc_rh, + state_lh, state_rh); +} + +static const char * +invert_action(const char *action) +{ + if(safe_str_eq(action, CRMD_ACTION_START)) { + return CRMD_ACTION_STOP; + + } else if(safe_str_eq(action, CRMD_ACTION_STOP)) { + return CRMD_ACTION_START; + + } else if(safe_str_eq(action, CRMD_ACTION_PROMOTE)) { + return CRMD_ACTION_DEMOTE; + + } else if(safe_str_eq(action, CRMD_ACTION_DEMOTE)) { + return CRMD_ACTION_PROMOTE; + + } else if(safe_str_eq(action, CRMD_ACTION_STARTED)) { + return CRMD_ACTION_STOPPED; + + } else if(safe_str_eq(action, CRMD_ACTION_STOPPED)) { + return CRMD_ACTION_STARTED; + + } + pe_err("Unknown action: %s", action); + return NULL; +} + + +gboolean +unpack_rsc_order(crm_data_t * xml_obj, pe_working_set_t *data_set) +{ + gboolean symmetrical_bool = TRUE; + + const char *id = crm_element_value(xml_obj, XML_ATTR_ID); + const char *type = crm_element_value(xml_obj, XML_ATTR_TYPE); + const char *id_rh = crm_element_value(xml_obj, XML_CONS_ATTR_TO); + const char *id_lh = crm_element_value(xml_obj, XML_CONS_ATTR_FROM); + const char *action = crm_element_value(xml_obj, XML_CONS_ATTR_ACTION); + const char *action_rh = crm_element_value(xml_obj, XML_CONS_ATTR_TOACTION); + + const char *symmetrical = crm_element_value( + xml_obj, XML_CONS_ATTR_SYMMETRICAL); + + resource_t *rsc_lh = NULL; + resource_t *rsc_rh = NULL; + + if(xml_obj == NULL) { + pe_config_err("No constraint object to process."); + return FALSE; + + } else if(id == NULL) { + pe_config_err("%s constraint must have an id", + crm_element_name(xml_obj)); + return FALSE; + + } else if(id_lh == NULL || id_rh == NULL) { + pe_config_err("Constraint %s needs two sides lh: %s rh: %s", + id, crm_str(id_lh), crm_str(id_rh)); + return FALSE; + } + + if(action == NULL) { + action = CRMD_ACTION_START; + } + if(action_rh == NULL) { + action_rh = action; + } + CRM_CHECK(action != NULL, return FALSE); + CRM_CHECK(action_rh != NULL, return FALSE); + + if(safe_str_eq(type, "before")) { + id_lh = crm_element_value(xml_obj, XML_CONS_ATTR_TO); + id_rh = crm_element_value(xml_obj, XML_CONS_ATTR_FROM); + action = crm_element_value(xml_obj, XML_CONS_ATTR_TOACTION); + action_rh = crm_element_value(xml_obj, XML_CONS_ATTR_ACTION); + if(action_rh == NULL) { + action_rh = CRMD_ACTION_START; + } + if(action == NULL) { + action = action_rh; + } + } + + CRM_CHECK(action != NULL, return FALSE); + CRM_CHECK(action_rh != NULL, return FALSE); + + rsc_lh = pe_find_resource(data_set->resources, id_rh); + rsc_rh = pe_find_resource(data_set->resources, id_lh); + + if(rsc_lh == NULL) { + pe_config_err("Constraint %s: no resource found for LHS of %s", id, id_lh); + return FALSE; + + } else if(rsc_rh == NULL) { + pe_config_err("Constraint %s: no resource found for RHS of %s", id, id_rh); + return FALSE; + } + + custom_action_order( + rsc_lh, generate_op_key(rsc_lh->id, action, 0), NULL, + rsc_rh, generate_op_key(rsc_rh->id, action_rh, 0), NULL, + pe_ordering_optional, data_set); + + if(rsc_rh->restart_type == pe_restart_restart + && safe_str_eq(action, action_rh)) { + if(safe_str_eq(action, CRMD_ACTION_START)) { + crm_debug_2("Recover start-start: %s-%s", + rsc_lh->id, rsc_rh->id); + order_start_start(rsc_lh, rsc_rh, pe_ordering_recover); + } else if(safe_str_eq(action, CRMD_ACTION_STOP)) { + crm_debug_2("Recover stop-stop: %s-%s", + rsc_rh->id, rsc_lh->id); + order_stop_stop(rsc_rh, rsc_lh, pe_ordering_recover); + } + } + + cl_str_to_boolean(symmetrical, &symmetrical_bool); + if(symmetrical_bool == FALSE) { + return TRUE; + } + + action = invert_action(action); + action_rh = invert_action(action_rh); + + custom_action_order( + rsc_rh, generate_op_key(rsc_rh->id, action_rh, 0), NULL, + rsc_lh, generate_op_key(rsc_lh->id, action, 0), NULL, + pe_ordering_optional, data_set); + + if(rsc_lh->restart_type == pe_restart_restart + && safe_str_eq(action, action_rh)) { + if(safe_str_eq(action, CRMD_ACTION_START)) { + crm_debug_2("Recover start-start (2): %s-%s", + rsc_lh->id, rsc_rh->id); + order_start_start(rsc_lh, rsc_rh, pe_ordering_recover); + } else if(safe_str_eq(action, CRMD_ACTION_STOP)) { + crm_debug_2("Recover stop-stop (2): %s-%s", + rsc_rh->id, rsc_lh->id); + order_stop_stop(rsc_rh, rsc_lh, pe_ordering_recover); + } + } + + return TRUE; +} + +gboolean +add_node_attrs(crm_data_t *xml_obj, node_t *node, pe_working_set_t *data_set) +{ + g_hash_table_insert(node->details->attrs, + crm_strdup("#"XML_ATTR_UNAME), + crm_strdup(node->details->uname)); + g_hash_table_insert(node->details->attrs, + crm_strdup("#"XML_ATTR_ID), + crm_strdup(node->details->id)); + if(safe_str_eq(node->details->id, data_set->dc_uuid)) { + data_set->dc_node = node; + node->details->is_dc = TRUE; + g_hash_table_insert(node->details->attrs, + crm_strdup("#"XML_ATTR_DC), + crm_strdup(XML_BOOLEAN_TRUE)); + } else { + g_hash_table_insert(node->details->attrs, + crm_strdup("#"XML_ATTR_DC), + crm_strdup(XML_BOOLEAN_FALSE)); + } + + unpack_instance_attributes( + xml_obj, XML_TAG_ATTR_SETS, NULL, + node->details->attrs, NULL, data_set->now); + + return TRUE; +} + +gboolean +unpack_rsc_location(crm_data_t * xml_obj, pe_working_set_t *data_set) +{ + const char *id_lh = crm_element_value(xml_obj, "rsc"); + const char *id = crm_element_value(xml_obj, XML_ATTR_ID); + resource_t *rsc_lh = pe_find_resource(data_set->resources, id_lh); + + if(rsc_lh == NULL) { + /* only a warn as BSC adds the constraint then the resource */ + pe_config_warn("No resource (con=%s, rsc=%s)", id, id_lh); + return FALSE; + + } else if(rsc_lh->is_managed == FALSE) { + crm_debug_2("Ignoring constraint %s: resource %s not managed", + id, id_lh); + return FALSE; + } + + xml_child_iter_filter( + xml_obj, rule_xml, XML_TAG_RULE, + crm_debug_2("Unpacking %s/%s", id, ID(rule_xml)); + generate_location_rule(rsc_lh, rule_xml, data_set); + ); + return TRUE; +} + +rsc_to_node_t * +generate_location_rule( + resource_t *rsc, crm_data_t *rule_xml, pe_working_set_t *data_set) +{ + const char *rule_id = NULL; + const char *score = NULL; + const char *boolean = NULL; + const char *role = NULL; + const char *attr_score = NULL; + + GListPtr match_L = NULL; + + int score_f = 0; + gboolean do_and = TRUE; + gboolean accept = TRUE; + gboolean raw_score = TRUE; + + rsc_to_node_t *location_rule = NULL; + + rule_id = crm_element_value(rule_xml, XML_ATTR_ID); + boolean = crm_element_value(rule_xml, XML_RULE_ATTR_BOOLEAN_OP); + role = crm_element_value(rule_xml, XML_RULE_ATTR_ROLE); + + crm_debug_2("Processing rule: %s", rule_id); + + if(role != NULL && text2role(role) == RSC_ROLE_UNKNOWN) { + pe_err("Bad role specified for %s: %s", rule_id, role); + return NULL; + } + + score = crm_element_value(rule_xml, XML_RULE_ATTR_SCORE); + if(score != NULL) { + score_f = char2score(score); + + } else { + score = crm_element_value( + rule_xml, XML_RULE_ATTR_SCORE_ATTRIBUTE); + if(score == NULL) { + score = crm_element_value( + rule_xml, XML_RULE_ATTR_SCORE_MANGLED); + } + if(score != NULL) { + raw_score = FALSE; + } + } + + if(safe_str_eq(boolean, "or")) { + do_and = FALSE; + } + + location_rule = rsc2node_new(rule_id, rsc, 0, NULL, data_set); + + if(location_rule == NULL) { + return NULL; + } + if(role != NULL) { + crm_debug_2("Setting role filter: %s", role); + location_rule->role_filter = text2role(role); + } + if(do_and) { + match_L = node_list_dup(data_set->nodes, TRUE, FALSE); + slist_iter( + node, node_t, match_L, lpc, + node->weight = score_f; + ); + } + + xml_child_iter( + rule_xml, expr, + + enum expression_type type = find_expression_type(expr); + if(type == not_expr) { + pe_err("Expression <%s id=%s...> is not valid", + crm_element_name(expr), crm_str(ID(expr))); + continue; + } + + slist_iter( + node, node_t, data_set->nodes, lpc, + + if(type == nested_rule) { + accept = test_rule( + expr, node->details->attrs, + RSC_ROLE_UNKNOWN, data_set->now); + } else { + accept = test_expression( + expr, node->details->attrs, + RSC_ROLE_UNKNOWN, data_set->now); + } + + if(raw_score == FALSE) { + attr_score = g_hash_table_lookup( + node->details->attrs, score); + if(attr_score == NULL) { + accept = FALSE; + pe_warn("node %s did not have a value" + " for %s", + node->details->uname, score); + } else { + score_f = char2score(score); + } + } + + if(!do_and && accept) { + node_t *local = pe_find_node_id( + match_L, node->details->id); + if(local == NULL) { + local = node_copy(node); + match_L = g_list_append(match_L, local); + } + local->weight = merge_weights( + local->weight, score_f); + crm_debug_5("node %s already matched", + node->details->uname); + + } else if(do_and && !accept) { + /* remove it */ + node_t *delete = pe_find_node_id( + match_L, node->details->id); + if(delete != NULL) { + match_L = g_list_remove(match_L,delete); + crm_debug_5("node %s did not match", + node->details->uname); + } + crm_free(delete); + } + ); + ); + + location_rule->node_list_rh = match_L; + if(location_rule->node_list_rh == NULL) { + crm_debug_2("No matching nodes for rule %s", rule_id); + return NULL; + } + + crm_debug_3("%s: %d nodes matched", + rule_id, g_list_length(location_rule->node_list_rh)); + crm_action_debug_3(print_rsc_to_node("Added", location_rule, FALSE)); + return location_rule; +} diff --git a/lib/crm/pengine/utils.c b/lib/crm/pengine/utils.c new file mode 100644 index 0000000000..d135308d0a --- /dev/null +++ b/lib/crm/pengine/utils.c @@ -0,0 +1,1651 @@ +/* $Id: utils.c,v 1.1 2006/05/31 14:59:12 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 + +void print_str_str(gpointer key, gpointer value, gpointer user_data); +gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data); +void unpack_operation( + action_t *action, crm_data_t *xml_obj, pe_working_set_t* data_set); + +void +pe_free_shallow(GListPtr alist) +{ + pe_free_shallow_adv(alist, TRUE); +} + +void +pe_free_shallow_adv(GListPtr alist, gboolean with_data) +{ + GListPtr item; + GListPtr item_next = alist; + while(item_next != NULL) { + item = item_next; + item_next = item_next->next; + + if(with_data) { +/* crm_debug_5("freeing %p", item->data); */ + crm_free(item->data); + } + + item->data = NULL; + item->next = NULL; + g_list_free(item); + } +} + +node_t * +pe_find_node_id(GListPtr nodes, const char *id) +{ + unsigned lpc = 0; + node_t *node = NULL; + + for(lpc = 0; lpc < g_list_length(nodes); lpc++) { + node = g_list_nth_data(nodes, lpc); + if(safe_str_eq(node->details->id, id)) { + return node; + } + } + /* error */ + return NULL; +} + +node_t * +node_copy(node_t *this_node) +{ + node_t *new_node = NULL; + + CRM_CHECK(this_node != NULL, return NULL); + crm_malloc0(new_node, sizeof(node_t)); + + CRM_CHECK(new_node != NULL, return NULL); + + crm_debug_5("Copying %p (%s) to %p", + this_node, this_node->details->uname, new_node); + new_node->weight = this_node->weight; + new_node->fixed = this_node->fixed; + new_node->details = this_node->details; + + return new_node; +} + +/* are the contents of list1 and list2 equal + * nodes with weight < 0 are ignored if filter == TRUE + * + * slow but linear + * + */ +gboolean +node_list_eq(GListPtr list1, GListPtr list2, gboolean filter) +{ + node_t *other_node; + + GListPtr lhs = list1; + GListPtr rhs = list2; + + slist_iter( + node, node_t, lhs, lpc, + + if(node == NULL || (filter && node->weight < 0)) { + continue; + } + + other_node = (node_t*) + pe_find_node_id(rhs, node->details->id); + + if(other_node == NULL || other_node->weight < 0) { + return FALSE; + } + ); + + lhs = list2; + rhs = list1; + + slist_iter( + node, node_t, lhs, lpc, + + if(node == NULL || (filter && node->weight < 0)) { + continue; + } + + other_node = (node_t*) + pe_find_node_id(rhs, node->details->id); + + if(other_node == NULL || other_node->weight < 0) { + return FALSE; + } + ); + + return TRUE; +} + +/* the intersection of list1 and list2 + */ +GListPtr +node_list_and(GListPtr list1, GListPtr list2, gboolean filter) +{ + GListPtr result = NULL; + unsigned lpc = 0; + + for(lpc = 0; lpc < g_list_length(list1); lpc++) { + node_t *node = (node_t*)g_list_nth_data(list1, lpc); + node_t *other_node = pe_find_node_id(list2, node->details->id); + node_t *new_node = NULL; + + if(other_node != NULL) { + new_node = node_copy(node); + } + + if(new_node != NULL) { + crm_debug_4("%s: %d + %d", node->details->uname, + other_node->weight, new_node->weight); + new_node->weight = merge_weights( + new_node->weight, other_node->weight); + + crm_debug_3("New node weight for %s: %d", + new_node->details->uname, new_node->weight); + + if(filter && new_node->weight < 0) { + crm_free(new_node); + new_node = NULL; + } + } + + if(new_node != NULL) { + result = g_list_append(result, new_node); + } + } + + return result; +} + + +/* list1 - list2 */ +GListPtr +node_list_minus(GListPtr list1, GListPtr list2, gboolean filter) +{ + GListPtr result = NULL; + + slist_iter( + node, node_t, list1, lpc, + node_t *other_node = pe_find_node_id(list2, node->details->id); + node_t *new_node = NULL; + + if(node == NULL || other_node != NULL + || (filter && node->weight < 0)) { + continue; + + } + new_node = node_copy(node); + result = g_list_append(result, new_node); + ); + + crm_debug_3("Minus result len: %d", g_list_length(result)); + + return result; +} + +/* list1 + list2 - (intersection of list1 and list2) */ +GListPtr +node_list_xor(GListPtr list1, GListPtr list2, gboolean filter) +{ + GListPtr result = NULL; + + slist_iter( + node, node_t, list1, lpc, + node_t *new_node = NULL; + node_t *other_node = (node_t*) + pe_find_node_id(list2, node->details->id); + + if(node == NULL || other_node != NULL + || (filter && node->weight < 0)) { + continue; + } + new_node = node_copy(node); + result = g_list_append(result, new_node); + ); + + + slist_iter( + node, node_t, list2, lpc, + node_t *new_node = NULL; + node_t *other_node = (node_t*) + pe_find_node_id(list1, node->details->id); + + if(node == NULL || other_node != NULL + || (filter && node->weight < 0)) { + continue; + } + new_node = node_copy(node); + result = g_list_append(result, new_node); + ); + + crm_debug_3("Xor result len: %d", g_list_length(result)); + return result; +} + +GListPtr +node_list_or(GListPtr list1, GListPtr list2, gboolean filter) +{ + node_t *other_node = NULL; + GListPtr result = NULL; + gboolean needs_filter = FALSE; + + result = node_list_dup(list1, FALSE, filter); + + slist_iter( + node, node_t, list2, lpc, + + if(node == NULL) { + continue; + } + + other_node = (node_t*)pe_find_node_id( + result, node->details->id); + + if(other_node != NULL) { + crm_debug_4("%s + %s: %d + %d", + node->details->uname, + other_node->details->uname, + node->weight, other_node->weight); + other_node->weight = merge_weights( + other_node->weight, node->weight); + + if(filter && node->weight < 0) { + needs_filter = TRUE; + } + + } else if(filter == FALSE || node->weight >= 0) { + node_t *new_node = node_copy(node); + result = g_list_append(result, new_node); + } + ); + + /* not the neatest way, but the most expedient for now */ + if(filter && needs_filter) { + GListPtr old_result = result; + result = node_list_dup(old_result, FALSE, filter); + pe_free_shallow_adv(old_result, TRUE); + } + + + return result; +} + +GListPtr +node_list_dup(GListPtr list1, gboolean reset, gboolean filter) +{ + GListPtr result = NULL; + + slist_iter( + this_node, node_t, list1, lpc, + node_t *new_node = NULL; + if(filter && this_node->weight < 0) { + continue; + } + + new_node = node_copy(this_node); + if(reset) { + new_node->weight = 0; + } + if(new_node != NULL) { + result = g_list_append(result, new_node); + } + ); + + return result; +} + +/* only for rsc_colocation constraints */ +rsc_colocation_t * +invert_constraint(rsc_colocation_t *constraint) +{ + rsc_colocation_t *inverted_con = NULL; + + crm_debug_3("Inverting constraint"); + if(constraint == NULL) { + pe_err("Cannot invert NULL constraint"); + return NULL; + } + + crm_malloc0(inverted_con, sizeof(rsc_colocation_t)); + + if(inverted_con == NULL) { + return NULL; + } + + inverted_con->id = constraint->id; + inverted_con->strength = constraint->strength; + + /* swap the direction */ + inverted_con->rsc_lh = constraint->rsc_rh; + inverted_con->rsc_rh = constraint->rsc_lh; + inverted_con->state_lh = constraint->state_rh; + inverted_con->state_rh = constraint->state_lh; + + crm_action_debug_3( + print_rsc_colocation("Inverted constraint", inverted_con, FALSE)); + + return inverted_con; +} + +/* + * Create a new color with the contents of "nodes" as the list of + * possible nodes that resources with this color can be run on. + * + * Typically, when creating a color you will provide the node list from + * the resource you will first assign the color to. + * + * If "colors" != NULL, it will be added to that list + * If "resources" != NULL, it will be added to every provisional resource + * in that list + */ +color_t * +create_color( + pe_working_set_t *data_set, resource_t *resource, GListPtr node_list) +{ + color_t *new_color = NULL; + + crm_debug_5("Creating color"); + crm_malloc0(new_color, sizeof(color_t)); + if(new_color == NULL) { + return NULL; + } + + new_color->id = data_set->color_id++; + new_color->local_weight = 1.0; + + crm_debug_5("Creating color details"); + crm_malloc0(new_color->details, sizeof(struct color_shared_s)); + + if(new_color->details == NULL) { + crm_free(new_color); + return NULL; + } + + new_color->details->id = new_color->id; + new_color->details->highest_priority = -1; + new_color->details->chosen_node = NULL; + new_color->details->candidate_nodes = NULL; + new_color->details->allocated_resources = NULL; + new_color->details->pending = TRUE; + + if(resource != NULL) { + crm_debug_5("populating node list"); + new_color->details->highest_priority = resource->priority; + new_color->details->candidate_nodes = + node_list_dup(node_list, TRUE, TRUE); + } + + crm_action_debug_3(print_color("Created color", new_color, TRUE)); + + CRM_CHECK(data_set != NULL, return NULL); + data_set->colors = g_list_append(data_set->colors, new_color); + return new_color; +} + +color_t * +copy_color(color_t *a_color) +{ + color_t *color_copy = NULL; + + if(a_color == NULL) { + pe_err("Cannot copy NULL"); + return NULL; + } + + crm_malloc0(color_copy, sizeof(color_t)); + if(color_copy != NULL) { + color_copy->id = a_color->id; + color_copy->details = a_color->details; + color_copy->local_weight = 1.0; + } + return color_copy; +} + +gint gslist_color_compare(gconstpointer a, gconstpointer b); +color_t * +find_color(GListPtr candidate_colors, color_t *other_color) +{ + GListPtr tmp = g_list_find_custom(candidate_colors, other_color, + gslist_color_compare); + if(tmp != NULL) { + return (color_t *)tmp->data; + } + return NULL; +} + + +gint gslist_color_compare(gconstpointer a, gconstpointer b) +{ + const color_t *color_a = (const color_t*)a; + const color_t *color_b = (const color_t*)b; + +/* crm_debug_5("%d vs. %d", a?color_a->id:-2, b?color_b->id:-2); */ + if(a == b) { + return 0; + } else if(a == NULL || b == NULL) { + return 1; + } else if(color_a->id == color_b->id) { + return 0; + } + return 1; +} + +gint sort_rsc_priority(gconstpointer a, gconstpointer b) +{ + const resource_t *resource1 = (const resource_t*)a; + const resource_t *resource2 = (const resource_t*)b; + + if(a == NULL && b == NULL) { return 0; } + if(a == NULL) { return 1; } + if(b == NULL) { return -1; } + + if(resource1->priority > resource2->priority) { + return -1; + } + + if(resource1->priority < resource2->priority) { + return 1; + } + + return 0; +} + +gint sort_rsc_node_weight(gconstpointer a, gconstpointer b) +{ + const resource_t *resource1 = (const resource_t*)a; + const resource_t *resource2 = (const resource_t*)b; + + const color_t *color1 = NULL; + const color_t *color2 = NULL; + + const node_t *node1 = NULL; + const node_t *node2 = NULL; + + CRM_ASSERT(resource1 != NULL); + CRM_ASSERT(resource2 != NULL); + + color1 = resource1->color; + color2 = resource2->color; + + CRM_CHECK(color1 != NULL, return 0); + CRM_CHECK(color2 != NULL, return 0); + node1 = color1->details->chosen_node; + node2 = color2->details->chosen_node; + + if(node1 == NULL && node2 == NULL) { return 0; } + if(node1 == NULL) { return 1; } + if(node2 == NULL) { return -1; } + + CRM_ASSERT(node1 != NULL); + CRM_ASSERT(node2 != NULL); + if(node1->weight > node2->weight) { + crm_debug("%s (%d) > %s (%d) : %s vs. %s", + node1->details->id, node1->weight, + node2->details->id, node2->weight, + resource1->id, resource2->id); + return -1; + } + + if(node1->weight < node2->weight) { + crm_debug("%s (%d) < %s (%d) : %s vs. %s", + node1->details->id, node1->weight, + node2->details->id, node2->weight, + resource1->id, resource2->id); + return 1; + } + crm_debug("%s (%d) == %s (%d) : %s vs. %s", + node1->details->id, node1->weight, + node2->details->id, node2->weight, + resource1->id, resource2->id); + + return 0; +} + +/* lowest to highest */ +gint sort_action_id(gconstpointer a, gconstpointer b) +{ + const action_wrapper_t *action_wrapper2 = (const action_wrapper_t*)a; + const action_wrapper_t *action_wrapper1 = (const action_wrapper_t*)b; + + if(a == NULL) { return 1; } + if(b == NULL) { return -1; } + + if(action_wrapper1->action->id > action_wrapper2->action->id) { + return -1; + } + + if(action_wrapper1->action->id < action_wrapper2->action->id) { + return 1; + } + return 0; +} + +gint sort_cons_strength(gconstpointer a, gconstpointer b) +{ + const rsc_colocation_t *rsc_constraint1 = (const rsc_colocation_t*)a; + const rsc_colocation_t *rsc_constraint2 = (const rsc_colocation_t*)b; + + if(a == NULL) { return 1; } + if(b == NULL) { return -1; } + + if(rsc_constraint1->strength > rsc_constraint2->strength) { + return 1; + } + + if(rsc_constraint1->strength < rsc_constraint2->strength) { + return -1; + } + return 0; +} + +gint sort_color_weight(gconstpointer a, gconstpointer b) +{ + const color_t *color1 = (const color_t*)a; + const color_t *color2 = (const color_t*)b; + + if(a == NULL) { return 1; } + if(b == NULL) { return -1; } + + if(color1->local_weight > color2->local_weight) { + return -1; + } + + if(color1->local_weight < color2->local_weight) { + return 1; + } + + return 0; +} + +/* return -1 if 'a' is more preferred + * return 1 if 'b' is more preferred + */ +gint sort_node_weight(gconstpointer a, gconstpointer b) +{ + const node_t *node1 = (const node_t*)a; + const node_t *node2 = (const node_t*)b; + + int node1_weight = 0; + int node2_weight = 0; + + if(a == NULL) { return 1; } + if(b == NULL) { return -1; } + + node1_weight = node1->weight; + node2_weight = node2->weight; + + if(node1->details->unclean || node1->details->shutdown) { + node1_weight = -INFINITY; + } + if(node2->details->unclean || node2->details->shutdown) { + node2_weight = -INFINITY; + } + + if(node1_weight > node2_weight) { + crm_debug_3("%s (%d) > %s (%d) : weight", + node1->details->uname, node1_weight, + node2->details->uname, node2_weight); + return -1; + } + + if(node1_weight < node2_weight) { + crm_debug_3("%s (%d) < %s (%d) : weight", + node1->details->uname, node1_weight, + node2->details->uname, node2_weight); + return 1; + } + + crm_debug_3("%s (%d) == %s (%d) : weight", + node1->details->uname, node1_weight, + node2->details->uname, node2_weight); + + /* now try to balance resources across the cluster */ + if(node1->details->num_resources + < node2->details->num_resources) { + crm_debug_3("%s (%d) < %s (%d) : resources", + node1->details->uname, node1->details->num_resources, + node2->details->uname, node2->details->num_resources); + return -1; + + } else if(node1->details->num_resources + > node2->details->num_resources) { + crm_debug_3("%s (%d) > %s (%d) : resources", + node1->details->uname, node1->details->num_resources, + node2->details->uname, node2->details->num_resources); + return 1; + } + + crm_debug_4("%s = %s", node1->details->uname, node2->details->uname); + return 0; +} + +action_t * +custom_action(resource_t *rsc, char *key, const char *task, + node_t *on_node, gboolean optional, gboolean save_action, + pe_working_set_t *data_set) +{ + action_t *action = NULL; + GListPtr possible_matches = NULL; + CRM_CHECK(key != NULL, return NULL); + CRM_CHECK(task != NULL, return NULL); + + if(save_action && rsc != NULL) { + possible_matches = find_actions(rsc->actions, key, on_node); + } + + if(possible_matches != NULL) { + crm_free(key); + + if(g_list_length(possible_matches) > 1) { + pe_warn("Action %s for %s on %s exists %d times", + task, rsc?rsc->id:"", + on_node?on_node->details->uname:"", + g_list_length(possible_matches)); + } + + action = g_list_nth_data(possible_matches, 0); + crm_debug_4("Found existing action (%d) %s for %s on %s", + action->id, task, rsc?rsc->id:"", + on_node?on_node->details->uname:""); + } + + if(action == NULL) { + if(save_action) { + crm_debug_2("Creating%s action %d: %s for %s on %s", + optional?"":" manditory", data_set->action_id, key, rsc?rsc->id:"", + on_node?on_node->details->uname:""); + } + + crm_malloc0(action, sizeof(action_t)); + if(action != NULL) { + if(save_action) { + action->id = data_set->action_id++; + } else { + action->id = 0; + } + action->rsc = rsc; + action->task = task; + action->node = on_node; + + action->actions_before = NULL; + action->actions_after = NULL; + action->failure_is_fatal = TRUE; + + action->pseudo = FALSE; + action->dumped = FALSE; + action->runnable = TRUE; + action->processed = FALSE; + action->optional = optional; + action->seen_count = 0; + + action->extra = g_hash_table_new_full( + g_str_hash, g_str_equal, + g_hash_destroy_str, g_hash_destroy_str); + + action->meta = g_hash_table_new_full( + g_str_hash, g_str_equal, + g_hash_destroy_str, g_hash_destroy_str); + + if(save_action) { + data_set->actions = g_list_append( + data_set->actions, action); + } + + action->uuid = key; + + if(rsc != NULL) { + action->op_entry = find_rsc_op_entry(rsc, key); + + unpack_operation( + action, action->op_entry, data_set); + + if(save_action) { + rsc->actions = g_list_append( + rsc->actions, action); + } + } + if(save_action) { + crm_debug_4("Action %d created", action->id); + } + } + } + + if(optional == FALSE && action->optional) { + crm_debug_2("Action %d (%s) marked manditory", + action->id, action->uuid); + action->optional = FALSE; + } + + if(rsc != NULL) { + enum action_tasks a_task = text2task(action->task); + int warn_level = LOG_DEBUG_3; + if(save_action) { + warn_level = LOG_WARNING; + } + + if(action->node != NULL && action->op_entry != NULL) { + unpack_instance_attributes( + action->op_entry, XML_TAG_ATTR_SETS, + action->node->details->attrs, + action->extra, NULL, data_set->now); + } + + if(action->node == NULL) { + action->runnable = FALSE; + + } else if(rsc->is_managed == FALSE) { + crm_log_maybe(warn_level, "Action %s %s is for %s (unmanaged)", + action->uuid, task, rsc->id); + action->optional = TRUE; +/* action->runnable = FALSE; */ + +#if 0 + } else if(action->node->details->unclean) { + crm_log_maybe(warn_level, "Action %s on %s is unrunnable (unclean)", + action->uuid, action->node?action->node->details->uname:""); + + action->runnable = FALSE; +#endif + } else if(action->node->details->online == FALSE) { + action->runnable = FALSE; + crm_log_maybe(warn_level, "Action %s on %s is unrunnable (offline)", + action->uuid, action->node->details->uname); + if(action->rsc->is_managed + && save_action + && a_task == stop_rsc) { + crm_log_maybe(warn_level, "Marking node %s unclean", + action->node->details->uname); + action->node->details->unclean = TRUE; + } + + } else if(action->needs == rsc_req_nothing) { + crm_debug_3("Action %s doesnt require anything", + action->uuid); + action->runnable = TRUE; +#if 0 + /* + * No point checking this + * - if we dont have quorum we cant stonith anyway + */ + } else if(action->needs == rsc_req_stonith) { + crm_debug_3("Action %s requires only stonith", action->uuid); + action->runnable = TRUE; +#endif + } else if(data_set->have_quorum == FALSE + && data_set->no_quorum_policy == no_quorum_stop) { + action->runnable = FALSE; + crm_debug("%s\t%s %s (cancelled : quorum)", + action->node->details->uname, + action->task, rsc->id); + + } else if(data_set->have_quorum == FALSE + && data_set->no_quorum_policy == no_quorum_freeze) { + crm_debug_3("Check resource is already active"); + if(rsc->fns->active(rsc, TRUE) == FALSE) { + action->runnable = FALSE; + crm_debug("%s\t%s %s (cancelled : quorum freeze)", + action->node->details->uname, + action->task, rsc->id); + } + + } else { + crm_debug_3("Action %s is runnable", action->uuid); + action->runnable = TRUE; + } + + if(save_action) { + switch(a_task) { + case stop_rsc: + rsc->stopping = TRUE; + break; + case start_rsc: + rsc->starting = FALSE; + if(action->runnable) { + rsc->starting = TRUE; + } + break; + default: + break; + } + } + } + return action; +} + +void +unpack_operation( + action_t *action, crm_data_t *xml_obj, pe_working_set_t* data_set) +{ + int lpc = 0; + const char *value = NULL; + const char *fields[] = { + XML_LRM_ATTR_INTERVAL, + "timeout", + "start_delay", + }; + + CRM_CHECK(action->rsc != NULL, return); + + if(xml_obj != NULL) { + value = crm_element_value(xml_obj, "prereq"); + } + if(value == NULL && safe_str_eq(action->task, CRMD_ACTION_START)) { + value = g_hash_table_lookup( + action->rsc->meta, "start_prereq"); + } + + if(value == NULL && safe_str_neq(action->task, CRMD_ACTION_START)) { + /* todo: integrate stop as an option? */ + action->needs = rsc_req_nothing; + value = "nothing (default)"; + + } else if(safe_str_eq(value, "nothing")) { + action->needs = rsc_req_nothing; + + } else if(safe_str_eq(value, "quorum")) { + action->needs = rsc_req_quorum; + + } else if(safe_str_eq(value, "fencing")) { + action->needs = rsc_req_stonith; + + } else if(data_set->no_quorum_policy == no_quorum_ignore) { + action->needs = rsc_req_nothing; + value = "nothing (default)"; + + } else if(data_set->no_quorum_policy == no_quorum_freeze + && data_set->stonith_enabled) { + action->needs = rsc_req_stonith; + value = "fencing (default)"; + + } else { + action->needs = rsc_req_quorum; + value = "quorum (default)"; + } + crm_debug_3("\tAction %s requires: %s", action->task, value); + + value = NULL; + if(xml_obj != NULL) { + value = crm_element_value(xml_obj, "on_fail"); + } + if(value == NULL && safe_str_eq(action->task, CRMD_ACTION_STOP)) { + value = g_hash_table_lookup( + action->rsc->meta, "on_stopfail"); + if(value != NULL) { +#if CRM_DEPRECATED_SINCE_2_0_2 + pe_config_err("The \"on_stopfail\" attribute used in" + " %s has been deprecated since 2.0.2", + action->rsc->id); +#else + pe_config_err("The \"on_stopfail\" attribute used in" + " %s has been deprecated since 2.0.2" + " and is now disabled", action->rsc->id); + value = NULL; +#endif + pe_config_err("Please use specify the \"on_fail\"" + " attribute on the \"stop\" operation" + " instead"); + } + } + if(value == NULL) { + + } else if(safe_str_eq(value, "block")) { + action->on_fail = action_fail_block; + + } else if(safe_str_eq(value, "fence")) { + action->on_fail = action_fail_fence; + value = "node fencing"; + + } else if(safe_str_eq(value, "ignore")) { + action->on_fail = action_fail_ignore; + value = "ignore"; + + } else if(safe_str_eq(value, "migrate")) { + action->on_fail = action_fail_migrate; + value = "force migration"; + + } else if(safe_str_eq(value, "stop")) { + action->fail_role = RSC_ROLE_STOPPED; + value = "stop resource"; + + } else if(safe_str_eq(value, "restart") + || safe_str_eq(value, "nothing")) { + action->on_fail = action_fail_recover; + value = "restart (and possibly migrate)"; + + } else { + pe_err("Resource %s: Unknown failure type (%s)", + action->rsc->id, value); + value = NULL; + } + + /* defaults */ + if(value == NULL && safe_str_eq(action->task, CRMD_ACTION_STOP)) { + if(data_set->stonith_enabled) { + action->on_fail = action_fail_fence; + value = "resource fence (default)"; + + } else { + action->on_fail = action_fail_block; + value = "resource block (default)"; + } + + } else if(value == NULL) { + action->on_fail = action_fail_recover; + value = "restart (and possibly migrate) (default)"; + } + + crm_debug_3("\t%s failure handling: %s", action->task, value); + + value = NULL; + if(xml_obj != NULL) { + value = crm_element_value(xml_obj, "role_after_failure"); + } + if(value != NULL && action->fail_role == RSC_ROLE_UNKNOWN) { + action->fail_role = text2role(value); + } + /* defaults */ + if(action->fail_role == RSC_ROLE_UNKNOWN) { + if(safe_str_eq(action->task, CRMD_ACTION_PROMOTE)) { + action->fail_role = RSC_ROLE_SLAVE; + } else { + action->fail_role = RSC_ROLE_STARTED; + } + } + crm_debug_3("\t%s failure results in: %s", + action->task, role2text(action->fail_role)); + + if(xml_obj == NULL) { + return; + } + + xml_prop_iter(xml_obj, p_name, p_value, + if(p_value != NULL) { + g_hash_table_insert(action->meta, crm_strdup(p_name), + crm_strdup(p_value)); + } + ); + + unpack_instance_attributes(xml_obj, XML_TAG_META_SETS, + NULL, action->meta, NULL, data_set->now); + + unpack_instance_attributes(xml_obj, XML_TAG_ATTR_SETS, + NULL, action->meta, NULL, data_set->now); + + for(;lpc < DIMOF(fields); lpc++) { + value = g_hash_table_lookup(action->meta, fields[lpc]); + if(value != NULL) { + char *tmp_ms = NULL; + int tmp_i = crm_get_msec(value); + if(tmp_i < 0) { + tmp_i = 0; + } + tmp_ms = crm_itoa(tmp_i); + g_hash_table_replace( + action->meta, crm_strdup(fields[lpc]), tmp_ms); + } + } + +/* if(safe_str_eq(native_data->agent->class, "stonith")) { */ +/* if(rsc->start_needs == rsc_req_stonith) { */ +/* pe_err("Stonith resources (eg. %s) cannot require" */ +/* " fencing to start", rsc->id); */ +/* } */ +/* rsc->start_needs = rsc_req_quorum; */ +/* } */ + +} + +crm_data_t * +find_rsc_op_entry(resource_t *rsc, const char *key) +{ + const char *name = NULL; + const char *value = NULL; + const char *interval = NULL; + char *match_key = NULL; + crm_data_t *op = NULL; + + xml_child_iter_filter( + rsc->ops_xml, operation, "op", + + name = crm_element_value(operation, "name"); + interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL); + value = crm_element_value(operation, "disabled"); + if(crm_is_true(value)) { + crm_debug_2("%s disabled", ID(operation)); + continue; + } + + match_key = generate_op_key( + rsc->id, name, crm_get_msec(interval)); + + if(safe_str_eq(key, match_key)) { + op = operation; + } + crm_free(match_key); + + if(op != NULL) { + return op; + } + ); + crm_debug_3("No match for %s", key); + return op; +} + +void +print_node(const char *pre_text, node_t *node, gboolean details) +{ + if(node == NULL) { + crm_debug_4("%s%s: ", + pre_text==NULL?"":pre_text, + pre_text==NULL?"":": "); + return; + } + + crm_debug_4("%s%s%sNode %s: (weight=%d, fixed=%s)", + pre_text==NULL?"":pre_text, + pre_text==NULL?"":": ", + node->details==NULL?"error ":node->details->online?"":"Unavailable/Unclean ", + node->details->uname, + node->weight, + node->fixed?"True":"False"); + + if(details && node != NULL && node->details != NULL) { + char *pe_mutable = crm_strdup("\t\t"); + crm_debug_4("\t\t===Node Attributes"); + g_hash_table_foreach(node->details->attrs, + print_str_str, pe_mutable); + crm_free(pe_mutable); + + crm_debug_4("\t\t=== Resources"); + slist_iter( + rsc, resource_t, node->details->running_rsc, lpc, + print_resource(LOG_DEBUG_4, "\t\t", rsc, FALSE); + ); + } +} + +/* + * Used by the HashTable for-loop + */ +void print_str_str(gpointer key, gpointer value, gpointer user_data) +{ + crm_debug_4("%s%s %s ==> %s", + user_data==NULL?"":(char*)user_data, + user_data==NULL?"":": ", + (char*)key, + (char*)value); +} + +void +print_color_details(const char *pre_text, + struct color_shared_s *color, + gboolean details) +{ + if(color == NULL) { + crm_debug_4("%s%s: ", + pre_text==NULL?"":pre_text, + pre_text==NULL?"":": "); + return; + } + crm_debug_4("%s%sColor %d: node=%s (from %d candidates)", + pre_text==NULL?"":pre_text, + pre_text==NULL?"":": ", + color->id, + color->chosen_node==NULL?"":color->chosen_node->details->uname, + g_list_length(color->candidate_nodes)); + if(details) { + slist_iter(node, node_t, color->candidate_nodes, lpc, + print_node("\t", node, FALSE)); + } +} + +void +print_color(const char *pre_text, color_t *color, gboolean details) +{ + if(color == NULL) { + crm_debug_4("%s%s: ", + pre_text==NULL?"":pre_text, + pre_text==NULL?"":": "); + return; + } + crm_debug_4("%s%sColor %d: (weight=%d, node=%s, possible=%d)", + pre_text==NULL?"":pre_text, + pre_text==NULL?"":": ", + color->id, + color->local_weight, + safe_val5("",color,details,chosen_node,details,uname), + g_list_length(color->details->candidate_nodes)); + if(details) { + print_color_details("\t", color->details, details); + } +} + +void +print_rsc_to_node(const char *pre_text, rsc_to_node_t *cons, gboolean details) +{ + if(cons == NULL) { + crm_debug_4("%s%s: ", + pre_text==NULL?"":pre_text, + pre_text==NULL?"":": "); + return; + } + crm_debug_4("%s%s%s Constraint %s (%p) - %d nodes:", + pre_text==NULL?"":pre_text, + pre_text==NULL?"":": ", + "rsc_to_node", + cons->id, cons, + g_list_length(cons->node_list_rh)); + + if(details == FALSE) { + crm_debug_4("\t%s (node placement rule)", + safe_val3(NULL, cons, rsc_lh, id)); + + slist_iter( + node, node_t, cons->node_list_rh, lpc, + print_node("\t\t-->", node, FALSE) + ); + } +} + +void +print_rsc_colocation(const char *pre_text, rsc_colocation_t *cons, gboolean details) +{ + if(cons == NULL) { + crm_debug_4("%s%s: ", + pre_text==NULL?"":pre_text, + pre_text==NULL?"":": "); + return; + } + crm_debug_4("%s%s%s Constraint %s (%p):", + pre_text==NULL?"":pre_text, + pre_text==NULL?"":": ", + XML_CONS_TAG_RSC_DEPEND, cons->id, cons); + + if(details == FALSE) { + + crm_debug_4("\t%s --> %s, %s", + safe_val3(NULL, cons, rsc_lh, id), + safe_val3(NULL, cons, rsc_rh, id), + strength2text(cons->strength)); + } +} + +void +print_resource( + int log_level, const char *pre_text, resource_t *rsc, gboolean details) +{ + long options = pe_print_log; + + if(rsc == NULL) { + crm_log_maybe(log_level-1, "%s%s: ", + pre_text==NULL?"":pre_text, + pre_text==NULL?"":": "); + return; + } + if(details) { + options |= pe_print_details; + } + rsc->fns->print(rsc, pre_text, options, &log_level); +} + +void +log_action(unsigned int log_level, const char *pre_text, action_t *action, gboolean details) +{ + const char *node_uname = NULL; + const char *node_uuid = NULL; + + if(action == NULL) { + + crm_log_maybe(log_level, "%s%s: ", + pre_text==NULL?"":pre_text, + pre_text==NULL?"":": "); + return; + } + + + if(action->pseudo) { + node_uname = NULL; + node_uuid = NULL; + + } else if(action->node != NULL) { + node_uname = action->node->details->uname; + node_uuid = action->node->details->id; + } else { + node_uname = ""; + node_uuid = NULL; + } + + switch(text2task(action->task)) { + case stonith_node: + case shutdown_crm: + crm_log_maybe(log_level, + "%s%s%sAction %d: %s%s%s%s%s%s", + pre_text==NULL?"":pre_text, + pre_text==NULL?"":": ", + action->pseudo?"Pseduo ":action->optional?"Optional ":action->runnable?action->processed?"":"(Provisional) ":"!!Non-Startable!! ", + action->id, action->uuid, + node_uname?"\ton ":"", + node_uname?node_uname:"", + node_uuid?"\t\t(":"", + node_uuid?node_uuid:"", + node_uuid?")":""); + break; + default: + crm_log_maybe(log_level, + "%s%s%sAction %d: %s %s%s%s%s%s%s", + pre_text==NULL?"":pre_text, + pre_text==NULL?"":": ", + action->optional?"Optional ":action->pseudo?"Pseduo ":action->runnable?action->processed?"":"(Provisional) ":"!!Non-Startable!! ", + action->id, action->uuid, + safe_val3("", action, rsc, id), + node_uname?"\ton ":"", + node_uname?node_uname:"", + node_uuid?"\t\t(":"", + node_uuid?node_uuid:"", + node_uuid?")":""); + + break; + } + + if(details) { + crm_log_maybe(log_level+1, "\t\t====== Preceeding Actions"); + slist_iter( + other, action_wrapper_t, action->actions_before, lpc, + log_action(log_level+1, "\t\t", other->action, FALSE); + ); +#if 1 + crm_log_maybe(log_level+1, "\t\t====== Subsequent Actions"); + slist_iter( + other, action_wrapper_t, action->actions_after, lpc, + log_action(log_level+1, "\t\t", other->action, FALSE); + ); +#endif + crm_log_maybe(log_level+1, "\t\t====== End"); + + } else { + crm_log_maybe(log_level, "\t\t(seen=%d, before=%d, after=%d)", + action->seen_count, + g_list_length(action->actions_before), + g_list_length(action->actions_after)); + } +} + + +void +pe_free_nodes(GListPtr nodes) +{ + GListPtr iterator = nodes; + while(iterator != NULL) { + node_t *node = (node_t*)iterator->data; + struct node_shared_s *details = node->details; + iterator = iterator->next; + + crm_debug_5("deleting node"); + crm_debug_5("%s is being deleted", details->uname); + print_node("delete", node, FALSE); + + if(details != NULL) { + if(details->attrs != NULL) { + g_hash_table_destroy(details->attrs); + } + pe_free_shallow_adv(details->running_rsc, FALSE); + crm_free(details); + } + crm_free(node); + } + if(nodes != NULL) { + g_list_free(nodes); + } +} + +void +pe_free_colors(GListPtr colors) +{ + GListPtr iterator = colors; + while(iterator != NULL) { + color_t *color = (color_t *)iterator->data; + struct color_shared_s *details = color->details; + iterator = iterator->next; + + if(details != NULL) { + pe_free_shallow(details->candidate_nodes); + pe_free_shallow_adv(details->allocated_resources, FALSE); + crm_free(details->chosen_node); + crm_free(details); + } + crm_free(color); + } + if(colors != NULL) { + g_list_free(colors); + } +} + + +void +pe_free_resources(GListPtr resources) +{ + resource_t *rsc = NULL; + GListPtr iterator = resources; + while(iterator != NULL) { + iterator = iterator; + rsc = (resource_t *)iterator->data; + iterator = iterator->next; + rsc->fns->free(rsc); + } + if(resources != NULL) { + g_list_free(resources); + } +} + +void +pe_free_action(action_t *action) +{ + if(action == NULL) { + return; + } + pe_free_shallow(action->actions_before);/* action_warpper_t* */ + pe_free_shallow(action->actions_after); /* action_warpper_t* */ + g_hash_table_destroy(action->extra); + g_hash_table_destroy(action->meta); + crm_free(action->uuid); + crm_free(action); +} + +void +pe_free_actions(GListPtr actions) +{ + GListPtr iterator = actions; + while(iterator != NULL) { + pe_free_action(iterator->data); + iterator = iterator->next; + } + if(actions != NULL) { + g_list_free(actions); + } +} + +void +pe_free_ordering(GListPtr constraints) +{ + GListPtr iterator = constraints; + while(iterator != NULL) { + order_constraint_t *order = iterator->data; + iterator = iterator->next; + + crm_free(order->lh_action_task); + crm_free(order->rh_action_task); + crm_free(order); + } + if(constraints != NULL) { + g_list_free(constraints); + } +} + + +void +pe_free_rsc_colocation(rsc_colocation_t *cons) +{ + if(cons != NULL) { + crm_debug_4("Freeing constraint %s (%p)", cons->id, cons); + crm_free(cons); + } +} + +void +pe_free_rsc_to_node(rsc_to_node_t *cons) +{ + if(cons != NULL) { + pe_free_shallow(cons->node_list_rh); + crm_free(cons); + } +} + +GListPtr +find_recurring_actions(GListPtr input, node_t *not_on_node) +{ + const char *value = NULL; + GListPtr result = NULL; + CRM_CHECK(input != NULL, return NULL); + + slist_iter( + action, action_t, input, lpc, + value = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL); + if(value == NULL) { + /* skip */ + } else if(safe_str_eq(CRMD_ACTION_CANCEL, action->task)) { + /* skip */ + } else if(not_on_node == NULL) { + crm_debug_5("(null) Found: %s", action->uuid); + result = g_list_append(result, action); + + } else if(action->node == NULL) { + /* skip */ + } else if(action->node->details != not_on_node->details) { + crm_debug_5("Found: %s", action->uuid); + result = g_list_append(result, action); + } + ); + + return result; +} + +GListPtr +find_actions(GListPtr input, const char *key, node_t *on_node) +{ + GListPtr result = NULL; + CRM_CHECK(key != NULL, return NULL); + + slist_iter( + action, action_t, input, lpc, + crm_debug_5("Matching %s against %s", key, action->uuid); + if(safe_str_neq(key, action->uuid)) { + continue; + + } else if(on_node == NULL) { + result = g_list_append(result, action); + + } else if(action->node == NULL) { + /* skip */ + crm_debug_2("While looking for %s action on %s, " + "found an unallocated one. Assigning" + " it to the requested node...", + key, on_node->details->uname); + + action->node = on_node; + result = g_list_append(result, action); + + } else if(safe_str_eq(on_node->details->id, + action->node->details->id)) { + result = g_list_append(result, action); + } + ); + + return result; +} + + +GListPtr +find_actions_exact(GListPtr input, const char *key, node_t *on_node) +{ + GListPtr result = NULL; + CRM_CHECK(key != NULL, return NULL); + + slist_iter( + action, action_t, input, lpc, + crm_debug_5("Matching %s against %s", key, action->uuid); + if(safe_str_neq(key, action->uuid)) { + crm_debug_3("Key mismatch: %s vs. %s", + key, action->uuid); + continue; + + } else if(on_node == NULL || action->node == NULL) { + crm_debug_3("on_node=%p, action->node=%p", + on_node, action->node); + continue; + + } else if(safe_str_eq(on_node->details->id, + action->node->details->id)) { + result = g_list_append(result, action); + } + crm_debug_2("Node mismatch: %s vs. %s", + on_node->details->id, action->node->details->id); + ); + + return result; +} + +void +set_id(crm_data_t * xml_obj, const char *prefix, int child) +{ + int id_len = 0; + gboolean use_prefix = TRUE; + gboolean use_child = TRUE; + + char *new_id = NULL; + const char *id = crm_element_value(xml_obj, XML_ATTR_ID); + + id_len = 1 + strlen(id); + + if(child > 999) { + pe_err("Are you insane?!?" + " The CRM does not support > 1000 children per resource"); + return; + + } else if(child < 0) { + use_child = FALSE; + + } else { + id_len += 4; /* child */ + } + + if(prefix == NULL || safe_str_eq(id, prefix)) { + use_prefix = FALSE; + } else { + id_len += (1 + strlen(prefix)); + } + + crm_malloc0(new_id, id_len); + + if(use_child) { + snprintf(new_id, id_len, "%s%s%s:%d", + use_prefix?prefix:"", use_prefix?":":"", id, child); + } else { + snprintf(new_id, id_len, "%s%s%s", + use_prefix?prefix:"", use_prefix?":":"", id); + } + + crm_xml_add(xml_obj, XML_ATTR_ID, new_id); + crm_free(new_id); +} + +rsc_to_node_t * +rsc2node_new(const char *id, resource_t *rsc, + int node_weight, node_t *foo_node, pe_working_set_t *data_set) +{ + rsc_to_node_t *new_con = NULL; + + if(rsc == NULL || id == NULL) { + pe_err("Invalid constraint %s for rsc=%p", crm_str(id), rsc); + return NULL; + } + + crm_malloc0(new_con, sizeof(rsc_to_node_t)); + if(new_con != NULL) { + new_con->id = id; + new_con->rsc_lh = rsc; + new_con->node_list_rh = NULL; + new_con->role_filter = RSC_ROLE_UNKNOWN; + + if(foo_node != NULL) { + node_t *copy = node_copy(foo_node); + copy->weight = node_weight; + new_con->node_list_rh = g_list_append(NULL, copy); + } else { + CRM_CHECK(node_weight == 0, return NULL); + } + + data_set->placement_constraints = g_list_append( + data_set->placement_constraints, new_con); + rsc->rsc_location = g_list_append( + rsc->rsc_location, new_con); + } + + return new_con; +} + + + +const char * +strength2text(enum con_strength strength) +{ + const char *result = ""; + switch(strength) + { + case pecs_ignore: + result = "ignore"; + break; + case pecs_must: + result = XML_STRENGTH_VAL_MUST; + break; + case pecs_must_not: + result = XML_STRENGTH_VAL_MUSTNOT; + break; + case pecs_startstop: + result = "start/stop"; + break; + } + return result; +} + +const char * +ordering_type2text(enum pe_ordering type) +{ + const char *result = ""; + switch(type) + { + case pe_ordering_manditory: + result = "manditory"; + break; + case pe_ordering_restart: + result = "restart"; + break; + case pe_ordering_recover: + result = "recover"; + break; + case pe_ordering_optional: + result = "optional"; + break; + case pe_ordering_postnotify: + result = "post_notify"; + break; + } + return result; +} diff --git a/lib/crm/pengine/utils.h b/lib/crm/pengine/utils.h new file mode 100644 index 0000000000..b0a0f99092 --- /dev/null +++ b/lib/crm/pengine/utils.h @@ -0,0 +1,171 @@ +/* $Id: utils.h,v 1.1 2006/05/31 14:59:12 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 + */ +#ifndef PE_UTILS__H +#define PE_UTILS__H +#include +#include + +/* Node helper functions */ +extern node_t *pe_find_node_id(GListPtr node_list, const char *id); + +extern node_t *node_copy(node_t *this_node) ; + + +/* Binary like operators for lists of nodes */ +extern GListPtr node_list_dup(GListPtr list1, gboolean reset, gboolean filter); + +extern GListPtr node_list_and(GListPtr list1, GListPtr list2, gboolean filter); + +extern GListPtr node_list_xor(GListPtr list1, GListPtr list2, gboolean filter); + +extern GListPtr node_list_minus(GListPtr list1,GListPtr list2,gboolean filter); + +extern gboolean node_list_eq(GListPtr list1, GListPtr list2, gboolean filter); + +extern GListPtr node_list_or(GListPtr list1, GListPtr list2, gboolean filter); + +extern void pe_free_shallow(GListPtr alist); +extern void pe_free_shallow_adv(GListPtr alist, gboolean with_data); + + +/* Constraint helper functions */ +extern rsc_colocation_t *invert_constraint(rsc_colocation_t *constraint); + +extern rsc_to_node_t *copy_constraint(rsc_to_node_t *constraint); + +/* Color helper functions */ +extern void add_color_to_rsc(resource_t *rsc, color_t *color); + +extern color_t *find_color(GListPtr candidate_colors, color_t *other_color); + +extern color_t *create_color( + pe_working_set_t *data_set, resource_t *resource, GListPtr resources); + +extern color_t *copy_color(color_t *a_color); + + +/* For creating the transition graph */ +extern crm_data_t *action2xml(action_t *action, gboolean as_input); + +/* Printing functions for debug */ +extern void print_node( + const char *pre_text, node_t *node, gboolean details); + +extern void print_resource( + int log_level, const char *pre_text, resource_t *rsc, gboolean details); + +extern void print_rsc_to_node( + const char *pre_text, rsc_to_node_t *cons, gboolean details); + +extern void print_rsc_colocation( + const char *pre_text, rsc_colocation_t *cons, gboolean details); + +extern void print_color( + const char *pre_text, color_t *color, gboolean details); + +extern void print_color_details( + const char *pre_text, struct color_shared_s *color, gboolean details); + +extern void log_action( + unsigned int log_level, const char *pre_text, action_t *action, gboolean details); + +/* Sorting functions */ +extern gint sort_rsc_priority(gconstpointer a, gconstpointer b); +extern gint sort_rsc_node_weight(gconstpointer a, gconstpointer b); +extern gint sort_cons_strength(gconstpointer a, gconstpointer b); +extern gint sort_color_weight(gconstpointer a, gconstpointer b); +extern gint sort_node_weight(gconstpointer a, gconstpointer b); +extern gint sort_action_id(gconstpointer a, gconstpointer b); + +extern crm_data_t *find_rsc_op_entry(resource_t *rsc, const char *key); + +extern action_t *custom_action( + resource_t *rsc, char *key, const char *task, node_t *on_node, + gboolean optional, gboolean foo, pe_working_set_t *data_set); + +extern rsc_to_node_t *rsc2node_new( + const char *id, resource_t *rsc, int weight, node_t *node, + pe_working_set_t *data_set); + +#define delete_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_DELETE, 0) +#define delete_action(rsc, node) custom_action( \ + rsc, delete_key(rsc), CRMD_ACTION_DELETE, node, \ + FALSE, TRUE, data_set); + +#define stopped_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_STOPPED, 0) +#define stopped_action(rsc, node, optional) custom_action( \ + rsc, stopped_key(rsc), CRMD_ACTION_STOPPED, node, \ + optional, TRUE, data_set); + +#define stop_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_STOP, 0) +#define stop_action(rsc, node, optional) custom_action( \ + rsc, stop_key(rsc), CRMD_ACTION_STOP, node, \ + optional, TRUE, data_set); + +#define start_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_START, 0) +#define start_action(rsc, node, optional) custom_action( \ + rsc, start_key(rsc), CRMD_ACTION_START, node, \ + optional, TRUE, data_set) + +#define started_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_STARTED, 0) +#define started_action(rsc, node, optional) custom_action( \ + rsc, started_key(rsc), CRMD_ACTION_STARTED, node, \ + optional, TRUE, data_set) + +#define promote_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_PROMOTE, 0) +#define promote_action(rsc, node, optional) custom_action( \ + rsc, promote_key(rsc), CRMD_ACTION_PROMOTE, node, \ + optional, TRUE, data_set) + +#define promoted_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_PROMOTED, 0) +#define promoted_action(rsc, node, optional) custom_action( \ + rsc, promoted_key(rsc), CRMD_ACTION_PROMOTED, node, \ + optional, TRUE, data_set) + +#define demote_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_DEMOTE, 0) +#define demote_action(rsc, node, optional) custom_action( \ + rsc, demote_key(rsc), CRMD_ACTION_DEMOTE, node, \ + optional, TRUE, data_set) + +#define demoted_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_DEMOTED, 0) +#define demoted_action(rsc, node, optional) custom_action( \ + rsc, demoted_key(rsc), CRMD_ACTION_DEMOTED, node, \ + optional, TRUE, data_set) + +extern GListPtr find_actions(GListPtr input, const char *key, node_t *on_node); +extern GListPtr find_actions_exact( + GListPtr input, const char *key, node_t *on_node); +extern GListPtr find_recurring_actions(GListPtr input, node_t *not_on_node); + +extern void set_id(crm_data_t *xml_obj, const char *prefix, int child); + +/* free the various structures */ +extern void pe_free_nodes(GListPtr nodes); +extern void pe_free_colors(GListPtr colors); +extern void pe_free_rsc_colocation(rsc_colocation_t *cons); +extern void pe_free_rsc_to_node(rsc_to_node_t *cons); +extern void pe_free_resources(GListPtr resources); +extern void pe_free_action(action_t *action); +extern void pe_free_actions(GListPtr actions); +extern void pe_free_ordering(GListPtr constraints); + +extern const char *strength2text(enum con_strength strength); +extern const char *ordering_type2text(enum pe_ordering type); + +#endif