diff --git a/cts/CTSlab.py b/cts/CTSlab.py index e9b6cbc760..0ec8068c92 100755 --- a/cts/CTSlab.py +++ b/cts/CTSlab.py @@ -1,446 +1,452 @@ #!/usr/bin/python '''CTS: Cluster Testing System: Lab environment module ''' __copyright__=''' Copyright (C) 2001,2005 Alan Robertson Licensed under the GNU GPL. ''' # # 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. from UserDict import UserDict import sys, types, string, string, signal, os pdir=os.path.dirname(sys.path[0]) sys.path.insert(0, pdir) # So that things work from the source directory try: from cts.CTSvars import * from cts.CM_ais import * from cts.CM_lha import crm_lha from cts.CTSaudits import AuditList from cts.CTStests import TestList from cts.CTSscenarios import * except ImportError: sys.stderr.write("abort: couldn't find cts libraries in [%s]\n" % ' '.join(sys.path)) sys.stderr.write("(check your install and PYTHONPATH)\n") sys.exit(-1) cm = None Tests = [] Chosen = [] scenario = None # Not really used, the handler in def sig_handler(signum, frame) : if cm: cm.log("Interrupted by signal %d"%signum) if scenario: scenario.summarize() if signum == 15 : if scenario: scenario.TearDown() sys.exit(1) class LabEnvironment(CtsLab): def __init__(self): CtsLab.__init__(self) # Get a random seed for the random number generator. self["DoStonith"] = 1 self["DoStandby"] = 1 self["DoFencing"] = 1 self["XmitLoss"] = "0.0" self["RecvLoss"] = "0.0" self["IPBase"] = "127.0.0.10" self["ClobberCIB"] = 0 self["CIBfilename"] = None self["CIBResource"] = 0 self["DoBSC"] = 0 self["use_logd"] = 0 self["oprofile"] = [] self["warn-inactive"] = 0 self["ListTests"] = 0 self["benchmark"] = 0 self["logrestartcmd"] = "/etc/init.d/syslog-ng restart 2>&1 > /dev/null" self["Schema"] = "pacemaker-1.0" self["Stack"] = "openais" self["stonith-type"] = "external/ssh" self["stonith-params"] = "hostlist=all,livedangerously=yes" self["at-boot"] = 1 # Does the cluster software start automatically when the node boots self["logger"] = ([StdErrLog(self)]) self["loop-minutes"] = 60 self["valgrind-prefix"] = None self["valgrind-procs"] = "cib crmd attrd pengine stonith-ng" self["valgrind-opts"] = """--leak-check=full --show-reachable=yes --trace-children=no --num-callers=25 --gen-suppressions=all --suppressions="""+CTSvars.CTS_home+"""/cts.supp""" #self["valgrind-opts"] = """--trace-children=no --num-callers=25 --gen-suppressions=all --suppressions="""+CTSvars.CTS_home+"""/cts.supp""" self["experimental-tests"] = 0 self["valgrind-tests"] = 0 self["unsafe-tests"] = 1 self["loop-tests"] = 1 self["scenario"] = "random" def usage(arg, status=1): print "Illegal argument " + arg print "usage: " + sys.argv[0] +" [options] number-of-iterations" print "\nCommon options: " print "\t [--at-boot (1|0)], does the cluster software start at boot time" print "\t [--nodes 'node list'], list of cluster nodes separated by whitespace" print "\t [--limit-nodes max], only use the first 'max' cluster nodes supplied with --nodes" print "\t [--stack (heartbeat|ais)], which cluster stack is installed" print "\t [--logfile path], where should the test software look for logs from cluster nodes" print "\t [--outputfile path], optional location for the test software to write logs to" print "\t [--syslog-facility name], which syslog facility should the test software log to" print "\t [--choose testcase-name], run only the named test" print "\t [--list-tests], list the valid tests" print "\t [--benchmark], add the timing information" print "\t " print "Options for release testing: " print "\t [--clobber-cib | -c ] Erase any existing configuration" print "\t [--populate-resources | -r] Generate a sample configuration" print "\t [--test-ip-base ip] Offset for generated IP address resources" print "\t " print "Additional (less common) options: " print "\t [--trunc (truncate logfile before starting)]" print "\t [--xmit-loss lost-rate(0.0-1.0)]" print "\t [--recv-loss lost-rate(0.0-1.0)]" print "\t [--standby (1 | 0 | yes | no)]" print "\t [--fencing (1 | 0 | yes | no)]" print "\t [--stonith (1 | 0 | yes | no)]" print "\t [--stonith-type type]" print "\t [--stonith-args name=value]" print "\t [--bsc]" print "\t [--once], run all valid tests once" print "\t [--no-loop-tests], dont run looping/time-based tests" print "\t [--no-unsafe-tests], dont run tests that are unsafe for use with ocfs2/drbd" print "\t [--valgrind-tests], include tests using valgrind" print "\t [--experimental-tests], include experimental tests" print "\t [--oprofile 'node list'], list of cluster nodes to run oprofile on]" print "\t [--qarsh] Use the QARSH backdoor to access nodes instead of SSH" print "\t [--seed random_seed]" print "\t [--set option=value]" sys.exit(status) # # A little test code... # if __name__ == '__main__': Environment = LabEnvironment() NumIter = 0 Version = 1 LimitNodes = 0 TruncateLog = 0 ListTests = 0 HaveSeed = 0 node_list = '' # Set the signal handler signal.signal(15, sig_handler) signal.signal(10, sig_handler) # Process arguments... skipthis=None args=sys.argv[1:] for i in range(0, len(args)): if skipthis: skipthis=None continue elif args[i] == "-l" or args[i] == "--limit-nodes": skipthis=1 LimitNodes = int(args[i+1]) elif args[i] == "-r" or args[i] == "--populate-resources": Environment["CIBResource"] = 1 elif args[i] == "-L" or args[i] == "--logfile": skipthis=1 Environment["LogFileName"] = args[i+1] elif args[i] == "--outputfile": skipthis=1 Environment["OutputFile"] = args[i+1] elif args[i] == "--test-ip-base": skipthis=1 Environment["IPBase"] = args[i+1] elif args[i] == "--oprofile": skipthis=1 Environment["oprofile"] = args[i+1].split(' ') elif args[i] == "--trunc": Environment["TruncateLog"]=1 elif args[i] == "--list-tests" or args[i] == "--list" : Environment["ListTests"]=1 elif args[i] == "--benchmark": Environment["benchmark"]=1 elif args[i] == "--bsc": Environment["DoBSC"] = 1 Environment["scenario"] = "basic-sanity" elif args[i] == "--qarsh": Environment.rsh.enable_qarsh() elif args[i] == "--fencing": skipthis=1 if args[i+1] == "1" or args[i+1] == "yes": Environment["DoFencing"] = 1 elif args[i+1] == "0" or args[i+1] == "no": Environment["DoFencing"] = 0 else: usage(args[i+1]) elif args[i] == "--stonith": skipthis=1 if args[i+1] == "1" or args[i+1] == "yes": Environment["DoStonith"]=1 elif args[i+1] == "0" or args[i+1] == "no": Environment["DoStonith"]=0 else: usage(args[i+1]) elif args[i] == "--stonith-type": Environment["stonith-type"] = args[i+1] skipthis=1 elif args[i] == "--stonith-args": Environment["stonith-params"] = args[i+1] skipthis=1 elif args[i] == "--standby": skipthis=1 if args[i+1] == "1" or args[i+1] == "yes": Environment["DoStandby"] = 1 elif args[i+1] == "0" or args[i+1] == "no": Environment["DoStandby"] = 0 else: usage(args[i+1]) elif args[i] == "--clobber-cib" or args[i] == "-c": Environment["ClobberCIB"] = 1 elif args[i] == "--cib-filename": skipthis=1 Environment["CIBfilename"] = args[i+1] elif args[i] == "--xmit-loss": try: float(args[i+1]) except ValueError: print ("--xmit-loss parameter should be float") usage(args[i+1]) skipthis=1 Environment["XmitLoss"] = args[i+1] elif args[i] == "--recv-loss": try: float(args[i+1]) except ValueError: print ("--recv-loss parameter should be float") usage(args[i+1]) skipthis=1 Environment["RecvLoss"] = args[i+1] elif args[i] == "--choose": skipthis=1 Chosen.append(args[i+1]) Environment["scenario"] = "sequence" elif args[i] == "--nodes": skipthis=1 node_list = args[i+1].split(' ') elif args[i] == "--syslog-facility" or args[i] == "--facility": skipthis=1 Environment["SyslogFacility"] = args[i+1] elif args[i] == "--seed": skipthis=1 Environment.SeedRandom(args[i+1]) elif args[i] == "--warn-inactive": Environment["warn-inactive"] = 1 elif args[i] == "--schema": skipthis=1 Environment["Schema"] = args[i+1] elif args[i] == "--ais": Environment["Stack"] = "openais" elif args[i] == "--at-boot" or args[i] == "--cluster-starts-at-boot": skipthis=1 if args[i+1] == "1" or args[i+1] == "yes": Environment["at-boot"] = 1 elif args[i+1] == "0" or args[i+1] == "no": Environment["at-boot"] = 0 else: usage(args[i+1]) elif args[i] == "--heartbeat" or args[i] == "--lha": Environment["Stack"] = "heartbeat" elif args[i] == "--hae": Environment["Stack"] = "openais" Environment["Schema"] = "hae" elif args[i] == "--stack": Environment["Stack"] = args[i+1] skipthis=1 elif args[i] == "--once": Environment["scenario"] = "all-once" elif args[i] == "--valgrind-tests": Environment["valgrind-tests"] = 1 elif args[i] == "--no-loop-tests": Environment["loop-tests"] = 0 + elif args[i] == "--loop-minutes": + skipthis=1 + try: + Environment["loop-minutes"]=int(args[i+1]) + except ValueError: + usage(args[i]) + elif args[i] == "--no-unsafe-tests": Environment["unsafe-tests"] = 0 elif args[i] == "--experimental-tests": Environment["experimental-tests"] = 1 elif args[i] == "--set": skipthis=1 (name, value) = args[i+1].split('=') Environment[name] = value else: try: NumIter=int(args[i]) except ValueError: usage(args[i]) - Environment["loop-minutes"] = int(Environment["loop-minutes"]) if Environment["DoBSC"]: NumIter = 2 LimitNodes = 1 Chosen.append("AddResource") Environment["ClobberCIB"] = 1 Environment["CIBResource"] = 0 Environment["logger"].append(FileLog(Environment, Environment["LogFileName"])) else: if Environment["OutputFile"]: Environment["logger"].append(FileLog(Environment, Environment["OutputFile"])) if Environment["SyslogFacility"]: Environment["logger"].append(SysLog(Environment)) if Environment["Stack"] == "heartbeat" or Environment["Stack"] == "lha": Environment["Stack"] = "heartbeat" Environment['CMclass'] = crm_lha elif Environment["Stack"] == "openais" or Environment["Stack"] == "ais" or Environment["Stack"] == "whitetank": Environment["Stack"] = "openais (whitetank)" Environment['CMclass'] = crm_whitetank Environment["use_logd"] = 0 elif Environment["Stack"] == "corosync" or Environment["Stack"] == "cs" or Environment["Stack"] == "flatiron": Environment["Stack"] = "corosync (flatiron)" Environment['CMclass'] = crm_flatiron Environment["use_logd"] = 0 else: print "Unknown stack: "+Environment["Stack"] sys.exit(1) if len(node_list) < 1: print "No nodes specified!" sys.exit(1) if LimitNodes > 0: if len(node_list) > LimitNodes: print("Limiting the number of nodes configured=%d (max=%d)" %(len(node_list), LimitNodes)) while len(node_list) > LimitNodes: node_list.pop(len(node_list)-1) Environment["nodes"] = node_list # Create the Cluster Manager object cm = Environment['CMclass'](Environment) if TruncateLog: Environment.log("Truncating %s" % LogFile) lf = open(LogFile, "w"); if lf != None: lf.truncate(0) lf.close() Audits = AuditList(cm) if Environment["ListTests"] == 1 : Tests = TestList(cm, Audits) Environment.log("Total %d tests"%len(Tests)) for test in Tests : Environment.log(str(test.name)); sys.exit(0) if len(Chosen) == 0: Tests = TestList(cm, Audits) else: for TestCase in Chosen: match = None for test in TestList(cm, Audits): if test.name == TestCase: match = test if not match: usage("--choose: No applicable/valid tests chosen") else: Tests.append(match) # Scenario selection if Environment["scenario"] == "basic-sanity": scenario = RandomTests(cm, [ BasicSanityCheck(Environment) ], Audits, Tests) elif Environment["scenario"] == "all-once": NumIter = len(Tests) scenario = AllOnce( cm, [ InitClusterManager(Environment), PacketLoss(Environment) ], Audits, Tests) elif Environment["scenario"] == "sequence": scenario = Sequence( cm, [ InitClusterManager(Environment), PacketLoss(Environment) ], Audits, Tests) else: scenario = RandomTests( cm, [ InitClusterManager(Environment), PacketLoss(Environment) ], Audits, Tests) Environment.log(">>>>>>>>>>>>>>>> BEGINNING " + repr(NumIter) + " TESTS ") Environment.log("Stack: %s" % Environment["Stack"]) Environment.log("Schema: %s" % Environment["Schema"]) Environment.log("Scenario: %s" % scenario.__doc__) Environment.log("Random Seed: %s" % Environment["RandSeed"]) Environment.log("System log files: %s" % Environment["LogFileName"]) Environment.dump() rc = Environment.run(scenario, NumIter) sys.exit(rc) diff --git a/lib/pengine/status.c b/lib/pengine/status.c index b172ebdfd5..6573523dad 100644 --- a/lib/pengine/status.c +++ b/lib/pengine/status.c @@ -1,280 +1,278 @@ /* * 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 -xmlNode * do_calculations( - pe_working_set_t *data_set, xmlNode *xml_input, ha_time_t *now); - extern xmlNode*get_object_root( const char *object_type, xmlNode *the_root); #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) { xmlNode * config = get_object_root( XML_CIB_TAG_CRMCONFIG, data_set->input); xmlNode * cib_nodes = get_object_root( XML_CIB_TAG_NODES, data_set->input); xmlNode * cib_resources = get_object_root( XML_CIB_TAG_RESOURCES, data_set->input); xmlNode * cib_status = get_object_root( XML_CIB_TAG_STATUS, data_set->input); xmlNode * cib_domains = get_object_root( XML_CIB_TAG_DOMAINS, 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 */ data_set->failed = create_xml_node(NULL, "failed-ops"); if(data_set->input == NULL) { return FALSE; } if(data_set->now == NULL) { data_set->now = new_ha_date(TRUE); } 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); } clear_bit_inplace(data_set->flags, pe_flag_have_quorum); if(crm_is_true(value)) { set_bit_inplace(data_set->flags, pe_flag_have_quorum); } data_set->op_defaults = get_object_root(XML_CIB_TAG_OPCONFIG, data_set->input); data_set->rsc_defaults = get_object_root(XML_CIB_TAG_RSCCONFIG, data_set->input); unpack_config(config, data_set); if(is_set(data_set->flags, pe_flag_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_domains(cib_domains, data_set); unpack_resources(cib_resources, data_set); unpack_status(cib_status, data_set); return TRUE; } static 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); } } static 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); } } static 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); } if(details->utilization != NULL) { g_hash_table_destroy(details->utilization); } pe_free_shallow_adv(details->running_rsc, FALSE); pe_free_shallow_adv(details->allocated_rsc, FALSE); crm_free(details); } crm_free(node); } if(nodes != NULL) { g_list_free(nodes); } } void cleanup_calculations(pe_working_set_t *data_set) { + pe_dataset = 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_debug_3("deleting resources"); pe_free_resources(data_set->resources); crm_debug_3("deleting actions"); pe_free_actions(data_set->actions); if(data_set->domains) { g_hash_table_destroy(data_set->domains); } crm_debug_3("deleting nodes"); pe_free_nodes(data_set->nodes); free_xml(data_set->graph); free_ha_date(data_set->now); free_xml(data_set->input); free_xml(data_set->failed); set_working_set_defaults(data_set); CRM_CHECK(data_set->ordering_constraints == NULL, ;); CRM_CHECK(data_set->placement_constraints == NULL, ;); xmlCleanupParser(); } void set_working_set_defaults(pe_working_set_t *data_set) { memset(data_set, 0, sizeof(pe_working_set_t)); data_set->order_id = 1; data_set->action_id = 1; data_set->no_quorum_policy = no_quorum_freeze; data_set->flags = 0x0ULL; set_bit_inplace(data_set->flags, pe_flag_stop_rsc_orphans); set_bit_inplace(data_set->flags, pe_flag_symmetric_cluster); set_bit_inplace(data_set->flags, pe_flag_is_managed_default); set_bit_inplace(data_set->flags, pe_flag_stop_action_orphans); } resource_t * pe_find_resource(GListPtr rsc_list, const char *id) { unsigned lpc = 0; resource_t *rsc = NULL; resource_t *match = NULL; if(id == NULL) { return NULL; } for(lpc = 0; lpc < g_list_length(rsc_list); lpc++) { rsc = g_list_nth_data(rsc_list, lpc); match = rsc->fns->find_rsc(rsc, id, TRUE, FALSE, NULL, TRUE); if(match != NULL) { return match; } } crm_debug_2("No match for %s", id); return NULL; } node_t * pe_find_node_id(GListPtr nodes, const char *id) { slist_iter(node, node_t, nodes, lpc, if(node && safe_str_eq(node->details->id, id)) { return node; } ); /* error */ return NULL; } node_t * pe_find_node(GListPtr nodes, const char *uname) { slist_iter(node, node_t, nodes, lpc, if(node && safe_str_eq(node->details->uname, uname)) { return node; } ); /* error */ return NULL; } diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c index c52c2ae9d7..18089d782c 100644 --- a/lib/pengine/utils.c +++ b/lib/pengine/utils.c @@ -1,1542 +1,1544 @@ /* * 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 +pe_working_set_t *pe_dataset = NULL; + extern xmlNode *get_object_root(const char *object_type,xmlNode *the_root); 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, xmlNode *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; if(with_data == FALSE && alist != NULL) { g_list_free(alist); return; } 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_1(item); } } 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_ASSERT(new_node != 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; } /* any node in list1 or list2 and not in the other gets a score of -INFINITY */ GListPtr node_list_exclude(GListPtr list1, GListPtr list2, gboolean merge_scores) { node_t *other_node = NULL; GListPtr result = NULL; result = node_list_dup(list1, FALSE, FALSE); slist_iter( node, node_t, result, lpc, other_node = pe_find_node_id(list2, node->details->id); if(other_node == NULL) { node->weight = -INFINITY; } else if(merge_scores) { node->weight = merge_weights(node->weight, other_node->weight); } ); slist_iter( node, node_t, list2, lpc, other_node = pe_find_node_id(result, node->details->id); if(other_node == NULL) { node_t *new_node = node_copy(node); new_node->weight = -INFINITY; result = g_list_append(result, new_node); } ); return result; } /* 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; } void dump_node_scores(int level, resource_t *rsc, const char *comment, GListPtr nodes) { GListPtr list = nodes; if(rsc) { list = rsc->allowed_nodes; } if(rsc && is_set(rsc->flags, pe_rsc_orphan)) { /* Don't show the allocation scores for orphans */ return; } slist_iter( node, node_t, list, lpc, char *score = score2char(node->weight); if(level == 0) { if(rsc) { fprintf(stdout, "%s: %s allocation score on %s: %s\n", comment, rsc->id, node->details->uname, score); } else { fprintf(stdout, "%s: %s = %s\n", comment, node->details->uname, score); } } else { if(rsc) { do_crm_log_unlikely(level, "%s: %s allocation score on %s: %s", comment, rsc->id, node->details->uname, score); } else { do_crm_log_unlikely(level, "%s: %s = %s", comment, node->details->uname, score); } } crm_free(score); ); if(rsc && rsc->children) { slist_iter( child, resource_t, rsc->children, lpc, dump_node_scores(level, child, comment, nodes); ); } } static void append_dump_text(gpointer key, gpointer value, gpointer user_data) { char **dump_text = user_data; int len = 0; char *new_text = NULL; len = strlen(*dump_text) + strlen(" ") + strlen(key) + strlen("=") + strlen(value) + 1; crm_malloc0(new_text, len); sprintf(new_text, "%s %s=%s", *dump_text, (char *)key, (char *)value); crm_free(*dump_text); *dump_text = new_text; } void dump_node_capacity(int level, const char *comment, node_t *node) { int len = 0; char *dump_text = NULL; len = strlen(comment) + strlen(": ") + strlen(node->details->uname) + strlen(" capacity:") + 1; crm_malloc0(dump_text, len); sprintf(dump_text, "%s: %s capacity:", comment, node->details->uname); g_hash_table_foreach(node->details->utilization, append_dump_text, &dump_text); if(level == 0) { fprintf(stdout, "%s\n", dump_text); } else { do_crm_log_unlikely(level, "%s", dump_text); } crm_free(dump_text); } void dump_rsc_utilization(int level, const char *comment, resource_t *rsc, node_t *node) { int len = 0; char *dump_text = NULL; len = strlen(comment) + strlen(": ") + strlen(rsc->id) + strlen(" utilization on ") + strlen(node->details->uname) + strlen(":") + 1; crm_malloc0(dump_text, len); sprintf(dump_text, "%s: %s utilization on %s:", comment, rsc->id, node->details->uname); g_hash_table_foreach(rsc->utilization, append_dump_text, &dump_text); if(level == 0) { fprintf(stdout, "%s\n", dump_text); } else { do_crm_log_unlikely(level, "%s", dump_text); } crm_free(dump_text); } gint sort_rsc_index(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->sort_index > resource2->sort_index) { return -1; } if(resource1->sort_index < resource2->sort_index) { return 1; } return 0; } 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; } 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:""); g_list_free(possible_matches); } if(action == NULL) { if(save_action) { crm_debug_4("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(save_action) { action->id = data_set->action_id++; } else { action->id = 0; } action->rsc = rsc; CRM_ASSERT(task != NULL); action->task = crm_strdup(task); action->node = on_node; action->uuid = key; action->failure_is_fatal = TRUE; action->runnable = TRUE; action->optional = optional; /* Implied by crm_malloc0()... action->actions_before = NULL; action->actions_after = NULL; action->pseudo = FALSE; action->dumped = FALSE; action->processed = FALSE; action->seen_count = 0; */ action->extra = g_hash_table_new_full( g_str_hash, g_str_equal, free, free); action->meta = g_hash_table_new_full( g_str_hash, g_str_equal, free, free); if(save_action) { data_set->actions = g_list_append( data_set->actions, action); } 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->have_node_attrs == FALSE && action->node != NULL && action->op_entry != NULL) { action->have_node_attrs = TRUE; unpack_instance_attributes( data_set->input, action->op_entry, XML_TAG_ATTR_SETS, action->node->details->attrs, action->extra, NULL, FALSE, data_set->now); } if(action->pseudo) { /* leave untouched */ } else if(action->node == NULL) { action->runnable = FALSE; } else if(is_not_set(rsc->flags, pe_rsc_managed) && g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL) == NULL) { do_crm_log_unlikely(LOG_DEBUG, "Action %s (unmanaged)", action->uuid); action->optional = TRUE; /* action->runnable = FALSE; */ } else if(action->node->details->online == FALSE) { action->runnable = FALSE; do_crm_log(warn_level, "Action %s on %s is unrunnable (offline)", action->uuid, action->node->details->uname); if(is_set(action->rsc->flags, pe_rsc_managed) && save_action && a_task == stop_rsc) { do_crm_log(warn_level, "Marking node %s unclean", action->node->details->uname); action->node->details->unclean = TRUE; } } else if(action->node->details->pending) { action->runnable = FALSE; do_crm_log(warn_level, "Action %s on %s is unrunnable (pending)", action->uuid, action->node->details->uname); } 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(is_set(data_set->flags, pe_flag_have_quorum) == FALSE && data_set->no_quorum_policy == no_quorum_stop) { action->runnable = FALSE; crm_debug("%s\t%s (cancelled : quorum)", action->node->details->uname, action->uuid); } else if(is_set(data_set->flags, pe_flag_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 (cancelled : quorum freeze)", action->node->details->uname, action->uuid); } } else { crm_debug_3("Action %s is runnable", action->uuid); action->runnable = TRUE; } if(save_action) { switch(a_task) { case stop_rsc: set_bit(rsc->flags, pe_rsc_stopping); break; case start_rsc: clear_bit(rsc->flags, pe_rsc_starting); if(action->runnable) { set_bit(rsc->flags, pe_rsc_starting); } break; default: break; } } } return action; } void unpack_operation( action_t *action, xmlNode *xml_obj, pe_working_set_t* data_set) { int value_i = 0; unsigned long long interval = 0; unsigned long long start_delay = 0; char *value_ms = NULL; const char *class = NULL; const char *value = NULL; const char *field = NULL; CRM_CHECK(action->rsc != NULL, return); unpack_instance_attributes(data_set->input, data_set->op_defaults, XML_TAG_META_SETS, NULL, action->meta, NULL, FALSE, data_set->now); xml_prop_iter(xml_obj, name, value, if(value != NULL && g_hash_table_lookup(action->meta, name) == NULL) { g_hash_table_insert(action->meta, crm_strdup(name), crm_strdup(value)); } ); unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_META_SETS, NULL, action->meta, NULL, FALSE, data_set->now); unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_ATTR_SETS, NULL, action->meta, NULL, FALSE, data_set->now); g_hash_table_remove(action->meta, "id"); class = g_hash_table_lookup(action->rsc->meta, "class"); value = g_hash_table_lookup(action->meta, "requires"); if(safe_str_eq(class, "stonith")) { action->needs = rsc_req_nothing; value = "nothing (fencing op)"; } else if(value == NULL && safe_str_neq(action->task, CRMD_ACTION_START)) { 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 || safe_str_eq(class, "stonith")) { action->needs = rsc_req_nothing; value = "nothing (default)"; } else if(data_set->no_quorum_policy == no_quorum_freeze && is_set(data_set->flags, pe_flag_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 = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL); if(safe_str_eq(action->task, CRMD_ACTION_STOP) && safe_str_eq(value, "standby")) { crm_config_err("on-fail=standby is not allowed for stop actions: %s", action->rsc->id); value = NULL; } 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"; if(is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) { crm_config_err("Specifying on_fail=fence and" " stonith-enabled=false makes no sense"); action->on_fail = action_fail_stop; action->fail_role = RSC_ROLE_STOPPED; value = "stop resource"; } } else if(safe_str_eq(value, "standby")) { action->on_fail = action_fail_standby; value = "node standby"; } else if(safe_str_eq(value, "ignore") || safe_str_eq(value, "nothing")) { 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->on_fail = action_fail_stop; action->fail_role = RSC_ROLE_STOPPED; value = "stop resource"; } else if(safe_str_eq(value, "restart")) { 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(is_set(data_set->flags, pe_flag_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 && safe_str_eq(action->task, CRMD_ACTION_MIGRATED)) { action->on_fail = action_migrate_failure; value = "atomic migration recovery (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 = g_hash_table_lookup(action->meta, "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)); field = XML_LRM_ATTR_INTERVAL; value = g_hash_table_lookup(action->meta, field); if(value != NULL) { interval = crm_get_interval(value); if(interval > 0) { value_ms = crm_itoa(interval); g_hash_table_replace(action->meta, crm_strdup(field), value_ms); } else { g_hash_table_remove(action->meta, field); } } field = XML_OP_ATTR_START_DELAY; value = g_hash_table_lookup(action->meta, field); if(value != NULL) { value_i = crm_get_msec(value); if(value_i < 0) { value_i = 0; } start_delay = value_i; value_ms = crm_itoa(value_i); g_hash_table_replace(action->meta, crm_strdup(field), value_ms); } else if(interval > 0 && g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN)) { char *date_str = NULL; char *date_str_mutable = NULL; ha_time_t *origin = NULL; value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN); date_str = crm_strdup(value); date_str_mutable = date_str; origin = parse_date(&date_str_mutable); crm_free(date_str); if(origin == NULL) { crm_config_err("Operation %s contained an invalid "XML_OP_ATTR_ORIGIN": %s", ID(xml_obj), value); } else { ha_time_t *delay = NULL; int rc = compare_date(origin, data_set->now); unsigned long long delay_s = 0; while(rc < 0) { add_seconds(origin, interval/1000); rc = compare_date(origin, data_set->now); } delay = subtract_time(origin, data_set->now); delay_s = date_in_seconds(delay); /* log_date(LOG_DEBUG_5, "delay", delay, ha_log_date|ha_log_time|ha_log_local); */ crm_info("Calculated a start delay of %llus for %s", delay_s, ID(xml_obj)); g_hash_table_replace(action->meta, crm_strdup(XML_OP_ATTR_START_DELAY), crm_itoa(delay_s * 1000)); start_delay = delay_s * 1000; free_ha_date(origin); free_ha_date(delay); } } field = XML_ATTR_TIMEOUT; value = g_hash_table_lookup(action->meta, field); if(value == NULL) { value = pe_pref( data_set->config_hash, "default-action-timeout"); } value_i = crm_get_msec(value); if(value_i < 0) { value_i = 0; } value_i += start_delay; value_ms = crm_itoa(value_i); g_hash_table_replace(action->meta, crm_strdup(field), value_ms); } xmlNode * find_rsc_op_entry(resource_t *rsc, const char *key) { int number = 0; const char *name = NULL; const char *value = NULL; const char *interval = NULL; char *match_key = NULL; xmlNode *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, "enabled"); if(value && crm_is_true(value) == FALSE) { continue; } number = crm_get_interval(interval); if(number < 0) { continue; } match_key = generate_op_key(rsc->id, name, number); if(safe_str_eq(key, match_key)) { op = operation; } crm_free(match_key); if(op != NULL) { return op; } ); if(strstr(key, CRMD_ACTION_MIGRATE) || strstr(key, CRMD_ACTION_MIGRATED)) { match_key = generate_op_key(rsc->id, "migrate", 0); op = find_rsc_op_entry(rsc, match_key); crm_free(match_key); } if(op == NULL) { 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_resource( int log_level, const char *pre_text, resource_t *rsc, gboolean details) { long options = pe_print_log; if(rsc == NULL) { do_crm_log(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 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* */ if(action->extra) { g_hash_table_destroy(action->extra); } if(action->meta) { g_hash_table_destroy(action->meta); } crm_free(action->task); crm_free(action->uuid); crm_free(action); } 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(value, "0")) { /* 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; } action_t * find_first_action(GListPtr input, const char *uuid, const char *task, node_t *on_node) { CRM_CHECK(uuid || task, return NULL); slist_iter( action, action_t, input, lpc, if(uuid != NULL && safe_str_neq(uuid, action->uuid)) { continue; } else if(task != NULL && safe_str_neq(task, action->task)) { continue; } else if(on_node == NULL) { return action; } else if(action->node == NULL) { continue; } else if(on_node->details == action->node->details) { return action; } ); return NULL; } 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(on_node->details == action->node->details) { 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(xmlNode * 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); } static void resource_node_score(resource_t *rsc, node_t *node, int score, const char *tag) { node_t *match = NULL; if(rsc->children) { slist_iter( child_rsc, resource_t, rsc->children, lpc, resource_node_score(child_rsc, node, score, tag); ); } crm_debug_2("Setting %s for %s on %s: %d", tag, rsc->id, node->details->uname, score); match = pe_find_node_id(rsc->allowed_nodes, node->details->id); if(match == NULL) { match = node_copy(node); match->weight = merge_weights(score, node->weight);; rsc->allowed_nodes = g_list_append(rsc->allowed_nodes, match); } match->weight = merge_weights(match->weight, score); } void resource_location(resource_t *rsc, node_t *node, int score, const char *tag, pe_working_set_t *data_set) { if(node != NULL) { resource_node_score(rsc, node, score, tag); } else if(data_set != NULL) { slist_iter( node, node_t, data_set->nodes, lpc, resource_node_score(rsc, node, score, tag); ); } else { slist_iter( node, node_t, rsc->allowed_nodes, lpc, resource_node_score(rsc, node, score, tag); ); } if(node == NULL && score == -INFINITY) { if(rsc->allocated_to) { crm_info("Deallocating %s from %s", rsc->id, rsc->allocated_to->details->uname); crm_free(rsc->allocated_to); rsc->allocated_to = NULL; } } } #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 xmlNode *xml_a = a; const xmlNode *xml_b = b; const char *a_xml_id = crm_element_value_const(xml_a, XML_ATTR_ID); const char *b_xml_id = crm_element_value_const(xml_b, XML_ATTR_ID); const char *a_task_id = crm_element_value_const(xml_a, XML_LRM_ATTR_CALLID); const char *b_task_id = crm_element_value_const(xml_b, XML_LRM_ATTR_CALLID); const char *a_key = crm_element_value_const(xml_a, XML_ATTR_TRANSITION_MAGIC); const char *b_key = crm_element_value_const(xml_b, XML_ATTR_TRANSITION_MAGIC); int dummy = -1; 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, crm_err("a: %s, b: %s", crm_str(a_xml_id), crm_str(b_xml_id)); 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", a_xml_id, a_call_id, b_xml_id, 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", a_xml_id, a_call_id, b_xml_id, b_call_id); sort_return(1); } crm_debug_5("%s (%d) == %s (%d) : continuing", a_xml_id, a_call_id, b_xml_id, 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, &dummy, &a_status, &a_rc, &dummy), sort_return(0)); CRM_CHECK(decode_transition_magic( b_key, &b_uuid, &b_id, &dummy, &b_status, &b_rc, &dummy), 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, crm_err("a: %s=%d, b: %s=%d", crm_str(a_xml_id), a_call_id, crm_str(b_xml_id), b_call_id); 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", a_xml_id, a_call_id, b_xml_id, b_call_id); sort_return(-1); } if(a_call_id == -1) { crm_debug_2("%s (%d) > %s (%d) : transition + call id", a_xml_id, a_call_id, b_xml_id, 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", a_xml_id, a_id, b_xml_id, 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", a_xml_id, a_id, b_xml_id, b_id); sort_return(1); } /* we should never end up here */ crm_err("%s (%d:%d:%s) ?? %s (%d:%d:%s) : default", a_xml_id, a_call_id, a_id, a_uuid, b_xml_id, b_call_id, b_id, b_uuid); CRM_CHECK(FALSE, sort_return(0)); } time_t get_timet_now(pe_working_set_t *data_set) { time_t now = 0; if(data_set && data_set->now) { now = data_set->now->tm_now; } if(now == 0) { /* eventually we should convert data_set->now into time_tm * for now, its only triggered by PE regression tests */ now = time(NULL); crm_crit("Defaulting to 'now'"); if(data_set && data_set->now) { data_set->now->tm_now = now; } } return now; } struct fail_search { resource_t *rsc; int count; long long last; char *key; }; static void get_failcount_by_prefix(gpointer key_p, gpointer value, gpointer user_data) { struct fail_search *search = user_data; const char *key = key_p; const char *match = strstr(key, search->key); if(match) { if(strstr(key, "last-failure-") == key && (key+13) == match) { search->last = crm_int_helper(value, NULL); } else if(strstr(key, "fail-count-") == key && (key+11) == match) { search->count += char2score(value); } } } int get_failcount(node_t *node, resource_t *rsc, int *last_failure, pe_working_set_t *data_set) { struct fail_search search = {rsc, 0, 0, NULL}; search.key = crm_strdup(rsc->id); if(is_not_set(rsc->flags, pe_rsc_unique)) { int lpc = 0; search.rsc = uber_parent(rsc); /* Strip the clone incarnation */ for(lpc = strlen(search.key); lpc > 0; lpc--) { if(search.key[lpc] == ':') { search.key[lpc+1] = 0; break; } } g_hash_table_foreach(node->details->attrs, get_failcount_by_prefix, &search); } else { /* Optimize the "normal" case */ char *key = NULL; const char *value = NULL; key = crm_concat("fail-count", rsc->id, '-'); value = g_hash_table_lookup(node->details->attrs, key); search.count = char2score(value); crm_free(key); key = crm_concat("last-failure", rsc->id, '-'); value = g_hash_table_lookup(node->details->attrs, key); search.last = crm_int_helper(value, NULL); crm_free(key); } if(search.count != 0 && search.last != 0 && rsc->failure_timeout) { if(last_failure) { *last_failure = search.last; } if(search.last > 0) { time_t now = get_timet_now(data_set); if(now > (search.last + rsc->failure_timeout)) { crm_notice("Failcount for %s on %s has expired (limit was %ds)", search.rsc->id, node->details->uname, rsc->failure_timeout); search.count = 0; } } } if(search.count != 0) { crm_info("%s has failed %s times on %s", search.rsc->id, score2char(search.count), node->details->uname); } crm_free(search.key); return search.count; } gboolean get_target_role(resource_t *rsc, enum rsc_role_e *role) { const char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE); CRM_CHECK(role != NULL, return FALSE); if(value == NULL || safe_str_eq("started", value) || safe_str_eq("default", value)) { return FALSE; } *role = text2role(value); if(*role == RSC_ROLE_UNKNOWN) { crm_config_err("%s: Unknown value for %s: %s", rsc->id, XML_RSC_ATTR_TARGET_ROLE, value); return FALSE; } else if(*role > RSC_ROLE_STARTED) { const char *stateful = g_hash_table_lookup(rsc->meta, "stateful"); if(rsc->variant == pe_master) { /* Next check isn't true during common_unpack() for the master */ } else if(crm_is_true(stateful) == FALSE) { pe_warn("%s is not part of a master/slave resource, a %s of '%s' makes no sense: %s", rsc->id, XML_RSC_ATTR_TARGET_ROLE, value, stateful); *role = RSC_ROLE_STARTED; return FALSE; } } return TRUE; } diff --git a/lib/pengine/utils.h b/lib/pengine/utils.h index 60e3060a92..918b9f3436 100644 --- a/lib/pengine/utils.h +++ b/lib/pengine/utils.h @@ -1,134 +1,135 @@ /* * 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 +extern pe_working_set_t *pe_dataset; extern node_t *node_copy(node_t *this_node) ; extern time_t get_timet_now(pe_working_set_t *data_set); extern int get_failcount(node_t *node, resource_t *rsc, int *last_failure, pe_working_set_t *data_set); /* Binary like operators for lists of nodes */ extern GListPtr node_list_exclude(GListPtr list1, GListPtr list2, gboolean merge_scores); 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); /* For creating the transition graph */ extern xmlNode *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 dump_node_scores(int level, resource_t *rsc, const char *comment, GListPtr nodes); extern void dump_node_capacity(int level, const char *comment, node_t *node); extern void dump_rsc_utilization(int level, const char *comment, resource_t *rsc, node_t *node); /* Sorting functions */ extern gint sort_rsc_priority(gconstpointer a, gconstpointer b); extern gint sort_rsc_index(gconstpointer a, gconstpointer b); extern xmlNode *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); #define delete_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_DELETE, 0) #define delete_action(rsc, node, optional) custom_action( \ rsc, delete_key(rsc), CRMD_ACTION_DELETE, node, \ optional, 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 action_t *find_first_action(GListPtr input, const char *uuid, const char *task, node_t *on_node); 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(xmlNode *xml_obj, const char *prefix, int child); extern void pe_free_action(action_t *action); extern void resource_location(resource_t *rsc, node_t *node, int score, const char *tag, pe_working_set_t *data_set); extern gint sort_op_by_callid(gconstpointer a, gconstpointer b); extern gboolean get_target_role(resource_t *rsc, enum rsc_role_e *role); extern resource_t *find_clone_instance(resource_t *rsc, const char *sub_id, pe_working_set_t *data_set); #endif diff --git a/pengine/clone.c b/pengine/clone.c index 6b562bd3b0..b48820e62a 100644 --- a/pengine/clone.c +++ b/pengine/clone.c @@ -1,1735 +1,1764 @@ /* * 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 VARIANT_CLONE 1 #include gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set); void child_stopping_constraints( clone_variant_data_t *clone_data, resource_t *self, resource_t *child, resource_t *last, pe_working_set_t *data_set); void child_starting_constraints( clone_variant_data_t *clone_data, resource_t *self, resource_t *child, resource_t *last, pe_working_set_t *data_set); static node_t * parent_node_instance(const resource_t *rsc, node_t *node) { node_t *ret = NULL; if(node != NULL) { ret = pe_find_node_id( rsc->parent->allowed_nodes, node->details->id); } return ret; } static gboolean did_fail(const resource_t *rsc) { if(is_set(rsc->flags, pe_rsc_failed)) { return TRUE; } slist_iter( child_rsc, resource_t, rsc->children, lpc, if(did_fail(child_rsc)) { return TRUE; } ); return FALSE; } gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set) { int level = LOG_DEBUG_3; node_t *node1 = NULL; node_t *node2 = NULL; gboolean can1 = TRUE; gboolean can2 = TRUE; gboolean with_scores = TRUE; const resource_t *resource1 = (const resource_t*)a; const resource_t *resource2 = (const resource_t*)b; CRM_ASSERT(resource1 != NULL); CRM_ASSERT(resource2 != NULL); /* allocation order: * - active instances * - instances running on nodes with the least copies * - active instances on nodes that cant support them or are to be fenced * - failed instances * - inactive instances */ do_crm_log_unlikely(level+1, "%s ? %s", resource1->id, resource2->id); if(resource1->running_on && resource2->running_on) { if(g_list_length(resource1->running_on) < g_list_length(resource2->running_on)) { do_crm_log_unlikely(level, "%s < %s: running_on", resource1->id, resource2->id); return -1; } else if(g_list_length(resource1->running_on) > g_list_length(resource2->running_on)) { do_crm_log_unlikely(level, "%s > %s: running_on", resource1->id, resource2->id); return 1; } } if(resource1->running_on) { node1 = resource1->running_on->data; } if(resource2->running_on) { node2 = resource2->running_on->data; } if(node1) { node_t *match = pe_find_node_id(resource1->allowed_nodes, node1->details->id); if(match == NULL || match->weight < 0) { do_crm_log_unlikely(level, "%s: current location is unavailable", resource1->id); node1 = NULL; can1 = FALSE; } } if(node2) { node_t *match = pe_find_node_id(resource2->allowed_nodes, node2->details->id); if(match == NULL || match->weight < 0) { do_crm_log_unlikely(level, "%s: current location is unavailable", resource2->id); node2 = NULL; can2 = FALSE; } } if(can1 != can2) { if(can1) { do_crm_log_unlikely(level, "%s < %s: availability of current location", resource1->id, resource2->id); return -1; } do_crm_log_unlikely(level, "%s > %s: availability of current location", resource1->id, resource2->id); return 1; } if(resource1->priority < resource2->priority) { do_crm_log_unlikely(level, "%s < %s: priority", resource1->id, resource2->id); return 1; } else if(resource1->priority > resource2->priority) { do_crm_log_unlikely(level, "%s > %s: priority", resource1->id, resource2->id); return -1; } if(node1 == NULL && node2 == NULL) { do_crm_log_unlikely(level, "%s == %s: not active", resource1->id, resource2->id); return 0; } if(node1 != node2) { if(node1 == NULL) { do_crm_log_unlikely(level, "%s > %s: active", resource1->id, resource2->id); return 1; } else if(node2 == NULL) { do_crm_log_unlikely(level, "%s < %s: active", resource1->id, resource2->id); return -1; } } can1 = can_run_resources(node1); can2 = can_run_resources(node2); if(can1 != can2) { if(can1) { do_crm_log_unlikely(level, "%s < %s: can", resource1->id, resource2->id); return -1; } do_crm_log_unlikely(level, "%s > %s: can", resource1->id, resource2->id); return 1; } node1 = parent_node_instance(resource1, node1); node2 = parent_node_instance(resource2, node2); if(node1 != NULL && node2 == NULL) { do_crm_log_unlikely(level, "%s < %s: not allowed", resource1->id, resource2->id); return -1; } else if(node1 == NULL && node2 != NULL) { do_crm_log_unlikely(level, "%s > %s: not allowed", resource1->id, resource2->id); return 1; } if(node1 == NULL) { do_crm_log_unlikely(level, "%s == %s: not allowed", resource1->id, resource2->id); return 0; } if(node1->count < node2->count) { do_crm_log_unlikely(level, "%s < %s: count", resource1->id, resource2->id); return -1; } else if(node1->count > node2->count) { do_crm_log_unlikely(level, "%s > %s: count", resource1->id, resource2->id); return 1; } if(with_scores) { int max = 0; int lpc = 0; GListPtr list1 = node_list_dup(resource1->allowed_nodes, FALSE, FALSE); GListPtr list2 = node_list_dup(resource2->allowed_nodes, FALSE, FALSE); list1 = g_list_sort_with_data(list1, sort_node_weight, data_set); list2 = g_list_sort_with_data(list2, sort_node_weight, data_set); max = g_list_length(list1); if(max < g_list_length(list2)) { max = g_list_length(list2); } for(;lpc < max; lpc++) { node1 = g_list_nth_data(list1, lpc); node2 = g_list_nth_data(list2, lpc); if(node1 == NULL) { do_crm_log_unlikely(level, "%s < %s: node score NULL", resource1->id, resource2->id); pe_free_shallow(list1); pe_free_shallow(list2); return 1; } else if(node2 == NULL) { do_crm_log_unlikely(level, "%s > %s: node score NULL", resource1->id, resource2->id); pe_free_shallow(list1); pe_free_shallow(list2); return -1; } if(node1->weight < node2->weight) { do_crm_log_unlikely(level, "%s < %s: node score", resource1->id, resource2->id); pe_free_shallow(list1); pe_free_shallow(list2); return 1; } else if(node1->weight > node2->weight) { do_crm_log_unlikely(level, "%s > %s: node score", resource1->id, resource2->id); pe_free_shallow(list1); pe_free_shallow(list2); return -1; } } pe_free_shallow(list1); pe_free_shallow(list2); } can1 = did_fail(resource1); can2 = did_fail(resource2); if(can1 != can2) { if(can1) { do_crm_log_unlikely(level, "%s > %s: failed", resource1->id, resource2->id); return 1; } do_crm_log_unlikely(level, "%s < %s: failed", resource1->id, resource2->id); return -1; } if(node1 && node2) { int max = 0; int lpc = 0; GListPtr list1 = g_list_append(NULL, node_copy(resource1->running_on->data)); GListPtr list2 = g_list_append(NULL, node_copy(resource2->running_on->data)); /* Possibly a replacement for the with_scores block above */ slist_iter( constraint, rsc_colocation_t, resource1->parent->rsc_cons_lhs, lpc, do_crm_log_unlikely(level+1, "Applying %s to %s", constraint->id, resource1->id); list1 = native_merge_weights( constraint->rsc_lh, resource1->id, list1, constraint->node_attribute, constraint->score/INFINITY, FALSE); ); slist_iter( constraint, rsc_colocation_t, resource2->parent->rsc_cons_lhs, lpc, do_crm_log_unlikely(level+1, "Applying %s to %s", constraint->id, resource2->id); list2 = native_merge_weights( constraint->rsc_lh, resource2->id, list2, constraint->node_attribute, constraint->score/INFINITY, FALSE); ); list1 = g_list_sort_with_data(list1, sort_node_weight, data_set); list2 = g_list_sort_with_data(list2, sort_node_weight, data_set); max = g_list_length(list1); if(max < g_list_length(list2)) { max = g_list_length(list2); } for(;lpc < max; lpc++) { node1 = g_list_nth_data(list1, lpc); node2 = g_list_nth_data(list2, lpc); if(node1 == NULL) { do_crm_log_unlikely(level, "%s < %s: colocated score NULL", resource1->id, resource2->id); pe_free_shallow(list1); pe_free_shallow(list2); return 1; } else if(node2 == NULL) { do_crm_log_unlikely(level, "%s > %s: colocated score NULL", resource1->id, resource2->id); pe_free_shallow(list1); pe_free_shallow(list2); return -1; } if(node1->weight < node2->weight) { do_crm_log_unlikely(level, "%s < %s: colocated score", resource1->id, resource2->id); pe_free_shallow(list1); pe_free_shallow(list2); return 1; } else if(node1->weight > node2->weight) { do_crm_log_unlikely(level, "%s > %s: colocated score", resource1->id, resource2->id); pe_free_shallow(list1); pe_free_shallow(list2); return -1; } } pe_free_shallow(list1); pe_free_shallow(list2); } do_crm_log_unlikely(level, "%s == %s: default %d", resource1->id, resource2->id, node2->weight); return 0; } static node_t * can_run_instance(resource_t *rsc, node_t *node) { node_t *local_node = NULL; clone_variant_data_t *clone_data = NULL; if(can_run_resources(node) == FALSE) { goto bail; } else if(is_set(rsc->flags, pe_rsc_orphan)) { goto bail; } local_node = parent_node_instance(rsc, node); get_clone_variant_data(clone_data, rsc->parent); if(local_node == NULL) { crm_warn("%s cannot run on %s: node not allowed", rsc->id, node->details->uname); goto bail; } else if(local_node->count < clone_data->clone_node_max) { return local_node; } else { crm_debug_2("%s cannot run on %s: node full", rsc->id, node->details->uname); } bail: if(node) { common_update_score(rsc, node->details->id, -INFINITY); } return NULL; } static node_t * color_instance(resource_t *rsc, pe_working_set_t *data_set) { node_t *chosen = NULL; node_t *local_node = NULL; crm_debug_2("Processing %s", rsc->id); if(is_not_set(rsc->flags, pe_rsc_provisional)) { return rsc->fns->location(rsc, NULL, FALSE); } else if(is_set(rsc->flags, pe_rsc_allocating)) { crm_debug("Dependency loop detected involving %s", rsc->id); return NULL; } if(rsc->allowed_nodes) { slist_iter(try_node, node_t, rsc->allowed_nodes, lpc, can_run_instance(rsc, try_node); ); } chosen = rsc->cmds->color(rsc, data_set); if(chosen) { local_node = pe_find_node_id( rsc->parent->allowed_nodes, chosen->details->id); if(local_node) { local_node->count++; } else if(is_set(rsc->flags, pe_rsc_managed)) { /* what to do? we can't enforce per-node limits in this case */ crm_config_err("%s not found in %s (list=%d)", chosen->details->id, rsc->parent->id, g_list_length(rsc->parent->allowed_nodes)); } } return chosen; } static void append_parent_colocation(resource_t *rsc, resource_t *child, gboolean all) { slist_iter(cons, rsc_colocation_t, rsc->rsc_cons, lpc, if(all || cons->score < 0 || cons->score == INFINITY) { child->rsc_cons = g_list_append(child->rsc_cons, cons); } ); slist_iter(cons, rsc_colocation_t, rsc->rsc_cons_lhs, lpc, if(all || cons->score < 0) { child->rsc_cons_lhs = g_list_append(child->rsc_cons_lhs, cons); } ); } node_t * clone_color(resource_t *rsc, pe_working_set_t *data_set) { int allocated = 0; int available_nodes = 0; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); if(is_not_set(rsc->flags, pe_rsc_provisional)) { return NULL; } else if(is_set(rsc->flags, pe_rsc_allocating)) { crm_debug("Dependency loop detected involving %s", rsc->id); return NULL; } set_bit(rsc->flags, pe_rsc_allocating); crm_debug_2("Processing %s", rsc->id); /* this information is used by sort_clone_instance() when deciding in which * order to allocate clone instances */ slist_iter( constraint, rsc_colocation_t, rsc->rsc_cons_lhs, lpc, rsc->allowed_nodes = constraint->rsc_lh->cmds->merge_weights( constraint->rsc_lh, rsc->id, rsc->allowed_nodes, constraint->node_attribute, constraint->score/INFINITY, TRUE); ); dump_node_scores(show_scores?0:scores_log_level, rsc, __FUNCTION__, rsc->allowed_nodes); /* count now tracks the number of clones currently allocated */ slist_iter(node, node_t, rsc->allowed_nodes, lpc, node->count = 0; ); slist_iter(child, resource_t, rsc->children, lpc, if(g_list_length(child->running_on) > 0) { node_t *child_node = child->running_on->data; node_t *local_node = parent_node_instance( child, child->running_on->data); if(local_node) { local_node->count++; } else { crm_err("%s is running on %s which isn't allowed", child->id, child_node->details->uname); } } ); rsc->children = g_list_sort_with_data(rsc->children, sort_clone_instance, data_set); /* count now tracks the number of clones we have allocated */ slist_iter(node, node_t, rsc->allowed_nodes, lpc, node->count = 0; ); rsc->allowed_nodes = g_list_sort_with_data( rsc->allowed_nodes, sort_node_weight, data_set); slist_iter(node, node_t, rsc->allowed_nodes, lpc, if(can_run_resources(node)) { available_nodes++; } ); slist_iter(child, resource_t, rsc->children, lpc, if(allocated >= clone_data->clone_max) { crm_debug("Child %s not allocated - limit reached", child->id); resource_location(child, NULL, -INFINITY, "clone_color:limit_reached", data_set); } else if (clone_data->clone_max < available_nodes) { /* Only include positive colocation preferences of dependant resources * if not every node will get a copy of the clone */ append_parent_colocation(rsc, child, TRUE); } else { append_parent_colocation(rsc, child, FALSE); } if(color_instance(child, data_set)) { allocated++; } ); crm_debug("Allocated %d %s instances of a possible %d", allocated, rsc->id, clone_data->clone_max); clear_bit(rsc->flags, pe_rsc_provisional); clear_bit(rsc->flags, pe_rsc_allocating); return NULL; } static void clone_update_pseudo_status( resource_t *rsc, gboolean *stopping, gboolean *starting, gboolean *active) { if(rsc->children) { slist_iter(child, resource_t, rsc->children, lpc, clone_update_pseudo_status(child, stopping, starting, active) ); return; } CRM_ASSERT(active != NULL); CRM_ASSERT(starting != NULL); CRM_ASSERT(stopping != NULL); if(rsc->running_on) { *active = TRUE; } slist_iter( action, action_t, rsc->actions, lpc, if(*starting && *stopping) { return; } else if(action->optional) { crm_debug_3("Skipping optional: %s", action->uuid); continue; } else if(action->pseudo == FALSE && action->runnable == FALSE){ crm_debug_3("Skipping unrunnable: %s", action->uuid); continue; } else if(safe_str_eq(RSC_STOP, action->task)) { crm_debug_2("Stopping due to: %s", action->uuid); *stopping = TRUE; } else if(safe_str_eq(RSC_START, action->task)) { if(action->runnable == FALSE) { crm_debug_3("Skipping pseudo-op: %s run=%d, pseudo=%d", action->uuid, action->runnable, action->pseudo); } else { crm_debug_2("Starting due to: %s", action->uuid); crm_debug_3("%s run=%d, pseudo=%d", action->uuid, action->runnable, action->pseudo); *starting = TRUE; } } ); } static action_t * find_rsc_action(resource_t *rsc, const char *key, gboolean active_only, GListPtr *list) { action_t *match = NULL; GListPtr possible = NULL; GListPtr active = NULL; possible = find_actions(rsc->actions, key, NULL); if(active_only) { slist_iter(op, action_t, possible, lpc, if(op->optional == FALSE) { active = g_list_append(active, op); } ); if(active && g_list_length(active) == 1) { match = g_list_nth_data(active, 0); } if(list) { *list = active; active = NULL; } } else if(possible && g_list_length(possible) == 1) { match = g_list_nth_data(possible, 0); } if(list) { *list = possible; possible = NULL; } if(possible) { g_list_free(possible); } if(active) { g_list_free(active); } return match; } static void child_ordering_constraints(resource_t *rsc, pe_working_set_t *data_set) { char *key = NULL; action_t *stop = NULL; action_t *start = NULL; action_t *last_stop = NULL; action_t *last_start = NULL; gboolean active_only = TRUE; /* change to false to get the old behavior */ clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); if(clone_data->ordered == FALSE) { return; } slist_iter( child, resource_t, rsc->children, lpc, key = stop_key(child); stop = find_rsc_action(child, key, active_only, NULL); crm_free(key); key = start_key(child); start = find_rsc_action(child, key, active_only, NULL); crm_free(key); if(stop) { if(last_stop) { /* child/child relative stop */ order_actions(stop, last_stop, pe_order_implies_left); } last_stop = stop; } if(start) { if(last_start) { /* child/child relative start */ order_actions(last_start, start, pe_order_implies_left); } last_start = start; } ); } void clone_create_actions(resource_t *rsc, pe_working_set_t *data_set) { gboolean child_active = FALSE; gboolean child_starting = FALSE; gboolean child_stopping = FALSE; action_t *stop = NULL; action_t *stopped = NULL; action_t *start = NULL; action_t *started = NULL; resource_t *last_start_rsc = NULL; resource_t *last_stop_rsc = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); crm_debug_2("Creating actions for %s", rsc->id); slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->create_actions(child_rsc, data_set); clone_update_pseudo_status( child_rsc, &child_stopping, &child_starting, &child_active); if(is_set(child_rsc->flags, pe_rsc_starting)) { last_start_rsc = child_rsc; } if(is_set(child_rsc->flags, pe_rsc_stopping)) { last_stop_rsc = child_rsc; } ); /* start */ start = start_action(rsc, NULL, !child_starting); started = custom_action(rsc, started_key(rsc), RSC_STARTED, NULL, !child_starting, TRUE, data_set); start->pseudo = TRUE; start->runnable = TRUE; started->pseudo = TRUE; started->priority = INFINITY; if(child_active || child_starting) { started->runnable = TRUE; } child_ordering_constraints(rsc, data_set); child_starting_constraints(clone_data, rsc, NULL, last_start_rsc, data_set); if(clone_data->start_notify == NULL) { clone_data->start_notify = create_notification_boundaries(rsc, RSC_START, start, started, data_set); } /* stop */ stop = stop_action(rsc, NULL, !child_stopping); stopped = custom_action(rsc, stopped_key(rsc), RSC_STOPPED, NULL, !child_stopping, TRUE, data_set); stop->pseudo = TRUE; stop->runnable = TRUE; stopped->pseudo = TRUE; stopped->runnable = TRUE; stopped->priority = INFINITY; child_stopping_constraints(clone_data, rsc, NULL, last_stop_rsc, data_set); if(clone_data->stop_notify == NULL) { clone_data->stop_notify = create_notification_boundaries(rsc, RSC_STOP, stop, stopped, data_set); if(clone_data->stop_notify && clone_data->start_notify) { order_actions(clone_data->stop_notify->post_done, clone_data->start_notify->pre, pe_order_optional); } } } void child_starting_constraints( clone_variant_data_t *clone_data, resource_t *rsc, resource_t *child, resource_t *last, pe_working_set_t *data_set) { if(child == NULL && last == NULL) { crm_debug("%s has no active children", rsc->id); return; } if(child != NULL) { order_start_start( rsc, child, pe_order_runnable_left|pe_order_implies_left_printed); new_rsc_order(child, RSC_START, rsc, RSC_STARTED, pe_order_implies_right_printed, data_set); } if(FALSE && clone_data->ordered) { if(child == NULL) { /* last child start before global started */ new_rsc_order(last, RSC_START, rsc, RSC_STARTED, pe_order_runnable_left, data_set); } else if(last == NULL) { /* global start before first child start */ order_start_start( rsc, child, pe_order_implies_left); } else { /* child/child relative start */ order_start_start(last, child, pe_order_implies_left); } } } void child_stopping_constraints( clone_variant_data_t *clone_data, resource_t *rsc, resource_t *child, resource_t *last, pe_working_set_t *data_set) { if(child == NULL && last == NULL) { crm_debug("%s has no active children", rsc->id); return; } if(child != NULL) { order_stop_stop(rsc, child, pe_order_shutdown|pe_order_implies_left_printed); new_rsc_order(child, RSC_STOP, rsc, RSC_STOPPED, pe_order_implies_right_printed, data_set); } if(FALSE && clone_data->ordered) { if(last == NULL) { /* first child stop before global stopped */ new_rsc_order(child, RSC_STOP, rsc, RSC_STOPPED, pe_order_runnable_left, data_set); } else if(child == NULL) { /* global stop before last child stop */ order_stop_stop( rsc, last, pe_order_implies_left); } else { /* child/child relative stop */ order_stop_stop(child, last, pe_order_implies_left); } } } void clone_internal_constraints(resource_t *rsc, pe_working_set_t *data_set) { resource_t *last_rsc = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); native_internal_constraints(rsc, data_set); /* global stop before stopped */ new_rsc_order(rsc, RSC_STOP, rsc, RSC_STOPPED, pe_order_runnable_left, data_set); /* global start before started */ new_rsc_order(rsc, RSC_START, rsc, RSC_STARTED, pe_order_runnable_left, data_set); /* global stopped before start */ new_rsc_order(rsc, RSC_STOPPED, rsc, RSC_START, pe_order_optional, data_set); slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->internal_constraints(child_rsc, data_set); child_starting_constraints( clone_data, rsc, child_rsc, last_rsc, data_set); child_stopping_constraints( clone_data, rsc, child_rsc, last_rsc, data_set); last_rsc = child_rsc; ); } -resource_t* -find_compatible_child( - resource_t *local_child, resource_t *rsc, enum rsc_role_e filter, gboolean current) +static void +assign_node(resource_t *rsc, node_t *node, gboolean force) +{ + if(rsc->children) { + slist_iter( + child_rsc, resource_t, rsc->children, lpc, + native_assign_node(child_rsc, NULL, node, force); + ); + return; + } + native_assign_node(rsc, NULL, node, force); +} + +static resource_t* +find_compatible_child_by_node( + resource_t *local_child, node_t *local_node, resource_t *rsc, enum rsc_role_e filter, gboolean current) { - node_t *local_node = NULL; node_t *node = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); - local_node = local_child->fns->location(local_child, NULL, current); if(local_node == NULL) { - crm_debug("Can't colocate unrunnable child %s with %s", + crm_err("Can't colocate unrunnable child %s with %s", local_child->id, rsc->id); return NULL; } slist_iter( child_rsc, resource_t, rsc->children, lpc, + /* enum rsc_role_e next_role = minimum_resource_state(child_rsc, current); */ enum rsc_role_e next_role = child_rsc->fns->state(child_rsc, current); node = child_rsc->fns->location(child_rsc, NULL, current); if(filter != RSC_ROLE_UNKNOWN && next_role != filter) { - crm_debug_2("Filtered %s", child_rsc->id); + crm_debug_3("Filtered %s", child_rsc->id); continue; } if(node && local_node && node->details == local_node->details) { - crm_info("Colocating %s with %s on %s", - local_child->id, child_rsc->id, node->details->uname); + crm_debug_2("Pairing %s with %s on %s", + local_child->id, child_rsc->id, node->details->uname); return child_rsc; } ); - crm_debug("Can't colocate child %s with %s", - local_child->id, rsc->id); + + crm_debug_3("Can't pair %s with %s", local_child->id, rsc->id); return NULL; } -void clone_rsc_colocation_lh( - resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint) +resource_t* +find_compatible_child( + resource_t *local_child, resource_t *rsc, enum rsc_role_e filter, gboolean current) { - gboolean do_interleave = FALSE; - resource_t *rsc = constraint->rsc_lh; + resource_t *pair = NULL; + GListPtr scratch = NULL; + node_t *local_node = NULL; clone_variant_data_t *clone_data = NULL; - clone_variant_data_t *clone_data_rh = NULL; + get_clone_variant_data(clone_data, rsc); - if(rsc == NULL) { - pe_err("rsc_lh was NULL for %s", constraint->id); - return; - - } else if(constraint->rsc_rh == NULL) { - pe_err("rsc_rh was NULL for %s", constraint->id); - return; - - } else { - crm_debug_4("Processing constraints from %s", rsc->id); + local_node = local_child->fns->location(local_child, NULL, current); + if(local_node) { + return find_compatible_child_by_node(local_child, local_node, rsc, filter, current); } - - get_clone_variant_data(clone_data, rsc); - if(constraint->rsc_rh->variant == pe_clone - || constraint->rsc_rh->variant == pe_master) { - get_clone_variant_data( - clone_data_rh, constraint->rsc_rh); - if(clone_data->clone_node_max - != clone_data_rh->clone_node_max) { - crm_config_err("Cannot interleave "XML_CIB_TAG_INCARNATION - " %s and %s because" - " they do not support the same number of" - " resources per node", - constraint->rsc_lh->id, constraint->rsc_rh->id); - - /* only the LHS side needs to be labeled as interleave */ - } else if(clone_data->interleave) { - do_interleave = TRUE; + scratch = node_list_dup(local_child->allowed_nodes, FALSE, TRUE); + scratch = g_list_sort_with_data(scratch, sort_node_weight, NULL); - } else if(constraint->score >= INFINITY) { - GListPtr lhs = NULL, rhs = NULL; - lhs = rsc_lh->allowed_nodes; - - slist_iter( - child_rsc, resource_t, rsc_rh->children, lpc, - node_t *chosen = child_rsc->fns->location(child_rsc, NULL, FALSE); - if(chosen != NULL) { - rhs = g_list_append(rhs, chosen); - } - ); - - rsc_lh->allowed_nodes = node_list_exclude(lhs, rhs, TRUE); - - pe_free_shallow_adv(rhs, FALSE); - pe_free_shallow(lhs); - return; + slist_iter( + node, node_t, scratch, lpc, + + pair = find_compatible_child_by_node( + local_child, node, rsc, filter, current); + if(pair) { + goto done; } + ); + + crm_debug("Can't pair %s with %s", local_child->id, rsc->id); + done: + slist_destroy(node_t, node, scratch, crm_free(node)); + return pair; +} - } else if(constraint->score >= INFINITY) { - crm_config_err("Manditory co-location of clones (%s) with other" - " non-clone (%s) resources is not supported", - rsc_lh->id, rsc_rh->id); - return; - } +void clone_rsc_colocation_lh( + resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint) +{ + /* -- Never called -- + * + * Instead we add the colocation constraints to the child and call from there + */ - if(do_interleave) { - resource_t *rh_child = NULL; - - slist_iter(lh_child, resource_t, rsc->children, lpc, - - CRM_ASSERT(lh_child != NULL); - rh_child = find_compatible_child( - lh_child, rsc_rh, RSC_ROLE_UNKNOWN, FALSE); - if(rh_child == NULL) { - crm_debug_2("No match found for %s", lh_child->id); - continue; - } - crm_debug("Interleaving %s with %s", lh_child->id, rh_child->id); - lh_child->cmds->rsc_colocation_lh( - lh_child, rh_child, constraint); - ); - return; - } + CRM_CHECK(FALSE, crm_err("This functionality is not thought to be used. Please report a bug.")); + CRM_CHECK(rsc_lh, return); + CRM_CHECK(rsc_rh, return); slist_iter( - child_rsc, resource_t, rsc->children, lpc, + child_rsc, resource_t, rsc_lh->children, lpc, - child_rsc->cmds->rsc_colocation_lh(child_rsc, constraint->rsc_rh, constraint); + child_rsc->cmds->rsc_colocation_lh(child_rsc, rsc_rh, constraint); ); + + return; } void clone_rsc_colocation_rh( resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint) { + gboolean do_interleave = FALSE; clone_variant_data_t *clone_data = NULL; + clone_variant_data_t *clone_data_lh = NULL; + CRM_CHECK(rsc_lh != NULL, return); CRM_CHECK(rsc_lh->variant == pe_native, return); - get_clone_variant_data(clone_data, rsc_rh); - - crm_debug_3("Processing constraint %s: %s -> %s %d", constraint->id, rsc_lh->id, rsc_rh->id, constraint->score); + get_clone_variant_data(clone_data, constraint->rsc_rh); + crm_debug_3("Processing constraint %s: %s -> %s %d", + constraint->id, rsc_lh->id, rsc_rh->id, constraint->score); + + if(constraint->rsc_lh->variant >= pe_clone) { + + get_clone_variant_data(clone_data_lh, constraint->rsc_lh); + if(clone_data->clone_node_max != clone_data_lh->clone_node_max) { + crm_config_err("Cannot interleave "XML_CIB_TAG_INCARNATION + " %s and %s because" + " they do not support the same number of" + " resources per node", + constraint->rsc_lh->id, constraint->rsc_rh->id); + + /* only the LHS side needs to be labeled as interleave */ + } else if(clone_data_lh->interleave) { + do_interleave = TRUE; + } + } if(rsc_rh == NULL) { pe_err("rsc_rh was NULL for %s", constraint->id); return; } else if(is_set(rsc_rh->flags, pe_rsc_provisional)) { crm_debug_3("%s is still provisional", rsc_rh->id); return; - + + } else if(do_interleave) { + resource_t *rh_child = NULL; + + rh_child = find_compatible_child(rsc_lh, rsc_rh, RSC_ROLE_UNKNOWN, FALSE); + + if(rh_child) { + crm_debug("Pairing %s with %s", rsc_lh->id, rh_child->id); + rsc_lh->cmds->rsc_colocation_lh(rsc_lh, rh_child, constraint); + + } else if(constraint->score >= INFINITY) { + crm_notice("Cannot pair %s with instance of %s", rsc_lh->id, rsc_rh->id); + assign_node(rsc_lh, NULL, TRUE); + + } else { + crm_debug("Cannot pair %s with instance of %s", rsc_lh->id, rsc_rh->id); + } + + return; + } else if(constraint->score >= INFINITY) { GListPtr lhs = NULL, rhs = NULL; lhs = rsc_lh->allowed_nodes; slist_iter( child_rsc, resource_t, rsc_rh->children, lpc, node_t *chosen = child_rsc->fns->location(child_rsc, NULL, FALSE); if(chosen != NULL) { rhs = g_list_append(rhs, chosen); } ); rsc_lh->allowed_nodes = node_list_exclude(lhs, rhs, FALSE); pe_free_shallow_adv(rhs, FALSE); pe_free_shallow(lhs); return; } slist_iter( child_rsc, resource_t, rsc_rh->children, lpc, child_rsc->cmds->rsc_colocation_rh(rsc_lh, child_rsc, constraint); ); } /* Clone <-> Clone ordering S : Start(ed) S' : Stop(ped) P : Promote(d) D : Demote(d) Started == Demoted First A then B A:0 B:0 Old New Old New S' S' S S' S' S' S' - S' S S S+ S' S S' S S S' S S' S S' S' - S S S - S S S' S S' S' P S' S' S' S' - S' P P P+ S' P S' P P S' P S' P S' S' - P P P - P P S' P D D P D D D D - D P P P+ D P D P P D P D P D D - P P P - P P D P Clone <-> Primitive ordering S : Start(ed) S' : Stop(ped) P : Promote(d) D : Demote(d) F : False T : True F' : A good idea? Started == Demoted First A then B A:0 B Old New Old Create Constraint S' S' S F S' S' S' F' S S' S T S S' S' F S' S S T S' S S' T S S S F' S S S' T S' S' S F S' S' S' F' P S' S T P S' S' F S' P S T S' P S' T P P S F' P P S' F S' S' S F S' S' S' F' D S' S T D S' S' F S' D S T S' D S' T D D S F' D D S' T */ static gboolean detect_restart(resource_t *rsc) { gboolean restart = FALSE; /* Look for restarts */ action_t *start = NULL; char *key = start_key(rsc); GListPtr possible_matches = find_actions(rsc->actions, key, NULL); crm_free(key); if(possible_matches) { start = possible_matches->data; g_list_free(possible_matches); } if(start != NULL && start->optional == FALSE) { restart = TRUE; crm_debug_2("Detected a restart for %s", rsc->id); } /* Otherwise, look for moves */ if(restart == FALSE) { GListPtr old_hosts = NULL; GListPtr new_hosts = NULL; GListPtr intersection = NULL; rsc->fns->location(rsc, &old_hosts, TRUE); rsc->fns->location(rsc, &new_hosts, FALSE); intersection = node_list_and(old_hosts, new_hosts, FALSE); if(intersection == NULL) { restart = TRUE; /* Actually a move but the result is the same */ crm_debug_2("Detected a move for %s", rsc->id); } slist_destroy(node_t, node, intersection, crm_free(node)); g_list_free(old_hosts); g_list_free(new_hosts); } return restart; } static void clone_rsc_order_lh_non_clone(resource_t *rsc, order_constraint_t *order, pe_working_set_t *data_set) { GListPtr hosts = NULL; GListPtr rh_hosts = NULL; GListPtr intersection = NULL; const char *reason = "unknown"; enum action_tasks task = start_rsc; enum rsc_role_e lh_role = RSC_ROLE_STARTED; int any_ordered = 0; gboolean down_stack = TRUE; crm_debug_2("Clone-to-* ordering: %s -> %s 0x%.6x", order->lh_action_task, order->rh_action_task, order->type); if(strstr(order->rh_action_task, "_"RSC_STOP"_0") || strstr(order->rh_action_task, "_"RSC_STOPPED"_0")) { task = stop_rsc; reason = "down activity"; lh_role = RSC_ROLE_STOPPED; order->rh_rsc->fns->location(order->rh_rsc, &rh_hosts, down_stack); } else if(strstr(order->rh_action_task, "_"RSC_DEMOTE"_0") || strstr(order->rh_action_task, "_"RSC_DEMOTED"_0")) { task = action_demote; reason = "demotion activity"; lh_role = RSC_ROLE_SLAVE; order->rh_rsc->fns->location(order->rh_rsc, &rh_hosts, down_stack); } else if(strstr(order->lh_action_task, "_"RSC_PROMOTE"_0") || strstr(order->lh_action_task, "_"RSC_PROMOTED"_0")) { task = action_promote; down_stack = FALSE; reason = "promote activity"; order->rh_rsc->fns->location(order->rh_rsc, &rh_hosts, down_stack); lh_role = RSC_ROLE_MASTER; } else if(strstr(order->rh_action_task, "_"RSC_START"_0") || strstr(order->rh_action_task, "_"RSC_STARTED"_0")) { task = start_rsc; down_stack = FALSE; reason = "up activity"; order->rh_rsc->fns->location(order->rh_rsc, &rh_hosts, down_stack); /* if(order->rh_rsc->variant > pe_clone) { */ /* lh_role = RSC_ROLE_SLAVE; */ /* } */ } else { crm_err("Unknown task: %s", order->rh_action_task); return; } /* slist_iter(h, node_t, rh_hosts, llpc, crm_info("RHH: %s", h->details->uname)); */ slist_iter( child_rsc, resource_t, rsc->children, lpc, gboolean create = FALSE; gboolean restart = FALSE; enum rsc_role_e lh_role_new = child_rsc->fns->state(child_rsc, FALSE); enum rsc_role_e lh_role_old = child_rsc->fns->state(child_rsc, TRUE); enum rsc_role_e child_role = child_rsc->fns->state(child_rsc, down_stack); crm_debug_4("Testing %s->%s for %s: %s vs. %s %s", order->lh_action_task, order->rh_action_task, child_rsc->id, role2text(lh_role), role2text(child_role), order->lh_action_task); if(rh_hosts == NULL) { crm_debug_3("Terminating search: %s.%d list is empty: no possible %s", order->rh_rsc->id, down_stack, reason); break; } if(lh_role_new == lh_role_old) { restart = detect_restart(child_rsc); if(restart == FALSE) { crm_debug_3("Ignoring %s->%s for %s: no relevant %s (no role change)", order->lh_action_task, order->rh_action_task, child_rsc->id, reason); continue; } } hosts = NULL; child_rsc->fns->location(child_rsc, &hosts, down_stack); intersection = node_list_and(hosts, rh_hosts, FALSE); /* slist_iter(h, node_t, hosts, llpc, crm_info("H: %s %s", child_rsc->id, h->details->uname)); */ if(intersection == NULL) { crm_debug_3("Ignoring %s->%s for %s: no relevant %s", order->lh_action_task, order->rh_action_task, child_rsc->id, reason); g_list_free(hosts); continue; } if(restart) { reason = "restart"; create = TRUE; } else if(down_stack) { if(lh_role_old > lh_role) { create = TRUE; } } else if(down_stack == FALSE) { if(lh_role_old < lh_role) { create = TRUE; } } else { any_ordered++; reason = "role"; crm_debug_4("Role: %s->%s for %s: %s vs. %s %s", order->lh_action_task, order->rh_action_task, child_rsc->id, role2text(lh_role_old), role2text(lh_role), order->lh_action_task); } if(create) { char *task = order->lh_action_task; any_ordered++; crm_debug("Enforcing %s->%s for %s on %s: found %s", order->lh_action_task, order->rh_action_task, child_rsc->id, ((node_t*)intersection->data)->details->uname, reason); order->lh_action_task = convert_non_atomic_task(task, child_rsc, TRUE, FALSE); child_rsc->cmds->rsc_order_lh(child_rsc, order, data_set); crm_free(order->lh_action_task); order->lh_action_task = task; } crm_debug_3("Processed %s->%s for %s on %s: %s", order->lh_action_task, order->rh_action_task, child_rsc->id, ((node_t*)intersection->data)->details->uname, reason); /* slist_iter(h, node_t, hosts, llpc, */ /* crm_info("H: %s %s", child_rsc->id, h->details->uname)); */ slist_destroy(node_t, node, intersection, crm_free(node)); g_list_free(hosts); ); g_list_free(rh_hosts); if(any_ordered == 0 && down_stack == FALSE) { GListPtr lh_hosts = NULL; if(order->type & pe_order_runnable_left) { rsc->fns->location(rsc, &lh_hosts, FALSE); } if(lh_hosts == NULL) { order->lh_action_task = convert_non_atomic_task(order->lh_action_task, rsc, TRUE, TRUE); native_rsc_order_lh(rsc, order, data_set); } g_list_free(lh_hosts); } order->type = pe_order_optional; } void clone_rsc_order_lh(resource_t *rsc, order_constraint_t *order, pe_working_set_t *data_set) { resource_t *r1 = NULL; resource_t *r2 = NULL; gboolean do_interleave = FALSE; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); crm_debug_4("%s->%s", order->lh_action_task, order->rh_action_task); if(order->rh_rsc == NULL) { order->lh_action_task = convert_non_atomic_task(order->lh_action_task, rsc, FALSE, TRUE); native_rsc_order_lh(rsc, order, data_set); return; } r1 = uber_parent(rsc); r2 = uber_parent(order->rh_rsc); if(r1 == r2) { native_rsc_order_lh(rsc, order, data_set); return; } if(order->rh_rsc->variant > pe_group && clone_data->interleave) { clone_variant_data_t *clone_data_rh = NULL; get_clone_variant_data(clone_data_rh, order->rh_rsc); if(clone_data->clone_node_max == clone_data_rh->clone_node_max) { /* only the LHS side needs to be labeled as interleave */ do_interleave = TRUE; } else { crm_config_err("Cannot interleave "XML_CIB_TAG_INCARNATION " %s and %s because they do not support the same" " number of resources per node", rsc->id, order->rh_rsc->id); } } if(order->rh_rsc == NULL) { do_interleave = FALSE; } if(do_interleave) { resource_t *lh_child = NULL; resource_t *rh_saved = order->rh_rsc; gboolean current = FALSE; if(strstr(order->lh_action_task, "_stop_0") || strstr(order->lh_action_task, "_demote_0")) { current = TRUE; } slist_iter( rh_child, resource_t, rh_saved->children, lpc, CRM_ASSERT(rh_child != NULL); lh_child = find_compatible_child(rh_child, rsc, RSC_ROLE_UNKNOWN, current); - if(lh_child == NULL) { - crm_debug_2("No match found for %s", rh_child->id); + if(lh_child == NULL && current) { + continue; + + } else if(lh_child == NULL) { + crm_debug("No match found for %s (%d)", rh_child->id, current); + + /* Me no like this hack - but what else can we do? + * + * If there is no-one active or about to be active + * on the same node as rh_child, then they must + * not be allowed to start + */ + if(order->type & (pe_order_runnable_left|pe_order_implies_right) /* Mandatory */) { + crm_info("Inhibiting %s from being active", rh_child->id); + assign_node(rh_child, NULL, TRUE); + } continue; } - crm_notice("Interleaving %s with %s", lh_child->id, rh_child->id); + crm_debug("Pairing %s with %s", lh_child->id, rh_child->id); order->rh_rsc = rh_child; lh_child->cmds->rsc_order_lh(lh_child, order, data_set); order->rh_rsc = rh_saved; ); } else { #if 0 if(order->type != pe_order_optional) { crm_debug("Upgraded ordering constraint %d - 0x%.6x", order->id, order->type); native_rsc_order_lh(rsc, order, data_set); } #endif if(order->rh_rsc->variant < pe_clone) { clone_rsc_order_lh_non_clone(rsc, order, data_set); } else if(order->type & pe_order_implies_left) { if(rsc->variant == order->rh_rsc->variant) { crm_debug_2("Clone-to-clone ordering: %s -> %s 0x%.6x", order->lh_action_task, order->rh_action_task, order->type); /* stop instances on the same nodes as stopping RHS instances */ slist_iter( child_rsc, resource_t, rsc->children, lpc, native_rsc_order_lh(child_rsc, order, data_set); ); } else { /* stop everything */ slist_iter( child_rsc, resource_t, rsc->children, lpc, native_rsc_order_lh(child_rsc, order, data_set); ); } } } if(do_interleave == FALSE || clone_data->ordered) { order->lh_action_task = convert_non_atomic_task(order->lh_action_task, rsc, FALSE, TRUE); native_rsc_order_lh(rsc, order, data_set); } if(is_set(rsc->flags, pe_rsc_notify)) { order->type = pe_order_optional; order->lh_action_task = convert_non_atomic_task(order->lh_action_task, rsc, TRUE, TRUE); native_rsc_order_lh(rsc, order, data_set); } } static void clone_rsc_order_rh_non_clone( resource_t *lh_p, action_t *lh_action, resource_t *rsc, order_constraint_t *order) { GListPtr hosts = NULL; GListPtr lh_hosts = NULL; GListPtr intersection = NULL; const char *reason = "unknown"; gboolean restart = FALSE; gboolean down_stack = TRUE; enum rsc_role_e rh_role = RSC_ROLE_STARTED; enum action_tasks task = start_rsc; enum rsc_role_e lh_role_new = lh_p->fns->state(lh_p, FALSE); enum rsc_role_e lh_role_old = lh_p->fns->state(lh_p, TRUE); /* Make sure the pre-req will be active */ if(order->type & pe_order_runnable_left) { lh_p->fns->location(lh_p, &lh_hosts, FALSE); if(lh_hosts == NULL) { crm_debug("Terminating search: Pre-requisite %s of %s is unrunnable", lh_p->id, rsc->id); native_rsc_order_rh(lh_action, rsc, order); return; } g_list_free(lh_hosts); lh_hosts = NULL; } if(strstr(order->lh_action_task, "_"RSC_STOP"_0") || strstr(order->lh_action_task, "_"RSC_STOPPED"_0")) { task = stop_rsc; reason = "down activity"; rh_role = RSC_ROLE_STOPPED; lh_p->fns->location(lh_p, &lh_hosts, down_stack); /* These actions are not possible for non-clones } else if(strstr(order->lh_action_task, "_"RSC_DEMOTE"_0") || strstr(order->lh_action_task, "_"RSC_DEMOTED"_0")) { task = action_demote; rh_role = RSC_ROLE_SLAVE; reason = "demotion activity"; lh_p->fns->location(lh_p, &lh_hosts, down_stack); } else if(strstr(order->lh_action_task, "_"RSC_PROMOTE"_0") || strstr(order->lh_action_task, "_"RSC_PROMOTED"_0")) { task = action_promote; down_stack = FALSE; reason = "promote activity"; lh_p->fns->location(lh_p, &lh_hosts, down_stack); rh_role = RSC_ROLE_MASTER; */ } else if(strstr(order->lh_action_task, "_"RSC_START"_0") || strstr(order->lh_action_task, "_"RSC_STARTED"_0")) { task = start_rsc; down_stack = FALSE; reason = "up activity"; lh_p->fns->location(lh_p, &lh_hosts, down_stack); } else { crm_err("Unknown action: %s", order->lh_action_task); return; } if(lh_role_new == lh_role_old) { restart = detect_restart(lh_action->rsc); if(FALSE && restart == FALSE) { crm_debug_3("Ignoring %s->%s for %s: no relevant %s (no role change)", lh_action->task, order->lh_action_task, lh_p->id, reason); goto cleanup; } } /* slist_iter(h, node_t, lh_hosts, llpc, crm_info("LHH: %s", h->details->uname)); */ slist_iter( child_rsc, resource_t, rsc->children, lpc, gboolean create = FALSE; enum rsc_role_e child_role = child_rsc->fns->state(child_rsc, down_stack); crm_debug_4("Testing %s->%s for %s: %s vs. %s %s", lh_action->task, order->lh_action_task, child_rsc->id, role2text(rh_role), role2text(child_role), order->lh_action_task); if(lh_hosts == NULL) { crm_debug_3("Terminating search: %s.%d list is empty: no possible %s", order->rh_rsc->id, down_stack, reason); break; } hosts = NULL; child_rsc->fns->location(child_rsc, &hosts, down_stack); intersection = node_list_and(hosts, lh_hosts, FALSE); if(intersection == NULL) { crm_debug_3("Ignoring %s->%s for %s: no relevant %s", lh_action->task, order->lh_action_task, child_rsc->id, reason); g_list_free(hosts); continue; } /* slist_iter(h, node_t, hosts, llpc, crm_info("H: %s %s", child_rsc->id, h->details->uname)); */ if(restart) { reason = "restart"; create = TRUE; } else if(down_stack && lh_role_old >= rh_role) { create = TRUE; } else if(down_stack == FALSE && lh_role_old <= rh_role) { create = TRUE; } else { reason = "role"; } if(create) { enum pe_ordering type = order->type; child_rsc->cmds->rsc_order_rh(lh_action, child_rsc, order); order->type = pe_order_optional; native_rsc_order_rh(lh_action, rsc, order); order->type = type; } crm_debug_3("Processed %s->%s for %s on %s: found %s%s", lh_action->task, order->lh_action_task, child_rsc->id, ((node_t*)intersection->data)->details->uname, reason, create?" - enforced":""); /* slist_iter(h, node_t, hosts, llpc, */ /* crm_info("H: %s %s", child_rsc->id, h->details->uname)); */ slist_destroy(node_t, node, intersection, crm_free(node)); g_list_free(hosts); ); cleanup: g_list_free(lh_hosts); } void clone_rsc_order_rh( action_t *lh_action, resource_t *rsc, order_constraint_t *order) { enum pe_ordering type = order->type; clone_variant_data_t *clone_data = NULL; resource_t *lh_p = uber_parent(lh_action->rsc); get_clone_variant_data(clone_data, rsc); crm_debug_2("%s->%s", order->lh_action_task, order->rh_action_task); if(safe_str_eq(CRM_OP_PROBED, lh_action->uuid)) { slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->rsc_order_rh(lh_action, child_rsc, order); ); if(rsc->fns->state(rsc, TRUE) < RSC_ROLE_STARTED && rsc->fns->state(rsc, FALSE) > RSC_ROLE_STOPPED) { order->type |= pe_order_implies_right; } } else if(lh_p && lh_p != rsc && lh_p->variant < pe_clone) { clone_rsc_order_rh_non_clone(lh_p, lh_action, rsc, order); return; } native_rsc_order_rh(lh_action, rsc, order); order->type = type; } void clone_rsc_location(resource_t *rsc, rsc_to_node_t *constraint) { clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); crm_debug_3("Processing location constraint %s for %s", constraint->id, rsc->id); native_rsc_location(rsc, constraint); slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->rsc_location(child_rsc, constraint); ); } void clone_expand(resource_t *rsc, pe_working_set_t *data_set) { clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); if(clone_data->start_notify) { collect_notification_data(rsc, TRUE, TRUE, clone_data->start_notify); expand_notification_data(clone_data->start_notify); create_notifications(rsc, clone_data->start_notify, data_set); } if(clone_data->stop_notify) { collect_notification_data(rsc, TRUE, TRUE, clone_data->stop_notify); expand_notification_data(clone_data->stop_notify); create_notifications(rsc, clone_data->stop_notify, data_set); } if(clone_data->promote_notify) { collect_notification_data(rsc, TRUE, TRUE, clone_data->promote_notify); expand_notification_data(clone_data->promote_notify); create_notifications(rsc, clone_data->promote_notify, data_set); } if(clone_data->demote_notify) { collect_notification_data(rsc, TRUE, TRUE, clone_data->demote_notify); expand_notification_data(clone_data->demote_notify); create_notifications(rsc, clone_data->demote_notify, data_set); } /* Now that the notifcations have been created we can expand the children */ slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->expand(child_rsc, data_set)); native_expand(rsc, data_set); /* The notifications are in the graph now, we can destroy the notify_data */ free_notification_data(clone_data->demote_notify); clone_data->demote_notify = NULL; free_notification_data(clone_data->stop_notify); clone_data->stop_notify = NULL; free_notification_data(clone_data->start_notify); clone_data->start_notify = NULL; free_notification_data(clone_data->promote_notify); clone_data->promote_notify = NULL; } static gint sort_rsc_id(gconstpointer a, gconstpointer b) { const resource_t *resource1 = (const resource_t*)a; const resource_t *resource2 = (const resource_t*)b; CRM_ASSERT(resource1 != NULL); CRM_ASSERT(resource2 != NULL); return strcmp(resource1->id, resource2->id); } static resource_t *find_instance_on(resource_t *rsc, node_t *node) { slist_iter(child, resource_t, rsc->children, lpc, GListPtr known_list = NULL; rsc_known_on(child, &known_list); slist_iter(known, node_t, known_list, lpc2, if(node->details == known->details) { g_list_free(known_list); return child; } ); g_list_free(known_list); ); return NULL; } gboolean clone_create_probe(resource_t *rsc, node_t *node, action_t *complete, gboolean force, pe_working_set_t *data_set) { gboolean any_created = FALSE; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); rsc->children = g_list_sort(rsc->children, sort_rsc_id); if(rsc->children == NULL) { pe_warn("Clone %s has no children", rsc->id); return FALSE; } if(is_not_set(rsc->flags, pe_rsc_unique) && clone_data->clone_node_max == 1) { /* only look for one copy */ resource_t *child = NULL; /* Try whoever we probed last time */ child = find_instance_on(rsc, node); if(child) { return child->cmds->create_probe( child, node, complete, force, data_set); } /* Try whoever we plan on starting there */ slist_iter( child_rsc, resource_t, rsc->children, lpc, node_t *local_node = child_rsc->fns->location(child_rsc, NULL, FALSE); if(local_node == NULL) { continue; } if(local_node->details == node->details) { return child_rsc->cmds->create_probe( child_rsc, node, complete, force, data_set); } ); /* Fall back to the first clone instance */ child = rsc->children->data; return child->cmds->create_probe(child, node, complete, force, data_set); } slist_iter( child_rsc, resource_t, rsc->children, lpc, if(child_rsc->cmds->create_probe( child_rsc, node, complete, force, data_set)) { any_created = TRUE; } if(any_created && is_not_set(rsc->flags, pe_rsc_unique) && clone_data->clone_node_max == 1) { /* only look for one copy (clone :0) */ break; } ); return any_created; } void clone_append_meta(resource_t *rsc, xmlNode *xml) { char *name = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); name = crm_meta_name(XML_RSC_ATTR_UNIQUE); crm_xml_add(xml, name, is_set(rsc->flags, pe_rsc_unique)?"true":"false"); crm_free(name); name = crm_meta_name(XML_RSC_ATTR_NOTIFY); crm_xml_add(xml, name, is_set(rsc->flags, pe_rsc_notify)?"true":"false"); crm_free(name); name = crm_meta_name(XML_RSC_ATTR_INCARNATION_MAX); crm_xml_add_int(xml, name, clone_data->clone_max); crm_free(name); name = crm_meta_name(XML_RSC_ATTR_INCARNATION_NODEMAX); crm_xml_add_int(xml, name, clone_data->clone_node_max); crm_free(name); } diff --git a/pengine/group.c b/pengine/group.c index bb2fd3e687..73f7147c1e 100644 --- a/pengine/group.c +++ b/pengine/group.c @@ -1,525 +1,540 @@ /* * 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 #define VARIANT_GROUP 1 #include node_t * group_color(resource_t *rsc, pe_working_set_t *data_set) { node_t *node = NULL; node_t *group_node = NULL; group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc); if(is_not_set(rsc->flags, pe_rsc_provisional)) { return rsc->allocated_to; } crm_debug_2("Processing %s", rsc->id); if(is_set(rsc->flags, pe_rsc_allocating)) { crm_debug("Dependency loop detected involving %s", rsc->id); return NULL; } if(group_data->first_child == NULL) { /* nothign to allocate */ clear_bit(rsc->flags, pe_rsc_provisional); return NULL; } set_bit(rsc->flags, pe_rsc_allocating); rsc->role = group_data->first_child->role; group_data->first_child->rsc_cons = g_list_concat( group_data->first_child->rsc_cons, rsc->rsc_cons); rsc->rsc_cons = NULL; group_data->first_child->rsc_cons_lhs = g_list_concat( group_data->first_child->rsc_cons_lhs, rsc->rsc_cons_lhs); rsc->rsc_cons_lhs = NULL; dump_node_scores(show_scores?0:scores_log_level, rsc, __PRETTY_FUNCTION__, rsc->allowed_nodes); slist_iter( child_rsc, resource_t, rsc->children, lpc, node = child_rsc->cmds->color(child_rsc, data_set); if(group_node == NULL) { group_node = node; } ); rsc->next_role = group_data->first_child->next_role; clear_bit(rsc->flags, pe_rsc_allocating); clear_bit(rsc->flags, pe_rsc_provisional); if(group_data->colocated) { return group_node; } return NULL; } void group_update_pseudo_status(resource_t *parent, resource_t *child); void group_create_actions(resource_t *rsc, pe_working_set_t *data_set) { action_t *op = NULL; const char *value = NULL; group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc); crm_debug_2("Creating actions for %s", rsc->id); slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->create_actions(child_rsc, data_set); group_update_pseudo_status(rsc, child_rsc); ); op = start_action(rsc, NULL, TRUE/* !group_data->child_starting */); op->pseudo = TRUE; op->runnable = TRUE; op = custom_action(rsc, started_key(rsc), RSC_STARTED, NULL, TRUE/* !group_data->child_starting */, TRUE, data_set); op->pseudo = TRUE; op->runnable = TRUE; op = stop_action(rsc, NULL, TRUE/* !group_data->child_stopping */); op->pseudo = TRUE; op->runnable = TRUE; op = custom_action(rsc, stopped_key(rsc), RSC_STOPPED, NULL, TRUE/* !group_data->child_stopping */, TRUE, data_set); op->pseudo = TRUE; op->runnable = TRUE; value = g_hash_table_lookup(rsc->meta, "stateful"); if(crm_is_true(value)) { op = custom_action(rsc, demote_key(rsc), RSC_DEMOTE, NULL, TRUE, TRUE, data_set); op->pseudo = TRUE; op->runnable = TRUE; op = custom_action(rsc, demoted_key(rsc), RSC_DEMOTED, NULL, TRUE, TRUE, data_set); op->pseudo = TRUE; op->runnable = TRUE; op = custom_action(rsc, promote_key(rsc), RSC_PROMOTE, NULL, TRUE, TRUE, data_set); op->pseudo = TRUE; op->runnable = TRUE; op = custom_action(rsc, promoted_key(rsc), RSC_PROMOTED, NULL, TRUE, TRUE, data_set); op->pseudo = TRUE; op->runnable = TRUE; } } void group_update_pseudo_status(resource_t *parent, resource_t *child) { group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, parent); if(group_data->ordered == FALSE) { /* If this group is not ordered, then leave the meta-actions as optional */ return; } if(group_data->child_stopping && group_data->child_starting) { return; } slist_iter( action, action_t, child->actions, lpc, if(action->optional) { continue; } if(safe_str_eq(RSC_STOP, action->task) && action->runnable) { group_data->child_stopping = TRUE; crm_debug_3("Based on %s the group is stopping", action->uuid); } else if(safe_str_eq(RSC_START, action->task) && action->runnable) { group_data->child_starting = TRUE; crm_debug_3("Based on %s the group is starting", action->uuid); } ); } void group_internal_constraints(resource_t *rsc, pe_working_set_t *data_set) { const char *value = NULL; gboolean stateful = FALSE; resource_t *last_rsc = NULL; group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc); native_internal_constraints(rsc, data_set); value = g_hash_table_lookup(rsc->meta, "stateful"); stateful = crm_is_true(value); new_rsc_order(rsc, RSC_STOPPED, rsc, RSC_START, pe_order_optional, data_set); new_rsc_order(rsc, RSC_STOP, rsc, RSC_STOPPED, pe_order_runnable_left|pe_order_implies_right|pe_order_implies_left, data_set); new_rsc_order(rsc, RSC_START, rsc, RSC_STARTED, pe_order_runnable_left, data_set); slist_iter( child_rsc, resource_t, rsc->children, lpc, int stop = pe_order_shutdown|pe_order_implies_right; int stopped = pe_order_implies_right_printed; int start = pe_order_implies_right|pe_order_runnable_left; int started = pe_order_runnable_left|pe_order_implies_right|pe_order_implies_right_printed; child_rsc->cmds->internal_constraints(child_rsc, data_set); if(last_rsc == NULL) { if(group_data->ordered) { stop |= pe_order_implies_left; stopped = pe_order_implies_right; } } else if(group_data->colocated) { rsc_colocation_new( "group:internal_colocation", NULL, INFINITY, child_rsc, last_rsc, NULL, NULL, data_set); } if(stateful) { new_rsc_order(rsc, RSC_DEMOTE, child_rsc, RSC_DEMOTE, stop|pe_order_implies_left_printed, data_set); new_rsc_order(child_rsc, RSC_DEMOTE, rsc, RSC_DEMOTED, stopped, data_set); new_rsc_order(child_rsc, RSC_PROMOTE, rsc, RSC_PROMOTED, started, data_set); new_rsc_order(rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE, pe_order_implies_left_printed, data_set); } order_start_start(rsc, child_rsc, pe_order_implies_left_printed); order_stop_stop(rsc, child_rsc, stop|pe_order_implies_left_printed); new_rsc_order(child_rsc, RSC_STOP, rsc, RSC_STOPPED, stopped, data_set); new_rsc_order(child_rsc, RSC_START, rsc, RSC_STARTED, started, data_set); if(group_data->ordered == FALSE) { order_start_start(rsc, child_rsc, start|pe_order_implies_left_printed); if(stateful) { new_rsc_order(rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE, start|pe_order_implies_left_printed, data_set); } } else if(last_rsc != NULL) { child_rsc->restart_type = pe_restart_restart; order_start_start(last_rsc, child_rsc, start); order_stop_stop(child_rsc, last_rsc, pe_order_implies_left); if(stateful) { new_rsc_order(last_rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE, start, data_set); new_rsc_order(child_rsc, RSC_DEMOTE, last_rsc, RSC_DEMOTE, pe_order_implies_left, data_set); } } else { /* If anyone in the group is starting, then * pe_order_implies_right will cause _everyone_ in the group * to be sent a start action * But this is safe since starting something that is already * started is required to be "safe" */ int flags = pe_order_implies_left|pe_order_implies_right|pe_order_runnable_right|pe_order_runnable_left; order_start_start(rsc, child_rsc, flags); if(stateful) { new_rsc_order(rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE, flags, data_set); } } last_rsc = child_rsc; ); if(group_data->ordered && last_rsc != NULL) { int stop_stop_flags = pe_order_implies_right; int stop_stopped_flags = pe_order_implies_left; order_stop_stop(rsc, last_rsc, stop_stop_flags); new_rsc_order(last_rsc, RSC_STOP, rsc, RSC_STOPPED, stop_stopped_flags, data_set); if(stateful) { new_rsc_order(rsc, RSC_DEMOTE, last_rsc, RSC_DEMOTE, stop_stop_flags, data_set); new_rsc_order(last_rsc, RSC_DEMOTE, rsc, RSC_DEMOTED, stop_stopped_flags, data_set); } } } void group_rsc_colocation_lh( resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint) { group_variant_data_t *group_data = NULL; if(rsc_lh == NULL) { pe_err("rsc_lh was NULL for %s", constraint->id); return; } else if(rsc_rh == NULL) { pe_err("rsc_rh was NULL for %s", constraint->id); return; } crm_debug_4("Processing constraints from %s", rsc_lh->id); get_group_variant_data(group_data, rsc_lh); if(group_data->colocated) { group_data->first_child->cmds->rsc_colocation_lh( group_data->first_child, rsc_rh, constraint); return; } else if(constraint->score >= INFINITY) { crm_config_err("%s: Cannot perform manditory colocation" " between non-colocated group and %s", rsc_lh->id, rsc_rh->id); return; } slist_iter( child_rsc, resource_t, rsc_lh->children, lpc, child_rsc->cmds->rsc_colocation_lh( child_rsc, rsc_rh, constraint); ); } void group_rsc_colocation_rh( resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint) { group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc_rh); CRM_CHECK(rsc_lh->variant == pe_native, return); crm_debug_3("Processing RH of constraint %s", constraint->id); print_resource(LOG_DEBUG_3, "LHS", rsc_lh, TRUE); if(is_set(rsc_rh->flags, pe_rsc_provisional)) { return; } else if(group_data->colocated && group_data->first_child) { - group_data->first_child->cmds->rsc_colocation_rh( + if(constraint->score >= INFINITY) { + /* Ensure RHS is _fully_ up before can start LHS */ + group_data->last_child->cmds->rsc_colocation_rh( + rsc_lh, group_data->last_child, constraint); + } else { + /* A partially active RHS is fine */ + group_data->first_child->cmds->rsc_colocation_rh( rsc_lh, group_data->first_child, constraint); + } + return; } else if(constraint->score >= INFINITY) { crm_config_err("%s: Cannot perform manditory colocation with" " non-colocated group: %s", rsc_lh->id, rsc_rh->id); return; } slist_iter( child_rsc, resource_t, rsc_rh->children, lpc, child_rsc->cmds->rsc_colocation_rh( rsc_lh, child_rsc, constraint); ); } void group_rsc_order_lh(resource_t *rsc, order_constraint_t *order, pe_working_set_t *data_set) { group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc); crm_debug_4("%s->%s", order->lh_action_task, order->rh_action_task); if(order->rh_rsc != NULL && (rsc == order->rh_rsc || rsc == order->rh_rsc->parent)) { native_rsc_order_lh(rsc, order, data_set); return; } #if 0 if(order->type != pe_order_optional) { native_rsc_order_lh(rsc, order, data_set); } if(order->type & pe_order_implies_left) { native_rsc_order_lh(group_data->first_child, order, data_set); } #endif order->lh_action_task = convert_non_atomic_task(order->lh_action_task, rsc, TRUE, TRUE); native_rsc_order_lh(rsc, order, data_set); } void group_rsc_order_rh( action_t *lh_action, resource_t *rsc, order_constraint_t *order) { enum pe_ordering type = order->type; group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc); crm_debug_3("%s/%p: %s->%s", rsc->id, order, lh_action->uuid, order->rh_action_task); if(rsc == NULL) { return; } if(safe_str_eq(CRM_OP_PROBED, lh_action->uuid)) { slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->rsc_order_rh(lh_action, child_rsc, order); ); if(rsc->fns->state(rsc, TRUE) < RSC_ROLE_STARTED && rsc->fns->state(rsc, FALSE) > RSC_ROLE_STOPPED) { order->type |= pe_order_implies_right; } } else if(lh_action->rsc != NULL && lh_action->rsc != rsc && lh_action->rsc != rsc->parent && lh_action->rsc->parent != rsc) { char *tmp = NULL; char *task_s = NULL; int interval = 0; enum action_tasks task = 0; + enum rsc_role_e next_role = minimum_resource_state(rsc, FALSE); parse_op_key(order->lh_action_task, &tmp, &task_s, &interval); task = text2task(task_s); crm_free(task_s); crm_free(tmp); switch(task) { case no_action: case monitor_rsc: case action_notify: case action_notified: case shutdown_crm: case stonith_node: break; case stop_rsc: case stopped_rsc: case action_demote: case action_demoted: order->type |= pe_order_complex_left; break; case start_rsc: case started_rsc: case action_promote: case action_promoted: order->type |= pe_order_complex_right; break; } if(group_data->ordered == FALSE) { /* Do for all children */ slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->rsc_order_rh(lh_action, child_rsc, order); ); } + + if(next_role < RSC_ROLE_STARTED + && task == stop_rsc + && next_role != rsc->fns->state(rsc, FALSE) /* Group is partially up */) { + native_rsc_order_rh(lh_action, group_data->last_child, order); + } } native_rsc_order_rh(lh_action, rsc, order); order->type = type; } void group_rsc_location(resource_t *rsc, rsc_to_node_t *constraint) { GListPtr saved = constraint->node_list_rh; GListPtr zero = node_list_dup(constraint->node_list_rh, TRUE, FALSE); gboolean reset_scores = TRUE; group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc); crm_debug("Processing rsc_location %s for %s", constraint->id, rsc->id); native_rsc_location(rsc, constraint); slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->rsc_location(child_rsc, constraint); if(group_data->colocated && reset_scores) { reset_scores = FALSE; constraint->node_list_rh = zero; } ); constraint->node_list_rh = saved; pe_free_shallow_adv(zero, TRUE); } void group_expand(resource_t *rsc, pe_working_set_t *data_set) { group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc); crm_debug_3("Processing actions from %s", rsc->id); CRM_CHECK(rsc != NULL, return); native_expand(rsc, data_set); slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->expand(child_rsc, data_set); ); } GListPtr group_merge_weights( resource_t *rsc, const char *rhs, GListPtr nodes, const char *attr, int factor, gboolean allow_rollback) { group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc); if(is_set(rsc->flags, pe_rsc_merging)) { crm_info("Breaking dependency loop with %s at %s", rsc->id, rhs); return nodes; } set_bit(rsc->flags, pe_rsc_merging); nodes = group_data->first_child->cmds->merge_weights( group_data->first_child, rhs, nodes, attr, factor, allow_rollback); slist_iter( constraint, rsc_colocation_t, rsc->rsc_cons_lhs, lpc, nodes = native_merge_weights( constraint->rsc_lh, rsc->id, nodes, constraint->node_attribute, constraint->score/INFINITY, allow_rollback); ); clear_bit(rsc->flags, pe_rsc_merging); return nodes; } void group_append_meta(resource_t *rsc, xmlNode *xml) { } diff --git a/pengine/regression.sh b/pengine/regression.sh index 2ca2041eee..82345cc4c1 100755 --- a/pengine/regression.sh +++ b/pengine/regression.sh @@ -1,365 +1,366 @@ #!/bin/bash # 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 # core=`dirname $0` . $core/regression.core.sh io_dir=$test_home/test10 create_mode="true" info Generating test outputs for these tests... # do_test file description info Done. echo "" info Performing the following tests from $io_dir create_mode="false" echo "" do_test simple1 "Offline " do_test simple2 "Start " do_test simple3 "Start 2 " do_test simple4 "Start Failed" do_test simple6 "Stop Start " do_test simple7 "Shutdown " #do_test simple8 "Stonith " #do_test simple9 "Lower version" #do_test simple10 "Higher version" do_test simple11 "Priority (ne)" do_test simple12 "Priority (eq)" do_test simple8 "Stickiness" echo "" do_test params-0 "Params: No change" do_test params-1 "Params: Changed" do_test params-2 "Params: Resource definition" do_test params-4 "Params: Reload" do_test novell-251689 "Resource definition change + target_role=stopped" do_test bug-lf-2106 "Restart all anonymous clone instances after config change" echo "" do_test orphan-0 "Orphan ignore" do_test orphan-1 "Orphan stop" echo "" do_test target-0 "Target Role : baseline" do_test target-1 "Target Role : master" do_test target-2 "Target Role : invalid" echo "" do_test domain "Failover domains" do_test base-score "Set a node's default score for all nodes" echo "" do_test date-1 "Dates" -t "2005-020" do_test date-2 "Date Spec - Pass" -t "2005-020T12:30" do_test date-3 "Date Spec - Fail" -t "2005-020T11:30" do_test probe-0 "Probe (anon clone)" do_test probe-1 "Pending Probe" do_test probe-2 "Correctly re-probe cloned groups" do_test probe-3 "Probe (pending node)" do_test probe-4 "Probe (pending node + stopped resource)" --rc 4 do_test standby "Standby" do_test comments "Comments" echo "" do_test rsc_dep1 "Must not " do_test rsc_dep3 "Must " do_test rsc_dep5 "Must not 3 " do_test rsc_dep7 "Must 3 " do_test rsc_dep10 "Must (but cant)" do_test rsc_dep2 "Must (running) " do_test rsc_dep8 "Must (running : alt) " do_test rsc_dep4 "Must (running + move)" do_test asymmetric "Asymmetric - require explicit location constraints" echo "" do_test order1 "Order start 1 " do_test order2 "Order start 2 " do_test order3 "Order stop " do_test order4 "Order (multiple) " do_test order5 "Order (move) " do_test order6 "Order (move w/ restart) " do_test order7 "Order (manditory) " do_test order-optional "Order (score=0) " do_test order-required "Order (score=INFINITY) " do_test bug-lf-2171 "Prevent group start when clone is stopped" do_test order-clone "Clone ordering should be able to prevent startup of dependant clones" do_test order-sets "Ordering for resource sets" do_test order-serialize "Serialize resources without inhibiting migration" do_test order-serialize-set "Serialize a set of resources without inhibiting migration" echo "" do_test coloc-loop "Colocation - loop" do_test coloc-many-one "Colocation - many-to-one" do_test coloc-list "Colocation - many-to-one with list" do_test coloc-group "Colocation - groups" do_test coloc-slave-anti "Anti-colocation with slave shouldn't prevent master colocation" do_test coloc-attr "Colocation based on node attributes" do_test coloc-negative-group "Negative colocation with a group" do_test coloc-intra-set "Intra-set colocation" #echo "" #do_test agent1 "version: lt (empty)" #do_test agent2 "version: eq " #do_test agent3 "version: gt " echo "" do_test attrs1 "string: eq (and) " do_test attrs2 "string: lt / gt (and)" do_test attrs3 "string: ne (or) " do_test attrs4 "string: exists " do_test attrs5 "string: not_exists " do_test attrs6 "is_dc: true " do_test attrs7 "is_dc: false " do_test attrs8 "score_attribute " echo "" do_test mon-rsc-1 "Schedule Monitor - start" do_test mon-rsc-2 "Schedule Monitor - move " do_test mon-rsc-3 "Schedule Monitor - pending start " do_test mon-rsc-4 "Schedule Monitor - move/pending start" echo "" do_test rec-rsc-0 "Resource Recover - no start " do_test rec-rsc-1 "Resource Recover - start " do_test rec-rsc-2 "Resource Recover - monitor " do_test rec-rsc-3 "Resource Recover - stop - ignore" do_test rec-rsc-4 "Resource Recover - stop - block " do_test rec-rsc-5 "Resource Recover - stop - fence " do_test rec-rsc-6 "Resource Recover - multiple - restart" do_test rec-rsc-7 "Resource Recover - multiple - stop " do_test rec-rsc-8 "Resource Recover - multiple - block " do_test rec-rsc-9 "Resource Recover - group/group" echo "" do_test quorum-1 "No quorum - ignore" do_test quorum-2 "No quorum - freeze" do_test quorum-3 "No quorum - stop " do_test quorum-4 "No quorum - start anyway" do_test quorum-5 "No quorum - start anyway (group)" do_test quorum-6 "No quorum - start anyway (clone)" echo "" do_test rec-node-1 "Node Recover - Startup - no fence" do_test rec-node-2 "Node Recover - Startup - fence " do_test rec-node-3 "Node Recover - HA down - no fence" do_test rec-node-4 "Node Recover - HA down - fence " do_test rec-node-5 "Node Recover - CRM down - no fence" do_test rec-node-6 "Node Recover - CRM down - fence " do_test rec-node-7 "Node Recover - no quorum - ignore " do_test rec-node-8 "Node Recover - no quorum - freeze " do_test rec-node-9 "Node Recover - no quorum - stop " do_test rec-node-10 "Node Recover - no quorum - stop w/fence" do_test rec-node-11 "Node Recover - CRM down w/ group - fence " do_test rec-node-12 "Node Recover - nothing active - fence " do_test rec-node-13 "Node Recover - failed resource + shutdown - fence " do_test rec-node-15 "Node Recover - unknown lrm section" do_test rec-node-14 "Serialize all stonith's" echo "" do_test multi1 "Multiple Active (stop/start)" echo "" do_test migrate-stop "Migration in a stopping stack" do_test migrate-start "Migration in a starting stack" do_test migrate-stop_start "Migration in a restarting stack" do_test migrate-stop-complex "Migration in a complex stopping stack" do_test migrate-start-complex "Migration in a complex starting stack" do_test migrate-stop-start-complex "Migration in a complex moving stack" do_test migrate-1 "Migrate (migrate)" do_test migrate-2 "Migrate (stable)" do_test migrate-3 "Migrate (failed migrate_to)" do_test migrate-4 "Migrate (failed migrate_from)" do_test novell-252693 "Migration in a stopping stack" do_test novell-252693-2 "Migration in a starting stack" do_test novell-252693-3 "Non-Migration in a starting and stopping stack" do_test bug-1820 "Migration in a group" do_test bug-1820-1 "Non-migration in a group" do_test migrate-5 "Primitive migration with a clone" #echo "" #do_test complex1 "Complex " echo "" do_test group1 "Group " do_test group2 "Group + Native " do_test group3 "Group + Group " do_test group4 "Group + Native (nothing)" do_test group5 "Group + Native (move) " do_test group6 "Group + Group (move) " do_test group7 "Group colocation" do_test group13 "Group colocation (cant run)" do_test group8 "Group anti-colocation" do_test group9 "Group recovery" do_test group10 "Group partial recovery" do_test group11 "Group target_role" do_test group14 "Group stop (graph terminated)" do_test group15 "-ve group colocation" do_test bug-1573 "Partial stop of a group with two children" do_test bug-1718 "Mandatory group ordering - Stop group_FUN" +do_test bug-lf-2422 "Dependancy on partially active group - stop ocfs:*" echo "" do_test clone-anon-probe-1 "Probe the correct (anonymous) clone instance for each node" do_test clone-anon-probe-2 "Avoid needless re-probing of anonymous clones" do_test clone-anon-failcount "Merge failcounts for anonymous clones" do_test inc0 "Incarnation start" do_test inc1 "Incarnation start order" do_test inc2 "Incarnation silent restart, stop, move" do_test inc3 "Inter-incarnation ordering, silent restart, stop, move" do_test inc4 "Inter-incarnation ordering, silent restart, stop, move (ordered)" do_test inc5 "Inter-incarnation ordering, silent restart, stop, move (restart 1)" do_test inc6 "Inter-incarnation ordering, silent restart, stop, move (restart 2)" do_test inc7 "Clone colocation" do_test inc8 "Clone anti-colocation" do_test inc9 "Non-unique clone" do_test inc10 "Non-unique clone (stop)" do_test inc11 "Primitive colocation with clones" do_test inc12 "Clone shutdown" do_test cloned-group "Make sure only the correct number of cloned groups are started" do_test clone-no-shuffle "Dont prioritize allocation of instances that must be moved" do_test clone-max-zero "Orphan processing with clone-max=0" do_test clone-anon-dup "Bug LF#2087 - Correctly parse the state of anonymous clones that are active more than once per node" do_test bug-lf-2160 "Dont shuffle clones due to colocation" do_test bug-lf-2213 "clone-node-max enforcement for cloned groups" do_test bug-lf-2153 "Clone ordering constraints" do_test bug-lf-2361 "Ensure clones observe mandatory ordering constraints if the LHS is unrunnable" do_test bug-lf-2317 "Avoid needless restart of primitive depending on a clone" do_test clone-colocate-instance-1 "Colocation with a specific clone instance (negative example)" do_test clone-colocate-instance-2 "Colocation with a specific clone instance" do_test clone-order-instance "Ordering with specific clone instances" echo "" do_test master-0 "Stopped -> Slave" do_test master-1 "Stopped -> Promote" do_test master-2 "Stopped -> Promote : notify" do_test master-3 "Stopped -> Promote : master location" do_test master-4 "Started -> Promote : master location" do_test master-5 "Promoted -> Promoted" do_test master-6 "Promoted -> Promoted (2)" do_test master-7 "Promoted -> Fenced" do_test master-8 "Promoted -> Fenced -> Moved" do_test master-9 "Stopped + Promotable + No quorum" do_test master-10 "Stopped -> Promotable : notify with monitor" do_test master-11 "Stopped -> Promote : colocation" do_test novell-239082 "Demote/Promote ordering" do_test novell-239087 "Stable master placement" do_test master-12 "Promotion based solely on rsc_location constraints" do_test master-13 "Include preferences of colocated resources when placing master" do_test master-demote "Ordering when actions depends on demoting a slave resource" do_test master-ordering "Prevent resources from starting that need a master" do_test bug-1765 "Master-Master Colocation (dont stop the slaves)" do_test master-group "Promotion of cloned groups" do_test bug-lf-1852 "Don't shuffle master/slave instances unnecessarily" do_test master-failed-demote "Dont retry failed demote actions" do_test master-failed-demote-2 "Dont retry failed demote actions (notify=false)" do_test master-depend "Ensure resources that depend on the master don't get allocated until the master does" do_test master-reattach "Re-attach to a running master" do_test master-allow-start "Don't include master score if it would prevent allocation" do_test master-colocation "Allow master instances placemaker to be influenced by colocation constraints" do_test master-pseudo "Make sure promote/demote pseudo actions are created correctly" do_test master-role "Prevent target-role from promoting more than master-max instances" do_test bug-lf-2358 "Master-Master anti-colocation" do_test master-promotion-constraint "Mandatory master colocation constraints" echo "" do_test managed-0 "Managed (reference)" do_test managed-1 "Not managed - down " do_test managed-2 "Not managed - up " echo "" do_test interleave-0 "Interleave (reference)" do_test interleave-1 "coloc - not interleaved" do_test interleave-2 "coloc - interleaved " do_test interleave-3 "coloc - interleaved (2)" do_test interleave-pseudo-stop "Interleaved clone during stonith" do_test interleave-stop "Interleaved clone during stop" do_test interleave-restart "Interleaved clone during dependancy restart" echo "" do_test notify-0 "Notify reference" do_test notify-1 "Notify simple" do_test notify-2 "Notify simple, confirm" do_test notify-3 "Notify move, confirm" do_test novell-239079 "Notification priority" #do_test notify-2 "Notify - 764" echo "" do_test 594 "OSDL #594" do_test 662 "OSDL #662" do_test 696 "OSDL #696" do_test 726 "OSDL #726" do_test 735 "OSDL #735" do_test 764 "OSDL #764" do_test 797 "OSDL #797" do_test 829 "OSDL #829" do_test 994 "OSDL #994" do_test 994-2 "OSDL #994 - with a dependant resource" do_test 1360 "OSDL #1360 - Clone stickiness" do_test 1484 "OSDL #1484 - on_fail=stop" do_test 1494 "OSDL #1494 - Clone stability" do_test unrunnable-1 "Unrunnable" do_test stonith-0 "Stonith loop - 1" do_test stonith-1 "Stonith loop - 2" do_test stonith-2 "Stonith loop - 3" do_test stonith-3 "Stonith startup" do_test bug-1572-1 "Recovery of groups depending on master/slave" do_test bug-1572-2 "Recovery of groups depending on master/slave when the master is never re-promoted" do_test bug-1685 "Depends-on-master ordering" do_test bug-1822 "Dont promote partially active groups" do_test bug-pm-11 "New resource added to a m/s group" do_test bug-pm-12 "Recover only the failed portion of a cloned group" do_test bug-n-387749 "Don't shuffle clone instances" do_test bug-n-385265 "Don't ignore the failure stickiness of group children - resource_idvscommon should stay stopped" do_test bug-n-385265-2 "Ensure groups are migrated instead of remaining partially active on the current node" do_test bug-lf-1920 "Correctly handle probes that find active resources" do_test bnc-515172 "Location constraint with multiple expressions" do_test colocate-primitive-with-clone "Optional colocation with a clone" do_test use-after-free-merge "Use-after-free in native_merge_weights" echo "" do_test systemhealth1 "System Health () #1" do_test systemhealth2 "System Health () #2" do_test systemhealth3 "System Health () #3" do_test systemhealthn1 "System Health (None) #1" do_test systemhealthn2 "System Health (None) #2" do_test systemhealthn3 "System Health (None) #3" do_test systemhealthm1 "System Health (Migrate On Red) #1" do_test systemhealthm2 "System Health (Migrate On Red) #2" do_test systemhealthm3 "System Health (Migrate On Red) #3" do_test systemhealtho1 "System Health (Only Green) #1" do_test systemhealtho2 "System Health (Only Green) #2" do_test systemhealtho3 "System Health (Only Green) #3" do_test systemhealthp1 "System Health (Progessive) #1" do_test systemhealthp2 "System Health (Progessive) #2" do_test systemhealthp3 "System Health (Progessive) #3" echo "" do_test utilization "Placement Strategy - utilization" do_test minimal "Placement Strategy - minimal" do_test balanced "Placement Strategy - balanced" echo "" do_test utilization-order1 "Utilization Order - Simple" do_test utilization-order2 "Utilization Order - Complex" do_test utilization-order3 "Utilization Order - Migrate" echo "" test_results diff --git a/pengine/test10/bug-lf-2422.dot b/pengine/test10/bug-lf-2422.dot new file mode 100644 index 0000000000..c4a9f41537 --- /dev/null +++ b/pengine/test10/bug-lf-2422.dot @@ -0,0 +1,93 @@ +digraph "g" { +"all_stopped" [ style=bold color="green" fontcolor="orange" ] +"c-o2stage_stop_0" -> "c-o2stage_stopped_0" [ style = bold] +"c-o2stage_stop_0" -> "o2stage:0_stop_0" [ style = bold] +"c-o2stage_stop_0" -> "o2stage:1_stop_0" [ style = bold] +"c-o2stage_stop_0" -> "o2stage:2_stop_0" [ style = bold] +"c-o2stage_stop_0" -> "o2stage:3_stop_0" [ style = bold] +"c-o2stage_stop_0" [ style=bold color="green" fontcolor="orange" ] +"c-o2stage_stopped_0" [ style=bold color="green" fontcolor="orange" ] +"c-ocfs_stop_0" -> "c-ocfs_stopped_0" [ style = bold] +"c-ocfs_stop_0" -> "ocfs:0_stop_0 qa-suse-4" [ style = bold] +"c-ocfs_stop_0" -> "ocfs:1_stop_0 qa-suse-1" [ style = bold] +"c-ocfs_stop_0" -> "ocfs:2_stop_0 qa-suse-3" [ style = bold] +"c-ocfs_stop_0" -> "ocfs:3_stop_0 qa-suse-2" [ style = bold] +"c-ocfs_stop_0" [ style=bold color="green" fontcolor="orange" ] +"c-ocfs_stopped_0" -> "c-o2stage_stop_0" [ style = bold] +"c-ocfs_stopped_0" [ style=bold color="green" fontcolor="orange" ] +"cmirror:0_stop_0 qa-suse-4" -> "all_stopped" [ style = bold] +"cmirror:0_stop_0 qa-suse-4" -> "o2cb:0_stop_0 qa-suse-4" [ style = bold] +"cmirror:0_stop_0 qa-suse-4" -> "o2stage:0_stopped_0" [ style = bold] +"cmirror:0_stop_0 qa-suse-4" [ style=bold color="green" fontcolor="black" ] +"cmirror:1_stop_0 qa-suse-1" -> "all_stopped" [ style = bold] +"cmirror:1_stop_0 qa-suse-1" -> "o2cb:1_stop_0 qa-suse-1" [ style = bold] +"cmirror:1_stop_0 qa-suse-1" -> "o2stage:1_stopped_0" [ style = bold] +"cmirror:1_stop_0 qa-suse-1" [ style=bold color="green" fontcolor="black" ] +"cmirror:2_stop_0 qa-suse-3" -> "all_stopped" [ style = bold] +"cmirror:2_stop_0 qa-suse-3" -> "o2cb:2_stop_0 qa-suse-3" [ style = bold] +"cmirror:2_stop_0 qa-suse-3" -> "o2stage:2_stopped_0" [ style = bold] +"cmirror:2_stop_0 qa-suse-3" [ style=bold color="green" fontcolor="black" ] +"cmirror:3_stop_0 qa-suse-2" -> "all_stopped" [ style = bold] +"cmirror:3_stop_0 qa-suse-2" -> "o2cb:3_stop_0 qa-suse-2" [ style = bold] +"cmirror:3_stop_0 qa-suse-2" -> "o2stage:3_stopped_0" [ style = bold] +"cmirror:3_stop_0 qa-suse-2" [ style=bold color="green" fontcolor="black" ] +"o2cb:0_stop_0 qa-suse-4" -> "all_stopped" [ style = bold] +"o2cb:0_stop_0 qa-suse-4" -> "o2stage:0_stopped_0" [ style = bold] +"o2cb:0_stop_0 qa-suse-4" [ style=bold color="green" fontcolor="black" ] +"o2cb:1_stop_0 qa-suse-1" -> "all_stopped" [ style = bold] +"o2cb:1_stop_0 qa-suse-1" -> "o2stage:1_stopped_0" [ style = bold] +"o2cb:1_stop_0 qa-suse-1" [ style=bold color="green" fontcolor="black" ] +"o2cb:2_stop_0 qa-suse-3" -> "all_stopped" [ style = bold] +"o2cb:2_stop_0 qa-suse-3" -> "o2stage:2_stopped_0" [ style = bold] +"o2cb:2_stop_0 qa-suse-3" [ style=bold color="green" fontcolor="black" ] +"o2cb:3_stop_0 qa-suse-2" -> "all_stopped" [ style = bold] +"o2cb:3_stop_0 qa-suse-2" -> "o2stage:3_stopped_0" [ style = bold] +"o2cb:3_stop_0 qa-suse-2" [ style=bold color="green" fontcolor="black" ] +"o2stage:0_stop_0" -> "cmirror:0_stop_0 qa-suse-4" [ style = bold] +"o2stage:0_stop_0" -> "o2cb:0_stop_0 qa-suse-4" [ style = bold] +"o2stage:0_stop_0" -> "o2stage:0_stopped_0" [ style = bold] +"o2stage:0_stop_0" [ style=bold color="green" fontcolor="orange" ] +"o2stage:0_stopped_0" -> "c-o2stage_stopped_0" [ style = bold] +"o2stage:0_stopped_0" [ style=bold color="green" fontcolor="orange" ] +"o2stage:1_stop_0" -> "cmirror:1_stop_0 qa-suse-1" [ style = bold] +"o2stage:1_stop_0" -> "o2cb:1_stop_0 qa-suse-1" [ style = bold] +"o2stage:1_stop_0" -> "o2stage:1_stopped_0" [ style = bold] +"o2stage:1_stop_0" [ style=bold color="green" fontcolor="orange" ] +"o2stage:1_stopped_0" -> "c-o2stage_stopped_0" [ style = bold] +"o2stage:1_stopped_0" [ style=bold color="green" fontcolor="orange" ] +"o2stage:2_stop_0" -> "cmirror:2_stop_0 qa-suse-3" [ style = bold] +"o2stage:2_stop_0" -> "o2cb:2_stop_0 qa-suse-3" [ style = bold] +"o2stage:2_stop_0" -> "o2stage:2_stopped_0" [ style = bold] +"o2stage:2_stop_0" [ style=bold color="green" fontcolor="orange" ] +"o2stage:2_stopped_0" -> "c-o2stage_stopped_0" [ style = bold] +"o2stage:2_stopped_0" [ style=bold color="green" fontcolor="orange" ] +"o2stage:3_stop_0" -> "cmirror:3_stop_0 qa-suse-2" [ style = bold] +"o2stage:3_stop_0" -> "o2cb:3_stop_0 qa-suse-2" [ style = bold] +"o2stage:3_stop_0" -> "o2stage:3_stopped_0" [ style = bold] +"o2stage:3_stop_0" [ style=bold color="green" fontcolor="orange" ] +"o2stage:3_stopped_0" -> "c-o2stage_stopped_0" [ style = bold] +"o2stage:3_stopped_0" [ style=bold color="green" fontcolor="orange" ] +"ocfs:0_stop_0 qa-suse-4" -> "all_stopped" [ style = bold] +"ocfs:0_stop_0 qa-suse-4" -> "c-ocfs_stopped_0" [ style = bold] +"ocfs:0_stop_0 qa-suse-4" -> "cmirror:0_stop_0 qa-suse-4" [ style = bold] +"ocfs:0_stop_0 qa-suse-4" -> "o2stage:0_stop_0" [ style = bold] +"ocfs:0_stop_0 qa-suse-4" [ style=bold color="green" fontcolor="black" ] +"ocfs:1_stop_0 qa-suse-1" -> "all_stopped" [ style = bold] +"ocfs:1_stop_0 qa-suse-1" -> "c-ocfs_stopped_0" [ style = bold] +"ocfs:1_stop_0 qa-suse-1" -> "cmirror:1_stop_0 qa-suse-1" [ style = bold] +"ocfs:1_stop_0 qa-suse-1" -> "o2stage:1_stop_0" [ style = bold] +"ocfs:1_stop_0 qa-suse-1" -> "ocfs:0_stop_0 qa-suse-4" [ style = bold] +"ocfs:1_stop_0 qa-suse-1" [ style=bold color="green" fontcolor="black" ] +"ocfs:2_stop_0 qa-suse-3" -> "all_stopped" [ style = bold] +"ocfs:2_stop_0 qa-suse-3" -> "c-ocfs_stopped_0" [ style = bold] +"ocfs:2_stop_0 qa-suse-3" -> "cmirror:2_stop_0 qa-suse-3" [ style = bold] +"ocfs:2_stop_0 qa-suse-3" -> "o2stage:2_stop_0" [ style = bold] +"ocfs:2_stop_0 qa-suse-3" -> "ocfs:1_stop_0 qa-suse-1" [ style = bold] +"ocfs:2_stop_0 qa-suse-3" [ style=bold color="green" fontcolor="black" ] +"ocfs:3_stop_0 qa-suse-2" -> "all_stopped" [ style = bold] +"ocfs:3_stop_0 qa-suse-2" -> "c-ocfs_stopped_0" [ style = bold] +"ocfs:3_stop_0 qa-suse-2" -> "cmirror:3_stop_0 qa-suse-2" [ style = bold] +"ocfs:3_stop_0 qa-suse-2" -> "o2stage:3_stop_0" [ style = bold] +"ocfs:3_stop_0 qa-suse-2" -> "ocfs:2_stop_0 qa-suse-3" [ style = bold] +"ocfs:3_stop_0 qa-suse-2" [ style=bold color="green" fontcolor="black" ] +} diff --git a/pengine/test10/bug-lf-2422.exp b/pengine/test10/bug-lf-2422.exp new file mode 100644 index 0000000000..6971610b0a --- /dev/null +++ b/pengine/test10/bug-lf-2422.exp @@ -0,0 +1,437 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pengine/test10/bug-lf-2422.scores b/pengine/test10/bug-lf-2422.scores new file mode 100644 index 0000000000..90d6b867bd --- /dev/null +++ b/pengine/test10/bug-lf-2422.scores @@ -0,0 +1,269 @@ +Allocation scores: +native_color: sbd_stonith allocation score on qa-suse-2: 0 +native_color: sbd_stonith allocation score on qa-suse-3: 0 +native_color: sbd_stonith allocation score on qa-suse-4: 0 +native_color: sbd_stonith allocation score on qa-suse-1: 0 +clone_color: c-o2stage allocation score on qa-suse-2: 0 +clone_color: c-o2stage allocation score on qa-suse-3: 0 +clone_color: c-o2stage allocation score on qa-suse-4: 0 +clone_color: c-o2stage allocation score on qa-suse-1: 0 +clone_color: o2stage:0 allocation score on qa-suse-2: 0 +clone_color: o2stage:0 allocation score on qa-suse-3: 0 +clone_color: o2stage:0 allocation score on qa-suse-4: 0 +clone_color: o2stage:0 allocation score on qa-suse-1: 0 +clone_color: dlm:0 allocation score on qa-suse-2: 0 +clone_color: dlm:0 allocation score on qa-suse-3: 0 +clone_color: dlm:0 allocation score on qa-suse-4: 1 +clone_color: dlm:0 allocation score on qa-suse-1: 0 +clone_color: clvm:0 allocation score on qa-suse-2: 0 +clone_color: clvm:0 allocation score on qa-suse-3: 0 +clone_color: clvm:0 allocation score on qa-suse-4: 1 +clone_color: clvm:0 allocation score on qa-suse-1: 0 +clone_color: o2cb:0 allocation score on qa-suse-2: 0 +clone_color: o2cb:0 allocation score on qa-suse-3: 0 +clone_color: o2cb:0 allocation score on qa-suse-4: 1 +clone_color: o2cb:0 allocation score on qa-suse-1: 0 +clone_color: cmirror:0 allocation score on qa-suse-2: 0 +clone_color: cmirror:0 allocation score on qa-suse-3: 0 +clone_color: cmirror:0 allocation score on qa-suse-4: 1 +clone_color: cmirror:0 allocation score on qa-suse-1: 0 +clone_color: o2stage:1 allocation score on qa-suse-2: 0 +clone_color: o2stage:1 allocation score on qa-suse-3: 0 +clone_color: o2stage:1 allocation score on qa-suse-4: 0 +clone_color: o2stage:1 allocation score on qa-suse-1: 0 +clone_color: dlm:1 allocation score on qa-suse-2: 0 +clone_color: dlm:1 allocation score on qa-suse-3: 0 +clone_color: dlm:1 allocation score on qa-suse-4: 0 +clone_color: dlm:1 allocation score on qa-suse-1: 1 +clone_color: clvm:1 allocation score on qa-suse-2: 0 +clone_color: clvm:1 allocation score on qa-suse-3: 0 +clone_color: clvm:1 allocation score on qa-suse-4: 0 +clone_color: clvm:1 allocation score on qa-suse-1: 1 +clone_color: o2cb:1 allocation score on qa-suse-2: 0 +clone_color: o2cb:1 allocation score on qa-suse-3: 0 +clone_color: o2cb:1 allocation score on qa-suse-4: 0 +clone_color: o2cb:1 allocation score on qa-suse-1: 1 +clone_color: cmirror:1 allocation score on qa-suse-2: 0 +clone_color: cmirror:1 allocation score on qa-suse-3: 0 +clone_color: cmirror:1 allocation score on qa-suse-4: 0 +clone_color: cmirror:1 allocation score on qa-suse-1: 1 +clone_color: o2stage:2 allocation score on qa-suse-2: 0 +clone_color: o2stage:2 allocation score on qa-suse-3: 0 +clone_color: o2stage:2 allocation score on qa-suse-4: 0 +clone_color: o2stage:2 allocation score on qa-suse-1: 0 +clone_color: dlm:2 allocation score on qa-suse-2: 0 +clone_color: dlm:2 allocation score on qa-suse-3: 1 +clone_color: dlm:2 allocation score on qa-suse-4: 0 +clone_color: dlm:2 allocation score on qa-suse-1: 0 +clone_color: clvm:2 allocation score on qa-suse-2: 0 +clone_color: clvm:2 allocation score on qa-suse-3: 1 +clone_color: clvm:2 allocation score on qa-suse-4: 0 +clone_color: clvm:2 allocation score on qa-suse-1: 0 +clone_color: o2cb:2 allocation score on qa-suse-2: 0 +clone_color: o2cb:2 allocation score on qa-suse-3: 1 +clone_color: o2cb:2 allocation score on qa-suse-4: 0 +clone_color: o2cb:2 allocation score on qa-suse-1: 0 +clone_color: cmirror:2 allocation score on qa-suse-2: 0 +clone_color: cmirror:2 allocation score on qa-suse-3: 1 +clone_color: cmirror:2 allocation score on qa-suse-4: 0 +clone_color: cmirror:2 allocation score on qa-suse-1: 0 +clone_color: o2stage:3 allocation score on qa-suse-2: 0 +clone_color: o2stage:3 allocation score on qa-suse-3: 0 +clone_color: o2stage:3 allocation score on qa-suse-4: 0 +clone_color: o2stage:3 allocation score on qa-suse-1: 0 +clone_color: dlm:3 allocation score on qa-suse-2: 1 +clone_color: dlm:3 allocation score on qa-suse-3: 0 +clone_color: dlm:3 allocation score on qa-suse-4: 0 +clone_color: dlm:3 allocation score on qa-suse-1: 0 +clone_color: clvm:3 allocation score on qa-suse-2: 1 +clone_color: clvm:3 allocation score on qa-suse-3: 0 +clone_color: clvm:3 allocation score on qa-suse-4: 0 +clone_color: clvm:3 allocation score on qa-suse-1: 0 +clone_color: o2cb:3 allocation score on qa-suse-2: 1 +clone_color: o2cb:3 allocation score on qa-suse-3: 0 +clone_color: o2cb:3 allocation score on qa-suse-4: 0 +clone_color: o2cb:3 allocation score on qa-suse-1: 0 +clone_color: cmirror:3 allocation score on qa-suse-2: 1 +clone_color: cmirror:3 allocation score on qa-suse-3: 0 +clone_color: cmirror:3 allocation score on qa-suse-4: 0 +clone_color: cmirror:3 allocation score on qa-suse-1: 0 +group_color: o2stage:0 allocation score on qa-suse-2: 0 +group_color: o2stage:0 allocation score on qa-suse-3: 0 +group_color: o2stage:0 allocation score on qa-suse-4: 0 +group_color: o2stage:0 allocation score on qa-suse-1: 0 +group_color: dlm:0 allocation score on qa-suse-2: 0 +group_color: dlm:0 allocation score on qa-suse-3: 0 +group_color: dlm:0 allocation score on qa-suse-4: 1 +group_color: dlm:0 allocation score on qa-suse-1: 0 +group_color: clvm:0 allocation score on qa-suse-2: 0 +group_color: clvm:0 allocation score on qa-suse-3: 0 +group_color: clvm:0 allocation score on qa-suse-4: 1 +group_color: clvm:0 allocation score on qa-suse-1: 0 +group_color: o2cb:0 allocation score on qa-suse-2: 0 +group_color: o2cb:0 allocation score on qa-suse-3: 0 +group_color: o2cb:0 allocation score on qa-suse-4: 1 +group_color: o2cb:0 allocation score on qa-suse-1: 0 +group_color: cmirror:0 allocation score on qa-suse-2: 0 +group_color: cmirror:0 allocation score on qa-suse-3: 0 +group_color: cmirror:0 allocation score on qa-suse-4: 1 +group_color: cmirror:0 allocation score on qa-suse-1: 0 +native_color: dlm:0 allocation score on qa-suse-2: 0 +native_color: dlm:0 allocation score on qa-suse-3: 0 +native_color: dlm:0 allocation score on qa-suse-4: 4 +native_color: dlm:0 allocation score on qa-suse-1: 0 +native_color: clvm:0 allocation score on qa-suse-2: -INFINITY +native_color: clvm:0 allocation score on qa-suse-3: -INFINITY +native_color: clvm:0 allocation score on qa-suse-4: 3 +native_color: clvm:0 allocation score on qa-suse-1: -INFINITY +native_color: o2cb:0 allocation score on qa-suse-2: -INFINITY +native_color: o2cb:0 allocation score on qa-suse-3: -INFINITY +native_color: o2cb:0 allocation score on qa-suse-4: -INFINITY +native_color: o2cb:0 allocation score on qa-suse-1: -INFINITY +native_color: cmirror:0 allocation score on qa-suse-2: -INFINITY +native_color: cmirror:0 allocation score on qa-suse-3: -INFINITY +native_color: cmirror:0 allocation score on qa-suse-4: -INFINITY +native_color: cmirror:0 allocation score on qa-suse-1: -INFINITY +group_color: o2stage:1 allocation score on qa-suse-2: 0 +group_color: o2stage:1 allocation score on qa-suse-3: 0 +group_color: o2stage:1 allocation score on qa-suse-4: -INFINITY +group_color: o2stage:1 allocation score on qa-suse-1: 0 +group_color: dlm:1 allocation score on qa-suse-2: 0 +group_color: dlm:1 allocation score on qa-suse-3: 0 +group_color: dlm:1 allocation score on qa-suse-4: -INFINITY +group_color: dlm:1 allocation score on qa-suse-1: 1 +group_color: clvm:1 allocation score on qa-suse-2: 0 +group_color: clvm:1 allocation score on qa-suse-3: 0 +group_color: clvm:1 allocation score on qa-suse-4: -INFINITY +group_color: clvm:1 allocation score on qa-suse-1: 1 +group_color: o2cb:1 allocation score on qa-suse-2: 0 +group_color: o2cb:1 allocation score on qa-suse-3: 0 +group_color: o2cb:1 allocation score on qa-suse-4: -INFINITY +group_color: o2cb:1 allocation score on qa-suse-1: 1 +group_color: cmirror:1 allocation score on qa-suse-2: 0 +group_color: cmirror:1 allocation score on qa-suse-3: 0 +group_color: cmirror:1 allocation score on qa-suse-4: -INFINITY +group_color: cmirror:1 allocation score on qa-suse-1: 1 +native_color: dlm:1 allocation score on qa-suse-2: 0 +native_color: dlm:1 allocation score on qa-suse-3: 0 +native_color: dlm:1 allocation score on qa-suse-4: -INFINITY +native_color: dlm:1 allocation score on qa-suse-1: 4 +native_color: clvm:1 allocation score on qa-suse-2: -INFINITY +native_color: clvm:1 allocation score on qa-suse-3: -INFINITY +native_color: clvm:1 allocation score on qa-suse-4: -INFINITY +native_color: clvm:1 allocation score on qa-suse-1: 3 +native_color: o2cb:1 allocation score on qa-suse-2: -INFINITY +native_color: o2cb:1 allocation score on qa-suse-3: -INFINITY +native_color: o2cb:1 allocation score on qa-suse-4: -INFINITY +native_color: o2cb:1 allocation score on qa-suse-1: -INFINITY +native_color: cmirror:1 allocation score on qa-suse-2: -INFINITY +native_color: cmirror:1 allocation score on qa-suse-3: -INFINITY +native_color: cmirror:1 allocation score on qa-suse-4: -INFINITY +native_color: cmirror:1 allocation score on qa-suse-1: -INFINITY +group_color: o2stage:2 allocation score on qa-suse-2: 0 +group_color: o2stage:2 allocation score on qa-suse-3: 0 +group_color: o2stage:2 allocation score on qa-suse-4: -INFINITY +group_color: o2stage:2 allocation score on qa-suse-1: -INFINITY +group_color: dlm:2 allocation score on qa-suse-2: 0 +group_color: dlm:2 allocation score on qa-suse-3: 1 +group_color: dlm:2 allocation score on qa-suse-4: -INFINITY +group_color: dlm:2 allocation score on qa-suse-1: -INFINITY +group_color: clvm:2 allocation score on qa-suse-2: 0 +group_color: clvm:2 allocation score on qa-suse-3: 1 +group_color: clvm:2 allocation score on qa-suse-4: -INFINITY +group_color: clvm:2 allocation score on qa-suse-1: -INFINITY +group_color: o2cb:2 allocation score on qa-suse-2: 0 +group_color: o2cb:2 allocation score on qa-suse-3: 1 +group_color: o2cb:2 allocation score on qa-suse-4: -INFINITY +group_color: o2cb:2 allocation score on qa-suse-1: -INFINITY +group_color: cmirror:2 allocation score on qa-suse-2: 0 +group_color: cmirror:2 allocation score on qa-suse-3: 1 +group_color: cmirror:2 allocation score on qa-suse-4: -INFINITY +group_color: cmirror:2 allocation score on qa-suse-1: -INFINITY +native_color: dlm:2 allocation score on qa-suse-2: 0 +native_color: dlm:2 allocation score on qa-suse-3: 4 +native_color: dlm:2 allocation score on qa-suse-4: -INFINITY +native_color: dlm:2 allocation score on qa-suse-1: -INFINITY +native_color: clvm:2 allocation score on qa-suse-2: -INFINITY +native_color: clvm:2 allocation score on qa-suse-3: 3 +native_color: clvm:2 allocation score on qa-suse-4: -INFINITY +native_color: clvm:2 allocation score on qa-suse-1: -INFINITY +native_color: o2cb:2 allocation score on qa-suse-2: -INFINITY +native_color: o2cb:2 allocation score on qa-suse-3: -INFINITY +native_color: o2cb:2 allocation score on qa-suse-4: -INFINITY +native_color: o2cb:2 allocation score on qa-suse-1: -INFINITY +native_color: cmirror:2 allocation score on qa-suse-2: -INFINITY +native_color: cmirror:2 allocation score on qa-suse-3: -INFINITY +native_color: cmirror:2 allocation score on qa-suse-4: -INFINITY +native_color: cmirror:2 allocation score on qa-suse-1: -INFINITY +group_color: o2stage:3 allocation score on qa-suse-2: 0 +group_color: o2stage:3 allocation score on qa-suse-3: -INFINITY +group_color: o2stage:3 allocation score on qa-suse-4: -INFINITY +group_color: o2stage:3 allocation score on qa-suse-1: -INFINITY +group_color: dlm:3 allocation score on qa-suse-2: 1 +group_color: dlm:3 allocation score on qa-suse-3: -INFINITY +group_color: dlm:3 allocation score on qa-suse-4: -INFINITY +group_color: dlm:3 allocation score on qa-suse-1: -INFINITY +group_color: clvm:3 allocation score on qa-suse-2: 1 +group_color: clvm:3 allocation score on qa-suse-3: -INFINITY +group_color: clvm:3 allocation score on qa-suse-4: -INFINITY +group_color: clvm:3 allocation score on qa-suse-1: -INFINITY +group_color: o2cb:3 allocation score on qa-suse-2: 1 +group_color: o2cb:3 allocation score on qa-suse-3: -INFINITY +group_color: o2cb:3 allocation score on qa-suse-4: -INFINITY +group_color: o2cb:3 allocation score on qa-suse-1: -INFINITY +group_color: cmirror:3 allocation score on qa-suse-2: 1 +group_color: cmirror:3 allocation score on qa-suse-3: -INFINITY +group_color: cmirror:3 allocation score on qa-suse-4: -INFINITY +group_color: cmirror:3 allocation score on qa-suse-1: -INFINITY +native_color: dlm:3 allocation score on qa-suse-2: 4 +native_color: dlm:3 allocation score on qa-suse-3: -INFINITY +native_color: dlm:3 allocation score on qa-suse-4: -INFINITY +native_color: dlm:3 allocation score on qa-suse-1: -INFINITY +native_color: clvm:3 allocation score on qa-suse-2: 3 +native_color: clvm:3 allocation score on qa-suse-3: -INFINITY +native_color: clvm:3 allocation score on qa-suse-4: -INFINITY +native_color: clvm:3 allocation score on qa-suse-1: -INFINITY +native_color: o2cb:3 allocation score on qa-suse-2: -INFINITY +native_color: o2cb:3 allocation score on qa-suse-3: -INFINITY +native_color: o2cb:3 allocation score on qa-suse-4: -INFINITY +native_color: o2cb:3 allocation score on qa-suse-1: -INFINITY +native_color: cmirror:3 allocation score on qa-suse-2: -INFINITY +native_color: cmirror:3 allocation score on qa-suse-3: -INFINITY +native_color: cmirror:3 allocation score on qa-suse-4: -INFINITY +native_color: cmirror:3 allocation score on qa-suse-1: -INFINITY +clone_color: c-ocfs allocation score on qa-suse-2: 0 +clone_color: c-ocfs allocation score on qa-suse-3: 0 +clone_color: c-ocfs allocation score on qa-suse-4: 0 +clone_color: c-ocfs allocation score on qa-suse-1: 0 +clone_color: ocfs:0 allocation score on qa-suse-2: 0 +clone_color: ocfs:0 allocation score on qa-suse-3: 0 +clone_color: ocfs:0 allocation score on qa-suse-4: 1 +clone_color: ocfs:0 allocation score on qa-suse-1: 0 +clone_color: ocfs:1 allocation score on qa-suse-2: 0 +clone_color: ocfs:1 allocation score on qa-suse-3: 0 +clone_color: ocfs:1 allocation score on qa-suse-4: 0 +clone_color: ocfs:1 allocation score on qa-suse-1: 1 +clone_color: ocfs:2 allocation score on qa-suse-2: 0 +clone_color: ocfs:2 allocation score on qa-suse-3: 1 +clone_color: ocfs:2 allocation score on qa-suse-4: 0 +clone_color: ocfs:2 allocation score on qa-suse-1: 0 +clone_color: ocfs:3 allocation score on qa-suse-2: 1 +clone_color: ocfs:3 allocation score on qa-suse-3: 0 +clone_color: ocfs:3 allocation score on qa-suse-4: 0 +clone_color: ocfs:3 allocation score on qa-suse-1: 0 +native_color: ocfs:0 allocation score on qa-suse-2: -INFINITY +native_color: ocfs:0 allocation score on qa-suse-3: -INFINITY +native_color: ocfs:0 allocation score on qa-suse-4: -INFINITY +native_color: ocfs:0 allocation score on qa-suse-1: -INFINITY +native_color: ocfs:1 allocation score on qa-suse-2: -INFINITY +native_color: ocfs:1 allocation score on qa-suse-3: -INFINITY +native_color: ocfs:1 allocation score on qa-suse-4: -INFINITY +native_color: ocfs:1 allocation score on qa-suse-1: -INFINITY +native_color: ocfs:2 allocation score on qa-suse-2: -INFINITY +native_color: ocfs:2 allocation score on qa-suse-3: -INFINITY +native_color: ocfs:2 allocation score on qa-suse-4: -INFINITY +native_color: ocfs:2 allocation score on qa-suse-1: -INFINITY +native_color: ocfs:3 allocation score on qa-suse-2: -INFINITY +native_color: ocfs:3 allocation score on qa-suse-3: -INFINITY +native_color: ocfs:3 allocation score on qa-suse-4: -INFINITY +native_color: ocfs:3 allocation score on qa-suse-1: -INFINITY diff --git a/pengine/test10/bug-lf-2422.xml b/pengine/test10/bug-lf-2422.xml new file mode 100644 index 0000000000..916ba9aabf --- /dev/null +++ b/pengine/test10/bug-lf-2422.xml @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pengine/test10/inc7.scores b/pengine/test10/inc7.scores index ce47bf8351..1172b3aaf8 100644 --- a/pengine/test10/inc7.scores +++ b/pengine/test10/inc7.scores @@ -1,70 +1,70 @@ Allocation scores: native_color: rsc0 allocation score on node1: 0 native_color: rsc0 allocation score on node2: 0 native_color: rsc0 allocation score on node3: 0 clone_color: rsc1 allocation score on node1: 0 clone_color: rsc1 allocation score on node2: 0 clone_color: rsc1 allocation score on node3: 0 clone_color: child_rsc1:0 allocation score on node1: 0 clone_color: child_rsc1:0 allocation score on node2: 0 clone_color: child_rsc1:0 allocation score on node3: 0 clone_color: child_rsc1:1 allocation score on node1: 0 clone_color: child_rsc1:1 allocation score on node2: 0 clone_color: child_rsc1:1 allocation score on node3: 0 clone_color: child_rsc1:2 allocation score on node1: 0 clone_color: child_rsc1:2 allocation score on node2: 0 clone_color: child_rsc1:2 allocation score on node3: 0 clone_color: child_rsc1:3 allocation score on node1: 0 clone_color: child_rsc1:3 allocation score on node2: 0 clone_color: child_rsc1:3 allocation score on node3: 0 clone_color: child_rsc1:4 allocation score on node1: 0 clone_color: child_rsc1:4 allocation score on node2: 0 clone_color: child_rsc1:4 allocation score on node3: 0 clone_color: rsc2 allocation score on node1: 0 clone_color: rsc2 allocation score on node2: 0 clone_color: rsc2 allocation score on node3: 0 clone_color: child_rsc2:0 allocation score on node1: 0 clone_color: child_rsc2:0 allocation score on node2: 0 clone_color: child_rsc2:0 allocation score on node3: 0 clone_color: child_rsc2:1 allocation score on node1: 0 clone_color: child_rsc2:1 allocation score on node2: 0 clone_color: child_rsc2:1 allocation score on node3: 0 clone_color: child_rsc2:2 allocation score on node1: 0 clone_color: child_rsc2:2 allocation score on node2: 0 clone_color: child_rsc2:2 allocation score on node3: 0 clone_color: child_rsc2:3 allocation score on node1: 0 clone_color: child_rsc2:3 allocation score on node2: 0 clone_color: child_rsc2:3 allocation score on node3: 0 clone_color: child_rsc2:4 allocation score on node1: 0 clone_color: child_rsc2:4 allocation score on node2: 0 clone_color: child_rsc2:4 allocation score on node3: 0 native_color: child_rsc2:0 allocation score on node1: 0 native_color: child_rsc2:0 allocation score on node2: 0 native_color: child_rsc2:0 allocation score on node3: 0 native_color: child_rsc2:1 allocation score on node1: 0 native_color: child_rsc2:1 allocation score on node2: 0 native_color: child_rsc2:1 allocation score on node3: 0 native_color: child_rsc2:2 allocation score on node1: 0 native_color: child_rsc2:2 allocation score on node2: 0 native_color: child_rsc2:2 allocation score on node3: 0 native_color: child_rsc2:3 allocation score on node1: 0 native_color: child_rsc2:3 allocation score on node2: 0 native_color: child_rsc2:3 allocation score on node3: 0 native_color: child_rsc2:4 allocation score on node1: 0 native_color: child_rsc2:4 allocation score on node2: -INFINITY native_color: child_rsc2:4 allocation score on node3: 0 native_color: child_rsc1:0 allocation score on node1: 0 -native_color: child_rsc1:0 allocation score on node2: 0 -native_color: child_rsc1:0 allocation score on node3: 0 -native_color: child_rsc1:1 allocation score on node1: 0 +native_color: child_rsc1:0 allocation score on node2: -INFINITY +native_color: child_rsc1:0 allocation score on node3: -INFINITY +native_color: child_rsc1:1 allocation score on node1: -INFINITY native_color: child_rsc1:1 allocation score on node2: 0 -native_color: child_rsc1:1 allocation score on node3: 0 -native_color: child_rsc1:2 allocation score on node1: 0 -native_color: child_rsc1:2 allocation score on node2: 0 +native_color: child_rsc1:1 allocation score on node3: -INFINITY +native_color: child_rsc1:2 allocation score on node1: -INFINITY +native_color: child_rsc1:2 allocation score on node2: -INFINITY native_color: child_rsc1:2 allocation score on node3: 0 native_color: child_rsc1:3 allocation score on node1: 0 -native_color: child_rsc1:3 allocation score on node2: 0 -native_color: child_rsc1:3 allocation score on node3: 0 +native_color: child_rsc1:3 allocation score on node2: -INFINITY +native_color: child_rsc1:3 allocation score on node3: -INFINITY native_color: child_rsc1:4 allocation score on node1: -INFINITY native_color: child_rsc1:4 allocation score on node2: 0 -native_color: child_rsc1:4 allocation score on node3: 0 +native_color: child_rsc1:4 allocation score on node3: -INFINITY diff --git a/pengine/test10/interleave-2.scores b/pengine/test10/interleave-2.scores index 4798ed4292..7f348d58c3 100644 --- a/pengine/test10/interleave-2.scores +++ b/pengine/test10/interleave-2.scores @@ -1,345 +1,345 @@ Allocation scores: native_color: DcIPaddr allocation score on c001n09: 0 native_color: DcIPaddr allocation score on c001n02: -INFINITY native_color: DcIPaddr allocation score on c001n03: -INFINITY native_color: DcIPaddr allocation score on c001n04: -INFINITY native_color: DcIPaddr allocation score on c001n05: -INFINITY native_color: DcIPaddr allocation score on c001n06: -INFINITY native_color: DcIPaddr allocation score on c001n07: -INFINITY native_color: DcIPaddr allocation score on c001n08: -INFINITY native_color: rsc_c001n09 allocation score on c001n09: 100 native_color: rsc_c001n09 allocation score on c001n02: 0 native_color: rsc_c001n09 allocation score on c001n03: 0 native_color: rsc_c001n09 allocation score on c001n04: 0 native_color: rsc_c001n09 allocation score on c001n05: 0 native_color: rsc_c001n09 allocation score on c001n06: 0 native_color: rsc_c001n09 allocation score on c001n07: 0 native_color: rsc_c001n09 allocation score on c001n08: 0 native_color: rsc_c001n02 allocation score on c001n09: 0 native_color: rsc_c001n02 allocation score on c001n02: INFINITY native_color: rsc_c001n02 allocation score on c001n03: 0 native_color: rsc_c001n02 allocation score on c001n04: 0 native_color: rsc_c001n02 allocation score on c001n05: 0 native_color: rsc_c001n02 allocation score on c001n06: 0 native_color: rsc_c001n02 allocation score on c001n07: 0 native_color: rsc_c001n02 allocation score on c001n08: 0 native_color: rsc_c001n03 allocation score on c001n09: 0 native_color: rsc_c001n03 allocation score on c001n02: 0 native_color: rsc_c001n03 allocation score on c001n03: INFINITY native_color: rsc_c001n03 allocation score on c001n04: 0 native_color: rsc_c001n03 allocation score on c001n05: 0 native_color: rsc_c001n03 allocation score on c001n06: 0 native_color: rsc_c001n03 allocation score on c001n07: 0 native_color: rsc_c001n03 allocation score on c001n08: 0 native_color: rsc_c001n04 allocation score on c001n09: 0 native_color: rsc_c001n04 allocation score on c001n02: 0 native_color: rsc_c001n04 allocation score on c001n03: 0 native_color: rsc_c001n04 allocation score on c001n04: INFINITY native_color: rsc_c001n04 allocation score on c001n05: 0 native_color: rsc_c001n04 allocation score on c001n06: 0 native_color: rsc_c001n04 allocation score on c001n07: 0 native_color: rsc_c001n04 allocation score on c001n08: 0 native_color: rsc_c001n05 allocation score on c001n09: 0 native_color: rsc_c001n05 allocation score on c001n02: 0 native_color: rsc_c001n05 allocation score on c001n03: 0 native_color: rsc_c001n05 allocation score on c001n04: 0 native_color: rsc_c001n05 allocation score on c001n05: INFINITY native_color: rsc_c001n05 allocation score on c001n06: 0 native_color: rsc_c001n05 allocation score on c001n07: 0 native_color: rsc_c001n05 allocation score on c001n08: 0 native_color: rsc_c001n06 allocation score on c001n09: 0 native_color: rsc_c001n06 allocation score on c001n02: 0 native_color: rsc_c001n06 allocation score on c001n03: 0 native_color: rsc_c001n06 allocation score on c001n04: 0 native_color: rsc_c001n06 allocation score on c001n05: 0 native_color: rsc_c001n06 allocation score on c001n06: INFINITY native_color: rsc_c001n06 allocation score on c001n07: 0 native_color: rsc_c001n06 allocation score on c001n08: 0 native_color: rsc_c001n07 allocation score on c001n09: 0 native_color: rsc_c001n07 allocation score on c001n02: 0 native_color: rsc_c001n07 allocation score on c001n03: 0 native_color: rsc_c001n07 allocation score on c001n04: 0 native_color: rsc_c001n07 allocation score on c001n05: 0 native_color: rsc_c001n07 allocation score on c001n06: 0 native_color: rsc_c001n07 allocation score on c001n07: INFINITY native_color: rsc_c001n07 allocation score on c001n08: 0 native_color: rsc_c001n08 allocation score on c001n09: 0 native_color: rsc_c001n08 allocation score on c001n02: 0 native_color: rsc_c001n08 allocation score on c001n03: 0 native_color: rsc_c001n08 allocation score on c001n04: 0 native_color: rsc_c001n08 allocation score on c001n05: 0 native_color: rsc_c001n08 allocation score on c001n06: 0 native_color: rsc_c001n08 allocation score on c001n07: 0 native_color: rsc_c001n08 allocation score on c001n08: INFINITY clone_color: DoFencing allocation score on c001n09: 0 clone_color: DoFencing allocation score on c001n02: 0 clone_color: DoFencing allocation score on c001n03: 0 clone_color: DoFencing allocation score on c001n04: 0 clone_color: DoFencing allocation score on c001n05: 0 clone_color: DoFencing allocation score on c001n06: 0 clone_color: DoFencing allocation score on c001n07: 0 clone_color: DoFencing allocation score on c001n08: 0 clone_color: child_DoFencing:0 allocation score on c001n09: 0 clone_color: child_DoFencing:0 allocation score on c001n02: 200 clone_color: child_DoFencing:0 allocation score on c001n03: 0 clone_color: child_DoFencing:0 allocation score on c001n04: 0 clone_color: child_DoFencing:0 allocation score on c001n05: 0 clone_color: child_DoFencing:0 allocation score on c001n06: 0 clone_color: child_DoFencing:0 allocation score on c001n07: 0 clone_color: child_DoFencing:0 allocation score on c001n08: 0 clone_color: child_DoFencing:1 allocation score on c001n09: 0 clone_color: child_DoFencing:1 allocation score on c001n02: 0 clone_color: child_DoFencing:1 allocation score on c001n03: 200 clone_color: child_DoFencing:1 allocation score on c001n04: 0 clone_color: child_DoFencing:1 allocation score on c001n05: 0 clone_color: child_DoFencing:1 allocation score on c001n06: 0 clone_color: child_DoFencing:1 allocation score on c001n07: 0 clone_color: child_DoFencing:1 allocation score on c001n08: 0 clone_color: child_DoFencing:2 allocation score on c001n09: 0 clone_color: child_DoFencing:2 allocation score on c001n02: 0 clone_color: child_DoFencing:2 allocation score on c001n03: 0 clone_color: child_DoFencing:2 allocation score on c001n04: 200 clone_color: child_DoFencing:2 allocation score on c001n05: 0 clone_color: child_DoFencing:2 allocation score on c001n06: 0 clone_color: child_DoFencing:2 allocation score on c001n07: 0 clone_color: child_DoFencing:2 allocation score on c001n08: 0 clone_color: child_DoFencing:3 allocation score on c001n09: 0 clone_color: child_DoFencing:3 allocation score on c001n02: 0 clone_color: child_DoFencing:3 allocation score on c001n03: 0 clone_color: child_DoFencing:3 allocation score on c001n04: 0 clone_color: child_DoFencing:3 allocation score on c001n05: 200 clone_color: child_DoFencing:3 allocation score on c001n06: 0 clone_color: child_DoFencing:3 allocation score on c001n07: 0 clone_color: child_DoFencing:3 allocation score on c001n08: 0 clone_color: child_DoFencing:4 allocation score on c001n09: 0 clone_color: child_DoFencing:4 allocation score on c001n02: 0 clone_color: child_DoFencing:4 allocation score on c001n03: 0 clone_color: child_DoFencing:4 allocation score on c001n04: 0 clone_color: child_DoFencing:4 allocation score on c001n05: 0 clone_color: child_DoFencing:4 allocation score on c001n06: 200 clone_color: child_DoFencing:4 allocation score on c001n07: 0 clone_color: child_DoFencing:4 allocation score on c001n08: 0 clone_color: child_DoFencing:5 allocation score on c001n09: 0 clone_color: child_DoFencing:5 allocation score on c001n02: 0 clone_color: child_DoFencing:5 allocation score on c001n03: 0 clone_color: child_DoFencing:5 allocation score on c001n04: 0 clone_color: child_DoFencing:5 allocation score on c001n05: 0 clone_color: child_DoFencing:5 allocation score on c001n06: 0 clone_color: child_DoFencing:5 allocation score on c001n07: 200 clone_color: child_DoFencing:5 allocation score on c001n08: 0 clone_color: child_DoFencing:6 allocation score on c001n09: 0 clone_color: child_DoFencing:6 allocation score on c001n02: 0 clone_color: child_DoFencing:6 allocation score on c001n03: 0 clone_color: child_DoFencing:6 allocation score on c001n04: 0 clone_color: child_DoFencing:6 allocation score on c001n05: 0 clone_color: child_DoFencing:6 allocation score on c001n06: 0 clone_color: child_DoFencing:6 allocation score on c001n07: 0 clone_color: child_DoFencing:6 allocation score on c001n08: 200 clone_color: child_DoFencing:7 allocation score on c001n09: 200 clone_color: child_DoFencing:7 allocation score on c001n02: 0 clone_color: child_DoFencing:7 allocation score on c001n03: 0 clone_color: child_DoFencing:7 allocation score on c001n04: 0 clone_color: child_DoFencing:7 allocation score on c001n05: 0 clone_color: child_DoFencing:7 allocation score on c001n06: 0 clone_color: child_DoFencing:7 allocation score on c001n07: 0 clone_color: child_DoFencing:7 allocation score on c001n08: 0 native_color: child_DoFencing:0 allocation score on c001n09: 0 native_color: child_DoFencing:0 allocation score on c001n02: 200 native_color: child_DoFencing:0 allocation score on c001n03: 0 native_color: child_DoFencing:0 allocation score on c001n04: 0 native_color: child_DoFencing:0 allocation score on c001n05: 0 native_color: child_DoFencing:0 allocation score on c001n06: 0 native_color: child_DoFencing:0 allocation score on c001n07: 0 native_color: child_DoFencing:0 allocation score on c001n08: 0 native_color: child_DoFencing:1 allocation score on c001n09: 0 native_color: child_DoFencing:1 allocation score on c001n02: -INFINITY native_color: child_DoFencing:1 allocation score on c001n03: 200 native_color: child_DoFencing:1 allocation score on c001n04: 0 native_color: child_DoFencing:1 allocation score on c001n05: 0 native_color: child_DoFencing:1 allocation score on c001n06: 0 native_color: child_DoFencing:1 allocation score on c001n07: 0 native_color: child_DoFencing:1 allocation score on c001n08: 0 native_color: child_DoFencing:2 allocation score on c001n09: 0 native_color: child_DoFencing:2 allocation score on c001n02: -INFINITY native_color: child_DoFencing:2 allocation score on c001n03: -INFINITY native_color: child_DoFencing:2 allocation score on c001n04: 200 native_color: child_DoFencing:2 allocation score on c001n05: 0 native_color: child_DoFencing:2 allocation score on c001n06: 0 native_color: child_DoFencing:2 allocation score on c001n07: 0 native_color: child_DoFencing:2 allocation score on c001n08: 0 native_color: child_DoFencing:3 allocation score on c001n09: 0 native_color: child_DoFencing:3 allocation score on c001n02: -INFINITY native_color: child_DoFencing:3 allocation score on c001n03: -INFINITY native_color: child_DoFencing:3 allocation score on c001n04: -INFINITY native_color: child_DoFencing:3 allocation score on c001n05: 200 native_color: child_DoFencing:3 allocation score on c001n06: 0 native_color: child_DoFencing:3 allocation score on c001n07: 0 native_color: child_DoFencing:3 allocation score on c001n08: 0 native_color: child_DoFencing:4 allocation score on c001n09: 0 native_color: child_DoFencing:4 allocation score on c001n02: -INFINITY native_color: child_DoFencing:4 allocation score on c001n03: -INFINITY native_color: child_DoFencing:4 allocation score on c001n04: -INFINITY native_color: child_DoFencing:4 allocation score on c001n05: -INFINITY native_color: child_DoFencing:4 allocation score on c001n06: 200 native_color: child_DoFencing:4 allocation score on c001n07: 0 native_color: child_DoFencing:4 allocation score on c001n08: 0 native_color: child_DoFencing:5 allocation score on c001n09: 0 native_color: child_DoFencing:5 allocation score on c001n02: -INFINITY native_color: child_DoFencing:5 allocation score on c001n03: -INFINITY native_color: child_DoFencing:5 allocation score on c001n04: -INFINITY native_color: child_DoFencing:5 allocation score on c001n05: -INFINITY native_color: child_DoFencing:5 allocation score on c001n06: -INFINITY native_color: child_DoFencing:5 allocation score on c001n07: 200 native_color: child_DoFencing:5 allocation score on c001n08: 0 native_color: child_DoFencing:6 allocation score on c001n09: 0 native_color: child_DoFencing:6 allocation score on c001n02: -INFINITY native_color: child_DoFencing:6 allocation score on c001n03: -INFINITY native_color: child_DoFencing:6 allocation score on c001n04: -INFINITY native_color: child_DoFencing:6 allocation score on c001n05: -INFINITY native_color: child_DoFencing:6 allocation score on c001n06: -INFINITY native_color: child_DoFencing:6 allocation score on c001n07: -INFINITY native_color: child_DoFencing:6 allocation score on c001n08: 200 native_color: child_DoFencing:7 allocation score on c001n09: 200 native_color: child_DoFencing:7 allocation score on c001n02: -INFINITY native_color: child_DoFencing:7 allocation score on c001n03: -INFINITY native_color: child_DoFencing:7 allocation score on c001n04: -INFINITY native_color: child_DoFencing:7 allocation score on c001n05: -INFINITY native_color: child_DoFencing:7 allocation score on c001n06: -INFINITY native_color: child_DoFencing:7 allocation score on c001n07: -INFINITY native_color: child_DoFencing:7 allocation score on c001n08: -INFINITY clone_color: CloneSet allocation score on c001n09: 0 clone_color: CloneSet allocation score on c001n02: 0 clone_color: CloneSet allocation score on c001n03: 0 clone_color: CloneSet allocation score on c001n04: 0 clone_color: CloneSet allocation score on c001n05: 0 clone_color: CloneSet allocation score on c001n06: 0 clone_color: CloneSet allocation score on c001n07: 0 clone_color: CloneSet allocation score on c001n08: 0 clone_color: child_CloneSet:0 allocation score on c001n09: 0 clone_color: child_CloneSet:0 allocation score on c001n02: 0 clone_color: child_CloneSet:0 allocation score on c001n03: 0 clone_color: child_CloneSet:0 allocation score on c001n04: 0 clone_color: child_CloneSet:0 allocation score on c001n05: 0 clone_color: child_CloneSet:0 allocation score on c001n06: 0 clone_color: child_CloneSet:0 allocation score on c001n07: 0 clone_color: child_CloneSet:0 allocation score on c001n08: 0 clone_color: child_CloneSet:1 allocation score on c001n09: 0 clone_color: child_CloneSet:1 allocation score on c001n02: 0 clone_color: child_CloneSet:1 allocation score on c001n03: 0 clone_color: child_CloneSet:1 allocation score on c001n04: 0 clone_color: child_CloneSet:1 allocation score on c001n05: 0 clone_color: child_CloneSet:1 allocation score on c001n06: 0 clone_color: child_CloneSet:1 allocation score on c001n07: 0 clone_color: child_CloneSet:1 allocation score on c001n08: 0 clone_color: child_CloneSet:2 allocation score on c001n09: 0 clone_color: child_CloneSet:2 allocation score on c001n02: 0 clone_color: child_CloneSet:2 allocation score on c001n03: 0 clone_color: child_CloneSet:2 allocation score on c001n04: 0 clone_color: child_CloneSet:2 allocation score on c001n05: 0 clone_color: child_CloneSet:2 allocation score on c001n06: 0 clone_color: child_CloneSet:2 allocation score on c001n07: 0 clone_color: child_CloneSet:2 allocation score on c001n08: 0 clone_color: child_CloneSet:3 allocation score on c001n09: 0 clone_color: child_CloneSet:3 allocation score on c001n02: 0 clone_color: child_CloneSet:3 allocation score on c001n03: 0 clone_color: child_CloneSet:3 allocation score on c001n04: 0 clone_color: child_CloneSet:3 allocation score on c001n05: 0 clone_color: child_CloneSet:3 allocation score on c001n06: 0 clone_color: child_CloneSet:3 allocation score on c001n07: 0 clone_color: child_CloneSet:3 allocation score on c001n08: 0 clone_color: child_CloneSet:4 allocation score on c001n09: 0 clone_color: child_CloneSet:4 allocation score on c001n02: 0 clone_color: child_CloneSet:4 allocation score on c001n03: 0 clone_color: child_CloneSet:4 allocation score on c001n04: 0 clone_color: child_CloneSet:4 allocation score on c001n05: 0 clone_color: child_CloneSet:4 allocation score on c001n06: 0 clone_color: child_CloneSet:4 allocation score on c001n07: 0 clone_color: child_CloneSet:4 allocation score on c001n08: 0 clone_color: child_CloneSet:5 allocation score on c001n09: 0 clone_color: child_CloneSet:5 allocation score on c001n02: 0 clone_color: child_CloneSet:5 allocation score on c001n03: 0 clone_color: child_CloneSet:5 allocation score on c001n04: 0 clone_color: child_CloneSet:5 allocation score on c001n05: 0 clone_color: child_CloneSet:5 allocation score on c001n06: 0 clone_color: child_CloneSet:5 allocation score on c001n07: 0 clone_color: child_CloneSet:5 allocation score on c001n08: 0 clone_color: child_CloneSet:6 allocation score on c001n09: 0 clone_color: child_CloneSet:6 allocation score on c001n02: 0 clone_color: child_CloneSet:6 allocation score on c001n03: 0 clone_color: child_CloneSet:6 allocation score on c001n04: 0 clone_color: child_CloneSet:6 allocation score on c001n05: 0 clone_color: child_CloneSet:6 allocation score on c001n06: 0 clone_color: child_CloneSet:6 allocation score on c001n07: 0 clone_color: child_CloneSet:6 allocation score on c001n08: 0 clone_color: child_CloneSet:7 allocation score on c001n09: 0 clone_color: child_CloneSet:7 allocation score on c001n02: 0 clone_color: child_CloneSet:7 allocation score on c001n03: 0 clone_color: child_CloneSet:7 allocation score on c001n04: 0 clone_color: child_CloneSet:7 allocation score on c001n05: 0 clone_color: child_CloneSet:7 allocation score on c001n06: 0 clone_color: child_CloneSet:7 allocation score on c001n07: 0 clone_color: child_CloneSet:7 allocation score on c001n08: 0 native_color: child_CloneSet:0 allocation score on c001n09: 0 -native_color: child_CloneSet:0 allocation score on c001n02: 0 -native_color: child_CloneSet:0 allocation score on c001n03: 0 -native_color: child_CloneSet:0 allocation score on c001n04: 0 -native_color: child_CloneSet:0 allocation score on c001n05: 0 -native_color: child_CloneSet:0 allocation score on c001n06: 0 -native_color: child_CloneSet:0 allocation score on c001n07: 0 -native_color: child_CloneSet:0 allocation score on c001n08: 0 +native_color: child_CloneSet:0 allocation score on c001n02: -INFINITY +native_color: child_CloneSet:0 allocation score on c001n03: -INFINITY +native_color: child_CloneSet:0 allocation score on c001n04: -INFINITY +native_color: child_CloneSet:0 allocation score on c001n05: -INFINITY +native_color: child_CloneSet:0 allocation score on c001n06: -INFINITY +native_color: child_CloneSet:0 allocation score on c001n07: -INFINITY +native_color: child_CloneSet:0 allocation score on c001n08: -INFINITY native_color: child_CloneSet:1 allocation score on c001n09: -INFINITY native_color: child_CloneSet:1 allocation score on c001n02: 0 -native_color: child_CloneSet:1 allocation score on c001n03: 0 -native_color: child_CloneSet:1 allocation score on c001n04: 0 -native_color: child_CloneSet:1 allocation score on c001n05: 0 -native_color: child_CloneSet:1 allocation score on c001n06: 0 -native_color: child_CloneSet:1 allocation score on c001n07: 0 -native_color: child_CloneSet:1 allocation score on c001n08: 0 +native_color: child_CloneSet:1 allocation score on c001n03: -INFINITY +native_color: child_CloneSet:1 allocation score on c001n04: -INFINITY +native_color: child_CloneSet:1 allocation score on c001n05: -INFINITY +native_color: child_CloneSet:1 allocation score on c001n06: -INFINITY +native_color: child_CloneSet:1 allocation score on c001n07: -INFINITY +native_color: child_CloneSet:1 allocation score on c001n08: -INFINITY native_color: child_CloneSet:2 allocation score on c001n09: -INFINITY native_color: child_CloneSet:2 allocation score on c001n02: -INFINITY native_color: child_CloneSet:2 allocation score on c001n03: 0 -native_color: child_CloneSet:2 allocation score on c001n04: 0 -native_color: child_CloneSet:2 allocation score on c001n05: 0 -native_color: child_CloneSet:2 allocation score on c001n06: 0 -native_color: child_CloneSet:2 allocation score on c001n07: 0 -native_color: child_CloneSet:2 allocation score on c001n08: 0 +native_color: child_CloneSet:2 allocation score on c001n04: -INFINITY +native_color: child_CloneSet:2 allocation score on c001n05: -INFINITY +native_color: child_CloneSet:2 allocation score on c001n06: -INFINITY +native_color: child_CloneSet:2 allocation score on c001n07: -INFINITY +native_color: child_CloneSet:2 allocation score on c001n08: -INFINITY native_color: child_CloneSet:3 allocation score on c001n09: -INFINITY native_color: child_CloneSet:3 allocation score on c001n02: -INFINITY native_color: child_CloneSet:3 allocation score on c001n03: -INFINITY native_color: child_CloneSet:3 allocation score on c001n04: 0 -native_color: child_CloneSet:3 allocation score on c001n05: 0 -native_color: child_CloneSet:3 allocation score on c001n06: 0 -native_color: child_CloneSet:3 allocation score on c001n07: 0 -native_color: child_CloneSet:3 allocation score on c001n08: 0 +native_color: child_CloneSet:3 allocation score on c001n05: -INFINITY +native_color: child_CloneSet:3 allocation score on c001n06: -INFINITY +native_color: child_CloneSet:3 allocation score on c001n07: -INFINITY +native_color: child_CloneSet:3 allocation score on c001n08: -INFINITY native_color: child_CloneSet:4 allocation score on c001n09: -INFINITY native_color: child_CloneSet:4 allocation score on c001n02: -INFINITY native_color: child_CloneSet:4 allocation score on c001n03: -INFINITY native_color: child_CloneSet:4 allocation score on c001n04: -INFINITY native_color: child_CloneSet:4 allocation score on c001n05: 0 -native_color: child_CloneSet:4 allocation score on c001n06: 0 -native_color: child_CloneSet:4 allocation score on c001n07: 0 -native_color: child_CloneSet:4 allocation score on c001n08: 0 +native_color: child_CloneSet:4 allocation score on c001n06: -INFINITY +native_color: child_CloneSet:4 allocation score on c001n07: -INFINITY +native_color: child_CloneSet:4 allocation score on c001n08: -INFINITY native_color: child_CloneSet:5 allocation score on c001n09: -INFINITY native_color: child_CloneSet:5 allocation score on c001n02: -INFINITY native_color: child_CloneSet:5 allocation score on c001n03: -INFINITY native_color: child_CloneSet:5 allocation score on c001n04: -INFINITY native_color: child_CloneSet:5 allocation score on c001n05: -INFINITY native_color: child_CloneSet:5 allocation score on c001n06: 0 -native_color: child_CloneSet:5 allocation score on c001n07: 0 -native_color: child_CloneSet:5 allocation score on c001n08: 0 +native_color: child_CloneSet:5 allocation score on c001n07: -INFINITY +native_color: child_CloneSet:5 allocation score on c001n08: -INFINITY native_color: child_CloneSet:6 allocation score on c001n09: -INFINITY native_color: child_CloneSet:6 allocation score on c001n02: -INFINITY native_color: child_CloneSet:6 allocation score on c001n03: -INFINITY native_color: child_CloneSet:6 allocation score on c001n04: -INFINITY native_color: child_CloneSet:6 allocation score on c001n05: -INFINITY native_color: child_CloneSet:6 allocation score on c001n06: -INFINITY native_color: child_CloneSet:6 allocation score on c001n07: 0 -native_color: child_CloneSet:6 allocation score on c001n08: 0 +native_color: child_CloneSet:6 allocation score on c001n08: -INFINITY native_color: child_CloneSet:7 allocation score on c001n09: -INFINITY native_color: child_CloneSet:7 allocation score on c001n02: -INFINITY native_color: child_CloneSet:7 allocation score on c001n03: -INFINITY native_color: child_CloneSet:7 allocation score on c001n04: -INFINITY native_color: child_CloneSet:7 allocation score on c001n05: -INFINITY native_color: child_CloneSet:7 allocation score on c001n06: -INFINITY native_color: child_CloneSet:7 allocation score on c001n07: -INFINITY native_color: child_CloneSet:7 allocation score on c001n08: 0 diff --git a/pengine/test10/interleave-3.scores b/pengine/test10/interleave-3.scores index 18081f910b..bf680d6c7d 100644 --- a/pengine/test10/interleave-3.scores +++ b/pengine/test10/interleave-3.scores @@ -1,345 +1,345 @@ Allocation scores: native_color: DcIPaddr allocation score on c001n09: 0 native_color: DcIPaddr allocation score on c001n02: -INFINITY native_color: DcIPaddr allocation score on c001n03: -INFINITY native_color: DcIPaddr allocation score on c001n04: -INFINITY native_color: DcIPaddr allocation score on c001n05: -INFINITY native_color: DcIPaddr allocation score on c001n06: -INFINITY native_color: DcIPaddr allocation score on c001n07: -INFINITY native_color: DcIPaddr allocation score on c001n08: -INFINITY native_color: rsc_c001n09 allocation score on c001n09: 100 native_color: rsc_c001n09 allocation score on c001n02: 0 native_color: rsc_c001n09 allocation score on c001n03: 0 native_color: rsc_c001n09 allocation score on c001n04: 0 native_color: rsc_c001n09 allocation score on c001n05: 0 native_color: rsc_c001n09 allocation score on c001n06: 0 native_color: rsc_c001n09 allocation score on c001n07: 0 native_color: rsc_c001n09 allocation score on c001n08: 0 native_color: rsc_c001n02 allocation score on c001n09: 0 native_color: rsc_c001n02 allocation score on c001n02: INFINITY native_color: rsc_c001n02 allocation score on c001n03: 0 native_color: rsc_c001n02 allocation score on c001n04: 0 native_color: rsc_c001n02 allocation score on c001n05: 0 native_color: rsc_c001n02 allocation score on c001n06: 0 native_color: rsc_c001n02 allocation score on c001n07: 0 native_color: rsc_c001n02 allocation score on c001n08: 0 native_color: rsc_c001n03 allocation score on c001n09: 0 native_color: rsc_c001n03 allocation score on c001n02: 0 native_color: rsc_c001n03 allocation score on c001n03: INFINITY native_color: rsc_c001n03 allocation score on c001n04: 0 native_color: rsc_c001n03 allocation score on c001n05: 0 native_color: rsc_c001n03 allocation score on c001n06: 0 native_color: rsc_c001n03 allocation score on c001n07: 0 native_color: rsc_c001n03 allocation score on c001n08: 0 native_color: rsc_c001n04 allocation score on c001n09: 0 native_color: rsc_c001n04 allocation score on c001n02: 0 native_color: rsc_c001n04 allocation score on c001n03: 0 native_color: rsc_c001n04 allocation score on c001n04: INFINITY native_color: rsc_c001n04 allocation score on c001n05: 0 native_color: rsc_c001n04 allocation score on c001n06: 0 native_color: rsc_c001n04 allocation score on c001n07: 0 native_color: rsc_c001n04 allocation score on c001n08: 0 native_color: rsc_c001n05 allocation score on c001n09: 0 native_color: rsc_c001n05 allocation score on c001n02: 0 native_color: rsc_c001n05 allocation score on c001n03: 0 native_color: rsc_c001n05 allocation score on c001n04: 0 native_color: rsc_c001n05 allocation score on c001n05: INFINITY native_color: rsc_c001n05 allocation score on c001n06: 0 native_color: rsc_c001n05 allocation score on c001n07: 0 native_color: rsc_c001n05 allocation score on c001n08: 0 native_color: rsc_c001n06 allocation score on c001n09: 0 native_color: rsc_c001n06 allocation score on c001n02: 0 native_color: rsc_c001n06 allocation score on c001n03: 0 native_color: rsc_c001n06 allocation score on c001n04: 0 native_color: rsc_c001n06 allocation score on c001n05: 0 native_color: rsc_c001n06 allocation score on c001n06: INFINITY native_color: rsc_c001n06 allocation score on c001n07: 0 native_color: rsc_c001n06 allocation score on c001n08: 0 native_color: rsc_c001n07 allocation score on c001n09: 0 native_color: rsc_c001n07 allocation score on c001n02: 0 native_color: rsc_c001n07 allocation score on c001n03: 0 native_color: rsc_c001n07 allocation score on c001n04: 0 native_color: rsc_c001n07 allocation score on c001n05: 0 native_color: rsc_c001n07 allocation score on c001n06: 0 native_color: rsc_c001n07 allocation score on c001n07: INFINITY native_color: rsc_c001n07 allocation score on c001n08: 0 native_color: rsc_c001n08 allocation score on c001n09: 0 native_color: rsc_c001n08 allocation score on c001n02: 0 native_color: rsc_c001n08 allocation score on c001n03: 0 native_color: rsc_c001n08 allocation score on c001n04: 0 native_color: rsc_c001n08 allocation score on c001n05: 0 native_color: rsc_c001n08 allocation score on c001n06: 0 native_color: rsc_c001n08 allocation score on c001n07: 0 native_color: rsc_c001n08 allocation score on c001n08: INFINITY clone_color: DoFencing allocation score on c001n09: 0 clone_color: DoFencing allocation score on c001n02: 0 clone_color: DoFencing allocation score on c001n03: 0 clone_color: DoFencing allocation score on c001n04: 0 clone_color: DoFencing allocation score on c001n05: 0 clone_color: DoFencing allocation score on c001n06: 0 clone_color: DoFencing allocation score on c001n07: 0 clone_color: DoFencing allocation score on c001n08: 0 clone_color: child_DoFencing:0 allocation score on c001n09: 0 clone_color: child_DoFencing:0 allocation score on c001n02: 200 clone_color: child_DoFencing:0 allocation score on c001n03: 0 clone_color: child_DoFencing:0 allocation score on c001n04: 0 clone_color: child_DoFencing:0 allocation score on c001n05: 0 clone_color: child_DoFencing:0 allocation score on c001n06: 0 clone_color: child_DoFencing:0 allocation score on c001n07: 0 clone_color: child_DoFencing:0 allocation score on c001n08: 0 clone_color: child_DoFencing:1 allocation score on c001n09: 0 clone_color: child_DoFencing:1 allocation score on c001n02: 0 clone_color: child_DoFencing:1 allocation score on c001n03: 200 clone_color: child_DoFencing:1 allocation score on c001n04: 0 clone_color: child_DoFencing:1 allocation score on c001n05: 0 clone_color: child_DoFencing:1 allocation score on c001n06: 0 clone_color: child_DoFencing:1 allocation score on c001n07: 0 clone_color: child_DoFencing:1 allocation score on c001n08: 0 clone_color: child_DoFencing:2 allocation score on c001n09: 0 clone_color: child_DoFencing:2 allocation score on c001n02: 0 clone_color: child_DoFencing:2 allocation score on c001n03: 0 clone_color: child_DoFencing:2 allocation score on c001n04: 200 clone_color: child_DoFencing:2 allocation score on c001n05: 0 clone_color: child_DoFencing:2 allocation score on c001n06: 0 clone_color: child_DoFencing:2 allocation score on c001n07: 0 clone_color: child_DoFencing:2 allocation score on c001n08: 0 clone_color: child_DoFencing:3 allocation score on c001n09: 0 clone_color: child_DoFencing:3 allocation score on c001n02: 0 clone_color: child_DoFencing:3 allocation score on c001n03: 0 clone_color: child_DoFencing:3 allocation score on c001n04: 0 clone_color: child_DoFencing:3 allocation score on c001n05: 200 clone_color: child_DoFencing:3 allocation score on c001n06: 0 clone_color: child_DoFencing:3 allocation score on c001n07: 0 clone_color: child_DoFencing:3 allocation score on c001n08: 0 clone_color: child_DoFencing:4 allocation score on c001n09: 0 clone_color: child_DoFencing:4 allocation score on c001n02: 0 clone_color: child_DoFencing:4 allocation score on c001n03: 0 clone_color: child_DoFencing:4 allocation score on c001n04: 0 clone_color: child_DoFencing:4 allocation score on c001n05: 0 clone_color: child_DoFencing:4 allocation score on c001n06: 200 clone_color: child_DoFencing:4 allocation score on c001n07: 0 clone_color: child_DoFencing:4 allocation score on c001n08: 0 clone_color: child_DoFencing:5 allocation score on c001n09: 0 clone_color: child_DoFencing:5 allocation score on c001n02: 0 clone_color: child_DoFencing:5 allocation score on c001n03: 0 clone_color: child_DoFencing:5 allocation score on c001n04: 0 clone_color: child_DoFencing:5 allocation score on c001n05: 0 clone_color: child_DoFencing:5 allocation score on c001n06: 0 clone_color: child_DoFencing:5 allocation score on c001n07: 200 clone_color: child_DoFencing:5 allocation score on c001n08: 0 clone_color: child_DoFencing:6 allocation score on c001n09: 0 clone_color: child_DoFencing:6 allocation score on c001n02: 0 clone_color: child_DoFencing:6 allocation score on c001n03: 0 clone_color: child_DoFencing:6 allocation score on c001n04: 0 clone_color: child_DoFencing:6 allocation score on c001n05: 0 clone_color: child_DoFencing:6 allocation score on c001n06: 0 clone_color: child_DoFencing:6 allocation score on c001n07: 0 clone_color: child_DoFencing:6 allocation score on c001n08: 200 clone_color: child_DoFencing:7 allocation score on c001n09: 200 clone_color: child_DoFencing:7 allocation score on c001n02: 0 clone_color: child_DoFencing:7 allocation score on c001n03: 0 clone_color: child_DoFencing:7 allocation score on c001n04: 0 clone_color: child_DoFencing:7 allocation score on c001n05: 0 clone_color: child_DoFencing:7 allocation score on c001n06: 0 clone_color: child_DoFencing:7 allocation score on c001n07: 0 clone_color: child_DoFencing:7 allocation score on c001n08: 0 clone_color: CloneSet allocation score on c001n09: 0 clone_color: CloneSet allocation score on c001n02: 0 clone_color: CloneSet allocation score on c001n03: 0 clone_color: CloneSet allocation score on c001n04: 0 clone_color: CloneSet allocation score on c001n05: 0 clone_color: CloneSet allocation score on c001n06: 0 clone_color: CloneSet allocation score on c001n07: 0 clone_color: CloneSet allocation score on c001n08: 0 clone_color: child_CloneSet:0 allocation score on c001n09: 0 clone_color: child_CloneSet:0 allocation score on c001n02: 0 clone_color: child_CloneSet:0 allocation score on c001n03: 0 clone_color: child_CloneSet:0 allocation score on c001n04: 0 clone_color: child_CloneSet:0 allocation score on c001n05: 0 clone_color: child_CloneSet:0 allocation score on c001n06: 0 clone_color: child_CloneSet:0 allocation score on c001n07: 0 clone_color: child_CloneSet:0 allocation score on c001n08: 0 clone_color: child_CloneSet:1 allocation score on c001n09: 0 clone_color: child_CloneSet:1 allocation score on c001n02: 0 clone_color: child_CloneSet:1 allocation score on c001n03: 0 clone_color: child_CloneSet:1 allocation score on c001n04: 0 clone_color: child_CloneSet:1 allocation score on c001n05: 0 clone_color: child_CloneSet:1 allocation score on c001n06: 0 clone_color: child_CloneSet:1 allocation score on c001n07: 0 clone_color: child_CloneSet:1 allocation score on c001n08: 0 clone_color: child_CloneSet:2 allocation score on c001n09: 0 clone_color: child_CloneSet:2 allocation score on c001n02: 0 clone_color: child_CloneSet:2 allocation score on c001n03: 0 clone_color: child_CloneSet:2 allocation score on c001n04: 0 clone_color: child_CloneSet:2 allocation score on c001n05: 0 clone_color: child_CloneSet:2 allocation score on c001n06: 0 clone_color: child_CloneSet:2 allocation score on c001n07: 0 clone_color: child_CloneSet:2 allocation score on c001n08: 0 clone_color: child_CloneSet:3 allocation score on c001n09: 0 clone_color: child_CloneSet:3 allocation score on c001n02: 0 clone_color: child_CloneSet:3 allocation score on c001n03: 0 clone_color: child_CloneSet:3 allocation score on c001n04: 0 clone_color: child_CloneSet:3 allocation score on c001n05: 0 clone_color: child_CloneSet:3 allocation score on c001n06: 0 clone_color: child_CloneSet:3 allocation score on c001n07: 0 clone_color: child_CloneSet:3 allocation score on c001n08: 0 clone_color: child_CloneSet:4 allocation score on c001n09: 0 clone_color: child_CloneSet:4 allocation score on c001n02: 0 clone_color: child_CloneSet:4 allocation score on c001n03: 0 clone_color: child_CloneSet:4 allocation score on c001n04: 0 clone_color: child_CloneSet:4 allocation score on c001n05: 0 clone_color: child_CloneSet:4 allocation score on c001n06: 0 clone_color: child_CloneSet:4 allocation score on c001n07: 0 clone_color: child_CloneSet:4 allocation score on c001n08: 0 clone_color: child_CloneSet:5 allocation score on c001n09: 0 clone_color: child_CloneSet:5 allocation score on c001n02: 0 clone_color: child_CloneSet:5 allocation score on c001n03: 0 clone_color: child_CloneSet:5 allocation score on c001n04: 0 clone_color: child_CloneSet:5 allocation score on c001n05: 0 clone_color: child_CloneSet:5 allocation score on c001n06: 0 clone_color: child_CloneSet:5 allocation score on c001n07: 0 clone_color: child_CloneSet:5 allocation score on c001n08: 0 clone_color: child_CloneSet:6 allocation score on c001n09: 0 clone_color: child_CloneSet:6 allocation score on c001n02: 0 clone_color: child_CloneSet:6 allocation score on c001n03: 0 clone_color: child_CloneSet:6 allocation score on c001n04: 0 clone_color: child_CloneSet:6 allocation score on c001n05: 0 clone_color: child_CloneSet:6 allocation score on c001n06: 0 clone_color: child_CloneSet:6 allocation score on c001n07: 0 clone_color: child_CloneSet:6 allocation score on c001n08: 0 clone_color: child_CloneSet:7 allocation score on c001n09: 0 clone_color: child_CloneSet:7 allocation score on c001n02: 0 clone_color: child_CloneSet:7 allocation score on c001n03: 0 clone_color: child_CloneSet:7 allocation score on c001n04: 0 clone_color: child_CloneSet:7 allocation score on c001n05: 0 clone_color: child_CloneSet:7 allocation score on c001n06: 0 clone_color: child_CloneSet:7 allocation score on c001n07: 0 clone_color: child_CloneSet:7 allocation score on c001n08: 0 native_color: child_CloneSet:0 allocation score on c001n09: 0 native_color: child_CloneSet:0 allocation score on c001n02: 0 native_color: child_CloneSet:0 allocation score on c001n03: 0 native_color: child_CloneSet:0 allocation score on c001n04: 0 native_color: child_CloneSet:0 allocation score on c001n05: 0 native_color: child_CloneSet:0 allocation score on c001n06: 0 native_color: child_CloneSet:0 allocation score on c001n07: 0 native_color: child_CloneSet:0 allocation score on c001n08: 0 native_color: child_CloneSet:1 allocation score on c001n09: -INFINITY native_color: child_CloneSet:1 allocation score on c001n02: 0 native_color: child_CloneSet:1 allocation score on c001n03: 0 native_color: child_CloneSet:1 allocation score on c001n04: 0 native_color: child_CloneSet:1 allocation score on c001n05: 0 native_color: child_CloneSet:1 allocation score on c001n06: 0 native_color: child_CloneSet:1 allocation score on c001n07: 0 native_color: child_CloneSet:1 allocation score on c001n08: 0 native_color: child_CloneSet:2 allocation score on c001n09: -INFINITY native_color: child_CloneSet:2 allocation score on c001n02: -INFINITY native_color: child_CloneSet:2 allocation score on c001n03: 0 native_color: child_CloneSet:2 allocation score on c001n04: 0 native_color: child_CloneSet:2 allocation score on c001n05: 0 native_color: child_CloneSet:2 allocation score on c001n06: 0 native_color: child_CloneSet:2 allocation score on c001n07: 0 native_color: child_CloneSet:2 allocation score on c001n08: 0 native_color: child_CloneSet:3 allocation score on c001n09: -INFINITY native_color: child_CloneSet:3 allocation score on c001n02: -INFINITY native_color: child_CloneSet:3 allocation score on c001n03: -INFINITY native_color: child_CloneSet:3 allocation score on c001n04: 0 native_color: child_CloneSet:3 allocation score on c001n05: 0 native_color: child_CloneSet:3 allocation score on c001n06: 0 native_color: child_CloneSet:3 allocation score on c001n07: 0 native_color: child_CloneSet:3 allocation score on c001n08: 0 native_color: child_CloneSet:4 allocation score on c001n09: -INFINITY native_color: child_CloneSet:4 allocation score on c001n02: -INFINITY native_color: child_CloneSet:4 allocation score on c001n03: -INFINITY native_color: child_CloneSet:4 allocation score on c001n04: -INFINITY native_color: child_CloneSet:4 allocation score on c001n05: 0 native_color: child_CloneSet:4 allocation score on c001n06: 0 native_color: child_CloneSet:4 allocation score on c001n07: 0 native_color: child_CloneSet:4 allocation score on c001n08: 0 native_color: child_CloneSet:5 allocation score on c001n09: -INFINITY native_color: child_CloneSet:5 allocation score on c001n02: -INFINITY native_color: child_CloneSet:5 allocation score on c001n03: -INFINITY native_color: child_CloneSet:5 allocation score on c001n04: -INFINITY native_color: child_CloneSet:5 allocation score on c001n05: -INFINITY native_color: child_CloneSet:5 allocation score on c001n06: 0 native_color: child_CloneSet:5 allocation score on c001n07: 0 native_color: child_CloneSet:5 allocation score on c001n08: 0 native_color: child_CloneSet:6 allocation score on c001n09: -INFINITY native_color: child_CloneSet:6 allocation score on c001n02: -INFINITY native_color: child_CloneSet:6 allocation score on c001n03: -INFINITY native_color: child_CloneSet:6 allocation score on c001n04: -INFINITY native_color: child_CloneSet:6 allocation score on c001n05: -INFINITY native_color: child_CloneSet:6 allocation score on c001n06: -INFINITY native_color: child_CloneSet:6 allocation score on c001n07: 0 native_color: child_CloneSet:6 allocation score on c001n08: 0 native_color: child_CloneSet:7 allocation score on c001n09: -INFINITY native_color: child_CloneSet:7 allocation score on c001n02: -INFINITY native_color: child_CloneSet:7 allocation score on c001n03: -INFINITY native_color: child_CloneSet:7 allocation score on c001n04: -INFINITY native_color: child_CloneSet:7 allocation score on c001n05: -INFINITY native_color: child_CloneSet:7 allocation score on c001n06: -INFINITY native_color: child_CloneSet:7 allocation score on c001n07: -INFINITY native_color: child_CloneSet:7 allocation score on c001n08: 0 -native_color: child_DoFencing:0 allocation score on c001n09: 0 +native_color: child_DoFencing:0 allocation score on c001n09: -INFINITY native_color: child_DoFencing:0 allocation score on c001n02: 200 -native_color: child_DoFencing:0 allocation score on c001n03: 0 -native_color: child_DoFencing:0 allocation score on c001n04: 0 -native_color: child_DoFencing:0 allocation score on c001n05: 0 -native_color: child_DoFencing:0 allocation score on c001n06: 0 -native_color: child_DoFencing:0 allocation score on c001n07: 0 -native_color: child_DoFencing:0 allocation score on c001n08: 0 -native_color: child_DoFencing:1 allocation score on c001n09: 0 +native_color: child_DoFencing:0 allocation score on c001n03: -INFINITY +native_color: child_DoFencing:0 allocation score on c001n04: -INFINITY +native_color: child_DoFencing:0 allocation score on c001n05: -INFINITY +native_color: child_DoFencing:0 allocation score on c001n06: -INFINITY +native_color: child_DoFencing:0 allocation score on c001n07: -INFINITY +native_color: child_DoFencing:0 allocation score on c001n08: -INFINITY +native_color: child_DoFencing:1 allocation score on c001n09: -INFINITY native_color: child_DoFencing:1 allocation score on c001n02: -INFINITY native_color: child_DoFencing:1 allocation score on c001n03: 200 -native_color: child_DoFencing:1 allocation score on c001n04: 0 -native_color: child_DoFencing:1 allocation score on c001n05: 0 -native_color: child_DoFencing:1 allocation score on c001n06: 0 -native_color: child_DoFencing:1 allocation score on c001n07: 0 -native_color: child_DoFencing:1 allocation score on c001n08: 0 -native_color: child_DoFencing:2 allocation score on c001n09: 0 +native_color: child_DoFencing:1 allocation score on c001n04: -INFINITY +native_color: child_DoFencing:1 allocation score on c001n05: -INFINITY +native_color: child_DoFencing:1 allocation score on c001n06: -INFINITY +native_color: child_DoFencing:1 allocation score on c001n07: -INFINITY +native_color: child_DoFencing:1 allocation score on c001n08: -INFINITY +native_color: child_DoFencing:2 allocation score on c001n09: -INFINITY native_color: child_DoFencing:2 allocation score on c001n02: -INFINITY native_color: child_DoFencing:2 allocation score on c001n03: -INFINITY native_color: child_DoFencing:2 allocation score on c001n04: 200 -native_color: child_DoFencing:2 allocation score on c001n05: 0 -native_color: child_DoFencing:2 allocation score on c001n06: 0 -native_color: child_DoFencing:2 allocation score on c001n07: 0 -native_color: child_DoFencing:2 allocation score on c001n08: 0 -native_color: child_DoFencing:3 allocation score on c001n09: 0 +native_color: child_DoFencing:2 allocation score on c001n05: -INFINITY +native_color: child_DoFencing:2 allocation score on c001n06: -INFINITY +native_color: child_DoFencing:2 allocation score on c001n07: -INFINITY +native_color: child_DoFencing:2 allocation score on c001n08: -INFINITY +native_color: child_DoFencing:3 allocation score on c001n09: -INFINITY native_color: child_DoFencing:3 allocation score on c001n02: -INFINITY native_color: child_DoFencing:3 allocation score on c001n03: -INFINITY native_color: child_DoFencing:3 allocation score on c001n04: -INFINITY native_color: child_DoFencing:3 allocation score on c001n05: 200 -native_color: child_DoFencing:3 allocation score on c001n06: 0 -native_color: child_DoFencing:3 allocation score on c001n07: 0 -native_color: child_DoFencing:3 allocation score on c001n08: 0 -native_color: child_DoFencing:4 allocation score on c001n09: 0 +native_color: child_DoFencing:3 allocation score on c001n06: -INFINITY +native_color: child_DoFencing:3 allocation score on c001n07: -INFINITY +native_color: child_DoFencing:3 allocation score on c001n08: -INFINITY +native_color: child_DoFencing:4 allocation score on c001n09: -INFINITY native_color: child_DoFencing:4 allocation score on c001n02: -INFINITY native_color: child_DoFencing:4 allocation score on c001n03: -INFINITY native_color: child_DoFencing:4 allocation score on c001n04: -INFINITY native_color: child_DoFencing:4 allocation score on c001n05: -INFINITY native_color: child_DoFencing:4 allocation score on c001n06: 200 -native_color: child_DoFencing:4 allocation score on c001n07: 0 -native_color: child_DoFencing:4 allocation score on c001n08: 0 -native_color: child_DoFencing:5 allocation score on c001n09: 0 +native_color: child_DoFencing:4 allocation score on c001n07: -INFINITY +native_color: child_DoFencing:4 allocation score on c001n08: -INFINITY +native_color: child_DoFencing:5 allocation score on c001n09: -INFINITY native_color: child_DoFencing:5 allocation score on c001n02: -INFINITY native_color: child_DoFencing:5 allocation score on c001n03: -INFINITY native_color: child_DoFencing:5 allocation score on c001n04: -INFINITY native_color: child_DoFencing:5 allocation score on c001n05: -INFINITY native_color: child_DoFencing:5 allocation score on c001n06: -INFINITY native_color: child_DoFencing:5 allocation score on c001n07: 200 -native_color: child_DoFencing:5 allocation score on c001n08: 0 -native_color: child_DoFencing:6 allocation score on c001n09: 0 +native_color: child_DoFencing:5 allocation score on c001n08: -INFINITY +native_color: child_DoFencing:6 allocation score on c001n09: -INFINITY native_color: child_DoFencing:6 allocation score on c001n02: -INFINITY native_color: child_DoFencing:6 allocation score on c001n03: -INFINITY native_color: child_DoFencing:6 allocation score on c001n04: -INFINITY native_color: child_DoFencing:6 allocation score on c001n05: -INFINITY native_color: child_DoFencing:6 allocation score on c001n06: -INFINITY native_color: child_DoFencing:6 allocation score on c001n07: -INFINITY native_color: child_DoFencing:6 allocation score on c001n08: 200 native_color: child_DoFencing:7 allocation score on c001n09: 200 native_color: child_DoFencing:7 allocation score on c001n02: -INFINITY native_color: child_DoFencing:7 allocation score on c001n03: -INFINITY native_color: child_DoFencing:7 allocation score on c001n04: -INFINITY native_color: child_DoFencing:7 allocation score on c001n05: -INFINITY native_color: child_DoFencing:7 allocation score on c001n06: -INFINITY native_color: child_DoFencing:7 allocation score on c001n07: -INFINITY native_color: child_DoFencing:7 allocation score on c001n08: -INFINITY diff --git a/pengine/test10/order-clone.scores b/pengine/test10/order-clone.scores index 24d2171bd3..03d2a582da 100644 --- a/pengine/test10/order-clone.scores +++ b/pengine/test10/order-clone.scores @@ -1,221 +1,221 @@ Allocation scores: native_color: fencing-sbd allocation score on hex-7: 0 native_color: fencing-sbd allocation score on hex-8: 0 native_color: fencing-sbd allocation score on hex-9: 0 native_color: fencing-sbd allocation score on hex-0: 0 clone_color: o2cb-clone allocation score on hex-7: 0 clone_color: o2cb-clone allocation score on hex-8: 0 clone_color: o2cb-clone allocation score on hex-9: 0 clone_color: o2cb-clone allocation score on hex-0: 0 clone_color: o2cb:0 allocation score on hex-7: 0 clone_color: o2cb:0 allocation score on hex-8: 0 clone_color: o2cb:0 allocation score on hex-9: 0 clone_color: o2cb:0 allocation score on hex-0: 0 clone_color: o2cb:1 allocation score on hex-7: 0 clone_color: o2cb:1 allocation score on hex-8: 0 clone_color: o2cb:1 allocation score on hex-9: 0 clone_color: o2cb:1 allocation score on hex-0: 0 clone_color: o2cb:2 allocation score on hex-7: 0 clone_color: o2cb:2 allocation score on hex-8: 0 clone_color: o2cb:2 allocation score on hex-9: 0 clone_color: o2cb:2 allocation score on hex-0: 0 clone_color: o2cb:3 allocation score on hex-7: 0 clone_color: o2cb:3 allocation score on hex-8: 0 clone_color: o2cb:3 allocation score on hex-9: 0 clone_color: o2cb:3 allocation score on hex-0: 0 clone_color: vg1-clone allocation score on hex-7: 0 clone_color: vg1-clone allocation score on hex-8: 0 clone_color: vg1-clone allocation score on hex-9: 0 clone_color: vg1-clone allocation score on hex-0: 0 clone_color: vg1:0 allocation score on hex-7: 0 clone_color: vg1:0 allocation score on hex-8: 0 clone_color: vg1:0 allocation score on hex-9: 0 clone_color: vg1:0 allocation score on hex-0: 0 clone_color: vg1:1 allocation score on hex-7: 0 clone_color: vg1:1 allocation score on hex-8: 0 clone_color: vg1:1 allocation score on hex-9: 0 clone_color: vg1:1 allocation score on hex-0: 0 clone_color: vg1:2 allocation score on hex-7: 0 clone_color: vg1:2 allocation score on hex-8: 0 clone_color: vg1:2 allocation score on hex-9: 0 clone_color: vg1:2 allocation score on hex-0: 0 clone_color: vg1:3 allocation score on hex-7: 0 clone_color: vg1:3 allocation score on hex-8: 0 clone_color: vg1:3 allocation score on hex-9: 0 clone_color: vg1:3 allocation score on hex-0: 0 clone_color: fs1-clone allocation score on hex-7: 0 clone_color: fs1-clone allocation score on hex-8: 0 clone_color: fs1-clone allocation score on hex-9: 0 clone_color: fs1-clone allocation score on hex-0: 0 clone_color: ocfs2-1:0 allocation score on hex-7: 0 clone_color: ocfs2-1:0 allocation score on hex-8: 0 clone_color: ocfs2-1:0 allocation score on hex-9: 0 clone_color: ocfs2-1:0 allocation score on hex-0: 0 clone_color: ocfs2-1:1 allocation score on hex-7: 0 clone_color: ocfs2-1:1 allocation score on hex-8: 0 clone_color: ocfs2-1:1 allocation score on hex-9: 0 clone_color: ocfs2-1:1 allocation score on hex-0: 0 clone_color: ocfs2-1:2 allocation score on hex-7: 0 clone_color: ocfs2-1:2 allocation score on hex-8: 0 clone_color: ocfs2-1:2 allocation score on hex-9: 0 clone_color: ocfs2-1:2 allocation score on hex-0: 0 clone_color: ocfs2-1:3 allocation score on hex-7: 0 clone_color: ocfs2-1:3 allocation score on hex-8: 0 clone_color: ocfs2-1:3 allocation score on hex-9: 0 clone_color: ocfs2-1:3 allocation score on hex-0: 0 native_color: ocfs2-1:0 allocation score on hex-7: 0 native_color: ocfs2-1:0 allocation score on hex-8: 0 native_color: ocfs2-1:0 allocation score on hex-9: 0 native_color: ocfs2-1:0 allocation score on hex-0: 0 native_color: ocfs2-1:1 allocation score on hex-7: 0 native_color: ocfs2-1:1 allocation score on hex-8: -INFINITY native_color: ocfs2-1:1 allocation score on hex-9: 0 native_color: ocfs2-1:1 allocation score on hex-0: 0 native_color: ocfs2-1:2 allocation score on hex-7: 0 native_color: ocfs2-1:2 allocation score on hex-8: -INFINITY native_color: ocfs2-1:2 allocation score on hex-9: -INFINITY native_color: ocfs2-1:2 allocation score on hex-0: 0 native_color: ocfs2-1:3 allocation score on hex-7: 0 native_color: ocfs2-1:3 allocation score on hex-8: -INFINITY native_color: ocfs2-1:3 allocation score on hex-9: -INFINITY native_color: ocfs2-1:3 allocation score on hex-0: -INFINITY clone_color: fs2-clone allocation score on hex-7: 0 clone_color: fs2-clone allocation score on hex-8: 0 clone_color: fs2-clone allocation score on hex-9: 0 clone_color: fs2-clone allocation score on hex-0: 0 clone_color: ocfs2-2:0 allocation score on hex-7: 0 clone_color: ocfs2-2:0 allocation score on hex-8: 0 clone_color: ocfs2-2:0 allocation score on hex-9: 0 clone_color: ocfs2-2:0 allocation score on hex-0: 0 clone_color: ocfs2-2:1 allocation score on hex-7: 0 clone_color: ocfs2-2:1 allocation score on hex-8: 0 clone_color: ocfs2-2:1 allocation score on hex-9: 0 clone_color: ocfs2-2:1 allocation score on hex-0: 0 clone_color: ocfs2-2:2 allocation score on hex-7: 0 clone_color: ocfs2-2:2 allocation score on hex-8: 0 clone_color: ocfs2-2:2 allocation score on hex-9: 0 clone_color: ocfs2-2:2 allocation score on hex-0: 0 clone_color: ocfs2-2:3 allocation score on hex-7: 0 clone_color: ocfs2-2:3 allocation score on hex-8: 0 clone_color: ocfs2-2:3 allocation score on hex-9: 0 clone_color: ocfs2-2:3 allocation score on hex-0: 0 native_color: ocfs2-2:0 allocation score on hex-7: 0 native_color: ocfs2-2:0 allocation score on hex-8: 0 native_color: ocfs2-2:0 allocation score on hex-9: 0 native_color: ocfs2-2:0 allocation score on hex-0: 0 native_color: ocfs2-2:1 allocation score on hex-7: 0 native_color: ocfs2-2:1 allocation score on hex-8: -INFINITY native_color: ocfs2-2:1 allocation score on hex-9: 0 native_color: ocfs2-2:1 allocation score on hex-0: 0 native_color: ocfs2-2:2 allocation score on hex-7: 0 native_color: ocfs2-2:2 allocation score on hex-8: -INFINITY native_color: ocfs2-2:2 allocation score on hex-9: -INFINITY native_color: ocfs2-2:2 allocation score on hex-0: 0 native_color: ocfs2-2:3 allocation score on hex-7: 0 native_color: ocfs2-2:3 allocation score on hex-8: -INFINITY native_color: ocfs2-2:3 allocation score on hex-9: -INFINITY native_color: ocfs2-2:3 allocation score on hex-0: -INFINITY -native_color: vg1:0 allocation score on hex-7: 0 +native_color: vg1:0 allocation score on hex-7: -INFINITY native_color: vg1:0 allocation score on hex-8: 0 -native_color: vg1:0 allocation score on hex-9: 0 -native_color: vg1:0 allocation score on hex-0: 0 -native_color: vg1:1 allocation score on hex-7: 0 +native_color: vg1:0 allocation score on hex-9: -INFINITY +native_color: vg1:0 allocation score on hex-0: -INFINITY +native_color: vg1:1 allocation score on hex-7: -INFINITY native_color: vg1:1 allocation score on hex-8: -INFINITY native_color: vg1:1 allocation score on hex-9: 0 -native_color: vg1:1 allocation score on hex-0: 0 -native_color: vg1:2 allocation score on hex-7: 0 +native_color: vg1:1 allocation score on hex-0: -INFINITY +native_color: vg1:2 allocation score on hex-7: -INFINITY native_color: vg1:2 allocation score on hex-8: -INFINITY native_color: vg1:2 allocation score on hex-9: -INFINITY native_color: vg1:2 allocation score on hex-0: 0 native_color: vg1:3 allocation score on hex-7: 0 native_color: vg1:3 allocation score on hex-8: -INFINITY native_color: vg1:3 allocation score on hex-9: -INFINITY native_color: vg1:3 allocation score on hex-0: -INFINITY -native_color: o2cb:0 allocation score on hex-7: 0 +native_color: o2cb:0 allocation score on hex-7: -INFINITY native_color: o2cb:0 allocation score on hex-8: 0 -native_color: o2cb:0 allocation score on hex-9: 0 -native_color: o2cb:0 allocation score on hex-0: 0 -native_color: o2cb:1 allocation score on hex-7: 0 +native_color: o2cb:0 allocation score on hex-9: -INFINITY +native_color: o2cb:0 allocation score on hex-0: -INFINITY +native_color: o2cb:1 allocation score on hex-7: -INFINITY native_color: o2cb:1 allocation score on hex-8: -INFINITY native_color: o2cb:1 allocation score on hex-9: 0 -native_color: o2cb:1 allocation score on hex-0: 0 -native_color: o2cb:2 allocation score on hex-7: 0 +native_color: o2cb:1 allocation score on hex-0: -INFINITY +native_color: o2cb:2 allocation score on hex-7: -INFINITY native_color: o2cb:2 allocation score on hex-8: -INFINITY native_color: o2cb:2 allocation score on hex-9: -INFINITY native_color: o2cb:2 allocation score on hex-0: 0 native_color: o2cb:3 allocation score on hex-7: 0 native_color: o2cb:3 allocation score on hex-8: -INFINITY native_color: o2cb:3 allocation score on hex-9: -INFINITY native_color: o2cb:3 allocation score on hex-0: -INFINITY clone_color: dlm-clone allocation score on hex-7: 0 clone_color: dlm-clone allocation score on hex-8: 0 clone_color: dlm-clone allocation score on hex-9: 0 clone_color: dlm-clone allocation score on hex-0: 0 clone_color: dlm:0 allocation score on hex-7: 0 clone_color: dlm:0 allocation score on hex-8: 0 clone_color: dlm:0 allocation score on hex-9: 0 clone_color: dlm:0 allocation score on hex-0: 0 clone_color: dlm:1 allocation score on hex-7: 0 clone_color: dlm:1 allocation score on hex-8: 0 clone_color: dlm:1 allocation score on hex-9: 0 clone_color: dlm:1 allocation score on hex-0: 0 clone_color: dlm:2 allocation score on hex-7: 0 clone_color: dlm:2 allocation score on hex-8: 0 clone_color: dlm:2 allocation score on hex-9: 0 clone_color: dlm:2 allocation score on hex-0: 0 clone_color: dlm:3 allocation score on hex-7: 0 clone_color: dlm:3 allocation score on hex-8: 0 clone_color: dlm:3 allocation score on hex-9: 0 clone_color: dlm:3 allocation score on hex-0: 0 clone_color: clvm-clone allocation score on hex-7: 0 clone_color: clvm-clone allocation score on hex-8: 0 clone_color: clvm-clone allocation score on hex-9: 0 clone_color: clvm-clone allocation score on hex-0: 0 clone_color: clvm:0 allocation score on hex-7: 0 clone_color: clvm:0 allocation score on hex-8: 0 clone_color: clvm:0 allocation score on hex-9: 0 clone_color: clvm:0 allocation score on hex-0: 0 clone_color: clvm:1 allocation score on hex-7: 0 clone_color: clvm:1 allocation score on hex-8: 0 clone_color: clvm:1 allocation score on hex-9: 0 clone_color: clvm:1 allocation score on hex-0: 0 clone_color: clvm:2 allocation score on hex-7: 0 clone_color: clvm:2 allocation score on hex-8: 0 clone_color: clvm:2 allocation score on hex-9: 0 clone_color: clvm:2 allocation score on hex-0: 0 clone_color: clvm:3 allocation score on hex-7: 0 clone_color: clvm:3 allocation score on hex-8: 0 clone_color: clvm:3 allocation score on hex-9: 0 clone_color: clvm:3 allocation score on hex-0: 0 -native_color: clvm:0 allocation score on hex-7: 0 +native_color: clvm:0 allocation score on hex-7: -INFINITY native_color: clvm:0 allocation score on hex-8: 0 -native_color: clvm:0 allocation score on hex-9: 0 -native_color: clvm:0 allocation score on hex-0: 0 -native_color: clvm:1 allocation score on hex-7: 0 +native_color: clvm:0 allocation score on hex-9: -INFINITY +native_color: clvm:0 allocation score on hex-0: -INFINITY +native_color: clvm:1 allocation score on hex-7: -INFINITY native_color: clvm:1 allocation score on hex-8: -INFINITY native_color: clvm:1 allocation score on hex-9: 0 -native_color: clvm:1 allocation score on hex-0: 0 -native_color: clvm:2 allocation score on hex-7: 0 +native_color: clvm:1 allocation score on hex-0: -INFINITY +native_color: clvm:2 allocation score on hex-7: -INFINITY native_color: clvm:2 allocation score on hex-8: -INFINITY native_color: clvm:2 allocation score on hex-9: -INFINITY native_color: clvm:2 allocation score on hex-0: 0 native_color: clvm:3 allocation score on hex-7: 0 native_color: clvm:3 allocation score on hex-8: -INFINITY native_color: clvm:3 allocation score on hex-9: -INFINITY native_color: clvm:3 allocation score on hex-0: -INFINITY native_color: dlm:0 allocation score on hex-7: -INFINITY native_color: dlm:0 allocation score on hex-8: -INFINITY native_color: dlm:0 allocation score on hex-9: -INFINITY native_color: dlm:0 allocation score on hex-0: -INFINITY native_color: dlm:1 allocation score on hex-7: -INFINITY native_color: dlm:1 allocation score on hex-8: -INFINITY native_color: dlm:1 allocation score on hex-9: -INFINITY native_color: dlm:1 allocation score on hex-0: -INFINITY native_color: dlm:2 allocation score on hex-7: -INFINITY native_color: dlm:2 allocation score on hex-8: -INFINITY native_color: dlm:2 allocation score on hex-9: -INFINITY native_color: dlm:2 allocation score on hex-0: -INFINITY native_color: dlm:3 allocation score on hex-7: -INFINITY native_color: dlm:3 allocation score on hex-8: -INFINITY native_color: dlm:3 allocation score on hex-9: -INFINITY native_color: dlm:3 allocation score on hex-0: -INFINITY diff --git a/pengine/utils.c b/pengine/utils.c index be50a36462..ddc604f33a 100644 --- a/pengine/utils.c +++ b/pengine/utils.c @@ -1,673 +1,724 @@ /* * 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 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, %d", safe_val3(NULL, cons, rsc_lh, id), safe_val3(NULL, cons, rsc_rh, id), cons->score); } } 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_to_node(GListPtr constraints) { GListPtr iterator = constraints; while(iterator != NULL) { rsc_to_node_t *cons = iterator->data; iterator = iterator->next; pe_free_shallow(cons->node_list_rh); crm_free(cons); } if(constraints != NULL) { g_list_free(constraints); } } 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; } else if(foo_node == NULL) { CRM_CHECK(node_weight == 0, 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 = merge_weights(node_weight, foo_node->weight); new_con->node_list_rh = g_list_append(NULL, copy); } 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 * ordering_type2text(enum pe_ordering type) { const char *result = ""; if(type & pe_order_implies_left) { /* was: mandatory */ result = "right_implies_left"; } else if(type & pe_order_implies_right) { /* was: recover */ result = "left_implies_right"; } else if(type & pe_order_optional) { /* pure ordering, nothing implied */ result = "optional"; } else if(type & pe_order_runnable_left) { result = "runnable"; /* } else { */ /* crm_err("Unknown ordering type: %.3x", type); */ } return result; } gboolean can_run_resources(const node_t *node) { if(node == NULL) { return FALSE; } + +#if 0 + if(node->weight < 0) { + return FALSE; + } +#endif if(node->details->online == FALSE || node->details->shutdown || node->details->unclean || node->details->standby) { crm_debug_2("%s: online=%d, unclean=%d, standby=%d", node->details->uname, node->details->online, node->details->unclean, node->details->standby); return FALSE; } return TRUE; } struct compare_data { const node_t *node1; const node_t *node2; int result; }; static void do_compare_capacity1(gpointer key, gpointer value, gpointer user_data) { int node1_capacity = 0; int node2_capacity = 0; struct compare_data *data = user_data; node1_capacity = crm_parse_int(value, "0"); node2_capacity = crm_parse_int(g_hash_table_lookup(data->node2->details->utilization, key), "0"); if (node1_capacity > node2_capacity) { data->result--; } else if (node1_capacity < node2_capacity) { data->result++; } } static void do_compare_capacity2(gpointer key, gpointer value, gpointer user_data) { int node1_capacity = 0; int node2_capacity = 0; struct compare_data *data = user_data; if (g_hash_table_lookup_extended(data->node1->details->utilization, key, NULL, NULL)) { return; } node1_capacity = 0; node2_capacity = crm_parse_int(value, "0"); if (node1_capacity > node2_capacity) { data->result--; } else if (node1_capacity < node2_capacity) { data->result++; } } /* rc < 0 if 'node1' has more capacity remaining * rc > 0 if 'node1' has less capacity remaining */ static int compare_capacity(const node_t *node1, const node_t *node2) { struct compare_data data; data.node1 = node1; data.node2 = node2; data.result = 0; g_hash_table_foreach(node1->details->utilization, do_compare_capacity1, &data); g_hash_table_foreach(node2->details->utilization, do_compare_capacity2, &data); return data.result; } /* return -1 if 'a' is more preferred * return 1 if 'b' is more preferred */ + gint sort_node_weight(gconstpointer a, gconstpointer b, gpointer data) { int level = LOG_DEBUG_3; const node_t *node1 = (const node_t*)a; const node_t *node2 = (const node_t*)b; - const pe_working_set_t *data_set = (const pe_working_set_t*)data; int node1_weight = 0; int node2_weight = 0; int result = 0; if(a == NULL) { return 1; } if(b == NULL) { return -1; } node1_weight = node1->weight; node2_weight = node2->weight; if(can_run_resources(node1) == FALSE) { node1_weight = -INFINITY; } if(can_run_resources(node2) == FALSE) { node2_weight = -INFINITY; } if(node1_weight > node2_weight) { do_crm_log_unlikely(level, "%s (%d) > %s (%d) : weight", node1->details->uname, node1_weight, node2->details->uname, node2_weight); return -1; } if(node1_weight < node2_weight) { do_crm_log_unlikely(level, "%s (%d) < %s (%d) : weight", node1->details->uname, node1_weight, node2->details->uname, node2_weight); return 1; } do_crm_log_unlikely(level, "%s (%d) == %s (%d) : weight", node1->details->uname, node1_weight, node2->details->uname, node2_weight); - if (safe_str_eq(data_set->placement_strategy, "minimal")) { + if (safe_str_eq(pe_dataset->placement_strategy, "minimal")) { goto equal; } - if (safe_str_eq(data_set->placement_strategy, "balanced")) { + if (safe_str_eq(pe_dataset->placement_strategy, "balanced")) { result = compare_capacity(node1, node2); if (result != 0) { return result; } } /* now try to balance resources across the cluster */ if(node1->details->num_resources < node2->details->num_resources) { do_crm_log_unlikely(level, "%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) { do_crm_log_unlikely(level, "%s (%d) > %s (%d) : resources", node1->details->uname, node1->details->num_resources, node2->details->uname, node2->details->num_resources); return 1; } equal: do_crm_log_unlikely(level, "%s = %s", node1->details->uname, node2->details->uname); return 0; } struct calculate_data { node_t *node; gboolean allocate; }; static void do_calculate_utilization(gpointer key, gpointer value, gpointer user_data) { const char *capacity = NULL; char *remain_capacity = NULL; struct calculate_data *data = user_data; capacity = g_hash_table_lookup(data->node->details->utilization, key); if (capacity) { if (data->allocate) { remain_capacity = crm_itoa(crm_parse_int(capacity, "0") - crm_parse_int(value, "0")); } else { remain_capacity = crm_itoa(crm_parse_int(capacity, "0") + crm_parse_int(value, "0")); } g_hash_table_replace(data->node->details->utilization, crm_strdup(key), remain_capacity); } } /* Specify 'allocate' to TRUE when allocating * Otherwise to FALSE when deallocating */ static void calculate_utilization(node_t *node, resource_t *rsc, gboolean allocate) { struct calculate_data data; data.node = node; data.allocate = allocate; g_hash_table_foreach(rsc->utilization, do_calculate_utilization, &data); if (allocate) { dump_rsc_utilization(show_utilization?0:utilization_log_level, __FUNCTION__, rsc, node); } } gboolean native_assign_node(resource_t *rsc, GListPtr nodes, node_t *chosen, gboolean force) { CRM_ASSERT(rsc->variant == pe_native); clear_bit(rsc->flags, pe_rsc_provisional); - if(chosen == NULL) { - crm_debug("Could not allocate a node for %s", rsc->id); - rsc->next_role = RSC_ROLE_STOPPED; - return FALSE; - - } else if(force == FALSE - && (can_run_resources(chosen) == FALSE || chosen->weight < 0)) { + if(force == FALSE + && chosen != NULL + && (can_run_resources(chosen) == FALSE || chosen->weight < 0)) { crm_debug("All nodes for resource %s are unavailable" ", unclean or shutting down (%s: %d, %d)", rsc->id, chosen->details->uname, can_run_resources(chosen), chosen->weight); rsc->next_role = RSC_ROLE_STOPPED; - return FALSE; + chosen = NULL; } /* todo: update the old node for each resource to reflect its * new resource count */ if(rsc->allocated_to) { node_t *old = rsc->allocated_to; + crm_info("Deallocating %s from %s", rsc->id, old->details->uname); old->details->allocated_rsc = g_list_remove( old->details->allocated_rsc, rsc); old->details->num_resources--; old->count--; calculate_utilization(old, rsc, FALSE); } + if(chosen == NULL) { + char *key = NULL; + GListPtr possible_matches = NULL; + + crm_debug("Could not allocate a node for %s", rsc->id); + rsc->next_role = RSC_ROLE_STOPPED; + + key = generate_op_key(rsc->id, CRMD_ACTION_STOP, 0); + possible_matches = find_actions(rsc->actions, key, NULL); + + slist_iter( + stop, action_t, possible_matches, lpc, + stop->optional = FALSE; + ); + + g_list_free(possible_matches); + crm_free(key); + + key = generate_op_key(rsc->id, CRMD_ACTION_START, 0); + possible_matches = find_actions(rsc->actions, key, NULL); + + slist_iter( + start, action_t, possible_matches, lpc, + start->runnable = FALSE; + ); + + g_list_free(possible_matches); + crm_free(key); + + return FALSE; + } + crm_debug("Assigning %s to %s", chosen->details->uname, rsc->id); crm_free(rsc->allocated_to); rsc->allocated_to = node_copy(chosen); chosen->details->allocated_rsc = g_list_append(chosen->details->allocated_rsc, rsc); chosen->details->num_resources++; chosen->count++; calculate_utilization(chosen, rsc, TRUE); - return TRUE; } char * convert_non_atomic_task(char *old_uuid, resource_t *rsc, gboolean allow_notify, gboolean free_original) { int interval = 0; char *uuid = NULL; char *rid = NULL; char *raw_task = NULL; int task = no_action; crm_debug_3("Processing %s", old_uuid); if(old_uuid == NULL) { return NULL; } else if(strstr(old_uuid, "notify") != NULL) { goto done; /* no conversion */ } else if(rsc->variant < pe_group) { goto done; /* no conversion */ } CRM_ASSERT(parse_op_key(old_uuid, &rid, &raw_task, &interval)); if(interval > 0) { goto done; /* no conversion */ } task = text2task(raw_task); switch(task) { case stop_rsc: case start_rsc: case action_notify: case action_promote: case action_demote: break; case stopped_rsc: case started_rsc: case action_notified: case action_promoted: case action_demoted: task--; break; case monitor_rsc: case shutdown_crm: case stonith_node: task = no_action; break; default: crm_err("Unknown action: %s", raw_task); task = no_action; break; } if(task != no_action) { if(is_set(rsc->flags, pe_rsc_notify) && allow_notify) { uuid = generate_notify_key(rid, "confirmed-post", task2text(task+1)); } else { uuid = generate_op_key(rid, task2text(task+1), 0); } crm_debug_2("Converted %s -> %s", old_uuid, uuid); } done: if(uuid == NULL) { uuid = crm_strdup(old_uuid); } if(free_original) { crm_free(old_uuid); } crm_free(raw_task); crm_free(rid); return uuid; } void order_actions( action_t *lh_action, action_t *rh_action, enum pe_ordering order) { action_wrapper_t *wrapper = NULL; GListPtr list = NULL; static int load_stopped_strlen = 0; if (!load_stopped_strlen) { load_stopped_strlen = strlen(LOAD_STOPPED); } if (strncmp(lh_action->uuid, LOAD_STOPPED, load_stopped_strlen) == 0 || strncmp(rh_action->uuid, LOAD_STOPPED, load_stopped_strlen) == 0) { if (lh_action->node == NULL || rh_action->node == NULL || lh_action->node->details != rh_action->node->details) { return; } } crm_debug_3("Ordering Action %s before %s", lh_action->uuid, rh_action->uuid); log_action(LOG_DEBUG_4, "LH (order_actions)", lh_action, FALSE); log_action(LOG_DEBUG_4, "RH (order_actions)", rh_action, FALSE); /* Filter dups, otherwise update_action_states() has too much work to do */ slist_iter(after, action_wrapper_t, lh_action->actions_after, lpc, if(after->action == rh_action && (after->type & order)) { return; } ); crm_malloc0(wrapper, sizeof(action_wrapper_t)); wrapper->action = rh_action; wrapper->type = order; list = lh_action->actions_after; list = g_list_append(list, wrapper); lh_action->actions_after = list; wrapper = NULL; /* order |= pe_order_implies_right; */ /* order ^= pe_order_implies_right; */ crm_malloc0(wrapper, sizeof(action_wrapper_t)); wrapper->action = lh_action; wrapper->type = order; list = rh_action->actions_before; list = g_list_append(list, wrapper); rh_action->actions_before = list; } 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) { do_crm_log_unlikely(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: do_crm_log_unlikely(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: do_crm_log_unlikely(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) { do_crm_log_unlikely(log_level+1, "\t\t====== Preceding Actions"); slist_iter( other, action_wrapper_t, action->actions_before, lpc, log_action(log_level+1, "\t\t", other->action, FALSE); ); do_crm_log_unlikely(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); ); do_crm_log_unlikely(log_level+1, "\t\t====== End"); } else { do_crm_log_unlikely(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)); } } action_t *get_pseudo_op(const char *name, pe_working_set_t *data_set) { action_t *op = NULL; const char *op_s = name; GListPtr possible_matches = NULL; possible_matches = find_actions(data_set->actions, name, NULL); if(possible_matches != NULL) { if(g_list_length(possible_matches) > 1) { pe_warn("Action %s exists %d times", name, g_list_length(possible_matches)); } op = g_list_nth_data(possible_matches, 0); g_list_free(possible_matches); } else { op = custom_action(NULL, crm_strdup(op_s), op_s, NULL, TRUE, TRUE, data_set); op->pseudo = TRUE; op->runnable = TRUE; } return op; } gboolean can_run_any(GListPtr nodes) { if(nodes == NULL) { return FALSE; } slist_iter( node, node_t, nodes, lpc, if(can_run_resources(node) && node->weight >= 0) { return TRUE; } ); return FALSE; } +enum rsc_role_e +minimum_resource_state(resource_t *rsc, gboolean current) +{ + enum rsc_role_e min_role = RSC_ROLE_MAX; + + if(rsc->children) { + slist_iter( + child_rsc, resource_t, rsc->children, lpc, + enum rsc_role_e role = child_rsc->fns->state(child_rsc, current); + if(role < min_role) { + min_role = role; + } + ); + return min_role; + } + return rsc->fns->state(rsc, current); +} diff --git a/pengine/utils.h b/pengine/utils.h index b58a8fc948..5a78daa62d 100644 --- a/pengine/utils.h +++ b/pengine/utils.h @@ -1,71 +1,73 @@ /* * 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_AUTILS__H #define PENGINE_AUTILS__H /* 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); 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 rsc_to_node_t *rsc2node_new( const char *id, resource_t *rsc, int weight, node_t *node, pe_working_set_t *data_set); extern void pe_free_rsc_to_node(GListPtr constraints); extern void pe_free_ordering(GListPtr constraints); extern const char *ordering_type2text(enum pe_ordering type); extern gboolean rsc_colocation_new( const char *id, const char *node_attr, int score, resource_t *rsc_lh, resource_t *rsc_rh, const char *state_lh, const char *state_rh, pe_working_set_t *data_set); extern rsc_to_node_t *generate_location_rule( resource_t *rsc, xmlNode *location_rule, pe_working_set_t *data_set); extern gint sort_node_weight(gconstpointer a, gconstpointer b, gpointer data_set); extern gboolean can_run_resources(const node_t *node); extern gboolean native_assign_node(resource_t *rsc, GListPtr candidates, node_t *chosen, gboolean force); extern char *convert_non_atomic_task(char *old_uuid, resource_t *rsc, gboolean allow_notify, gboolean free_original); extern void order_actions(action_t *lh_action, action_t *rh_action, enum pe_ordering order); extern void log_action(unsigned int log_level, const char *pre_text, action_t *action, gboolean details); extern action_t *get_pseudo_op(const char *name, pe_working_set_t *data_set); extern gboolean can_run_any(GListPtr nodes); extern resource_t *find_compatible_child( resource_t *local_child, resource_t *rsc, enum rsc_role_e filter, gboolean current); +extern enum rsc_role_e minimum_resource_state(resource_t *rsc, gboolean current); + #define STONITH_UP "stonith_up" #define STONITH_DONE "stonith_complete" #define ALL_STOPPED "all_stopped" #define LOAD_STOPPED "load_stopped" #endif diff --git a/shell/modules/cibconfig.py b/shell/modules/cibconfig.py index de13f9932f..d66275c79c 100644 --- a/shell/modules/cibconfig.py +++ b/shell/modules/cibconfig.py @@ -1,2306 +1,2315 @@ # Copyright (C) 2008 Dejan Muhamedagic # # 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 # import sys import subprocess import copy import xml.dom.minidom import re from singletonmixin import Singleton from userprefs import Options, UserPrefs from vars import Vars from cliformat import * from utils import * from xmlutil import * from msg import * from parse import CliParser from clidisplay import CliDisplay from cibstatus import CibStatus from idmgmt import IdMgmt from ra import RAInfo def show_unrecognized_elems(doc): try: conf = doc.getElementsByTagName("configuration")[0] except: common_warn("CIB has no configuration element") return for topnode in conf.childNodes: if not is_element(topnode): continue if is_defaults(topnode): continue if not topnode.tagName in cib_topnodes: common_warn("unrecognized CIB element %s" % c.tagName) continue for c in topnode.childNodes: if not is_element(c): continue if not c.tagName in cib_object_map: common_warn("unrecognized CIB element %s" % c.tagName) # # object sets (enables operations on sets of elements) # def mkset_obj(*args): if args and args[0] == "xml": obj = lambda: CibObjectSetRaw(*args[1:]) else: obj = lambda: CibObjectSetCli(*args) return obj() class CibObjectSet(object): ''' Edit or display a set of cib objects. repr() for objects representation and save() used to store objects into internal structures are defined in subclasses. ''' def __init__(self, *args): self.obj_list = [] def _open_url(self,src): import urllib try: return urllib.urlopen(src) except: pass if src == "-": return sys.stdin try: return open(src) except: pass common_err("could not open %s" % src) return False def init_aux_lists(self): ''' Before edit, initialize two auxiliary lists which will hold a list of objects to be removed and a list of objects which were created. Then, we can create a new object list which will match the current state of affairs, i.e. the object set after the last edit. ''' self.remove_objs = copy.copy(self.obj_list) self.add_objs = [] def recreate_obj_list(self): ''' Recreate obj_list: remove deleted objects and add created objects ''' for obj in self.remove_objs: self.obj_list.remove(obj) self.obj_list += self.add_objs rmlist = [] for obj in self.obj_list: if obj.invalid: rmlist.append(obj) for obj in rmlist: self.obj_list.remove(obj) def edit_save(self,s,erase = False): ''' Save string s to a tmp file. Invoke editor to edit it. Parse/save the resulting file. In case of syntax error, allow user to reedit. If erase is True, erase the CIB first. If no changes are done, return silently. ''' tmp = str2tmp(s) if not tmp: return False filehash = hash(s) rc = False while True: if edit_file(tmp) != 0: break try: f = open(tmp,'r') except IOError, msg: common_err(msg) break s = ''.join(f) f.close() if hash(s) == filehash: # file unchanged rc = True break if erase: cib_factory.erase() if not self.save(s): if ask("Do you want to edit again?"): continue rc = True break try: os.unlink(tmp) except: pass return rc def edit(self): if options.batch: common_info("edit not allowed in batch mode") return False cli_display.set_no_pretty() s = self.repr() cli_display.reset_no_pretty() return self.edit_save(s) def filter_save(self,filter,s): ''' Pipe string s through a filter. Parse/save the output. If no changes are done, return silently. ''' rc,outp = filter_string(filter,s) if rc != 0: return False if hash(outp) == hash(s): return True return self.save(outp) def filter(self,filter): cli_display.set_no_pretty() s = self.repr(format = -1) cli_display.reset_no_pretty() return self.filter_save(filter,s) def save_to_file(self,fname): if fname == "-": f = sys.stdout else: if not options.batch and os.access(fname,os.F_OK): if not ask("File %s exists. Do you want to overwrite it?"%fname): return False try: f = open(fname,"w") except IOError, msg: common_err(msg) return False rc = True cli_display.set_no_pretty() s = self.repr() cli_display.reset_no_pretty() if s: f.write(s) f.write('\n') elif self.obj_list: rc = False if f != sys.stdout: f.close() return rc def show(self): s = self.repr() if not s: if self.obj_list: # objects could not be displayed return False else: return True page_string(s) def import_file(self,method,fname): if not cib_factory.is_cib_sane(): return False if method == "replace": if options.interactive and cib_factory.has_cib_changed(): if not ask("This operation will erase all changes. Do you want to proceed?"): return False cib_factory.erase() f = self._open_url(fname) if not f: return False s = ''.join(f) if f != sys.stdin: f.close() return self.save(s, method == "update") def repr(self): ''' Return a string with objects's representations (either CLI or XML). ''' return '' def save(self, s, update = False): ''' For each object: - try to find a corresponding object in obj_list - if (update and not found) or found: replace the object in the obj_list with the new object - if not found: create new See below for specific implementations. ''' pass def semantic_check(self): ''' Test objects for sanity. This is about semantics. ''' rc = 0 for obj in self.obj_list: rc |= obj.check_sanity() return rc def lookup_cli(self,cli_list): for obj in self.obj_list: if obj.matchcli(cli_list): return obj def lookup(self,xml_obj_type,obj_id): for obj in self.obj_list: if obj.match(xml_obj_type,obj_id): return obj def drop_remaining(self): 'Any remaining objects in obj_list are deleted.' l = [x.obj_id for x in self.remove_objs] return cib_factory.delete(*l) def get_comments(cli_list): if not cli_list: return [] last = cli_list[len(cli_list)-1] try: if last[0] == "comments": cli_list.pop() return last[1] except: pass return [] class CibObjectSetCli(CibObjectSet): ''' Edit or display a set of cib objects (using cli notation). ''' def __init__(self, *args): CibObjectSet.__init__(self, *args) self.obj_list = cib_factory.mkobj_list("cli",*args) def repr(self, format = 1): "Return a string containing cli format of all objects." if not self.obj_list: return '' return '\n'.join(obj.repr_cli(format = format) \ for obj in processing_sort_cli(self.obj_list)) def process(self, cli_list, update = False): ''' Create new objects or update existing ones. ''' myobj = obj = self.lookup_cli(cli_list) if update and not obj: obj = cib_factory.find_object_for_cli(cli_list) if obj: rc = cib_factory.update_from_cli(obj,cli_list) != False if myobj: self.remove_objs.remove(myobj) else: obj = cib_factory.create_from_cli(cli_list) rc = obj != None if rc: self.add_objs.append(obj) return rc def save(self, s, update = False): ''' Save a user supplied cli format configuration. On errors user is typically asked to review the configuration (for instance on editting). On syntax error (return code 1), no changes are done, but on semantic errors (return code 2), some changes did take place so object list must be updated properly. Finally, once syntax check passed, there's no way back, all changes are applied to the current configuration. TODO: Implement undo configuration changes. ''' l = [] rc = True err_buf.start_tmp_lineno() cp = CliParser() for cli_text in lines2cli(s): err_buf.incr_lineno() cli_list = cp.parse(cli_text) if cli_list: l.append(cli_list) elif cli_list == False: rc = False err_buf.stop_tmp_lineno() # we can't proceed if there was a syntax error, but we # can ask the user to fix problems if not rc: return rc self.init_aux_lists() if l: for cli_list in processing_sort_cli(l): if self.process(cli_list,update) == False: rc = False if not self.drop_remaining(): # this is tricky, we don't know what was removed! # it could happen that the user dropped a resource # which was running and therefore couldn't be removed rc = False self.recreate_obj_list() return rc cib_verify = "crm_verify -V -p" class CibObjectSetRaw(CibObjectSet): ''' Edit or display one or more CIB objects (XML). ''' def __init__(self, *args): CibObjectSet.__init__(self, *args) self.obj_list = cib_factory.mkobj_list("xml",*args) def repr(self, format = "ignored"): "Return a string containing xml of all objects." doc = cib_factory.objlist2doc(self.obj_list) s = doc.toprettyxml(user_prefs.xmlindent) doc.unlink() return s def repr_configure(self): ''' Return a string containing xml of configure and its children. ''' doc = cib_factory.objlist2doc(self.obj_list) conf_node = doc.getElementsByTagName("configuration")[0] s = conf_node.toprettyxml(user_prefs.xmlindent) doc.unlink() return s def process(self, node, update = False): if not cib_factory.is_cib_sane(): return False myobj = obj = self.lookup(node.tagName,node.getAttribute("id")) if update and not obj: obj = cib_factory.find_object_for_node(node) if obj: rc = cib_factory.update_from_node(obj,node) if myobj: self.remove_objs.remove(obj) else: new_obj = cib_factory.create_from_node(node) rc = new_obj != None if rc: self.add_objs.append(new_obj) return rc def save(self, s, update = False): try: doc = xml.dom.minidom.parseString(s) except xml.parsers.expat.ExpatError,msg: cib_parse_err(msg,s) return False rc = True sanitize_cib(doc) show_unrecognized_elems(doc) newnodes = get_interesting_nodes(doc,[]) self.init_aux_lists() if newnodes: for node in processing_sort(newnodes): if not self.process(node,update): rc = False if not self.drop_remaining(): rc = False doc.unlink() self.recreate_obj_list() return rc def verify(self): if not self.obj_list: return True cli_display.set_no_pretty() rc = pipe_string(cib_verify,self.repr(format = -1)) cli_display.reset_no_pretty() return rc in (0,1) def ptest(self, nograph, scores, utilization, verbosity): if not cib_factory.is_cib_sane(): return False if verbosity: ptest = "ptest -X -%s" % verbosity.upper() if scores: ptest = "%s -s" % ptest if utilization: ptest = "%s -U" % ptest if user_prefs.dotty and not nograph: fd,dotfile = mkstemp() ptest = "%s -D %s" % (ptest,dotfile) else: dotfile = None doc = cib_factory.objlist2doc(self.obj_list) cib = doc.childNodes[0] status = cib_status.get_status() if not status: common_err("no status section found") return False cib.appendChild(doc.importNode(status,1)) pipe_string(ptest,doc.toprettyxml()) doc.unlink() if dotfile: show_dot_graph(dotfile) vars.tmpfiles.append(dotfile) else: if not nograph: common_info("install graphviz to see a transition graph") return True # # XML generate utilities # def set_id(node,oldnode,id_hint,id_required = True): ''' Set the id attribute for the node. Procedure: - if the node already contains "id", keep it - if the old node contains "id", copy that - if neither is true, then create a new one using id_hint (exception: if not id_required, then no new id is generated) Finally, save the new id in id_store. ''' old_id = None new_id = node.getAttribute("id") if oldnode and oldnode.getAttribute("id"): old_id = oldnode.getAttribute("id") if not new_id: new_id = old_id if not new_id: if id_required: new_id = id_store.new(node,id_hint) else: id_store.save(new_id) if new_id: node.setAttribute("id",new_id) if oldnode and old_id == new_id: set_id_used_attr(oldnode) def mkxmlsimple(e,oldnode,id_hint): ''' Create an xml node from the (name,dict) pair. The name is the name of the element. The dict contains a set of attributes. ''' node = cib_factory.createElement(e[0]) for n,v in e[1]: if n == "$children": # this one's skipped continue if n == "operation": v = v.lower() if n.startswith('$'): n = n.lstrip('$') if (type(v) != type('') and type(v) != type(u'')) \ or v: # skip empty strings node.setAttribute(n,v) id_ref = node.getAttribute("id-ref") if id_ref: id_ref_2 = cib_factory.resolve_id_ref(e[0],id_ref) node.setAttribute("id-ref",id_ref_2) else: set_id(node,lookup_node(node,oldnode),id_hint) return node def mkxmlnvpairs(e,oldnode,id_hint): ''' Create xml from the (name,dict) pair. The name is the name of the element. The dict contains a set of nvpairs. Stuff such as instance_attributes. NB: Other tags not containing nvpairs are fine if the dict is empty. ''' - node = cib_factory.createElement(e[0]) + xml_node_type = e[0] in vars.defaults_tags and "meta_attributes" or e[0] + node = cib_factory.createElement(xml_node_type) match_node = lookup_node(node,oldnode) #if match_node: #print "found nvpairs set:",match_node.tagName,match_node.getAttribute("id") id_ref = find_value(e[1],"$id-ref") if id_ref: id_ref_2 = cib_factory.resolve_id_ref(e[0],id_ref) node.setAttribute("id-ref",id_ref_2) if e[0] != "operations": return node # id_ref is the only attribute (if not operations) e[1].remove(["$id-ref",id_ref]) v = find_value(e[1],"$id") if v: node.setAttribute("id",v) e[1].remove(["$id",v]) + elif e[0] in vars.nvset_cli_names: + node.setAttribute("id",id_hint) else: if e[0] == "operations": # operations don't need no id set_id(node,match_node,id_hint,id_required = False) else: set_id(node,match_node,id_hint) try: subpfx = vars.subpfx_list[e[0]] except: subpfx = '' subpfx = subpfx and "%s_%s" % (id_hint,subpfx) or id_hint nvpair_pfx = node.getAttribute("id") or subpfx for n,v in e[1]: nvpair = cib_factory.createElement("nvpair") node.appendChild(nvpair) nvpair.setAttribute("name",n) if v != None: nvpair.setAttribute("value",v) set_id(nvpair,lookup_node(nvpair,match_node),nvpair_pfx) return node def mkxmlop(e,oldnode,id_hint): ''' Create an operation xml from the (name,dict) pair. ''' node = cib_factory.createElement(e[0]) inst_attr = [] for n,v in e[1]: if n in olist(vars.req_op_attributes + vars.op_attributes): node.setAttribute(n,v) else: inst_attr.append([n,v]) tmp = cib_factory.createElement("operations") oldops = lookup_node(tmp,oldnode) # first find old operations oldop = lookup_node(node,oldops) set_id(node,oldop,id_hint) if inst_attr: e = ["instance_attributes",inst_attr] nia = mkxmlnvpairs(e,oldop,node.getAttribute("id")) node.appendChild(nia) return node def mkxmldate(e,oldnode,id_hint): ''' Create a date_expression xml from the (name,dict) pair. ''' node = cib_factory.createElement(e[0]) operation = find_value(e[1],"operation").lower() node.setAttribute("operation", operation) old_date = lookup_node(node,oldnode) # first find old date element set_id(node,old_date,id_hint) date_spec_attr = [] for n,v in e[1]: if n in olist(vars.date_ops) or n == "operation": continue elif n in vars.in_range_attrs: node.setAttribute(n,v) else: date_spec_attr.append([n,v]) if not date_spec_attr: return node elem = operation == "date_spec" and "date_spec" or "duration" tmp = cib_factory.createElement(elem) old_date_spec = lookup_node(tmp,old_date) # first find old date element set_id(tmp,old_date_spec,id_hint) for n,v in date_spec_attr: tmp.setAttribute(n,v) node.appendChild(tmp) return node def mkxmlrsc_set(e,oldnode,id_hint): ''' Create a resource_set xml from the (name,dict) pair. ''' node = cib_factory.createElement(e[0]) old_rsc_set = lookup_node(node,oldnode) # first find old date element set_id(node,old_rsc_set,id_hint) for ref in e[1]: if ref[0] == "resource_ref": ref_node = cib_factory.createElement(ref[0]) ref_node.setAttribute(ref[1][0],ref[1][1]) node.appendChild(ref_node) elif ref[0] in ("sequential", "action", "role"): node.setAttribute(ref[0], ref[1]) return node conv_list = { "params": "instance_attributes", "meta": "meta_attributes", "property": "cluster_property_set", - "rsc_defaults": "meta_attributes", - "op_defaults": "meta_attributes", + "rsc_defaults": "rsc_defaults", + "op_defaults": "op_defaults", "attributes": "instance_attributes", "utilization": "utilization", "operations": "operations", "op": "op", } def mkxmlnode(e,oldnode,id_hint): ''' Create xml from the (name,dict) pair. The name is the name of the element. The dict contains either a set of nvpairs or a set of attributes. The id is either generated or copied if found in the provided xml. Stuff such as instance_attributes. ''' if e[0] in conv_list: e[0] = conv_list[e[0]] - if e[0] in ("instance_attributes","meta_attributes","operations","cluster_property_set","utilization"): + if e[0] in ("instance_attributes","meta_attributes","operations","rsc_defaults","op_defaults","cluster_property_set","utilization"): return mkxmlnvpairs(e,oldnode,id_hint) elif e[0] == "op": return mkxmlop(e,oldnode,id_hint) elif e[0] == "date_expression": return mkxmldate(e,oldnode,id_hint) elif e[0] == "resource_set": return mkxmlrsc_set(e,oldnode,id_hint) else: return mkxmlsimple(e,oldnode,id_hint) def set_nvpair(set_node,name,value): n_id = set_node.getAttribute("id") for c in set_node.childNodes: if is_element(c) and c.getAttribute("name") == name: c.setAttribute("value",value) return np = cib_factory.createElement("nvpair") np.setAttribute("name",name) np.setAttribute("value",value) new_id = id_store.new(np,n_id) np.setAttribute("id",new_id) set_node.appendChild(np) # # cib element classes (CibObject the parent class) # class CibObject(object): ''' The top level object of the CIB. Resources and constraints. ''' state_fmt = "%16s %-8s%-8s%-8s%-8s%-8s%-4s" set_names = {} def __init__(self,xml_obj_type,obj_id = None): if not xml_obj_type in cib_object_map: unsupported_err(xml_obj_type) return self.obj_type = cib_object_map[xml_obj_type][0] self.parent_type = cib_object_map[xml_obj_type][2] self.xml_obj_type = xml_obj_type self.origin = "" # where did it originally come from? self.nocli = False # we don't support this one self.nocli_warn = True # don't issue warnings all the time self.updated = False # was the object updated self.invalid = False # the object has been invalidated (removed) self.moved = False # the object has been moved (from/to a container) self.recreate = False # constraints to be recreated self.parent = None # object superior (group/clone/ms) self.children = [] # objects inferior if obj_id: if not self.mknode(obj_id): self = None # won't do :( else: self.obj_id = None self.node = None def dump_state(self): 'Print object status' print self.state_fmt % \ (self.obj_id,self.origin,self.updated,self.moved,self.invalid, \ self.parent and self.parent.obj_id or "", \ len(self.children)) def repr_cli_xml(self,node,format): h = cli_display.keyword("xml") l = node.toprettyxml('\t').split('\n') l = [x for x in l if x] # drop empty lines if format > 0: return "%s %s" % (h,' \\\n'.join(l)) else: return "%s %s" % (h,''.join(l)) def repr_cli(self,node = None,format = 1): ''' CLI representation for the node. repr_cli_head and repr_cli_child in subclasess. ''' if not node: node = self.node if self.nocli: return self.repr_cli_xml(node,format) l = [] head_s = self.repr_cli_head(node) if not head_s: # everybody must have a head return None comments = [] l.append(head_s) cli_add_description(node,l) for c in node.childNodes: if is_comment(c): comments.append(c.data) continue if not is_element(c): continue s = self.repr_cli_child(c,format) if s: l.append(s) return self.cli_format(l,comments,format) def repr_cli_child(self,c,format): if c.tagName in self.set_names: return "%s %s" % \ (cli_display.keyword(self.set_names[c.tagName]), \ cli_pairs(nvpairs2list(c))) def cli2node(self,cli,oldnode = None): ''' Convert CLI representation to a DOM node. Defined in subclasses. ''' cli_list = mk_cli_list(cli) if not cli_list: return None if not oldnode: if self.obj_type == "property": oldnode = cib_factory.topnode[cib_object_map[self.xml_obj_type][2]] else: oldnode = self.node comments = get_comments(cli_list) node = self.cli_list2node(cli_list,oldnode) if comments and node: stuff_comments(node,comments) return node def cli_format(self,l,comments,format): ''' Format and add comment (if any). ''' s = cli_format(l,format) cs = '\n'.join(comments) return (comments and format >=0) and '\n'.join([cs,s]) or s def move_comments(self): ''' Move comments to the top of the node. ''' l = [] firstelem = None for n in self.node.childNodes: if is_comment(n): if firstelem: l.append(n) else: if not firstelem and is_element(n): firstelem = n for comm_node in l: common_debug("move comm %s" % comm_node.toprettyxml()) self.node.insertBefore(comm_node, firstelem) common_debug("obj %s node: %s" % (self.obj_id,self.node.toprettyxml())) def mknode(self,obj_id): if not cib_factory.is_cib_sane(): return False if id_store.id_in_use(obj_id): return False if self.xml_obj_type in vars.defaults_tags: tag = "meta_attributes" else: tag = self.xml_obj_type self.node = cib_factory.createElement(tag) self.obj_id = obj_id self.node.setAttribute("id",self.obj_id) self.origin = "user" return True def can_be_renamed(self): ''' Return False if this object can't be renamed. ''' if is_rsc_running(self.obj_id): common_err("cannot rename a running resource (%s)" % self.obj_id) return False if not is_live_cib() and self.node.tagName == "node": common_err("cannot rename nodes") return False return True def attr_exists(self,attr): if not attr in self.node.attributes.keys(): no_attribute_err(attr,self.obj_id) return False return True def cli_use_validate(self): ''' Check validity of the object, as we know it. It may happen that we don't recognize a construct, but that the object is still valid for the CRM. In that case, the object is marked as "CLI read only", i.e. we will neither convert it to CLI nor try to edit it in that format. The validation procedure: we convert xml to cli and then back to xml. If the two xml representations match then we can understand the xml. ''' if not self.node: return True if not self.attr_exists("id"): return False cli_display.set_no_pretty() cli_text = self.repr_cli(format = 0) cli_display.reset_no_pretty() if not cli_text: return False common_debug("clitext: %s" % cli_text) xml2 = self.cli2node(cli_text) if not xml2: return False rc = xml_cmp(self.node, xml2, show = True) xml2.unlink() return rc def check_sanity(self): ''' Right now, this is only for primitives. And groups/clones/ms and cluster properties. ''' return 0 def matchcli(self,cli_list): head = cli_list[0] return self.obj_type == head[0] \ and self.obj_id == find_value(head[1],"id") def match(self,xml_obj_type,obj_id): return self.xml_obj_type == xml_obj_type and self.obj_id == obj_id def obj_string(self): return "%s:%s" % (self.obj_type,self.obj_id) def reset_updated(self): self.updated = False self.moved = False self.recreate = False for child in self.children: child.reset_updated() def propagate_updated(self): if self.parent: self.parent.updated = self.updated self.parent.propagate_updated() def top_parent(self): '''Return the top parent or self''' if self.parent: return self.parent.top_parent() else: return self def find_child_in_node(self,child): for c in self.node.childNodes: if not is_element(c): continue if c.tagName == child.obj_type and \ c.getAttribute("id") == child.obj_id: return c return None def filter(self,*args): "Filter objects." if not args: return True if args[0] == "NOOBJ": return False if args[0] == "changed": return self.updated or self.origin == "user" if args[0].startswith("type:"): return self.obj_type == args[0][5:] return self.obj_id in args def mk_cli_list(cli): 'Sometimes we get a string and sometimes a list.' if type(cli) == type('') or type(cli) == type(u''): cp = CliParser() # what follows looks strange, but the last string actually matters # the previous ones may be comments and are collected by the parser for s in lines2cli(cli): cli_list = cp.parse(s) return cli_list else: return cli class CibNode(CibObject): ''' Node and node's attributes. ''' set_names = { "instance_attributes": "attributes", "utilization": "utilization", } def repr_cli_head(self,node): obj_type = vars.cib_cli_map[node.tagName] node_id = node.getAttribute("id") uname = node.getAttribute("uname") s = cli_display.keyword(obj_type) if node_id != uname: s = '%s $id="%s"' % (s, node_id) s = '%s %s' % (s, cli_display.id(uname)) type = node.getAttribute("type") if type != vars.node_default_type: s = '%s:%s' % (s, type) return s def cli_list2node(self,cli_list,oldnode): head = copy.copy(cli_list[0]) head[0] = backtrans[head[0]] obj_id = find_value(head[1],"$id") if not obj_id: obj_id = find_value(head[1],"uname") if not obj_id: return None type = find_value(head[1],"type") if not type: type = vars.node_default_type head[1].append(["type",type]) headnode = mkxmlsimple(head,cib_factory.topnode[cib_object_map[self.xml_obj_type][2]],'node') id_hint = headnode.getAttribute("id") for e in cli_list[1:]: n = mkxmlnode(e,oldnode,id_hint) headnode.appendChild(n) remove_id_used_attributes(cib_factory.topnode[cib_object_map[self.xml_obj_type][2]]) return headnode def get_ra(node): ra_type = node.getAttribute("type") ra_class = node.getAttribute("class") ra_provider = node.getAttribute("provider") return RAInfo(ra_class,ra_type,ra_provider) class CibPrimitive(CibObject): ''' Primitives. ''' set_names = { "instance_attributes": "params", "meta_attributes": "meta", "utilization": "utilization", } def repr_cli_head(self,node): obj_type = vars.cib_cli_map[node.tagName] node_id = node.getAttribute("id") ra_type = node.getAttribute("type") ra_class = node.getAttribute("class") ra_provider = node.getAttribute("provider") s1 = s2 = '' if ra_class: s1 = "%s:"%ra_class if ra_provider: s2 = "%s:"%ra_provider s = cli_display.keyword(obj_type) id = cli_display.id(node_id) return "%s %s %s" % (s, id, ''.join((s1,s2,ra_type))) def repr_cli_child(self,c,format): if c.tagName in self.set_names: return "%s %s" % \ (cli_display.keyword(self.set_names[c.tagName]), \ cli_pairs(nvpairs2list(c))) elif c.tagName == "operations": return cli_operations(c,format) def cli_list2node(self,cli_list,oldnode): ''' Convert a CLI description to DOM node. Try to preserve as many ids as possible in case there's an old XML version. ''' head = copy.copy(cli_list[0]) head[0] = backtrans[head[0]] headnode = mkxmlsimple(head,oldnode,'rsc') id_hint = headnode.getAttribute("id") operations = None for e in cli_list[1:]: n = mkxmlnode(e,oldnode,id_hint) if keyword_cmp(e[0], "operations"): operations = n if not keyword_cmp(e[0], "op"): headnode.appendChild(n) else: if not operations: operations = mkxmlnode(["operations",{}],oldnode,id_hint) headnode.appendChild(operations) operations.appendChild(n) remove_id_used_attributes(oldnode) return headnode def add_operation(self,cli_list): # check if there is already an op with the same interval comments = get_comments(cli_list) head = copy.copy(cli_list[0]) name = find_value(head[1], "name") interval = find_value(head[1], "interval") if find_operation(self.node,name,interval): common_err("%s already has a %s op with interval %s" % \ (self.obj_id, name, interval)) return None # drop the rsc attribute head[1].remove(["rsc",self.obj_id]) # create an xml node mon_node = mkxmlsimple(head, None, self.obj_id) # get the place to append it to try: op_node = self.node.getElementsByTagName("operations")[0] except: op_node = cib_factory.createElement("operations") self.node.appendChild(op_node) op_node.appendChild(mon_node) if comments and self.node: stuff_comments(self.node,comments) # the resource is updated self.updated = True self.propagate_updated() return self def check_sanity(self): ''' Check operation timeouts and if all required parameters are defined. ''' if not self.node: # eh? common_err("%s: no xml (strange)" % self.obj_id) return user_prefs.get_check_rc() rc3 = sanity_check_meta(self.obj_id,self.node,vars.rsc_meta_attributes) ra = get_ra(self.node) if not ra.mk_ra_node(): # no RA found? if cib_factory.is_asymm_cluster(): return rc3 ra.error("no such resource agent") return user_prefs.get_check_rc() params = [] for c in self.node.childNodes: if not is_element(c): continue if c.tagName == "instance_attributes": params += nvpairs2list(c) rc1 = ra.sanity_check_params(self.obj_id, params) actions = {} for c in self.node.childNodes: if not is_element(c): continue if c.tagName == "operations": for c2 in c.childNodes: if is_element(c2) and c2.tagName == "op": op,pl = op2list(c2) if op: actions[op] = pl default_timeout = get_default_timeout() rc2 = ra.sanity_check_ops(self.obj_id, actions, default_timeout) return rc1 | rc2 | rc3 class CibContainer(CibObject): ''' Groups and clones and ms. ''' set_names = { "instance_attributes": "params", "meta_attributes": "meta", } def repr_cli_head(self,node): try: obj_type = vars.cib_cli_map[node.tagName] except: unsupported_err(node.tagName) return None node_id = node.getAttribute("id") children = [] for c in node.childNodes: if not is_element(c): continue if (obj_type == "group" and is_primitive(c)) or \ is_child_rsc(c): children.append(cli_display.rscref(c.getAttribute("id"))) elif obj_type in vars.clonems_tags and is_child_rsc(c): children.append(cli_display.rscref(c.getAttribute("id"))) s = cli_display.keyword(obj_type) id = cli_display.id(node_id) return "%s %s %s" % (s, id, ' '.join(children)) def cli_list2node(self,cli_list,oldnode): head = copy.copy(cli_list[0]) head[0] = backtrans[head[0]] headnode = mkxmlsimple(head,oldnode,'grp') id_hint = headnode.getAttribute("id") for e in cli_list[1:]: n = mkxmlnode(e,oldnode,id_hint) headnode.appendChild(n) v = find_value(head[1],"$children") if v: for child_id in v: obj = cib_factory.find_object(child_id) if obj: n = obj.node.cloneNode(1) headnode.appendChild(n) else: no_object_err(child_id) remove_id_used_attributes(oldnode) return headnode def check_sanity(self): ''' Check meta attributes. ''' if not self.node: # eh? common_err("%s: no xml (strange)" % self.obj_id) return user_prefs.get_check_rc() if self.obj_type == "group": l = vars.rsc_meta_attributes elif self.obj_type == "clone": l = vars.clone_meta_attributes elif self.obj_type == "ms": l = vars.clone_meta_attributes + vars.ms_meta_attributes rc = sanity_check_nvpairs(self.obj_id,self.node,l) return rc class CibLocation(CibObject): ''' Location constraint. ''' def repr_cli_head(self,node): obj_type = vars.cib_cli_map[node.tagName] node_id = node.getAttribute("id") rsc = cli_display.rscref(node.getAttribute("rsc")) s = cli_display.keyword(obj_type) id = cli_display.id(node_id) s = "%s %s %s"%(s,id,rsc) pref_node = node.getAttribute("node") score = cli_display.score(get_score(node)) if pref_node: return "%s %s %s" % (s,score,pref_node) else: return s def repr_cli_child(self,c,format): if c.tagName == "rule": return "%s %s" % \ (cli_display.keyword("rule"), cli_rule(c)) def cli_list2node(self,cli_list,oldnode): head = copy.copy(cli_list[0]) head[0] = backtrans[head[0]] headnode = mkxmlsimple(head,oldnode,'location') id_hint = headnode.getAttribute("id") oldrule = None for e in cli_list[1:]: if e[0] in ("expression","date_expression"): n = mkxmlnode(e,oldrule,id_hint) else: n = mkxmlnode(e,oldnode,id_hint) if keyword_cmp(e[0], "rule"): add_missing_attr(n) rule = n headnode.appendChild(n) oldrule = lookup_node(rule,oldnode,location_only=True) else: rule.appendChild(n) remove_id_used_attributes(oldnode) return headnode class CibSimpleConstraint(CibObject): ''' Colocation and order constraints. ''' def repr_cli_head(self,node): obj_type = vars.cib_cli_map[node.tagName] node_id = node.getAttribute("id") s = cli_display.keyword(obj_type) id = cli_display.id(node_id) score = cli_display.score(get_score(node)) if node.getElementsByTagName("resource_set"): col = rsc_set_constraint(node,obj_type) else: col = two_rsc_constraint(node,obj_type) if not col: return None symm = node.getAttribute("symmetrical") if symm: col.append("symmetrical=%s"%symm) return "%s %s %s %s" % (s,id,score,' '.join(col)) def repr_cli_child(self,c,format): pass # no children here def cli_list2node(self,cli_list,oldnode): head = copy.copy(cli_list[0]) head[0] = backtrans[head[0]] headnode = mkxmlsimple(head,oldnode,'') id_hint = headnode.getAttribute("id") for e in cli_list[1:]: # if more than one element, it's a resource set n = mkxmlnode(e,oldnode,id_hint) headnode.appendChild(n) remove_id_used_attributes(oldnode) return headnode class CibProperty(CibObject): ''' Cluster properties. ''' def repr_cli_head(self,node): return '%s $id="%s"' % \ (cli_display.keyword(self.obj_type), node.getAttribute("id")) def repr_cli_child(self,c,format): name = c.getAttribute("name") if "value" in c.attributes.keys(): value = c.getAttribute("value") else: value = None return nvpair_format(name,value) def cli_list2node(self,cli_list,oldnode): head = copy.copy(cli_list[0]) head[0] = backtrans[head[0]] obj_id = find_value(head[1],"$id") if not obj_id: obj_id = cib_object_map[self.xml_obj_type][3] headnode = mkxmlnode(head,oldnode,obj_id) remove_id_used_attributes(oldnode) return headnode def matchcli(self,cli_list): head = cli_list[0] - return self.obj_type == head[0] \ - and self.obj_id == find_value(head[1],"$id") + if self.obj_type != head[0]: + return False + # if no id specified return True + # (match the first of a kind) + if not find_value(head[1],"$id"): + return True + return self.obj_id == find_value(head[1],"$id") def check_sanity(self): ''' Match properties with PE metadata. ''' if not self.node: # eh? common_err("%s: no xml (strange)" % self.obj_id) return user_prefs.get_check_rc() l = [] if self.obj_type == "property": l = get_pe_property_list() + get_crmd_property_list() l += ("dc-version","cluster-infrastructure","last-lrm-refresh") elif self.obj_type == "op_defaults": l = vars.op_attributes elif self.obj_type == "rsc_defaults": l = vars.rsc_meta_attributes rc = sanity_check_nvpairs(self.obj_id,self.node,l) return rc # ################################################################ # # cib factory # cib_piped = "cibadmin -p" def get_default_timeout(): t = cib_factory.get_op_default("timeout") if t: return t t = cib_factory.get_property("default-action-timeout") if t: return t if not vars.pe_metadata: vars.pe_metadata = RAInfo("pengine","metadata") if not vars.pe_metadata: return 0 return vars.pe_metadata.param_default("default-action-timeout") def get_pe_property_list(): if not vars.pe_metadata: vars.pe_metadata = RAInfo("pengine","metadata") if not vars.pe_metadata: return [] return vars.pe_metadata.params().keys() def get_crmd_property_list(): if not vars.crmd_metadata: vars.crmd_metadata = RAInfo("crmd","metadata") if not vars.crmd_metadata: return [] return vars.crmd_metadata.params().keys() # xml -> cli translations (and classes) cib_object_map = { "node": ( "node", CibNode, "nodes" ), "primitive": ( "primitive", CibPrimitive, "resources" ), "group": ( "group", CibContainer, "resources" ), "clone": ( "clone", CibContainer, "resources" ), "master": ( "ms", CibContainer, "resources" ), "rsc_location": ( "location", CibLocation, "constraints" ), "rsc_colocation": ( "colocation", CibSimpleConstraint, "constraints" ), "rsc_order": ( "order", CibSimpleConstraint, "constraints" ), "cluster_property_set": ( "property", CibProperty, "crm_config", "cib-bootstrap-options" ), "rsc_defaults": ( "rsc_defaults", CibProperty, "rsc_defaults", "rsc-options" ), "op_defaults": ( "op_defaults", CibProperty, "op_defaults", "op-options" ), } backtrans = odict() # generate a translation cli -> tag for key in cib_object_map: backtrans[cib_object_map[key][0]] = key cib_topnodes = [] # get a list of parents for key in cib_object_map: if not cib_object_map[key][2] in cib_topnodes: cib_topnodes.append(cib_object_map[key][2]) def can_migrate(node): for c in node.childNodes: if not is_element(c) or c.tagName != "meta_attributes": continue pl = nvpairs2list(c) if find_value(pl,"allow-migrate") == "true": return True return False cib_upgrade = "cibadmin --upgrade --force" class CibFactory(Singleton): ''' Juggle with CIB objects. See check_structure below for details on the internal cib representation. ''' shadowcmd = ">/dev/null 1: common_warn("%s contains more than one %s, using first" % \ (obj.obj_id,attr_list_type)) id = node_l[0].getAttribute("id") if not id: common_err("%s reference not found" % id_ref) return id_ref # hope that user will fix that return id # verify if id_ref exists node_l = self.doc.getElementsByTagName(attr_list_type) for node in node_l: if node.getAttribute("id") == id_ref: return id_ref common_err("%s reference not found" % id_ref) return id_ref # hope that user will fix that def _get_attr_value(self,obj_type,attr): if not self.is_cib_sane(): return None for obj in self.cib_objects: if obj.obj_type == obj_type and obj.node: pl = nvpairs2list(obj.node) v = find_value(pl, attr) if v: return v return None def get_property(self,property): ''' Get the value of the given cluster property. ''' return self._get_attr_value("property",property) def get_op_default(self,attr): ''' Get the value of the attribute from op_defaults. ''' return self._get_attr_value("op_defaults",attr) def is_asymm_cluster(self): symm = self.get_property("symmetric-cluster") return symm and symm != "true" def new_object(self,obj_type,obj_id): "Create a new object of type obj_type." if id_store.id_in_use(obj_id): return None for xml_obj_type,v in cib_object_map.items(): if v[0] == obj_type: obj = v[1](xml_obj_type,obj_id) if obj.obj_id: return obj else: return None return None def mkobj_list(self,mode,*args): obj_list = [] for obj in self.cib_objects: f = lambda: obj.filter(*args) if not f(): continue if mode == "cli" and obj.nocli and obj.nocli_warn: obj.nocli_warn = False obj_cli_warn(obj.obj_id) obj_list.append(obj) return obj_list def is_cib_empty(self): return not self.mkobj_list("cli","type:primitive") def has_cib_changed(self): return self.mkobj_list("xml","changed") or self.remove_queue def verify_constraints(self,node): ''' Check if all resources referenced in a constraint exist ''' rc = True constraint_id = node.getAttribute("id") for obj_id in referenced_resources(node): if not self.find_object(obj_id): constraint_norefobj_err(constraint_id,obj_id) rc = False return rc def verify_rsc_children(self,node): ''' Check prerequisites: a) all children must exist b) no child may have other parent than me (or should we steal children?) c) there may not be duplicate children ''' obj_id = node.getAttribute("id") if not obj_id: common_err("element %s has no id" % node.tagName) return False try: obj_type = cib_object_map[node.tagName][0] except: common_err("element %s (%s) not recognized"%(node.tagName,obj_id)) return False c_ids = get_rsc_children_ids(node) if not c_ids: return True rc = True c_dict = {} for child_id in c_ids: if not self.verify_child(child_id,obj_type,obj_id): rc = False if child_id in c_dict: common_err("in group %s child %s listed more than once"%(obj_id,child_id)) rc = False c_dict[child_id] = 1 return rc def verify_child(self,child_id,obj_type,obj_id): 'Check if child exists and obj_id is (or may become) its parent.' child = self.find_object(child_id) if not child: no_object_err(child_id) return False if child.parent and child.parent.obj_id != obj_id: common_err("%s already in use at %s"%(child_id,child.parent.obj_id)) return False if obj_type == "group" and child.obj_type != "primitive": common_err("a group may contain only primitives; %s is %s"%(child_id,child.obj_type)) return False if not child.obj_type in vars.children_tags: common_err("%s may contain a primitive or a group; %s is %s"%(obj_type,child_id,child.obj_type)) return False return True def verify_element(self,node): ''' Can we create this object given its CLI representation. This is not about syntax, we're past that, but about semantics. Right now we check if the children, if any, are fit for the parent. And if this is a constraint, if all referenced resources are present. ''' rc = True if not self.verify_rsc_children(node): rc = False if not self.verify_constraints(node): rc = False return rc def create_object(self,*args): return self.create_from_cli(CliParser().parse(list(args))) != None def set_property_cli(self,cli_list): comments = get_comments(cli_list) head_pl = cli_list[0] obj_type = head_pl[0].lower() pset_id = find_value(head_pl[1],"$id") if pset_id: head_pl[1].remove(["$id",pset_id]) else: pset_id = cib_object_map[backtrans[obj_type]][3] obj = self.find_object(pset_id) if not obj: if not is_id_valid(pset_id): invalid_id_err(pset_id) return None obj = self.new_object(obj_type,pset_id) if not obj: return None self.topnode[obj.parent_type].appendChild(obj.node) obj.origin = "user" self.cib_objects.append(obj) for n,v in head_pl[1]: set_nvpair(obj.node,n,v) if comments and obj.node: stuff_comments(obj.node,comments) obj.updated = True return obj def add_op(self,cli_list): '''Add an op to a primitive.''' head = cli_list[0] # does the referenced primitive exist rsc_id = find_value(head[1],"rsc") rsc_obj = self.find_object(rsc_id) if not rsc_obj: no_object_err(rsc_id) return None if rsc_obj.obj_type != "primitive": common_err("%s is not a primitive" % rsc_id) return None return rsc_obj.add_operation(cli_list) def create_from_cli(self,cli): 'Create a new cib object from the cli representation.' cli_list = mk_cli_list(cli) if not cli_list: return None head = cli_list[0] obj_type = head[0].lower() obj_id = find_value(head[1],"id") if obj_id and not is_id_valid(obj_id): invalid_id_err(obj_id) return None if len(cli_list) >= 2 and cli_list[1][0] == "raw": doc = xml.dom.minidom.parseString(cli_list[1][1]) return self.create_from_node(doc.childNodes[0]) if obj_type in olist(vars.nvset_cli_names): return self.set_property_cli(cli_list) if obj_type == "op": return self.add_op(cli_list) if obj_type == "node": obj = self.find_object(obj_id) # make an exception and allow updating nodes if obj: self.merge_from_cli(obj,cli_list) return obj obj = self.new_object(obj_type,obj_id) if not obj: return None node = obj.cli2node(cli_list) return self.add_element(obj, node) def update_from_cli(self,obj,cli_list): 'Update element from the cli intermediate.' id_store.remove_xml(obj.node) if len(cli_list) >= 2 and cli_list[1][0] == "raw": doc = xml.dom.minidom.parseString(cli_list[1][1]) id_store.store_xml(doc.childNodes[0]) return self.update_element(obj,doc.childNodes[0]) return self.update_element(obj,obj.cli2node(cli_list)) def update_from_node(self,obj,node): 'Update element from a doc node.' id_store.replace_xml(obj.node,node) return self.update_element(obj,node) def update_element(self,obj,newnode): 'Update element from a doc node.' if not newnode: return False if not self.is_cib_sane(): id_store.replace_xml(newnode,obj.node) return False oldnode = obj.node if xml_cmp(oldnode,newnode): newnode.unlink() return True # the new and the old versions are equal obj.node = newnode if not self.test_element(obj,newnode): id_store.replace_xml(newnode,oldnode) obj.node = oldnode newnode.unlink() return False obj.node = self.replaceNode(newnode,oldnode) obj.nocli = False # try again after update self.adjust_children(obj) if not obj.cli_use_validate(): obj.nocli_warn = True obj.nocli = True oldnode.unlink() obj.updated = True obj.propagate_updated() return True def merge_from_cli(self,obj,cli_list): node = obj.cli2node(cli_list) if not node: return rc = merge_nodes(obj.node, node) if rc: obj.updated = True obj.propagate_updated() def update_moved(self,obj): 'Updated the moved flag. Mark affected constraints.' obj.moved = not obj.moved if obj.moved: for c_obj in self.related_constraints(obj): c_obj.recreate = True def adjust_children(self,obj): ''' All stuff children related: manage the nodes of children, update the list of children for the parent, update parents in the children. ''' new_children_ids = get_rsc_children_ids(obj.node) if not new_children_ids: return old_children = obj.children obj.children = [self.find_object(x) for x in new_children_ids] self._relink_orphans_to_top(old_children,obj.children) self._update_children(obj) def _relink_child_to_top(self,obj): 'Relink a child to the top node.' obj.node.parentNode.removeChild(obj.node) self.topnode[obj.parent_type].appendChild(obj.node) if obj.origin == "cib": self.update_moved(obj) obj.parent = None def _update_children(self,obj): '''For composite objects: update all children nodes. ''' # unlink all and find them in the new node for child in obj.children: oldnode = child.node child.node = obj.find_child_in_node(child) if child.children: # and children of children self._update_children(child) rmnode(oldnode) if not child.parent and child.origin == "cib": self.update_moved(child) if child.parent and child.parent != obj: child.parent.updated = True # the other parent updated child.parent = obj def _relink_orphans_to_top(self,old_children,new_children): "New orphans move to the top level for the object type." for child in old_children: if child not in new_children: self._relink_child_to_top(child) def test_element(self,obj,node): if not node.getAttribute("id"): return False - if not self.verify_element(node): - return False + if not obj.xml_obj_type in vars.defaults_tags: + if not self.verify_element(node): + return False if user_prefs.is_check_always() \ and obj.check_sanity() > 1: return False return True def update_links(self,obj): ''' Update the structure links for the object (obj.children, obj.parent). Update also the dom nodes, if necessary. ''' obj.children = [] if obj.obj_type not in vars.container_tags: return for c in obj.node.childNodes: if is_child_rsc(c): child = self.find_object_for_node(c) if not child: missing_obj_err(c) continue child.parent = obj obj.children.append(child) if not c.isSameNode(child.node): rmnode(child.node) child.node = c def add_element(self,obj,node): obj.node = node obj.obj_id = node.getAttribute("id") if not self.test_element(obj, node): id_store.remove_xml(node) node.unlink() return None common_debug("append child %s to %s" % \ (obj.obj_id,self.topnode[obj.parent_type].tagName)) self.topnode[obj.parent_type].appendChild(node) self.adjust_children(obj) self.redirect_children_constraints(obj) if not obj.cli_use_validate(): self.nocli_warn = True obj.nocli = True self.update_links(obj) obj.origin = "user" self.cib_objects.append(obj) return obj def create_from_node(self,node): 'Create a new cib object from a document node.' if not node: return None try: obj_type = cib_object_map[node.tagName][0] except: return None if is_defaults(node): node = get_rscop_defaults_meta_node(node) if not node: return None if node.ownerDocument != self.doc: node = self.doc.importNode(node,1) obj = self.new_object(obj_type, node.getAttribute("id")) if not obj: return None if not id_store.store_xml(node): return None return self.add_element(obj, node) def cib_objects_string(self, obj_list = None): l = [] if not obj_list: obj_list = self.cib_objects for obj in obj_list: l.append(obj.obj_string()) return ' '.join(l) def _remove_obj(self,obj): "Remove a cib object and its children." # remove children first # can't remove them here from obj.children! common_debug("remove object %s" % obj.obj_string()) for child in obj.children: #self._remove_obj(child) # just relink, don't remove children self._relink_child_to_top(child) if obj.parent: # remove obj from its parent, if any obj.parent.children.remove(obj) id_store.remove_xml(obj.node) rmnode(obj.node) obj.invalid = True self.add_to_remove_queue(obj) self.cib_objects.remove(obj) for c_obj in self.related_constraints(obj): if is_simpleconstraint(c_obj.node) and obj.children: # the first child inherits constraints rename_rscref(c_obj,obj.obj_id,obj.children[0].obj_id) delete_rscref(c_obj,obj.obj_id) if silly_constraint(c_obj.node,obj.obj_id): # remove invalid constraints self._remove_obj(c_obj) if not self._no_constraint_rm_msg: err_buf.info("hanging %s deleted" % c_obj.obj_string()) def related_constraints(self,obj): if not is_resource(obj.node): return [] c_list = [] for obj2 in self.cib_objects: if not is_constraint(obj2.node): continue if rsc_constraint(obj.obj_id,obj2.node): c_list.append(obj2) return c_list def redirect_children_constraints(self,obj): ''' Redirect constraints to the new parent ''' for child in obj.children: for c_obj in self.related_constraints(child): rename_rscref(c_obj,child.obj_id,obj.obj_id) # drop useless constraints which may have been created above for c_obj in self.related_constraints(obj): if silly_constraint(c_obj.node,obj.obj_id): self._no_constraint_rm_msg = True self._remove_obj(c_obj) self._no_constraint_rm_msg = False def add_to_remove_queue(self,obj): if obj.origin == "cib": self.remove_queue.append(obj) #print self.cib_objects_string(self.remove_queue) def delete_1(self,obj): ''' Remove an object and its parent in case the object is the only child. ''' if obj.parent and len(obj.parent.children) == 1: self.delete_1(obj.parent) if obj in self.cib_objects: # don't remove parents twice self._remove_obj(obj) def delete(self,*args): 'Delete a cib object.' if not self.doc: empty_cib_err() return False rc = True l = [] for obj_id in args: obj = self.find_object(obj_id) if not obj: no_object_err(obj_id) rc = False continue if is_rsc_running(obj_id): common_warn("resource %s is running, can't delete it" % obj_id) else: l.append(obj) if l: l = processing_sort_cli(l) for obj in reversed(l): self.delete_1(obj) return rc def rename(self,old_id,new_id): ''' Rename a cib object. - check if the resource (if it's a resource) is stopped - check if the new id is not taken - find the object with old id - rename old id to new id in all related objects (constraints) - if the object came from the CIB, then it must be deleted and the one with the new name created - rename old id to new id in the object ''' if not self.doc: empty_cib_err() return False if id_store.id_in_use(new_id): return False obj = self.find_object(old_id) if not obj: no_object_err(old_id) return False if not obj.can_be_renamed(): return False for c_obj in self.related_constraints(obj): rename_rscref(c_obj,old_id,new_id) rename_id(obj.node,old_id,new_id) obj.obj_id = new_id id_store.rename(old_id,new_id) obj.updated = True obj.propagate_updated() def erase(self): "Remove all cib objects." # remove only bottom objects and no constraints # the rest will automatically follow if not self.doc: empty_cib_err() return False erase_ok = True l = [] for obj in [obj for obj in self.cib_objects \ if not obj.children and not is_constraint(obj.node) \ and obj.obj_type != "node" ]: if is_rsc_running(obj.obj_id): common_warn("resource %s is running, can't delete it" % obj.obj_id) erase_ok = False else: l.append(obj) if not erase_ok: common_err("CIB erase aborted (nothing was deleted)") return False self._no_constraint_rm_msg = True for obj in l: self.delete(obj.obj_id) self._no_constraint_rm_msg = False remaining = 0 for obj in self.cib_objects: if obj.obj_type != "node": remaining += 1 if remaining > 0: common_err("strange, but these objects remained:") for obj in self.cib_objects: if obj.obj_type != "node": print >> sys.stderr, obj.obj_string() self.cib_objects = [] return True def erase_nodes(self): "Remove nodes only." if not self.doc: empty_cib_err() return False l = [obj for obj in self.cib_objects if obj.obj_type == "node"] for obj in l: self.delete(obj.obj_id) def refresh(self): "Refresh from the CIB." self.reset() self.initialize() user_prefs = UserPrefs.getInstance() options = Options.getInstance() err_buf = ErrorBuffer.getInstance() vars = Vars.getInstance() cib_factory = CibFactory.getInstance() cli_display = CliDisplay.getInstance() cib_status = CibStatus.getInstance() id_store = IdMgmt.getInstance() # vim:ts=4:sw=4:et: diff --git a/shell/regression/testcases/Makefile.am b/shell/regression/testcases/Makefile.am index c819d71430..7c17e4bdff 100644 --- a/shell/regression/testcases/Makefile.am +++ b/shell/regression/testcases/Makefile.am @@ -1,32 +1,32 @@ # # Author: Sun Jiang Dong # Copyright (c) 2004 International Business Machines # # 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 testcasesdir = $(datadir)/$(PACKAGE)/tests/shell/testcases -testcases_SCRIPTS = confbasic-xml.filter xmlonly.sh +testcases_SCRIPTS = confbasic-xml.filter ra.filter xmlonly.sh testcases_DATA = BSC basicset common.excl \ confbasic confbasic-xml delete file node ra resource shadow \ confbasic-xml.exp confbasic.exp delete.exp file.exp \ node.exp ra.exp resource.exp shadow.exp confbasic-xml.filter: ln xmlonly.sh $@ # shouldn't need this next line... EXTRA_DIST = $(testcases_SCRIPTS) $(testcases_DATA) diff --git a/shell/regression/testcases/ra.filter b/shell/regression/testcases/ra.filter new file mode 100755 index 0000000000..a964613d4c --- /dev/null +++ b/shell/regression/testcases/ra.filter @@ -0,0 +1,15 @@ +#!/usr/bin/awk -f +# reduce the providers list to heartbeat and pacemaker +# (prevents other providers creeping in) +function reduce(a) { + a["heartbeat"]=1; a["pacemaker"]=1; + s=""; + for( i=1; i<=NF; i++ ) + if( $i in a ) + s=s" "$i; + return substr(s,2); +} +n==1 { n=0; print reduce(a); next; } +/providers IPaddr/ { n=1; } +/providers Dummy/ { n=1; } +{ print } diff --git a/shell/templates/clvm b/shell/templates/clvm index 1d90ef92fe..4961edc7b2 100644 --- a/shell/templates/clvm +++ b/shell/templates/clvm @@ -1,48 +1,51 @@ %name clvm # Cluster-aware lvm (cloned) # # This template generates a cloned instance of clvm and one # volume group # # NB: You need just one clvm, regardless of how many volume # groups. In other words, you can use this template only for one # volume group and to make another one, you'll have to edit the # resulting configuration yourself. %required -# Name the volume group -# (for example: vg-1) -# NB: The clone is going to be named c- (e.g. c-vg-1) +# Name the volume group (for example: vg-1) +# The LVM resource will be in a cloned group with the rest +# of the prerequisite resources. The clone is going to be named c- +# (e.g. c-vg-1) %% id # The volume group name %% volgrpname %generate +primitive %_:id ocf:heartbeat:LVM + params volgrpname="%_:volgrpname" + op start timeout=60s + op stop timeout=60s + op monitor interval=30s timeout=60s + primitive dlm ocf:pacemaker:controld + op start timeout=90s + op stop timeout=100s primitive clvm ocf:lvm2:clvmd params daemon_timeout="30" + op start timeout=90s + op stop timeout=100s primitive cmirror ocf:lvm2:cmirrord params daemon_timeout="30" + op start timeout=90s + op stop timeout=100s -group lvm2stage dlm clvm cmirror - -clone c-lvm2stage lvm2stage - meta interleave="true" ordered="true" - -primitive %_:id ocf:heartbeat:LVM - params volgrpname="%_:volgrpname" +group g-%_:id dlm clvm cmirror %_:id -clone c-%_:id %_:id +clone c-%_:id g-%_:id meta interleave="true" ordered="true" - -colocation colo-%_:id-lvm2stage inf: c-%_:id c-lvm2stage - -order order-%_:id-lvm2stage inf: c-lvm2stage c-%_:id diff --git a/shell/templates/ocfs2 b/shell/templates/ocfs2 index 75536710bc..b28acd5836 100644 --- a/shell/templates/ocfs2 +++ b/shell/templates/ocfs2 @@ -1,64 +1,68 @@ %name ocfs2 # ocfs2 filesystem (cloned) # # This template generates a cloned instance of the ocfs2 filesystem # # NB: You need only one dlm/o2cb/clvm, regardless of how many # filesystems. In other words, you can use this template only for # one filesystem and to make another one, you'll have to edit the # resulting configuration yourself. %required -# Name the ocfs2 filesystem -# (for example: bigfs) -# NB: The clone is going to be named c- (e.g. c-bigfs) +# Name the ocfs2 filesystem (for example: bigfs) +# The filesystem resource will be in a cloned group with the rest +# of the prerequisite resources. The clone is going to be named c- +# (e.g. c-bigfs) %% id # The mount point %% directory # The device %% device # optional parameters for the ocfs2 filesystem %optional # mount options %% options %generate primitive %_:id ocf:heartbeat:Filesystem params directory="%_:directory" fstype="ocfs2" device="%_:device" opt options="%_:options" + op start timeout=60s + op stop timeout=60s -monitor %_:id 20:40 - -clone c-%_:id %_:id - meta interleave="true" ordered="true" +monitor %_:id 30s:60s primitive dlm ocf:pacemaker:controld + op start timeout=90s + op stop timeout=100s primitive clvm ocf:lvm2:clvmd + op start timeout=90s + op stop timeout=100s primitive cmirror ocf:lvm2:cmirrord + op start timeout=90s + op stop timeout=100s primitive o2cb ocf:ocfs2:o2cb + op start timeout=90s + op stop timeout=100s -group o2stage dlm clvm o2cb cmirror - -clone c-o2stage o2stage meta interleave="true" - -colocation colo-%_:id-o2stage inf: c-%_:id c-o2stage +group g-%_:id dlm clvm o2cb cmirror %_:id -order order-%_:id-o2stage inf: c-o2stage c-%_:id +clone c-%_:id g-%_:id meta interleave="true" diff --git a/tools/crm_mon.c b/tools/crm_mon.c index 17469322fd..1f9ba64285 100644 --- a/tools/crm_mon.c +++ b/tools/crm_mon.c @@ -1,1922 +1,1926 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include <../lib/pengine/unpack.h> /* GMainLoop *mainloop = NULL; */ void wait_for_refresh(int offset, const char *prefix, int msec); void clean_up(int rc); void crm_diff_update(const char *event, xmlNode *msg); gboolean mon_refresh_display(gpointer user_data); int cib_connect(gboolean full); char *xml_file = NULL; char *as_html_file = NULL; char *pid_file = NULL; char *snmp_target = NULL; char *snmp_community = NULL; gboolean as_console = TRUE;; gboolean simple_status = FALSE; gboolean group_by_node = FALSE; gboolean inactive_resources = FALSE; gboolean web_cgi = FALSE; int reconnect_msec = 5000; gboolean daemonize = FALSE; GMainLoop *mainloop = NULL; guint timer_id = 0; const char *crm_mail_host = NULL; const char *crm_mail_prefix = NULL; const char *crm_mail_from = NULL; const char *crm_mail_to = NULL; const char *external_agent = NULL; const char *external_recipient = NULL; cib_t *cib = NULL; xmlNode *current_cib = NULL; gboolean one_shot = FALSE; gboolean has_warnings = FALSE; gboolean print_failcount = FALSE; gboolean print_operations = FALSE; gboolean print_timing = FALSE; gboolean print_nodes_attr = FALSE; #define FILTER_STR {"shutdown", "terminate", "standby", "fail-count", \ "last-failure", "probe_complete", "#id", "#uname", \ "#is_dc", NULL} gboolean log_diffs = FALSE; gboolean log_updates = FALSE; long last_refresh = 0; crm_trigger_t *refresh_trigger = NULL; /* * 1.3.6.1.4.1.32723 has been assigned to the project by IANA * http://www.iana.org/assignments/enterprise-numbers */ #define PACEMAKER_PREFIX "1.3.6.1.4.1.32723" #define PACEMAKER_TRAP_PREFIX PACEMAKER_PREFIX ".1" #define snmp_crm_trap_oid PACEMAKER_TRAP_PREFIX #define snmp_crm_oid_node PACEMAKER_TRAP_PREFIX ".1" #define snmp_crm_oid_rsc PACEMAKER_TRAP_PREFIX ".2" #define snmp_crm_oid_task PACEMAKER_TRAP_PREFIX ".3" #define snmp_crm_oid_desc PACEMAKER_TRAP_PREFIX ".4" #define snmp_crm_oid_status PACEMAKER_TRAP_PREFIX ".5" #define snmp_crm_oid_rc PACEMAKER_TRAP_PREFIX ".6" #define snmp_crm_oid_trc PACEMAKER_TRAP_PREFIX ".7" #if CURSES_ENABLED # define print_dot() if(as_console) { \ printw("."); \ clrtoeol(); \ refresh(); \ } else { \ fprintf(stdout, "."); \ } #else # define print_dot() fprintf(stdout, "."); #endif #if CURSES_ENABLED # define print_as(fmt, args...) if(as_console) { \ printw(fmt, ##args); \ clrtoeol(); \ refresh(); \ } else { \ fprintf(stdout, fmt, ##args); \ } #else # define print_as(fmt, args...) fprintf(stdout, fmt, ##args); #endif static void blank_screen(void) { #if CURSES_ENABLED int lpc = 0; for(lpc = 0; lpc < LINES; lpc++) { move(lpc, 0); clrtoeol(); } move(0, 0); refresh(); #endif } static gboolean mon_timer_popped(gpointer data) { int rc = cib_ok; if(timer_id > 0) { g_source_remove(timer_id); } rc = cib_connect(TRUE); if(rc != cib_ok) { print_dot(); timer_id = g_timeout_add(reconnect_msec, mon_timer_popped, NULL); } return FALSE; } static void mon_cib_connection_destroy(gpointer user_data) { print_as("Connection to the CIB terminated\n"); if(cib) { print_as("Reconnecting..."); cib->cmds->signoff(cib); timer_id = g_timeout_add(reconnect_msec, mon_timer_popped, NULL); } return; } /* * Mainloop signal handler. */ static void mon_shutdown(int nsig) { clean_up(LSB_EXIT_OK); } +#if ON_DARWIN +# define sighandler_t sig_t +#endif + #if CURSES_ENABLED static sighandler_t ncurses_winch_handler; static void mon_winresize(int nsig) { static int not_done; int lines = 0, cols = 0; if(!not_done++) { if(ncurses_winch_handler) /* the original ncurses WINCH signal handler does the * magic of retrieving the new window size; * otherwise, we'd have to use ioctl or tgetent */ (*ncurses_winch_handler) (SIGWINCH); getmaxyx(stdscr, lines, cols); resizeterm(lines,cols); mainloop_set_trigger(refresh_trigger); } not_done--; } #endif int cib_connect(gboolean full) { int rc = cib_ok; static gboolean need_pass = TRUE; CRM_CHECK(cib != NULL, return cib_missing); if(getenv("CIB_passwd") != NULL) { need_pass = FALSE; } if(cib->state != cib_connected_query && cib->state != cib_connected_command) { crm_debug_4("Connecting to the CIB"); if(as_console && need_pass && cib->variant == cib_remote) { need_pass = FALSE; print_as("Password:"); } rc = cib->cmds->signon(cib, crm_system_name, cib_query); if(rc != cib_ok) { return rc; } current_cib = get_cib_copy(cib); mon_refresh_display(NULL); if(full) { if(rc == cib_ok) { rc = cib->cmds->set_connection_dnotify(cib, mon_cib_connection_destroy); if(rc == cib_NOTSUPPORTED) { print_as("Notification setup failed, won't be able to reconnect after failure"); if(as_console) { sleep(2); } rc = cib_ok; } } if(rc == cib_ok) { cib->cmds->del_notify_callback(cib, T_CIB_DIFF_NOTIFY, crm_diff_update); rc = cib->cmds->add_notify_callback(cib, T_CIB_DIFF_NOTIFY, crm_diff_update); } if(rc != cib_ok) { print_as("Notification setup failed, could not monitor CIB actions"); if(as_console) { sleep(2); } clean_up(-rc); } } } return rc; } static struct crm_option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?', "\tThis text"}, {"version", 0, 0, '$', "\tVersion information" }, {"verbose", 0, 0, 'V', "\tIncrease debug output"}, {"-spacer-", 1, 0, '-', "\nModes:"}, {"as-html", 1, 0, 'h', "Write cluster status to the named file"}, {"web-cgi", 0, 0, 'w', "\tWeb mode with output suitable for cgi"}, {"simple-status", 0, 0, 's', "Display the cluster status once as a simple one line output (suitable for nagios)"}, {"snmp-traps", 1, 0, 'S', "Send SNMP traps to this station", !ENABLE_SNMP}, {"snmp-community", 1, 0, 'C', "Specify community for SNMP traps(default is NULL)", !ENABLE_SNMP}, {"mail-to", 1, 0, 'T', "Send Mail alerts to this user. See also --mail-from, --mail-host, --mail-prefix", !ENABLE_ESMTP}, {"-spacer-", 1, 0, '-', "\nDisplay Options:"}, {"group-by-node", 0, 0, 'n', "\tGroup resources by node" }, {"inactive", 0, 0, 'r', "\tDisplay inactive resources" }, {"failcounts", 0, 0, 'f', "\tDisplay resource fail counts"}, {"operations", 0, 0, 'o', "\tDisplay resource operation history" }, {"timing-details", 0, 0, 't', "\tDisplay resource operation history with timing details" }, {"show-node-attributes", 0, 0, 'A', "Display node attributes\n" }, {"-spacer-", 1, 0, '-', "\nAdditional Options:"}, {"interval", 1, 0, 'i', "\tUpdate frequency in seconds" }, {"one-shot", 0, 0, '1', "\tDisplay the cluster status once on the console and exit"}, {"disable-ncurses",0, 0, 'N', "\tDisable the use of ncurses", !CURSES_ENABLED}, {"daemonize", 0, 0, 'd', "\tRun in the background as a daemon"}, {"pid-file", 1, 0, 'p', "\t(Advanced) Daemon pid file location"}, {"mail-from", 1, 0, 'F', "\tMail alerts should come from the named user", !ENABLE_ESMTP}, {"mail-host", 1, 0, 'H', "\tMail alerts should be sent via the named host", !ENABLE_ESMTP}, {"mail-prefix", 1, 0, 'P', "Subjects for mail alerts should start with this string", !ENABLE_ESMTP}, {"external-agent", 1, 0, 'E', "A program to run when resource operations take place."}, {"external-recipient",1, 0, 'e', "A recipient for your program (assuming you want the program to send something to someone)."}, {"xml-file", 1, 0, 'x', NULL, 1}, {"-spacer-", 1, 0, '-', "\nExamples:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', "Display the cluster´s status on the console with updates as they occur:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_mon", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Display the cluster´s status on the console just once then exit:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_mon -1", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Display your cluster´s status, group resources by node, and include inactive resources in the list:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_mon --group-by-node --inactive", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Start crm_mon as a background daemon and have it write the cluster´s status to an HTML file:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_mon --daemonize --as-html /path/to/docroot/filename.html", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Start crm_mon as a background daemon and have it send email alerts:", pcmk_option_paragraph|!ENABLE_ESMTP}, {"-spacer-", 1, 0, '-', " crm_mon --daemonize --mail-to user@example.com --mail-host mail.example.com", pcmk_option_example|!ENABLE_ESMTP}, {"-spacer-", 1, 0, '-', "Start crm_mon as a background daemon and have it send SNMP alerts:", pcmk_option_paragraph|!ENABLE_SNMP}, {"-spacer-", 1, 0, '-', " crm_mon --daemonize --snmp-traps snmptrapd.example.com", pcmk_option_example|!ENABLE_SNMP}, {NULL, 0, 0, 0} }; int main(int argc, char **argv) { int flag; int argerr = 0; int exit_code = 0; int option_index = 0; pid_file = crm_strdup("/tmp/ClusterMon.pid"); crm_log_init(NULL, LOG_CRIT, FALSE, FALSE, argc, argv, TRUE); crm_set_options("V?$i:nrh:dp:s1wx:oftANS:T:F:H:P:E:e:C:", "mode [options]", long_options, "Provides a summary of cluster's current state." "\n\nOutputs varying levels of detail in a number of different formats.\n"); #ifndef ON_DARWIN /* prevent zombies */ signal(SIGCLD, SIG_IGN); #endif if (strcmp(crm_system_name, "crm_mon.cgi")==0) { web_cgi = TRUE; one_shot = TRUE; } while (1) { flag = crm_get_option(argc, argv, &option_index); if (flag == -1) break; switch(flag) { case 'V': cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); break; case 'i': reconnect_msec = crm_get_msec(optarg); break; case 'n': group_by_node = TRUE; break; case 'r': inactive_resources = TRUE; break; case 'd': daemonize = TRUE; break; case 't': print_timing = TRUE; print_operations = TRUE; break; case 'o': print_operations = TRUE; break; case 'f': print_failcount = TRUE; break; case 'A': print_nodes_attr = TRUE; break; case 'p': crm_free(pid_file); pid_file = crm_strdup(optarg); break; case 'x': xml_file = crm_strdup(optarg); one_shot = TRUE; break; case 'h': as_html_file = crm_strdup(optarg); break; case 'w': web_cgi = TRUE; one_shot = TRUE; break; case 's': simple_status = TRUE; one_shot = TRUE; break; case 'S': snmp_target = optarg; break; case 'T': crm_mail_to = optarg; break; case 'F': crm_mail_from = optarg; break; case 'H': crm_mail_host = optarg; break; case 'P': crm_mail_prefix = optarg; break; case 'E': external_agent = optarg; break; case 'e': external_recipient = optarg; break; case '1': one_shot = TRUE; break; case 'N': as_console = FALSE; break; case 'C': snmp_community = optarg; break; case '$': case '?': crm_help(flag, LSB_EXIT_OK); break; default: printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag); ++argerr; break; } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) printf("%s ", argv[optind++]); printf("\n"); } if (argerr) { crm_help('?', LSB_EXIT_GENERIC); } if(one_shot) { as_console = FALSE; } else if(daemonize) { as_console = FALSE; cl_log_enable_stderr(FALSE); if(!as_html_file && !snmp_target && !crm_mail_to && !external_agent) { printf("Looks like you forgot to specify one or more of: --as-html, --mail-to, --snmp-target, --external-agent\n"); crm_help('?', LSB_EXIT_GENERIC); } crm_make_daemon(crm_system_name, TRUE, pid_file); } else if(as_console) { #if CURSES_ENABLED initscr(); cbreak(); noecho(); cl_log_enable_stderr(FALSE); #else one_shot = TRUE; as_console = FALSE; printf("Defaulting to one-shot mode\n"); printf("You need to have curses available at compile time to enable console mode\n"); #endif } crm_info("Starting %s", crm_system_name); if(xml_file != NULL) { current_cib = filename2xml(xml_file); mon_refresh_display(NULL); return exit_code; } if(current_cib == NULL) { cib = cib_new(); if(!one_shot) { print_as("Attempting connection to the cluster..."); } do { exit_code = cib_connect(!one_shot); if(one_shot) { break; } else if(exit_code != cib_ok) { print_dot(); sleep(reconnect_msec/1000); } } while(exit_code == cib_connection); if(exit_code != cib_ok) { print_as("\nConnection to cluster failed: %s\n", cib_error2string(exit_code)); if(as_console) { sleep(2); } clean_up(-exit_code); } } if(one_shot) { return exit_code; } mainloop = g_main_new(FALSE); mainloop_add_signal(SIGTERM, mon_shutdown); mainloop_add_signal(SIGINT, mon_shutdown); #if CURSES_ENABLED if(as_console) { ncurses_winch_handler = signal(SIGWINCH, mon_winresize); if(ncurses_winch_handler == SIG_DFL || ncurses_winch_handler == SIG_IGN || ncurses_winch_handler == SIG_ERR) ncurses_winch_handler = NULL; } #endif refresh_trigger = mainloop_add_trigger(G_PRIORITY_LOW, mon_refresh_display, NULL); g_main_run(mainloop); g_main_destroy(mainloop); crm_info("Exiting %s", crm_system_name); clean_up(0); return 0; /* never reached */ } void wait_for_refresh(int offset, const char *prefix, int msec) { int lpc = msec / 1000; struct timespec sleept = {1 , 0}; if(as_console == FALSE) { timer_id = g_timeout_add(msec, mon_timer_popped, NULL); return; } crm_notice("%sRefresh in %ds...", prefix?prefix:"", lpc); while(lpc > 0) { #if CURSES_ENABLED move(offset, 0); /* printw("%sRefresh in \033[01;32m%ds\033[00m...", prefix?prefix:"", lpc); */ printw("%sRefresh in %ds...\n", prefix?prefix:"", lpc); clrtoeol(); refresh(); #endif lpc--; if(lpc == 0) { timer_id = g_timeout_add( 1000, mon_timer_popped, NULL); } else { if (nanosleep(&sleept, NULL) != 0) { return; } } } } #define mon_warn(fmt...) do { \ if (!has_warnings) { \ print_as("Warning:"); \ } else { \ print_as(","); \ } \ print_as(fmt); \ has_warnings = TRUE; \ } while(0) static int print_simple_status(pe_working_set_t *data_set) { node_t *dc = NULL; int nodes_online = 0; int nodes_standby = 0; dc = data_set->dc_node; if(dc == NULL) { mon_warn("No DC "); } slist_iter(node, node_t, data_set->nodes, lpc2, if(node->details->standby && node->details->online) { nodes_standby++; } else if(node->details->online) { nodes_online++; } else { mon_warn("offline node: %s", node->details->uname); } ); if (!has_warnings) { print_as("Ok: %d nodes online", nodes_online); if (nodes_standby > 0) { print_as(", %d standby nodes", nodes_standby); } print_as(", %d resources configured", g_list_length(data_set->resources)); } print_as("\n"); return 0; } extern int get_failcount(node_t *node, resource_t *rsc, int *last_failure, pe_working_set_t *data_set); static void print_date(time_t time) { int lpc = 0; char date_str[26]; asctime_r(localtime(&time), date_str); for(; lpc < 26; lpc++) { if(date_str[lpc] == '\n') { date_str[lpc] = 0; } } print_as("'%s'", date_str); } static void print_rsc_summary(pe_working_set_t *data_set, node_t *node, resource_t *rsc, gboolean all) { gboolean printed = FALSE; time_t last_failure = 0; char *fail_attr = crm_concat("fail-count", rsc->id, '-'); const char *value = g_hash_table_lookup(node->details->attrs, fail_attr); int failcount = char2score(value); /* Get the true value, not the effective one from get_failcount() */ get_failcount(node, rsc, (int*)&last_failure, data_set); crm_free(fail_attr); if(all || failcount || last_failure > 0) { printed = TRUE; print_as(" %s: migration-threshold=%d", rsc->id, rsc->migration_threshold); } if(failcount > 0) { printed = TRUE; print_as(" fail-count=%d", failcount); } if(last_failure > 0) { printed = TRUE; print_as(" last-failure="); print_date(last_failure); } if(printed) { print_as("\n"); } } static void print_rsc_history(pe_working_set_t *data_set, node_t *node, xmlNode *rsc_entry) { GListPtr op_list = NULL; gboolean print_name = TRUE; GListPtr sorted_op_list = NULL; const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); resource_t *rsc = pe_find_resource(data_set->resources, rsc_id); xml_child_iter_filter( rsc_entry, rsc_op, XML_LRM_TAG_RSC_OP, op_list = g_list_append(op_list, rsc_op); ); sorted_op_list = g_list_sort(op_list, sort_op_by_callid); slist_iter(xml_op, xmlNode, sorted_op_list, lpc, const char *value = NULL; const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID); const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); const char *op_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC); const char *interval = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL); int rc = crm_parse_int(op_rc, "0"); if(safe_str_eq(task, CRMD_ACTION_STATUS) && safe_str_eq(interval, "0")) { task = "probe"; } if(rc == 7 && safe_str_eq(task, "probe")) { continue; } else if(safe_str_eq(task, CRMD_ACTION_NOTIFY)) { continue; } if(print_name) { print_name = FALSE; if(rsc == NULL) { print_as("Orphan resource: %s", rsc_id); } else { print_rsc_summary(data_set, node, rsc, TRUE); } } print_as(" + (%s) %s:", call, task); if(safe_str_neq(interval, "0")) { print_as(" interval=%sms", interval); } if(print_timing) { int int_value; const char *attr = "last-rc-change"; value = crm_element_value(xml_op, attr); if(value) { int_value = crm_parse_int(value, NULL); print_as(" %s=", attr); print_date(int_value); } attr = "last-run"; value = crm_element_value(xml_op, attr); if(value) { int_value = crm_parse_int(value, NULL); print_as(" %s=", attr); print_date(int_value); } attr = "exec-time"; value = crm_element_value(xml_op, attr); if(value) { int_value = crm_parse_int(value, NULL); print_as(" %s=%dms", attr, int_value); } attr = "queue-time"; value = crm_element_value(xml_op, attr); if(value) { int_value = crm_parse_int(value, NULL); print_as(" %s=%dms", attr, int_value); } } print_as(" rc=%s (%s)\n", op_rc, execra_code2string(rc)); ); /* no need to free the contents */ g_list_free(sorted_op_list); } static void print_attr_msg(node_t *node, GListPtr rsc_list, const char *attrname, const char *attrvalue) { slist_iter(rsc, resource_t, rsc_list, lpc2, const char *type = g_hash_table_lookup(rsc->meta, "type"); if(rsc->children != NULL) { print_attr_msg(node, rsc->children, attrname, attrvalue); } if(safe_str_eq(type, "ping") || safe_str_eq(type, "pingd")) { const char *name = "pingd"; const char *multiplier = NULL; char **host_list = NULL; int host_list_num = 0; int expected_score = 0; if(g_hash_table_lookup(rsc->meta, "name") != NULL) { name = g_hash_table_lookup(rsc->meta, "name"); } /* To identify the resource with the attribute name. */ if(safe_str_eq(name, attrname)) { int value = crm_parse_int(attrvalue, "0"); multiplier = g_hash_table_lookup(rsc->meta, "multiplier"); host_list = g_strsplit(g_hash_table_lookup(rsc->meta, "host_list"), " ", 0); host_list_num = g_strv_length(host_list); g_strfreev(host_list); /* pingd multiplier is the same as the default value. */ expected_score = host_list_num * crm_parse_int(multiplier, "1"); /* pingd is abnormal score. */ if(value <= 0) { print_as("\t: Connectivity is lost"); } else if(value < expected_score) { print_as("\t: Connectivity is degraded (Expected=%d)", expected_score); } } } ); } static void print_node_attribute(gpointer name, gpointer value, gpointer node_data) { int i; node_t *node = (node_t *)node_data; const char *filt_str[] = FILTER_STR; /* filtering automatic attributes */ for(i = 0; filt_str[i] != NULL; i++) { if(g_str_has_prefix(name, filt_str[i])) { return; } } print_as(" + %-32s\t: %-10s", (char *)name, (char *)value); print_attr_msg(node, node->details->running_rsc, name, value); print_as("\n"); } static void print_node_summary(pe_working_set_t *data_set, gboolean operations) { xmlNode *lrm_rsc = NULL; xmlNode *cib_status = get_object_root(XML_CIB_TAG_STATUS, data_set->input); if(operations) { print_as("\nOperations:\n"); } else { print_as("\nMigration summary:\n"); } xml_child_iter_filter( cib_status, node_state, XML_CIB_TAG_STATE, node_t *node = pe_find_node_id(data_set->nodes, ID(node_state)); if(node == NULL || node->details->online == FALSE){ continue; } print_as("* Node %s: ", crm_element_value(node_state, XML_ATTR_UNAME)); print_as("\n"); lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE); xml_child_iter_filter( lrm_rsc, rsc_entry, XML_LRM_TAG_RESOURCE, if(operations) { print_rsc_history(data_set, node, rsc_entry); } else { const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); resource_t *rsc = pe_find_resource(data_set->resources, rsc_id); if(rsc) { print_rsc_summary(data_set, node, rsc, FALSE); } else { print_as(" %s: orphan\n", rsc_id); } } ); ); } static char * add_list_element(char *list, const char *value) { int len = 0; int last = 0; if(value == NULL) { return list; } if(list) { last = strlen(list); } len = last + 2; /* +1 space, +1 EOS */ len += strlen(value); crm_realloc(list, len); sprintf(list + last, " %s", value); return list; } static int print_status(pe_working_set_t *data_set) { static int updates = 0; node_t *dc = NULL; char *since_epoch = NULL; char *online_nodes = NULL; char *offline_nodes = NULL; xmlNode *dc_version = NULL; xmlNode *quorum_node = NULL; xmlNode *stack = NULL; time_t a_time = time(NULL); int configured_resources = 0; int print_opts = pe_print_ncurses; const char *quorum_votes = "unknown"; if(as_console) { blank_screen(); } else { print_opts = pe_print_printf; } updates++; dc = data_set->dc_node; print_as("============\n"); if(a_time == (time_t)-1) { crm_perror(LOG_ERR,"set_node_tstamp(): Invalid time returned"); return 1; } since_epoch = ctime(&a_time); if(since_epoch != NULL) { print_as("Last updated: %s", since_epoch); } stack = get_xpath_object("//nvpair[@name='cluster-infrastructure']", data_set->input, LOG_DEBUG); if(stack) { print_as("Stack: %s\n", crm_element_value(stack, XML_NVPAIR_ATTR_VALUE)); } dc_version = get_xpath_object("//nvpair[@name='dc-version']", data_set->input, LOG_DEBUG); if(dc == NULL) { print_as("Current DC: NONE\n"); } else { const char *quorum = crm_element_value(data_set->input, XML_ATTR_HAVE_QUORUM); if(safe_str_neq(dc->details->uname, dc->details->id)) { print_as("Current DC: %s (%s)", dc->details->uname, dc->details->id); } else { print_as("Current DC: %s", dc->details->uname); } print_as(" - partition %s quorum\n", crm_is_true(quorum)?"with":"WITHOUT"); if(dc_version) { print_as("Version: %s\n", crm_element_value(dc_version, XML_NVPAIR_ATTR_VALUE)); } } quorum_node = get_xpath_object("//nvpair[@name='"XML_ATTR_EXPECTED_VOTES"']", data_set->input, LOG_DEBUG); if(quorum_node) { quorum_votes = crm_element_value(quorum_node, XML_NVPAIR_ATTR_VALUE); } slist_iter(rsc, resource_t, data_set->resources, lpc, if(is_not_set(rsc->flags, pe_rsc_orphan)) { configured_resources++; } ); print_as("%d Nodes configured, %s expected votes\n", g_list_length(data_set->nodes), quorum_votes); print_as("%d Resources configured.\n", configured_resources); print_as("============\n\n"); slist_iter(node, node_t, data_set->nodes, lpc2, const char *node_mode = NULL; if(node->details->unclean) { if(node->details->online && node->details->unclean) { node_mode = "UNCLEAN (online)"; } else if(node->details->pending) { node_mode = "UNCLEAN (pending)"; } else { node_mode = "UNCLEAN (offline)"; } } else if(node->details->pending) { node_mode = "pending"; } else if(node->details->standby_onfail && node->details->online) { node_mode = "standby (on-fail)"; } else if(node->details->standby) { if(node->details->online) { node_mode = "standby"; } else { node_mode = "OFFLINE (standby)"; } } else if(node->details->online) { node_mode = "online"; if(group_by_node == FALSE) { online_nodes = add_list_element(online_nodes, node->details->uname); continue; } } else { node_mode = "OFFLINE"; if(group_by_node == FALSE) { offline_nodes = add_list_element(offline_nodes, node->details->uname); continue; } } if(safe_str_eq(node->details->uname, node->details->id)) { print_as("Node %s: %s\n", node->details->uname, node_mode); } else { print_as("Node %s (%s): %s\n", node->details->uname, node->details->id, node_mode); } if(group_by_node) { slist_iter(rsc, resource_t, node->details->running_rsc, lpc2, rsc->fns->print( rsc, "\t", print_opts|pe_print_rsconly, stdout); ); } ); if(online_nodes) { print_as("Online: [%s ]\n", online_nodes); crm_free(online_nodes); } if(offline_nodes) { print_as("OFFLINE: [%s ]\n", offline_nodes); crm_free(offline_nodes); } if(group_by_node == FALSE && inactive_resources) { print_as("\nFull list of resources:\n"); } else if(inactive_resources) { print_as("\nInactive resources:\n"); } if(group_by_node == FALSE || inactive_resources) { print_as("\n"); slist_iter(rsc, resource_t, data_set->resources, lpc2, gboolean is_active = rsc->fns->active(rsc, TRUE); gboolean partially_active = rsc->fns->active(rsc, FALSE); if(is_set(rsc->flags, pe_rsc_orphan) && is_active == FALSE) { continue; } else if(group_by_node == FALSE) { if(partially_active || inactive_resources) { rsc->fns->print(rsc, NULL, print_opts, stdout); } } else if(is_active == FALSE && inactive_resources) { rsc->fns->print(rsc, NULL, print_opts, stdout); } ); } if(print_nodes_attr) { print_as("\nNode Attributes:\n"); slist_iter( node, node_t, data_set->nodes, lpc, print_as("* Node %s:\n", node->details->uname); g_hash_table_foreach(node->details->attrs, print_node_attribute, node); ); } if(print_operations || print_failcount) { print_node_summary(data_set, print_operations); } if(xml_has_children(data_set->failed)) { print_as("\nFailed actions:\n"); xml_child_iter(data_set->failed, xml_op, int val = 0; const char *id = ID(xml_op); const char *last = crm_element_value(xml_op, "last_run"); const char *node = crm_element_value(xml_op, XML_ATTR_UNAME); const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID); const char *rc = crm_element_value(xml_op, XML_LRM_ATTR_RC); const char *status = crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS); val = crm_parse_int(status, "0"); print_as(" %s (node=%s, call=%s, rc=%s, status=%s", id, node, call, rc, op_status2text(val)); if(last) { time_t run_at = crm_parse_int(last, "0"); print_as(", last-run=%s, queued=%sms, exec=%sms\n", ctime(&run_at), crm_element_value(xml_op, "exec_time"), crm_element_value(xml_op, "queue_time")); } val = crm_parse_int(rc, "0"); print_as("): %s\n", execra_code2string(val)); ); } #if CURSES_ENABLED if(as_console) { refresh(); } #endif return 0; } static int print_html_status(pe_working_set_t *data_set, const char *filename, gboolean web_cgi) { FILE *stream; node_t *dc = NULL; static int updates = 0; char *filename_tmp = NULL; if (web_cgi) { stream=stdout; fprintf(stream, "Content-type: text/html\n\n"); } else { filename_tmp = crm_concat(filename, "tmp", '.'); stream = fopen(filename_tmp, "w"); if(stream == NULL) { crm_perror(LOG_ERR,"Cannot open %s for writing", filename_tmp); crm_free(filename_tmp); return -1; } } updates++; dc = data_set->dc_node; fprintf(stream, ""); fprintf(stream, ""); fprintf(stream, "Cluster status"); /* content="%d;url=http://webdesign.about.com" */ fprintf(stream, "", reconnect_msec/1000); fprintf(stream, ""); /*** SUMMARY ***/ fprintf(stream, "

Cluster summary

"); { char *now_str = NULL; time_t now = time(NULL); now_str = ctime(&now); now_str[24] = EOS; /* replace the newline */ fprintf(stream, "Last updated: %s
\n", now_str); } if(dc == NULL) { fprintf(stream, "Current DC: NONE
"); } else { fprintf(stream, "Current DC: %s (%s)
", dc->details->uname, dc->details->id); } fprintf(stream, "%d Nodes configured.
", g_list_length(data_set->nodes)); fprintf(stream, "%d Resources configured.
", g_list_length(data_set->resources)); /*** CONFIG ***/ fprintf(stream, "

Config Options

\n"); fprintf(stream, "\n"); fprintf(stream, "\n", is_set(data_set->flags, pe_flag_stonith_enabled)?"enabled":"disabled"); fprintf(stream, "\n", is_set(data_set->flags, pe_flag_symmetric_cluster)?"":"a-"); fprintf(stream, "\n
STONITH of failed nodes:%s
Cluster is:%ssymmetric
No Quorum Policy:"); switch (data_set->no_quorum_policy) { case no_quorum_freeze: fprintf(stream, "Freeze resources"); break; case no_quorum_stop: fprintf(stream, "Stop ALL resources"); break; case no_quorum_ignore: fprintf(stream, "Ignore"); break; case no_quorum_suicide: fprintf(stream, "Suicide"); break; } fprintf(stream, "\n
\n"); /*** NODE LIST ***/ fprintf(stream, "

Node List

\n"); fprintf(stream, "
    \n"); slist_iter(node, node_t, data_set->nodes, lpc2, fprintf(stream, "
  • "); if(node->details->standby_onfail && node->details->online) { fprintf(stream, "Node: %s (%s): %s",node->details->uname, node->details->id,"standby (on-fail)\n"); } else if(node->details->standby && node->details->online) { fprintf(stream, "Node: %s (%s): %s",node->details->uname, node->details->id,"standby\n"); } else if(node->details->standby) { fprintf(stream, "Node: %s (%s): %s",node->details->uname, node->details->id,"OFFLINE (standby)\n"); } else if(node->details->online) { fprintf(stream, "Node: %s (%s): %s",node->details->uname, node->details->id,"online\n"); } else { fprintf(stream, "Node: %s (%s): %s",node->details->uname, node->details->id,"OFFLINE\n"); } if(group_by_node) { fprintf(stream, "
      \n"); slist_iter(rsc, resource_t, node->details->running_rsc, lpc2, fprintf(stream, "
    • "); rsc->fns->print(rsc, NULL, pe_print_html|pe_print_rsconly, stream); fprintf(stream, "
    • \n"); ); fprintf(stream, "
    \n"); } fprintf(stream, "
  • \n"); ); fprintf(stream, "
\n"); if(group_by_node && inactive_resources) { fprintf(stream, "

Inactive Resources

\n"); } else if(group_by_node == FALSE) { fprintf(stream, "

Resource List

\n"); } if(group_by_node == FALSE || inactive_resources) { slist_iter(rsc, resource_t, data_set->resources, lpc2, gboolean is_active = rsc->fns->active(rsc, TRUE); gboolean partially_active = rsc->fns->active(rsc, FALSE); if(is_set(rsc->flags, pe_rsc_orphan) && is_active == FALSE) { continue; } else if(group_by_node == FALSE) { if(partially_active || inactive_resources) { rsc->fns->print(rsc, NULL, pe_print_html, stream); } } else if(is_active == FALSE && inactive_resources) { rsc->fns->print(rsc, NULL, pe_print_html, stream); } ); } fprintf(stream, ""); fflush(stream); fclose(stream); if (!web_cgi) { if(rename(filename_tmp, filename) != 0) { crm_perror(LOG_ERR,"Unable to rename %s->%s", filename_tmp, filename); } crm_free(filename_tmp); } return 0; } #if ENABLE_SNMP #include #include #include #include #include #include #define add_snmp_field(list, oid_string, value) do { \ oid name[MAX_OID_LEN]; \ size_t name_length = MAX_OID_LEN; \ if (snmp_parse_oid(oid_string, name, &name_length)) { \ int s_rc = snmp_add_var(list, name, name_length, 's', (value)); \ if(s_rc != 0) { \ crm_err("Could not add %s=%s rc=%d", oid_string, value, s_rc); \ } else { \ crm_debug_2("Added %s=%s", oid_string, value); \ } \ } else { \ crm_err("Could not parse OID: %s", oid_string); \ } \ } while(0) \ #define add_snmp_field_int(list, oid_string, value) do { \ oid name[MAX_OID_LEN]; \ size_t name_length = MAX_OID_LEN; \ if (snmp_parse_oid(oid_string, name, &name_length)) { \ if(NULL == snmp_pdu_add_variable( \ list, name, name_length, ASN_INTEGER, \ (u_char *) & value, sizeof(value))) { \ crm_err("Could not add %s=%d", oid_string, value); \ } else { \ crm_debug_2("Added %s=%d", oid_string, value); \ } \ } else { \ crm_err("Could not parse OID: %s", oid_string); \ } \ } while(0) \ static int snmp_input(int operation, netsnmp_session *session, int reqid, netsnmp_pdu *pdu, void *magic) { return 1; } static netsnmp_session *crm_snmp_init(const char *target, char *community) { static netsnmp_session *session = NULL; #ifdef NETSNMPV53 char target53[128]; snprintf(target53, sizeof(target53), "%s:162", target); #endif if(session) { return session; } if(target == NULL) { return NULL; } if(crm_log_level > LOG_INFO) { char *debug_tokens = crm_strdup("run:shell,snmptrap,tdomain"); debug_register_tokens(debug_tokens); snmp_set_do_debugging(1); } crm_malloc0(session, sizeof(netsnmp_session)); snmp_sess_init(session); session->version = SNMP_VERSION_2c; session->callback = snmp_input; session->callback_magic = NULL; if(community) { session->community_len = strlen(community); session->community = (unsigned char*)community; } session = snmp_add(session, #ifdef NETSNMPV53 netsnmp_tdomain_transport(target53, 0, "udp"), #else netsnmp_transport_open_client("snmptrap", target), #endif NULL, NULL); if (session == NULL) { snmp_sess_perror("Could not create snmp transport", session); } return session; } #endif static int send_snmp_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc, int status, const char *desc) { int ret = 1; #if ENABLE_SNMP static oid snmptrap_oid[] = { 1,3,6,1,6,3,1,1,4,1,0 }; static oid sysuptime_oid[] = { 1,3,6,1,2,1,1,3,0 }; netsnmp_pdu *trap_pdu; netsnmp_session *session = crm_snmp_init(snmp_target, snmp_community); trap_pdu = snmp_pdu_create(SNMP_MSG_TRAP2); if ( !trap_pdu ) { crm_err("Failed to create SNMP notification"); return SNMPERR_GENERR; } if(1) { /* send uptime */ char csysuptime[20]; time_t now = time(NULL); sprintf(csysuptime, "%ld", now); snmp_add_var(trap_pdu, sysuptime_oid, sizeof(sysuptime_oid) / sizeof(oid), 't', csysuptime); } /* Indicate what the trap is by setting snmpTrapOid.0 */ ret = snmp_add_var(trap_pdu, snmptrap_oid, sizeof(snmptrap_oid) / sizeof(oid), 'o', snmp_crm_trap_oid); if (ret != 0) { crm_err("Failed set snmpTrapOid.0=%s", snmp_crm_trap_oid); return ret; } /* Add extries to the trap */ add_snmp_field(trap_pdu, snmp_crm_oid_rsc, rsc); add_snmp_field(trap_pdu, snmp_crm_oid_node, node); add_snmp_field(trap_pdu, snmp_crm_oid_task, task); add_snmp_field(trap_pdu, snmp_crm_oid_desc, desc); add_snmp_field_int(trap_pdu, snmp_crm_oid_rc, rc); add_snmp_field_int(trap_pdu, snmp_crm_oid_trc, target_rc); add_snmp_field_int(trap_pdu, snmp_crm_oid_status, status); /* Send and cleanup */ ret = snmp_send(session, trap_pdu); if(ret == 0) { /* error */ snmp_sess_perror("Could not send SNMP trap", session); snmp_free_pdu(trap_pdu); ret = SNMPERR_GENERR; } else { ret = SNMPERR_SUCCESS; } #else crm_err("Sending SNMP traps is not supported by this installation"); #endif return ret; } #if ENABLE_ESMTP #include #include static void print_recipient_status( smtp_recipient_t recipient, const char *mailbox, void *arg) { const smtp_status_t *status; status = smtp_recipient_status (recipient); printf ("%s: %d %s", mailbox, status->code, status->text); } static void event_cb (smtp_session_t session, int event_no, void *arg, ...) { int *ok; va_list alist; va_start(alist, arg); switch(event_no) { case SMTP_EV_CONNECT: case SMTP_EV_MAILSTATUS: case SMTP_EV_RCPTSTATUS: case SMTP_EV_MESSAGEDATA: case SMTP_EV_MESSAGESENT: case SMTP_EV_DISCONNECT: break; case SMTP_EV_WEAK_CIPHER: { int bits = va_arg(alist, long); ok = va_arg(alist, int*); crm_debug("SMTP_EV_WEAK_CIPHER, bits=%d - accepted.", bits); *ok = 1; break; } case SMTP_EV_STARTTLS_OK: crm_debug("SMTP_EV_STARTTLS_OK - TLS started here."); break; case SMTP_EV_INVALID_PEER_CERTIFICATE: { long vfy_result = va_arg(alist, long); ok = va_arg(alist, int*); /* There is a table in handle_invalid_peer_certificate() of mail-file.c */ crm_err("SMTP_EV_INVALID_PEER_CERTIFICATE: %ld", vfy_result); *ok = 1; break; } case SMTP_EV_NO_PEER_CERTIFICATE: ok = va_arg(alist, int*); crm_debug("SMTP_EV_NO_PEER_CERTIFICATE - accepted."); *ok = 1; break; case SMTP_EV_WRONG_PEER_CERTIFICATE: ok = va_arg(alist, int*); crm_debug("SMTP_EV_WRONG_PEER_CERTIFICATE - accepted."); *ok = 1; break; case SMTP_EV_NO_CLIENT_CERTIFICATE: ok = va_arg(alist, int*); crm_debug("SMTP_EV_NO_CLIENT_CERTIFICATE - accepted."); *ok = 1; break; default: crm_debug("Got event: %d - ignored.\n", event_no); } va_end(alist); } #endif #define BODY_MAX 2048 #if ENABLE_ESMTP static void crm_smtp_debug (const char *buf, int buflen, int writing, void *arg) { char type = 0; int lpc = 0, last = 0, level = *(int*)arg; if (writing == SMTP_CB_HEADERS) { type = 'H'; } else if(writing) { type = 'C'; } else { type = 'S'; } for(; lpc < buflen; lpc++) { switch(buf[lpc]) { case 0: case '\n': if(last > 0) { do_crm_log(level, " %.*s", lpc-last, buf+last); } else { do_crm_log(level, "%c: %.*s", type, lpc-last, buf+last); } last = lpc + 1; break; } } } #endif static int send_custom_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc, int status, const char *desc) { pid_t pid; /*setenv needs chars, these are ints*/ char *rc_s = crm_itoa(rc); char *status_s = crm_itoa(status); char *target_rc_s = crm_itoa(target_rc); crm_debug("Sending external notification to '%s' via '%s'", external_recipient, external_agent); setenv("CRM_notify_recipient",external_recipient,1); setenv("CRM_notify_node",node,1); setenv("CRM_notify_rsc",rsc,1); setenv("CRM_notify_task",task,1); setenv("CRM_notify_desc",desc,1); setenv("CRM_notify_rc",rc_s,1); setenv("CRM_notify_target_rc",target_rc_s,1); setenv("CRM_notify_status",status_s,1); pid=fork(); if(pid == -1) { cl_perror("notification fork() failed."); } if(pid == 0) { /* crm_debug("notification: I am the child. Executing the nofitication program."); */ execl(external_agent,external_agent,NULL); } crm_debug_2("Finished running custom notification program '%s'.",external_agent); crm_free(target_rc_s); crm_free(status_s); crm_free(rc_s); return 0; } static int send_smtp_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc, int status, const char *desc) { #if ENABLE_ESMTP smtp_session_t session; smtp_message_t message; auth_context_t authctx; struct sigaction sa; int len = 20; int noauth = 1; int smtp_debug = LOG_DEBUG; char crm_mail_body[BODY_MAX]; char *crm_mail_subject = NULL; if(node == NULL) { node = "-"; } if(rsc == NULL) { rsc = "-"; } if(desc == NULL) { desc = "-"; } if(crm_mail_to == NULL) { return 1; } if(crm_mail_host == NULL) { crm_mail_host = "localhost:25"; } if(crm_mail_prefix == NULL) { crm_mail_prefix = "Cluster notification"; } crm_debug("Sending '%s' mail to %s via %s", crm_mail_prefix, crm_mail_to, crm_mail_host); len += strlen(crm_mail_prefix); len += strlen(task); len += strlen(rsc); len += strlen(node); len += strlen(desc); len++; crm_malloc0(crm_mail_subject, len); snprintf(crm_mail_subject, len, "%s - %s event for %s on %s: %s\r\n", crm_mail_prefix, task, rsc, node, desc); len = 0; len += snprintf(crm_mail_body+len, BODY_MAX-len, "\r\n%s\r\n", crm_mail_prefix); len += snprintf(crm_mail_body+len, BODY_MAX-len, "====\r\n\r\n"); if(rc==target_rc) { len += snprintf(crm_mail_body+len, BODY_MAX-len, "Completed operation %s for resource %s on %s\r\n", task, rsc, node); } else { len += snprintf(crm_mail_body+len, BODY_MAX-len, "Operation %s for resource %s on %s failed: %s\r\n", task, rsc, node, desc); } len += snprintf(crm_mail_body+len, BODY_MAX-len, "\r\nDetails:\r\n"); len += snprintf(crm_mail_body+len, BODY_MAX-len, "\toperation status: (%d) %s\r\n", status, op_status2text(status)); if(status == LRM_OP_DONE) { len += snprintf(crm_mail_body+len, BODY_MAX-len, "\tscript returned: (%d) %s\r\n", rc, execra_code2string(rc)); len += snprintf(crm_mail_body+len, BODY_MAX-len, "\texpected return value: (%d) %s\r\n", target_rc, execra_code2string(target_rc)); } auth_client_init(); session = smtp_create_session(); message = smtp_add_message(session); smtp_starttls_enable (session, Starttls_ENABLED); sa.sa_handler = SIG_IGN; sigemptyset (&sa.sa_mask); sa.sa_flags = 0; sigaction (SIGPIPE, &sa, NULL); smtp_set_server (session, crm_mail_host); authctx = auth_create_context (); auth_set_mechanism_flags (authctx, AUTH_PLUGIN_PLAIN, 0); smtp_set_eventcb(session, event_cb, NULL); /* Now tell libESMTP it can use the SMTP AUTH extension. */ if (!noauth) { crm_debug("Adding authentication context"); smtp_auth_set_context (session, authctx); } if(crm_mail_from == NULL) { struct utsname us; char auto_from[BODY_MAX]; CRM_ASSERT(uname(&us) == 0); snprintf(auto_from, BODY_MAX, "crm_mon@%s", us.nodename); smtp_set_reverse_path (message, auto_from); } else { /* NULL is ok */ smtp_set_reverse_path (message, crm_mail_from); } smtp_set_header (message, "To", NULL/*phrase*/, NULL/*addr*/); /* "Phrase" */ smtp_add_recipient (message, crm_mail_to); /* Set the Subject: header and override any subject line in the message headers. */ smtp_set_header (message, "Subject", crm_mail_subject); smtp_set_header_option (message, "Subject", Hdr_OVERRIDE, 1); smtp_set_message_str(message, crm_mail_body); smtp_set_monitorcb (session, crm_smtp_debug, &smtp_debug, 1); if (smtp_start_session (session)) { char buf[128]; int rc = smtp_errno(); crm_err("SMTP server problem: %s (%d)", smtp_strerror (rc, buf, sizeof buf), rc); } else { char buf[128]; int rc = smtp_errno(); const smtp_status_t *smtp_status = smtp_message_transfer_status(message); if(rc != 0) { crm_err("SMTP server problem: %s (%d)", smtp_strerror (rc, buf, sizeof buf), rc); } crm_info("Send status: %d %s", smtp_status->code, crm_str(smtp_status->text)); smtp_enumerate_recipients (message, print_recipient_status, NULL); } smtp_destroy_session(session); auth_destroy_context(authctx); auth_client_exit(); #endif return 0; } static void handle_rsc_op(xmlNode *rsc_op) { int rc = -1; int status = -1; int action = -1; int interval = 0; int target_rc = -1; int transition_num = -1; gboolean notify = TRUE; char *rsc = NULL; char *task = NULL; const char *desc = NULL; const char *node = NULL; const char *magic = NULL; const char *id = ID(rsc_op); char *update_te_uuid = NULL; xmlNode *n = rsc_op; magic = crm_element_value(rsc_op, XML_ATTR_TRANSITION_MAGIC); if(magic == NULL) { /* non-change */ return; } if(FALSE == decode_transition_magic( magic, &update_te_uuid, &transition_num, &action, &status, &rc, &target_rc)) { crm_err("Invalid event %s detected for %s", magic, id); return; } if(parse_op_key(id, &rsc, &task, &interval) == FALSE) { crm_err("Invalid event detected for %s", id); goto bail; } while(n != NULL && safe_str_neq(XML_CIB_TAG_STATE, TYPE(n))) { n = n->parent; } node = crm_element_value(n, XML_ATTR_UNAME); if(node == NULL) { node = ID(n); } if(node == NULL) { crm_err("No node detected for event %s (%s)", magic, id); goto bail; } /* look up where we expected it to be? */ desc = cib_error2string(cib_ok); if(status == LRM_OP_DONE && target_rc == rc) { crm_notice("%s of %s on %s completed: %s", task, rsc, node, desc); if(rc == EXECRA_NOT_RUNNING) { notify = FALSE; } } else if(status == LRM_OP_DONE) { desc = execra_code2string(rc); crm_warn("%s of %s on %s failed: %s", task, rsc, node, desc); } else { desc = op_status2text(status); crm_warn("%s of %s on %s failed: %s", task, rsc, node, desc); } if(notify && snmp_target) { send_snmp_trap(node, rsc, task, target_rc, rc, status, desc); } if(notify && crm_mail_to) { send_smtp_trap(node, rsc, task, target_rc, rc, status, desc); } if(notify && external_agent) { send_custom_trap(node, rsc, task, target_rc, rc, status, desc); } bail: crm_free(update_te_uuid); crm_free(rsc); crm_free(task); } void crm_diff_update(const char *event, xmlNode *msg) { int rc = -1; long now = time(NULL); const char *op = NULL; unsigned int log_level = LOG_INFO; xmlNode *diff = NULL; xmlNode *cib_last = NULL; xmlNode *update = get_message_xml(msg, F_CIB_UPDATE); print_dot(); if(msg == NULL) { crm_err("NULL update"); return; } crm_element_value_int(msg, F_CIB_RC, &rc); op = crm_element_value(msg, F_CIB_OPERATION); diff = get_message_xml(msg, F_CIB_UPDATE_RESULT); if(rc < cib_ok) { log_level = LOG_WARNING; do_crm_log(log_level, "[%s] %s ABORTED: %s", event, op, cib_error2string(rc)); return; } if(current_cib != NULL) { cib_last = current_cib; current_cib = NULL; rc = cib_process_diff(op, cib_force_diff, NULL, NULL, diff, cib_last, ¤t_cib, NULL); if(rc != cib_ok) { crm_debug("Update didn't apply, requesting full copy: %s", cib_error2string(rc)); free_xml(current_cib); current_cib = NULL; } } if(current_cib == NULL) { current_cib = get_cib_copy(cib); } if(log_diffs && diff) { log_cib_diff(LOG_DEBUG, diff, op); } if(log_updates && update != NULL) { print_xml_formatted(LOG_DEBUG, "raw_update", update, NULL); } if(diff && (crm_mail_to || snmp_target || external_agent)) { /* Process operation updates */ xmlXPathObject *xpathObj = xpath_search( diff, "//"F_CIB_UPDATE_RESULT"//"XML_TAG_DIFF_ADDED"//"XML_LRM_TAG_RSC_OP); if(xpathObj && xpathObj->nodesetval->nodeNr > 0) { int lpc = 0, max = xpathObj->nodesetval->nodeNr; for(lpc = 0; lpc < max; lpc++) { xmlNode *rsc_op = getXpathResult(xpathObj, lpc); handle_rsc_op(rsc_op); } } if (xpathObj) { xmlXPathFreeObject(xpathObj); } } if((now - last_refresh) > (reconnect_msec/1000)) { /* Force a refresh */ mon_refresh_display(NULL); } else { mainloop_set_trigger(refresh_trigger); } free_xml(cib_last); } gboolean mon_refresh_display(gpointer user_data) { xmlNode *cib_copy = copy_xml(current_cib); pe_working_set_t data_set; last_refresh = time(NULL); if(cli_config_update(&cib_copy, NULL, FALSE) == FALSE) { if(cib) { cib->cmds->signoff(cib); } print_as("Upgrade failed: %s", cib_error2string(cib_dtd_validation)); if(as_console) { sleep(2); } clean_up(LSB_EXIT_GENERIC); return FALSE; } set_working_set_defaults(&data_set); data_set.input = cib_copy; cluster_status(&data_set); if(as_html_file || web_cgi) { if (print_html_status(&data_set, as_html_file, web_cgi) != 0) { fprintf(stderr, "Critical: Unable to output html file\n"); clean_up(LSB_EXIT_GENERIC); } } else if(daemonize) { /* do nothing */ } else if (simple_status) { print_simple_status(&data_set); if (has_warnings) { clean_up(LSB_EXIT_GENERIC); } } else { print_status(&data_set); } cleanup_calculations(&data_set); return TRUE; } /* * De-init ncurses, signoff from the CIB and deallocate memory. */ void clean_up(int rc) { #if ENABLE_SNMP netsnmp_session *session = crm_snmp_init(NULL, NULL); if(session) { snmp_close(session); snmp_shutdown("snmpapp"); } #endif #if CURSES_ENABLED if(as_console) { as_console = FALSE; echo(); nocbreak(); endwin(); } #endif if (cib != NULL) { cib->cmds->signoff(cib); cib_delete(cib); cib = NULL; } crm_free(as_html_file); crm_free(xml_file); crm_free(pid_file); if(rc >= 0) { exit(rc); } return; }