Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/crm/cib/cibio.c b/crm/cib/cibio.c
index 4fe2c688af..ace0abc18d 100644
--- a/crm/cib/cibio.c
+++ b/crm/cib/cibio.c
@@ -1,432 +1,434 @@
-/* $Id: cibio.c,v 1.21 2004/04/29 15:33:03 andrew Exp $ */
+/* $Id: cibio.c,v 1.22 2004/05/11 17:54:02 andrew Exp $ */
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.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 <portability.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <crm/crm.h>
#include <libxml/tree.h>
#include <crm/cib.h>
#include <cibio.h>
#include <crm/common/msgutils.h> // for getNow()
#include <crm/msg_xml.h>
#include <crm/common/xmlutils.h>
#include <crm/dmalloc_wrapper.h>
const char * local_resource_path[] =
{
XML_CIB_TAG_STATUS,
};
const char * resource_path[] =
{
XML_CIB_TAG_RESOURCES,
};
const char * node_path[] =
{
XML_CIB_TAG_NODES,
};
const char * constraint_path[] =
{
XML_CIB_TAG_CONSTRAINTS,
};
gboolean initialized = FALSE;
xmlNodePtr the_cib = NULL;
xmlNodePtr node_search = NULL;
xmlNodePtr resource_search = NULL;
xmlNodePtr constraint_search = NULL;
xmlNodePtr status_search = NULL;
/*
* It is the callers responsibility to free both the new CIB (output)
* and the new CIB (input)
*/
xmlNodePtr
createEmptyCib(void)
{
xmlNodePtr cib_root = NULL, config = NULL, status = NULL;
cib_root = create_xml_node(NULL, XML_TAG_CIB);
config = create_xml_node(cib_root, XML_CIB_TAG_CONFIGURATION);
status = create_xml_node(cib_root, XML_CIB_TAG_STATUS);
set_node_tstamp(cib_root);
set_node_tstamp(config);
set_node_tstamp(status);
set_xml_property_copy(cib_root, "version", "1");
set_xml_property_copy(cib_root, "generated", "true");
create_xml_node(config, XML_CIB_TAG_NODES);
create_xml_node(config, XML_CIB_TAG_RESOURCES);
create_xml_node(config, XML_CIB_TAG_CONSTRAINTS);
if (verifyCibXml(cib_root)) {
FNRET(cib_root);
}
cl_log(LOG_CRIT,
"The generated CIB did not pass integrity testing!!"
" All hope is lost.");
FNRET(NULL);
}
gboolean
verifyCibXml(xmlNodePtr cib)
{
gboolean is_valid = TRUE;
xmlNodePtr tmp_node = NULL;
FNIN();
if (cib == NULL) {
cl_log(LOG_ERR, "XML Buffer was empty.");
FNRET(FALSE);
}
tmp_node = get_object_root(XML_CIB_TAG_NODES, cib);
if (tmp_node == NULL) is_valid = FALSE;
tmp_node = get_object_root(XML_CIB_TAG_RESOURCES, cib);
if (tmp_node == NULL) is_valid = FALSE;
tmp_node = get_object_root(XML_CIB_TAG_CONSTRAINTS, cib);
if (tmp_node == NULL) is_valid = FALSE;
tmp_node = get_object_root(XML_CIB_TAG_STATUS, cib);
if (tmp_node == NULL) is_valid = FALSE;
// more integrity tests
FNRET(is_valid);
}
/*
* It is the callers responsibility to free the output of this function
*/
xmlNodePtr
readCibXml(char *buffer)
{
xmlNodePtr root = string2xml(buffer);
if (verifyCibXml(root) == FALSE) {
free_xml(root);
FNRET(createEmptyCib());
}
FNRET(root);
}
/*
* It is the callers responsibility to free the output of this function
*/
xmlNodePtr
readCibXmlFile(const char *filename)
{
int s_res = -1;
struct stat buf;
xmlNodePtr root = NULL;
FNIN();
if(filename != NULL) {
s_res = stat(filename, &buf);
}
if (s_res == 0) {
FILE *cib_file = fopen(filename, "r");
root = file2xml(cib_file);
set_xml_property_copy(root, "generated", "false");
fclose(cib_file);
} else {
cl_log(LOG_WARNING,
"Stat of (%s) failed, file does not exist.",
CIB_FILENAME);
}
if (verifyCibXml(root) == FALSE) {
free_xml(root);
// FNRET(createEmptyCib());
root = NULL;
}
FNRET(root);
}
/*
* The caller should never free the return value
*/
xmlNodePtr
get_the_CIB(void)
{
FNIN();
FNRET(the_cib);
}
gboolean
uninitializeCib(void)
{
xmlNodePtr tmp_cib = the_cib;
FNIN();
if(tmp_cib == NULL) {
cl_log(LOG_ERR, "The CIB has already been deallocated.");
FNRET(FALSE);
}
initialized = FALSE;
the_cib = NULL;
node_search = NULL;
resource_search = NULL;
constraint_search = NULL;
status_search = NULL;
cl_log(LOG_WARNING, "Deallocating the CIB.");
free_xml(tmp_cib);
cl_log(LOG_WARNING, "The CIB has been deallocated.");
FNRET(TRUE);
}
/*
* This method will not free the old CIB pointer or the new one.
* We rely on the caller to have saved a pointer to the old CIB
* and to free the old/bad one depending on what is appropriate.
*/
gboolean
initializeCib(xmlNodePtr new_cib)
{
if (verifyCibXml(new_cib)) {
initialized = FALSE;
the_cib = new_cib;
// update search paths
/* not used yet...
node_search =
get_object_root(XML_CIB_TAG_NODES, new_cib);
resource_search =
get_object_root(XML_CIB_TAG_RESOURCES, new_cib);
constraint_search =
get_object_root(XML_CIB_TAG_CONSTRAINTS, new_cib);
status_search =
get_object_root(XML_CIB_TAG_STATUS, new_cib);
*/
initialized = TRUE;
CRM_DEBUG("CIB initialized");
FNRET(TRUE);
}
else {
cl_log(LOG_ERR, "CIB Verification failed");
}
FNRET(FALSE);
}
int
moveFile(const char *oldname,
const char *newname,
gboolean backup,
char *ext)
{
/* move 'oldname' to 'newname' by creating a hard link to it
* and then removing the original hard link
*/
int res = 0;
struct stat tmp;
int s_res = stat(newname, &tmp);
FNIN();
cl_log(LOG_INFO, "Stat of %s (code: %d).", newname, s_res);
if (s_res >= 0)
{
if (backup == TRUE) {
char backname[1024];
static const char *back_ext = "bak";
if (ext != NULL) back_ext = (char*)ext;
snprintf(backname, sizeof(backname)-1,
"%s.%s", newname, back_ext);
moveFile(newname, backname, FALSE, NULL);
} else {
res = unlink(newname);
if (res < 0) {
perror("Could not remove the current backup of Cib");
FNRET(-1);
}
}
}
s_res = stat(oldname, &tmp);
cl_log(LOG_INFO, "Stat of %s (code: %d).", oldname, s_res);
if (s_res >= 0) {
res = link(oldname, newname);
if (res < 0) {
perror("Could not create backup of current Cib");
FNRET(-2);
}
res = unlink(oldname);
if (res < 0) {
perror("Could not unlink the current Cib");
FNRET(-3);
}
}
FNRET(0);
}
int
activateCibBuffer(char *buffer, const char *filename)
{
int result = -1;
xmlNodePtr local_cib = NULL;
FNIN();
local_cib = readCibXml(buffer);
result = activateCibXml(local_cib, filename);
FNRET(result);
}
/*
* This method will free the old CIB pointer on success and the new one
* on failure.
*/
int
activateCibXml(xmlNodePtr new_cib, const char *filename)
{
int error_code = 0;
xmlNodePtr saved_cib = get_the_CIB();
const char *filename_bak = CIB_BACKUP; // calculate
xmlDocPtr foo;
FNIN();
-
-
if (initializeCib(new_cib) == TRUE) {
int res = moveFile(filename, filename_bak, FALSE, NULL);
if (res < 0) {
cl_log(LOG_INFO,
"Could not make backup of the current Cib "
"(code: %d)... aborting update.", res);
error_code = -1;
} else {
cl_log(LOG_INFO,
"Writing CIB out to %s",
CIB_FILENAME);
if (new_cib->doc == NULL) {
cl_log(LOG_INFO,
"Writing of a node tree with a NULL "
"document will fail, creating a new "
"back link.");
foo = xmlNewDoc("1.0");
xmlDocSetRootElement(foo, new_cib);
xmlSetTreeDoc(new_cib,foo);
}
-
+ time_t now = time(NULL);
+ char *now_str = asctime(localtime(&now));
+ set_xml_property_copy(new_cib, "last_written",now_str);
+ free(now_str);
+
/* save it.
* set arg 3 to 0 to disable line breaks,1 to enable
* res == num bytes saved
*/
res = xmlSaveFormatFile(filename,
new_cib->doc,
1);
/* for some reason, reading back after saving with
* line-breaks doesnt go real well
*/
cl_log(LOG_INFO,
"Saved %d bytes to the Cib as XML",
res);
if (res < 0) {
// assume 0 is good
if (moveFile(filename_bak,
filename,
FALSE,
NULL) < -1) {
cl_log(LOG_CRIT,
"Could not restore the "
"backup of the current Cib "
"(code: %d)... panic!",
res);
error_code = -2;
// should probably exit here
} else if (initializeCib(saved_cib) == FALSE) {
// oh we are so dead
cl_log(LOG_CRIT,
"Could not re-initialize "
"with the old CIB. "
"Everything is about to go "
"pear shaped");
error_code = -3;
} else {
cl_log(LOG_CRIT,
"Update of Cib failed "
"(code: %d)... reverted to "
"last known valid version",
res);
error_code = -4;
}
}
}
}
else
{
cl_log(LOG_INFO, "Ignoring invalid or NULL Cib");
error_code = -5;
}
// Make sure memory is cleaned up appropriately
if (error_code != 0) {
// CRM_DEBUG("Freeing new CIB %p", new_cib);
free_xml(new_cib);
} else {
// CRM_DEBUG("Freeing saved CIB %p", saved_cib);
free_xml(saved_cib);
}
FNRET(error_code);
}
diff --git a/crm/cib/cibmessages.c b/crm/cib/cibmessages.c
index 4f15bb416c..133e0d20d0 100644
--- a/crm/cib/cibmessages.c
+++ b/crm/cib/cibmessages.c
@@ -1,463 +1,462 @@
-/* $Id: cibmessages.c,v 1.32 2004/04/29 15:33:03 andrew Exp $ */
+/* $Id: cibmessages.c,v 1.33 2004/05/11 17:54:02 andrew Exp $ */
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.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 <portability.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <crm/crm.h>
#include <clplumbing/cl_log.h>
#include <libxml/tree.h>
#include <time.h>
#include <crm/common/msgutils.h>
#include <crm/common/xmlutils.h>
#include <crm/cib.h>
#include <cibio.h>
#include <crm/msg_xml.h>
#include <clplumbing/cl_log.h>
#include <cibprimatives.h>
#include <cibmessages.h>
#include <crm/dmalloc_wrapper.h>
enum cib_result updateList(xmlNodePtr local_cib,
xmlNodePtr update_command,
xmlNodePtr failed,
int operation,
const char *section);
xmlNodePtr createCibFragmentAnswer(const char *section, xmlNodePtr failed);
gboolean replace_section(const char *section,
xmlNodePtr tmpCib,
xmlNodePtr command);
gboolean check_generation(xmlNodePtr newCib, xmlNodePtr oldCib);
gboolean update_results(xmlNodePtr failed,
xmlNodePtr target,
int operation,
int return_code);
xmlNodePtr
cib_process_request(const char *op,
const xmlNodePtr options,
const xmlNodePtr fragment,
enum cib_result *result)
{
const char *verbose = NULL;
const char *section = NULL;
const char *output_section = NULL;
xmlNodePtr failed = NULL;
xmlNodePtr cib_answer = NULL;
gboolean update_the_cib = FALSE;
int cib_update_op = CIB_OP_NONE;
xmlNodePtr tmpCib;
char *new_value = NULL;
char *old_value = NULL;
int int_value = -1;
FNIN();
*result = CIBRES_OK;
verbose = xmlGetProp(options, XML_ATTR_VERBOSE);
section = xmlGetProp(options, XML_ATTR_FILTER_TYPE);
failed = create_xml_node(NULL, XML_TAG_FAILED);
cl_log(LOG_DEBUG, "[cib] Processing \"%s\" event", op);
if(op == NULL) {
*result = CIBRES_FAILED;
cl_log(LOG_ERR, "No operation specified\n");
} else if(strcmp("noop", op) == 0) {
;
} else if(strcmp("quit", op) == 0) {
cl_log(LOG_WARNING,
"The CRMd has asked us to exit... complying");
exit(0);
} else if (strcmp(CRM_OPERATION_PING, op) == 0) {
cib_answer =
createPingAnswerFragment(CRM_SYSTEM_CIB, "ok");
} else if (strcmp(CRM_OPERATION_BUMP, op) == 0) {
tmpCib = get_cib_copy();
CRM_DEBUG("Handling a %s for section=%s of the cib",
CRM_OPERATION_BUMP, section);
// modify the timestamp
set_node_tstamp(tmpCib);
old_value =
xmlGetProp(get_the_CIB(), XML_ATTR_GENERATION);
if(old_value != NULL) {
new_value = (char*)cl_malloc(128*(sizeof(char)));
int_value = atoi(old_value);
sprintf(new_value, "%d", ++int_value);
} else {
new_value = cl_strdup("0");
}
cl_log(LOG_DEBUG, "Generation %d(%s)->%s",
int_value, old_value, new_value);
set_xml_property_copy(tmpCib, XML_ATTR_GENERATION, new_value);
cl_free(new_value);
if(activateCibXml(tmpCib, CIB_FILENAME) >= 0) {
verbose = "true";
} else {
*result = CIBRES_FAILED;
}
} else if (strcmp("query", op) == 0) {
CRM_DEBUG("Handling a query for section=%s of the cib",
section);
/* force a pick-up of the relevant section before
* returning
*/
verbose = "true";
} else if (strcmp(CRM_OPERATION_ERASE, op) == 0) {
xmlNodePtr new_cib = createEmptyCib();
// Preserve generation counters etc
copy_in_properties(new_cib, get_the_CIB());
if (activateCibXml(new_cib, CIB_FILENAME) < 0) {
*result = CIBRES_FAILED;
}
} else if (strcmp(CRM_OPERATION_CREATE, op) == 0) {
update_the_cib = TRUE;
cib_update_op = CIB_OP_ADD;
} else if (strcmp(CRM_OPERATION_UPDATE, op) == 0
|| strcmp(CRM_OPERATION_WELCOME, op) == 0
|| strcmp(CRM_OPERATION_SHUTDOWN_REQ, op) == 0) {
update_the_cib = TRUE;
cib_update_op = CIB_OP_MODIFY;
} else if (strcmp(CRM_OPERATION_DELETE, op) == 0) {
update_the_cib = TRUE;
cib_update_op = CIB_OP_DELETE;
} else if (strcmp(CRM_OPERATION_REPLACE, op) == 0) {
CRM_DEBUG("Replacing section=%s of the cib", section);
section = xmlGetProp(fragment, XML_ATTR_SECTION);
if (section == NULL
|| strlen(section) == 0
|| strcmp("all", section) == 0) {
tmpCib = copy_xml_node_recursive(
find_xml_node(fragment, XML_TAG_CIB));
} else {
tmpCib = copy_xml_node_recursive(get_the_CIB());
replace_section(section, tmpCib, fragment);
}
/*if(check_generation(cib_updates, tmpCib) == FALSE)
*result = "discarded old update";
else */
if (activateCibXml(tmpCib, CIB_FILENAME) < 0)
*result = CIBRES_FAILED;
} else {
*result = CIBRES_FAILED_NOTSUPPORTED;
cl_log(LOG_ERR, "Action [%s] is not supported by the CIB", op);
}
if (update_the_cib) {
CRM_DEBUG("Backing up CIB");
tmpCib = copy_xml_node_recursive(get_the_CIB());
section = xmlGetProp(fragment, XML_ATTR_SECTION);
CRM_DEBUG("Updating section=%s of the cib (op=%s)",
section, op);
// should we be doing this?
// do logging
// make changes to a temp copy then activate
if(section == NULL) {
cl_log(LOG_ERR, "No section specified in %s",
XML_ATTR_FILTER_TYPE);
*result = CIBRES_FAILED_NOSECTION;
} else if(strcmp("all", section) == 0
&& cib_update_op == CIB_OP_DELETE) {
// delete
/* order is no longer important here */
updateList(tmpCib, fragment, failed, cib_update_op,
XML_CIB_TAG_STATUS);
updateList(tmpCib, fragment, failed, cib_update_op,
XML_CIB_TAG_CONSTRAINTS);
updateList(tmpCib, fragment, failed, cib_update_op,
XML_CIB_TAG_RESOURCES);
updateList(tmpCib, fragment, failed, cib_update_op,
XML_CIB_TAG_NODES);
} else if(strcmp("all", section) == 0) {
/* order is no longer important here */
updateList(tmpCib, fragment, failed, cib_update_op,
XML_CIB_TAG_NODES);
updateList(tmpCib, fragment, failed, cib_update_op,
XML_CIB_TAG_RESOURCES);
updateList(tmpCib, fragment, failed, cib_update_op,
XML_CIB_TAG_CONSTRAINTS);
updateList(tmpCib, fragment, failed, cib_update_op,
XML_CIB_TAG_STATUS);
} else {
*result = updateList(tmpCib, fragment, failed,
cib_update_op, section);
}
CRM_DEBUG("Activating temporary CIB");
/* if(check_generation(cib_updates, tmpCib) == FALSE) */
/* status = "discarded old update"; */
/* else */
if (activateCibXml(tmpCib, CIB_FILENAME) < 0) {
*result = CIBRES_FAILED_ACTIVATION;
} else if (failed->children != NULL) {
*result = CIBRES_FAILED;
}
CRM_DEBUG("CIB update status: %d", *result);
}
output_section = section;
if (failed->children != NULL || *result != CIBRES_OK) {
cib_answer = createCibFragmentAnswer(NULL /*"all"*/, failed);
} else if (verbose != NULL && strcmp("true", verbose) == 0) {
cib_answer = createCibFragmentAnswer(output_section, failed);
}
free_xml(failed);
FNRET(cib_answer);
}
gboolean
replace_section(const char *section, xmlNodePtr tmpCib, xmlNodePtr fragment)
{
xmlNodePtr parent = NULL,
cib_updates = NULL,
new_section = NULL,
old_section = NULL;
FNIN();
cib_updates = find_xml_node(fragment, XML_TAG_CIB);
/* find the old and new versions of the section */
new_section = get_object_root(section, cib_updates);
old_section = get_object_root(section, tmpCib);
if(old_section == NULL) {
cl_log(LOG_ERR,
"The CIB is corrupt, cannot replace missing section %s",
section);
FNRET(FALSE);
} else if(new_section == NULL) {
cl_log(LOG_ERR,
"The CIB is corrupt, cannot set section %s to nothing",
section);
FNRET(FALSE);
}
parent = old_section->parent;
/* unlink and free the old one */
unlink_xml_node(old_section);
free_xml(old_section);
/* add the new copy */
add_node_copy(parent, new_section);
FNRET(TRUE);
}
enum cib_result
updateList(xmlNodePtr local_cib, xmlNodePtr update_fragment, xmlNodePtr failed,
int operation, const char *section)
{
xmlNodePtr child = NULL;
xmlNodePtr this_section = get_object_root(section, local_cib);
xmlNodePtr cib_updates = find_xml_node(update_fragment, XML_TAG_CIB);
xmlNodePtr xml_section = get_object_root(section, cib_updates);
if (section == NULL || xml_section == NULL) {
cl_log(LOG_ERR, "Section %s not found in message."
" CIB update is corrupt, ignoring.", section);
return CIBRES_FAILED_NOSECTION;
}
if(CIB_OP_NONE > operation > CIB_OP_MAX) {
cl_log(LOG_ERR, "Invalid operation on section %s", section);
return CIBRES_FAILED;
}
set_node_tstamp(this_section);
child = xml_section->children;
while(child != NULL) {
if(operation == CIB_OP_DELETE) {
update_results(failed, child, operation,
delete_cib_object(this_section, child));
} else if(operation == CIB_OP_MODIFY) {
update_results(failed, child, operation,
update_cib_object(this_section, child,
FALSE));
} else {
update_results(failed, child, operation,
add_cib_object(this_section, child));
}
child = child->next;
}
if (failed->children != NULL)
return CIBRES_FAILED;
else
return CIBRES_OK;
}
xmlNodePtr
createCibFragmentAnswer(const char *section, xmlNodePtr failed)
{
xmlNodePtr fragment = create_xml_node(NULL, XML_TAG_FRAGMENT);
FNIN();
set_xml_property_copy(fragment, XML_ATTR_SECTION, section);
if (section == NULL
|| strlen(section) == 0
|| strcmp("all", section) == 0) {
add_node_copy(fragment, get_the_CIB());
} else {
xmlNodePtr cib = create_xml_node(fragment, XML_TAG_CIB);
add_node_copy(cib, get_object_root(section, get_the_CIB()));
copy_in_properties(cib, get_the_CIB());
}
if (failed != NULL && failed->children != NULL) {
add_node_copy(fragment, failed);
}
FNRET(fragment);
}
gboolean
check_generation(xmlNodePtr newCib, xmlNodePtr oldCib)
{
char *new_value = xmlGetProp(newCib, XML_ATTR_GENERATION);
char *old_value = xmlGetProp(oldCib, XML_ATTR_GENERATION);
int int_new_value = -1;
int int_old_value = -1;
if(old_value != NULL) int_old_value = atoi(old_value);
if(new_value != NULL) int_new_value = atoi(new_value);
if(int_new_value >= int_old_value) {
return TRUE;
} else {
cl_log(LOG_ERR, "Generation from update (%d) is older than %d",
int_new_value, int_old_value);
}
return FALSE;
}
gboolean
update_results(xmlNodePtr failed,
xmlNodePtr target,
int operation,
int return_code)
{
gboolean was_error = FALSE;
const char *error_msg = NULL;
const char *operation_msg = NULL;
xmlNodePtr xml_node;
FNIN();
if (return_code != CIBRES_OK)
{
error_msg = cib_error2string(return_code);
operation_msg = cib_op2string(operation);
- xml_node = create_xml_node(failed,
- XML_FAIL_TAG_CIB);
+ xml_node = create_xml_node(failed, XML_FAIL_TAG_CIB);
was_error = TRUE;
set_xml_property_copy(xml_node,
XML_FAILCIB_ATTR_ID,
ID(target));
set_xml_property_copy(xml_node,
XML_FAILCIB_ATTR_OBJTYPE,
TYPE(target));
set_xml_property_copy(xml_node,
XML_FAILCIB_ATTR_OP,
operation_msg);
set_xml_property_copy(xml_node,
XML_FAILCIB_ATTR_REASON,
error_msg);
cl_log(LOG_DEBUG,
"Action %s failed: %s (cde=%d)",
operation_msg,
error_msg,
return_code);
}
FNRET(was_error);
}
diff --git a/crm/crmd/election.c b/crm/crmd/election.c
index f6334fa6e2..92b44d83eb 100644
--- a/crm/crmd/election.c
+++ b/crm/crmd/election.c
@@ -1,586 +1,608 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.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 <portability.h>
#include <crm/crm.h>
#include <crmd_fsa.h>
#include <libxml/tree.h>
#include <crm/msg_xml.h>
#include <crm/common/xmlutils.h>
#include <crm/common/ipcutils.h>
#include <crm/common/msgutils.h>
#include <crm/cib.h>
#include <string.h>
#include <crmd_messages.h>
#include <crm/dmalloc_wrapper.h>
GHashTable *joined_nodes = NULL;
/* A_ELECTION_VOTE */
enum crmd_fsa_input
do_election_vote(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
enum crmd_fsa_input election_result = I_NULL;
FNIN();
/* dont vote if we're in one of these states or wanting to shut down */
switch(cur_state) {
case S_RECOVERY:
case S_RECOVERY_DC:
case S_STOPPING:
case S_RELEASE_DC:
case S_TERMINATE:
FNRET(I_NULL);
// log warning
break;
default:
if(is_set(fsa_input_register, R_SHUTDOWN)) {
FNRET(I_NULL);
// log warning
}
break;
}
send_request(NULL, NULL, CRM_OPERATION_VOTE, NULL, CRM_SYSTEM_CRMD);
FNRET(election_result);
}
gboolean
timer_popped(gpointer data)
{
fsa_timer_t *timer = (fsa_timer_t *)data;
cl_log(LOG_INFO, "#!!#!!# Timer %s just popped!",
fsa_input2string(timer->fsa_input));
stopTimer(timer); // dont make it go off again
s_crmd_fsa(C_TIMER_POPPED, timer->fsa_input, NULL);
return TRUE;
}
gboolean
do_dc_heartbeat(gpointer data)
{
fsa_timer_t *timer = (fsa_timer_t *)data;
// cl_log(LOG_DEBUG, "#!!#!!# Heartbeat timer just popped!");
gboolean was_sent = send_request(NULL, NULL, CRM_OPERATION_HBEAT,
NULL, CRM_SYSTEM_CRMD);
if(was_sent == FALSE) {
// this is bad
stopTimer(timer); // dont make it go off again
s_crmd_fsa(C_HEARTBEAT_FAILED, I_SHUTDOWN, NULL);
}
return TRUE;
}
/* A_ELECTION_COUNT */
enum crmd_fsa_input
do_election_count_vote(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
gboolean we_loose = FALSE;
xmlNodePtr vote = (xmlNodePtr)data;
unsigned int my_born = -1, your_born = -1;
int lpc = 0, my_index = -1, your_index = -1;
enum crmd_fsa_input election_result = I_NULL;
const char *vote_from = xmlGetProp(vote, XML_ATTR_HOSTFROM);
const char *lowest_uname = NULL;
int lowest_bornon = 0;
FNIN();
if(vote_from == NULL || strcmp(vote_from, fsa_our_uname) == 0) {
// dont count our own vote
FNRET(election_result);
}
if(fsa_membership_copy->members_size < 1) {
// if even we are not in the cluster then we should not vote
FNRET(I_FAIL);
}
lowest_uname = fsa_membership_copy->members[0].node_uname;
lowest_bornon = fsa_membership_copy->members[0].node_born_on;
for(; lpc < fsa_membership_copy->members_size; lpc++) {
const char *node_uname =
fsa_membership_copy->members[lpc].node_uname;
int this_born_on =
fsa_membership_copy->members[lpc].node_born_on;
if(node_uname == NULL) {
continue;
}
if(strcmp(vote_from, node_uname) == 0) {
your_born = this_born_on;
your_index = lpc;
} else if (strcmp(fsa_our_uname, node_uname) == 0) {
my_born = this_born_on;
my_index = lpc;
}
if(lowest_bornon > this_born_on) {
lowest_uname = node_uname;
lowest_bornon = this_born_on;
} else if(lowest_bornon == this_born_on
&& strcmp(lowest_uname, node_uname) > 0) {
lowest_uname = node_uname;
lowest_bornon = this_born_on;
}
}
#if 0
cl_log(LOG_DEBUG, "%s (bornon=%d), our bornon (%d)",
vote_from, your_born, my_born);
cl_log(LOG_DEBUG, "%s %s %s",
fsa_our_uname,
strcmp(fsa_our_uname, vote_from) < 0?"<":">=",
vote_from);
#endif
cl_log(LOG_DEBUG, "Election winner should be %s (born_on=%d)",
lowest_uname, lowest_bornon);
if(lowest_uname != NULL && strcmp(lowest_uname, fsa_our_uname) == 0){
cl_log(LOG_DEBUG, "Election win: lowest born_on and uname");
election_result = I_ELECTION_DC;
} else if(your_born < my_born) {
cl_log(LOG_DEBUG, "Election fail: born_on");
we_loose = TRUE;
} else if(your_born == my_born
&& strcmp(fsa_our_uname, vote_from) > 0) {
cl_log(LOG_DEBUG, "Election fail: uname");
we_loose = TRUE;
} else {
CRM_DEBUG("We might win... we should vote (possibly again)");
election_result = I_DC_TIMEOUT;
}
if(we_loose) {
if(fsa_input_register & R_THE_DC) {
cl_log(LOG_DEBUG, "Give up the DC");
election_result = I_RELEASE_DC;
} else {
cl_log(LOG_DEBUG, "We werent the DC anyway");
election_result = I_NOT_DC;
}
}
if(we_loose || election_result == I_ELECTION_DC) {
// cancel timer, its been decided
stopTimer(election_timeout);
}
FNRET(election_result);
}
/* A_ELECT_TIMER_START, A_ELECTION_TIMEOUT */
// we won
enum crmd_fsa_input
do_election_timer_ctrl(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
FNIN();
if(action & A_ELECT_TIMER_START) {
CRM_DEBUG("Starting the election timer...");
startTimer(election_timeout);
} else if(action & A_ELECT_TIMER_STOP || action & A_ELECTION_TIMEOUT) {
CRM_DEBUG("Stopping the election timer...");
stopTimer(election_timeout);
} else {
cl_log(LOG_ERR, "unexpected action %s",
fsa_action2string(action));
}
if(action & A_ELECTION_TIMEOUT) {
CRM_DEBUG("The election timer went off, we win!");
FNRET(I_ELECTION_DC);
}
FNRET(I_NULL);
}
/* A_DC_TIMER_STOP, A_DC_TIMER_START */
enum crmd_fsa_input
do_dc_timer_control(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
gboolean timer_op_ok = TRUE;
FNIN();
if(action & A_DC_TIMER_STOP) {
timer_op_ok = stopTimer(election_trigger);
}
/* dont start a timer that wasnt already running */
if(action & A_DC_TIMER_START && timer_op_ok) {
startTimer(election_trigger);
}
FNRET(I_NULL);
}
/* A_DC_TAKEOVER */
enum crmd_fsa_input
do_dc_takeover(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
FNIN();
CRM_DEBUG("################## Taking over the DC ##################");
set_bit_inplace(&fsa_input_register, R_THE_DC);
CRM_DEBUG("Am I the DC? %s", AM_I_DC?"yes":"no");
set_bit_inplace(&fsa_input_register, R_JOIN_OK);
set_bit_inplace(&fsa_input_register, R_INVOKE_PE);
clear_bit_inplace(&fsa_input_register, R_CIB_DONE);
clear_bit_inplace(&fsa_input_register, R_HAVE_CIB);
startTimer(dc_heartbeat);
+
+ /* Update the CIB to indicate we are the DC
+ *
+ * Has the side benefit of setting our state to active
+ * if we are the only node around
+ */
+ xmlNodePtr tmp1 = create_xml_node(NULL, XML_CIB_TAG_STATE);
+
+ set_node_tstamp(tmp1);
+ set_xml_property_copy(tmp1, XML_ATTR_ID, fsa_our_uname);
+ set_xml_property_copy(tmp1, "source", fsa_our_uname);
+ set_xml_property_copy(tmp1, "state", "active");
+ set_xml_property_copy(tmp1, "exp_state", "active");
+ set_xml_property_copy(tmp1, "is_dc", "true");
+
+ xmlNodePtr fragment = create_cib_fragment(tmp1, NULL);
+
+ send_request(NULL, fragment, CRM_OPERATION_UPDATE,
+ NULL, CRM_SYSTEM_DCIB);
+
+ free_xml(fragment);
+ free_xml(tmp1);
FNRET(I_NULL);
}
/* A_DC_RELEASE */
enum crmd_fsa_input
do_dc_release(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
enum crmd_fsa_input result = I_NULL;
FNIN();
CRM_DEBUG("################## Releasing the DC ##################");
stopTimer(dc_heartbeat);
if(action & A_DC_RELEASE) {
clear_bit_inplace(&fsa_input_register, R_THE_DC);
/* get a new CIB from the new DC */
clear_bit_inplace(&fsa_input_register, R_HAVE_CIB);
} else if (action & A_DC_RELEASED) {
if(cur_state == S_STOPPING) {
result = I_SHUTDOWN; // necessary?
result = I_RELEASE_SUCCESS;
}
#if 0
else if( are there errors ) {
// we cant stay up if not healthy
// or perhaps I_ERROR and go to S_RECOVER?
result = I_SHUTDOWN;
}
#endif
else
result = I_RELEASE_SUCCESS;
} else {
cl_log(LOG_ERR, "Warning, do_dc_release invoked for action %s",
fsa_action2string(action));
}
CRM_DEBUG("Am I still the DC? %s", AM_I_DC?"yes":"no");
FNRET(result);
}
/* A_JOIN_WELCOME, A_JOIN_WELCOME_ALL */
enum crmd_fsa_input
do_send_welcome(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
int lpc = 0, size = 0, num_sent = 0;
oc_node_t *members;
gboolean was_sent = TRUE;
FNIN();
if(action & A_JOIN_WELCOME && data == NULL) {
cl_log(LOG_ERR,
"Attempt to send welcome message "
"without a message to reply to!");
FNRET(I_NULL);
} else if(action & A_JOIN_WELCOME) {
xmlNodePtr welcome = (xmlNodePtr)data;
const char *join_to = xmlGetProp(welcome, XML_ATTR_HOSTFROM);
if(join_to != NULL) {
send_request(NULL, NULL, CRM_OPERATION_WELCOME,
join_to, CRM_SYSTEM_CRMD);
}
FNRET(I_NULL);
}
// welcome everyone...
/* Give everyone a chance to join before invoking the PolicyEngine */
stopTimer(integration_timer);
startTimer(integration_timer);
members = fsa_membership_copy->members;
size = fsa_membership_copy->members_size;
if(joined_nodes != NULL) {
g_hash_table_destroy(joined_nodes);
joined_nodes = g_hash_table_new(&g_str_hash, &g_str_equal);
}
for(; members != NULL && lpc < size; lpc++) {
const char *new_node = members[lpc].node_uname;
if(strcmp(fsa_our_uname, new_node) == 0) {
// dont send one to ourselves
continue;
}
CRM_DEBUG("Sending welcome message to %s (%d)",
new_node, was_sent);
num_sent++;
was_sent = was_sent
&& send_request(NULL, NULL, CRM_OPERATION_WELCOME,
new_node, CRM_SYSTEM_CRMD);
CRM_DEBUG("Sent welcome message to %s (%d)",
new_node, was_sent);
}
if(was_sent == FALSE)
FNRET(I_FAIL);
/* No point hanging around in S_INTEGRATION if we're the only ones here! */
if(num_sent == 0) {
// that was the last outstanding join ack)
cl_log(LOG_INFO,"That was the last outstanding join ack");
FNRET(I_SUCCESS);
} else {
cl_log(LOG_DEBUG,
"Still waiting on %d outstanding join acks",
num_sent);
//dont waste time by invoking the pe yet;
}
FNRET(I_NULL);
}
/* A_JOIN_ACK */
enum crmd_fsa_input
do_ack_welcome(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
xmlNodePtr welcome = (xmlNodePtr)data;
xmlNodePtr cib_copy;
xmlNodePtr tmp1;
xmlNodePtr tmp2;
FNIN();
#if 0
if(we are sick) {
log error ;
FNRET(I_NULL);
}
#endif
cib_copy = get_cib_copy();
tmp1 = get_object_root(XML_CIB_TAG_STATUS, cib_copy);
tmp2 = create_cib_fragment(tmp1, NULL);
send_ha_reply(fsa_cluster_conn, welcome, tmp2);
free_xml(tmp2);
free_xml(cib_copy);
FNRET(I_NULL);
}
/* A_ANNOUNCE */
enum crmd_fsa_input
do_announce(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
FNIN();
/* Once we hear from the DC, we can stop the timer
*
* This timer was started either on startup or when a node
* left the CCM list
*/
/* dont announce if we're in one of these states */
switch(cur_state) {
case S_RECOVERY:
case S_RECOVERY_DC:
case S_RELEASE_DC:
case S_TERMINATE:
FNRET(I_NULL);
// log warning
break;
default:
break;
}
if(AM_I_OPERATIONAL) {
send_request(NULL, NULL, CRM_OPERATION_ANNOUNCE,
NULL, CRM_SYSTEM_DC);
} else {
/* Delay announce until we have finished local startup */
FNRET(I_NULL);
}
FNRET(I_NULL);
}
/* A_JOIN_PROCESS_ACK */
enum crmd_fsa_input
do_process_welcome_ack(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
int lpc = 0, size = 0;
oc_node_t *members;
gboolean is_a_member = FALSE;
xmlNodePtr tmp1;
xmlNodePtr join_ack = (xmlNodePtr)data;
xmlNodePtr cib_fragment;
const char *join_from = xmlGetProp(join_ack, XML_ATTR_HOSTFROM);
FNIN();
FNIN();
members = fsa_membership_copy->members;
size = fsa_membership_copy->members_size;
for(; lpc < size; lpc++) {
const char *new_node = members[lpc].node_uname;
if(strcmp(join_from, new_node) == 0) {
is_a_member = TRUE;
}
}
cib_fragment = find_xml_node(join_ack, XML_TAG_FRAGMENT);
if(is_a_member == FALSE) {
cl_log(LOG_ERR, "Node %s is not known to us", join_from);
/* make sure any information from this node is discarded,
* it is invalid
*/
free_xml(cib_fragment);
FNRET(I_FAIL);
}
cl_log(LOG_DEBUG, "Welcoming node %s after ACK", join_from);
// add them to our list of "active" nodes
g_hash_table_insert(joined_nodes, strdup(join_from),strdup(join_from));
if(cib_fragment == NULL) {
cl_log(LOG_ERR,
"No status information was part of the"
" Welcome ACK from %s",
join_from);
FNRET(I_NULL);
}
/* TODO: check the fragment is only for the status section
= get_xml_attr(cib_fragment, NULL,
XML_ATTR_FILTER_TYPE, TRUE); */
/* Make changes so that state=active for this node when the update
* is processed by A_CIB_INVOKE
*/
tmp1 = find_xml_node(cib_fragment, XML_TAG_CIB);
tmp1 = get_object_root(XML_CIB_TAG_STATUS, tmp1);
tmp1 = find_entity(tmp1, XML_CIB_TAG_STATE, join_from, FALSE);
set_xml_property_copy(tmp1, "state", "active");
if(g_hash_table_size(joined_nodes)
== fsa_membership_copy->members_size) {
// that was the last outstanding join ack)
cl_log(LOG_INFO,"That was the last outstanding join ack");
FNRET(I_SUCCESS);
} else {
cl_log(LOG_DEBUG,
"Still waiting on %d outstanding join acks",
size);
//dont waste time by invoking the pe yet;
}
FNRET(I_CIB_OP);
}
diff --git a/crm/crmd/fsa.c b/crm/crmd/fsa.c
index 904e6658ce..994f9667e2 100644
--- a/crm/crmd/fsa.c
+++ b/crm/crmd/fsa.c
@@ -1,1088 +1,1098 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.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 <portability.h>
#include <crm/crm.h>
#include <crmd_fsa.h>
#include <fsa_matrix.h>
#include <fsa_proto.h>
#include <stdio.h>
#include <crm/common/xmlutils.h>
#include <crm/common/msgutils.h>
#include <crm/msg_xml.h>
#include <clplumbing/Gmain_timeout.h>
#include <crmd_messages.h>
#include <string.h>
#include <time.h>
#include <crm/dmalloc_wrapper.h>
long long
do_state_transition(long long actions,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_state next_state,
enum crmd_fsa_input current_input,
void *data);
#ifdef DOT_FSA_ACTIONS
# ifdef FSA_TRACE
# define IF_FSA_ACTION(x,y) \
if(is_set(actions,x)) { \
CRM_DEBUG("Invoking action %s (%.16llx)", \
fsa_action2string(x), x); \
last_action = x; \
actions = clear_bit(actions, x); \
next_input = y(x, cause, cur_state, last_input, data); \
if( (x & O_DC_TICKLE) == 0 && next_input != I_DC_HEARTBEAT ) \
fprintf(dot_strm, \
"\t// %s:\t%s\t(data? %s)\t(result=%s)\n", \
fsa_input2string(cur_input), \
fsa_action2string(x), \
data==NULL?"no":"yes", \
fsa_input2string(next_input)); \
fflush(dot_strm); \
CRM_DEBUG("Result of action %s was %s", \
fsa_action2string(x), fsa_input2string(next_input)); \
}
# else
# define IF_FSA_ACTION(x,y) \
if(is_set(actions,x)) { \
last_action = x; \
actions = clear_bit(actions, x); \
next_input = y(x, cause, cur_state, last_input, data); \
if( (x & O_DC_TICKLE) == 0 && next_input != I_DC_HEARTBEAT ) \
fprintf(dot_strm, \
"\t// %s:\t%s\t(data? %s)\t(result=%s)\n", \
fsa_input2string(cur_input), \
fsa_action2string(x), \
data==NULL?"no":"yes", \
fsa_input2string(next_input)); \
fflush(dot_strm); \
}
# endif
#else
# ifdef FSA_TRACE
# define IF_FSA_ACTION(x,y) \
if(is_set(actions,x)) { \
CRM_DEBUG("Invoking action %s (%.16llx)", \
fsa_action2string(x), x); \
last_action = x; \
actions = clear_bit(actions, x); \
next_input = y(x, cause, cur_state, last_input, data); \
CRM_DEBUG("Result of action %s was %s", \
fsa_action2string(x), fsa_input2string(next_input)); \
}
# else
# define IF_FSA_ACTION(x,y) \
if(is_set(actions,x)) { \
last_action = x; \
actions = clear_bit(actions, x); \
next_input = y(x, cause, cur_state, last_input, data); \
}
# endif
#endif
#define ELSEIF_FSA_ACTION(x,y) else IF_FSA_ACTION(x,y)
const char *dot_intro = "digraph \"g\" {\n"
" size = \"30,30\"\n"
" graph [\n"
" fontsize = \"12\"\n"
" fontname = \"Times-Roman\"\n"
" fontcolor = \"black\"\n"
" bb = \"0,0,398.922306,478.927856\"\n"
" color = \"black\"\n"
" ]\n"
" node [\n"
" fontsize = \"12\"\n"
" fontname = \"Times-Roman\"\n"
" fontcolor = \"black\"\n"
" shape = \"ellipse\"\n"
" color = \"black\"\n"
" ]\n"
" edge [\n"
" fontsize = \"12\"\n"
" fontname = \"Times-Roman\"\n"
" fontcolor = \"black\"\n"
" color = \"black\"\n"
" ]\n"
"// special nodes\n"
" \"S_PENDING\" \n"
" [\n"
" color = \"blue\"\n"
" fontcolor = \"blue\"\n"
" ]\n"
" \"S_TERMINATE\" \n"
" [\n"
" color = \"red\"\n"
" fontcolor = \"red\"\n"
" ]\n"
"\n"
"// DC only nodes\n"
" \"S_RECOVERY_DC\" [ fontcolor = \"green\" ]\n"
" \"S_INTEGRATION\" [ fontcolor = \"green\" ]\n"
" \"S_POLICY_ENGINE\" [ fontcolor = \"green\" ]\n"
" \"S_TRANSITION_ENGINE\" [ fontcolor = \"green\" ]\n"
" \"S_RELEASE_DC\" [ fontcolor = \"green\" ]\n"
" \"S_IDLE\" [ fontcolor = \"green\" ]\n";
static FILE *dot_strm = NULL;
enum crmd_fsa_state fsa_state;
oc_node_list_t *fsa_membership_copy;
ll_cluster_t *fsa_cluster_conn;
ll_lrm_t *fsa_lrm_conn;
long long fsa_input_register;
long long fsa_actions = A_NOTHING;
const char *fsa_our_uname;
fsa_timer_t *election_trigger = NULL; /* */
fsa_timer_t *election_timeout = NULL; /* */
fsa_timer_t *shutdown_escalation_timmer = NULL; /* */
fsa_timer_t *integration_timer = NULL;
fsa_timer_t *dc_heartbeat = NULL;
long long
toggle_bit(long long action_list, long long action)
{
// CRM_DEBUG("Toggling bit %.16llx", action);
action_list ^= action;
// CRM_DEBUG("Result %.16llx", action_list & action);
return action_list;
}
long long
clear_bit(long long action_list, long long action)
{
// CRM_DEBUG("Clearing bit\t%.16llx", action);
// ensure its set
action_list |= action;
// then toggle
action_list = action_list ^ action;
return action_list;
}
long long
set_bit(long long action_list, long long action)
{
// CRM_DEBUG("Adding bit\t%.16llx", action);
action_list |= action;
return action_list;
}
void
toggle_bit_inplace(long long *action_list, long long action)
{
*action_list = toggle_bit(*action_list, action);
}
void
clear_bit_inplace(long long *action_list, long long action)
{
*action_list = clear_bit(*action_list, action);
}
void
set_bit_inplace(long long *action_list, long long action)
{
*action_list = set_bit(*action_list, action);
}
gboolean
is_set(long long action_list, long long action)
{
// CRM_DEBUG("Checking bit\t%.16llx", action);
return ((action_list & action) == action);
}
gboolean
startTimer(fsa_timer_t *timer)
{
if(((int)timer->source_id) < 0) {
timer->source_id =
Gmain_timeout_add(timer->period_ms,
timer->callback,
(void*)timer);
/*
CRM_DEBUG("#!!#!!# Started %s timer (%d)",
fsa_input2string(timer->fsa_input),
timer->source_id);
*/
} else {
cl_log(LOG_INFO, "#!!#!!# Timer %s already running (%d)",
fsa_input2string(timer->fsa_input),
timer->source_id);
return FALSE;
}
return TRUE;
}
gboolean
stopTimer(fsa_timer_t *timer)
{
if(((int)timer->source_id) > 0) {
/*
CRM_DEBUG("#!!#!!# Stopping %s timer (%d)",
fsa_input2string(timer->fsa_input),
timer->source_id);
*/
g_source_remove(timer->source_id);
timer->source_id = -2;
} else {
cl_log(LOG_INFO, "#!!#!!# Timer %s already stopped (%d)",
fsa_input2string(timer->fsa_input),
timer->source_id);
return FALSE;
}
return TRUE;
}
enum crmd_fsa_state
s_crmd_fsa(enum crmd_fsa_cause cause,
enum crmd_fsa_input initial_input,
void *data)
{
long long actions = fsa_actions;
long long new_actions = A_NOTHING;
long long last_action = A_NOTHING;
enum crmd_fsa_input last_input = initial_input;
enum crmd_fsa_input cur_input;
enum crmd_fsa_input next_input;
enum crmd_fsa_state last_state, cur_state, next_state, starting_state;
FNIN();
starting_state = fsa_state;
cur_input = initial_input;
next_input = initial_input;
last_state = starting_state;
cur_state = starting_state;
next_state = starting_state;
#ifdef FSA_TRACE
CRM_DEBUG("FSA invoked with Cause: %s\n\tState: %s, Input: %s",
fsa_cause2string(cause),
fsa_state2string(cur_state),
fsa_input2string(cur_input));
#endif
if(dot_strm == NULL) {
dot_strm = fopen("/tmp/live.dot", "w");
fprintf(dot_strm, "%s", dot_intro);
}
/*
* Process actions in order of priority but do only one
* action at a time to avoid complicating the ordering.
*
* Actions may result in a new I_ event, these are added to
* (not replace) existing actions before the next iteration.
*
*/
while(next_input != I_NULL || actions != A_NOTHING) {
if(next_input == I_WAIT_FOR_EVENT) {
/* we may be waiting for an a-sync task to "happen"
* and until it does, we cant do anything else
*
* Re-add the last action
*/
actions |= last_action;
cl_log(LOG_INFO, "Wait until something else happens");
break;
}
#ifdef FSA_TRACE
CRM_DEBUG("FSA while loop:\tState: %s, Input: %s",
fsa_state2string(cur_state),
fsa_input2string(cur_input));
#endif
/* update input variables */
cur_input = next_input;
if(cur_input != I_NULL) {
last_input = cur_input;
}
/* get the next batch of actions */
new_actions = crmd_fsa_actions[cur_input][cur_state];
if(new_actions != A_NOTHING) {
#ifdef FSA_TRACE
CRM_DEBUG("Adding actions %.16llx", new_actions);
#endif
actions |= new_actions;
}
/* logging : *before* the state is changed */
IF_FSA_ACTION(A_ERROR, do_log)
ELSEIF_FSA_ACTION(A_WARN, do_log)
ELSEIF_FSA_ACTION(A_LOG, do_log)
/* update state variables */
next_state = crmd_fsa_state[cur_input][cur_state];
last_state = cur_state;
cur_state = next_state;
fsa_state = next_state;
/* start doing things... */
/*
* Hook for change of state.
* Allows actions to be added or removed when entering a state
*/
if(last_state != cur_state){
actions = do_state_transition(actions, cause,
last_state, cur_state,
last_input, data);
}
/* this is always run, some inputs/states may make various
* actions irrelevant/invalid
*/
actions = clear_flags(actions, cause, cur_state, cur_input);
/* regular action processing in order of action priority
*
* Make sure all actions that connect to required systems
* are performed first
*/
if(actions == A_NOTHING) {
cl_log(LOG_INFO, "Nothing to do");
next_input = I_NULL;
/* // check registers, see if anything is pending
if(is_set(fsa_input_register, R_SHUTDOWN)) {
CRM_DEBUG("(Re-)invoking shutdown");
next_input = I_SHUTDOWN;
} else if(is_set(fsa_input_register, R_INVOKE_PE)) {
CRM_DEBUG("Invoke the PE somehow");
}
*/
}
/* get out of here NOW! before anything worse happens */
ELSEIF_FSA_ACTION(A_EXIT_1, do_exit)
ELSEIF_FSA_ACTION(A_STARTUP, do_startup)
ELSEIF_FSA_ACTION(A_CIB_START, do_cib_control)
ELSEIF_FSA_ACTION(A_HA_CONNECT, do_ha_control)
ELSEIF_FSA_ACTION(A_LRM_CONNECT,do_lrm_control)
ELSEIF_FSA_ACTION(A_CCM_CONNECT,do_ccm_control)
ELSEIF_FSA_ACTION(A_ANNOUNCE, do_announce)
/* sub-system start */
ELSEIF_FSA_ACTION(A_PE_START, do_pe_control)
ELSEIF_FSA_ACTION(A_TE_START, do_te_control)
/* sub-system restart
*/
ELSEIF_FSA_ACTION(O_CIB_RESTART,do_cib_control)
ELSEIF_FSA_ACTION(O_PE_RESTART, do_pe_control)
ELSEIF_FSA_ACTION(O_TE_RESTART, do_te_control)
ELSEIF_FSA_ACTION(A_STARTED, do_started)
/* DC Timer */
ELSEIF_FSA_ACTION(O_DC_TIMER_RESTART, do_dc_timer_control)
ELSEIF_FSA_ACTION(A_DC_TIMER_STOP, do_dc_timer_control)
ELSEIF_FSA_ACTION(A_DC_TIMER_START, do_dc_timer_control)
/*
* Highest priority actions
*/
ELSEIF_FSA_ACTION(A_TE_COPYTO, do_te_copyto)
ELSEIF_FSA_ACTION(A_SHUTDOWN_REQ, do_shutdown_req)
ELSEIF_FSA_ACTION(A_MSG_ROUTE, do_msg_route)
ELSEIF_FSA_ACTION(A_RECOVER, do_recover)
ELSEIF_FSA_ACTION(A_ELECTION_VOTE, do_election_vote)
ELSEIF_FSA_ACTION(A_ELECT_TIMER_START, do_election_timer_ctrl)
ELSEIF_FSA_ACTION(A_ELECT_TIMER_STOP, do_election_timer_ctrl)
ELSEIF_FSA_ACTION(A_ELECTION_COUNT, do_election_count_vote)
ELSEIF_FSA_ACTION(A_ELECTION_TIMEOUT, do_election_timer_ctrl)
/*
* "Get this over with" actions
*/
ELSEIF_FSA_ACTION(A_MSG_STORE, do_msg_store)
ELSEIF_FSA_ACTION(A_NODE_BLOCK, do_node_block)
/*
* High priority actions
* Update the cache first
*/
ELSEIF_FSA_ACTION(A_CCM_UPDATE_CACHE, do_ccm_update_cache)
ELSEIF_FSA_ACTION(A_CCM_EVENT, do_ccm_event)
/*
* Medium priority actions
*/
ELSEIF_FSA_ACTION(A_DC_TAKEOVER, do_dc_takeover)
ELSEIF_FSA_ACTION(A_DC_RELEASE, do_dc_release)
ELSEIF_FSA_ACTION(A_JOIN_WELCOME_ALL, do_send_welcome)
ELSEIF_FSA_ACTION(A_JOIN_WELCOME, do_send_welcome)
ELSEIF_FSA_ACTION(A_JOIN_ACK, do_ack_welcome)
ELSEIF_FSA_ACTION(A_JOIN_PROCESS_ACK, do_process_welcome_ack)
/*
* Low(er) priority actions
* Make sure the CIB is always updated before invoking the
* PE, and the PE before the TE
*/
ELSEIF_FSA_ACTION(A_UPDATE_NODESTATUS, do_lrm_invoke)
ELSEIF_FSA_ACTION(A_CIB_INVOKE_LOCAL, do_cib_invoke)
ELSEIF_FSA_ACTION(A_CIB_INVOKE, do_cib_invoke)
ELSEIF_FSA_ACTION(A_CIB_BUMPGEN, do_cib_invoke)
ELSEIF_FSA_ACTION(A_LRM_INVOKE, do_lrm_invoke)
ELSEIF_FSA_ACTION(A_LRM_EVENT, do_lrm_event)
ELSEIF_FSA_ACTION(A_TE_CANCEL, do_te_invoke)
ELSEIF_FSA_ACTION(A_PE_INVOKE, do_pe_invoke)
ELSEIF_FSA_ACTION(A_TE_INVOKE, do_te_invoke)
/* sub-system stop */
ELSEIF_FSA_ACTION(A_PE_STOP, do_pe_control)
ELSEIF_FSA_ACTION(A_TE_STOP, do_te_control)
ELSEIF_FSA_ACTION(A_DC_RELEASED, do_dc_release)
ELSEIF_FSA_ACTION(A_HA_DISCONNECT, do_ha_control)
ELSEIF_FSA_ACTION(A_CCM_DISCONNECT, do_ccm_control)
ELSEIF_FSA_ACTION(A_LRM_DISCONNECT, do_lrm_control)
ELSEIF_FSA_ACTION(A_CIB_STOP, do_cib_control)
/* time to go now... */
/* Some of these can probably be consolidated */
ELSEIF_FSA_ACTION(A_SHUTDOWN, do_shutdown)
ELSEIF_FSA_ACTION(A_STOP, do_stop)
/* exit gracefully */
ELSEIF_FSA_ACTION(A_EXIT_0, do_exit)
// ELSEIF_FSA_ACTION(A_, do_)
- else if(is_message()) {
+ else if((actions & A_MSG_PROCESS) != 0
+ || is_message()) {
xmlNodePtr stored_msg = NULL;
fsa_message_queue_t msg = get_message();
if(is_message() == FALSE) {
actions = clear_bit(actions, A_MSG_PROCESS);
}
if(msg == NULL || msg->message == NULL) {
cl_log(LOG_ERR,
"Invalid stored message");
continue;
}
-
+
+ /*
+ * This is where we should clean up old messages
+ * The problem is that we dont always know the
+ * type of the data (and therefore the correct way
+ * to free it). A wrapper is probably required.
+ */
data = msg->message;
#ifdef DOT_FSA_ACTIONS
fprintf(dot_strm,
"\t// %s:\t%s\t(data? %s)",
fsa_input2string(cur_input),
fsa_action2string(A_MSG_PROCESS),
stored_msg==NULL?"no":"yes");
fflush(dot_strm);
#endif
#ifdef FSA_TRACE
CRM_DEBUG("Invoking action %s (%.16llx)",
fsa_action2string(A_MSG_PROCESS),
A_MSG_PROCESS);
#endif
stored_msg = (xmlNodePtr)data;
#ifdef FSA_TRACE
xml_message_debug(stored_msg,"FSA processing message");
#endif
next_input = handle_message(stored_msg);
#ifdef DOT_FSA_ACTIONS
fprintf(dot_strm, "\t(result=%s)\n",
fsa_input2string(next_input));
#endif
CRM_DEBUG("Result of action %s was %s",
fsa_action2string(A_MSG_PROCESS),
fsa_input2string(next_input));
/* Error checking and reporting */
} else if(cur_input != I_NULL && is_set(actions, A_NOTHING)) {
cl_log(LOG_WARNING,
"No action specified for input,state (%s,%s)",
fsa_input2string(cur_input),
fsa_state2string(cur_state));
next_input = I_NULL;
} else if(cur_input == I_NULL && is_set(actions, A_NOTHING)) {
#ifdef FSA_TRACE
cl_log(LOG_INFO, "Nothing left to do");
#endif
} else {
cl_log(LOG_ERR, "Action %s (0x%llx) not supported ",
fsa_action2string(actions), actions);
next_input = I_ERROR;
}
}
#ifdef FSA_TRACE
CRM_DEBUG("################# Exiting the FSA (%s) ##################",
fsa_state2string(fsa_state));
#endif
#ifdef DOT_FSA_ACTIONS
fprintf(dot_strm,
"\t// ### Exiting the FSA (%s)\n",
fsa_state2string(fsa_state));
fflush(dot_strm);
#endif
// cleanup inputs?
fsa_actions = actions;
FNRET(fsa_state);
}
/* A_NODE_BLOCK */
enum crmd_fsa_input
do_node_block(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
xmlNodePtr xml_message = (xmlNodePtr)data;
const char *host_from = xmlGetProp(xml_message, XML_ATTR_HOSTFROM);
FNIN();
(void)host_from;
FNRET(I_NULL);
}
const char *
fsa_input2string(int input)
{
const char *inputAsText = NULL;
switch(input){
case I_NULL:
inputAsText = "I_NULL";
break;
case I_CCM_EVENT:
inputAsText = "I_CCM_EVENT";
break;
case I_CIB_OP:
inputAsText = "I_CIB_OP";
break;
case I_CIB_UPDATE:
inputAsText = "I_CIB_UPDATE";
break;
case I_DC_TIMEOUT:
inputAsText = "I_DC_TIMEOUT";
break;
case I_ELECTION:
inputAsText = "I_ELECTION";
break;
case I_RELEASE_DC:
inputAsText = "I_RELEASE_DC";
break;
case I_ELECTION_DC:
inputAsText = "I_ELECTION_DC";
break;
case I_ERROR:
inputAsText = "I_ERROR";
break;
case I_FAIL:
inputAsText = "I_FAIL";
break;
case I_INTEGRATION_TIMEOUT:
inputAsText = "I_INTEGRATION_TIMEOUT";
break;
case I_NODE_JOIN:
inputAsText = "I_NODE_JOIN";
break;
case I_NODE_LEFT:
inputAsText = "I_NODE_LEFT";
break;
case I_NODE_LEAVING:
inputAsText = "I_NODE_LEAVING";
break;
case I_NOT_DC:
inputAsText = "I_NOT_DC";
break;
case I_RECOVERED:
inputAsText = "I_RECOVERED";
break;
case I_RELEASE_FAIL:
inputAsText = "I_RELEASE_FAIL";
break;
case I_RELEASE_SUCCESS:
inputAsText = "I_RELEASE_SUCCESS";
break;
case I_RESTART:
inputAsText = "I_RESTART";
break;
case I_REQUEST:
inputAsText = "I_REQUEST";
break;
case I_ROUTER:
inputAsText = "I_ROUTER";
break;
case I_SHUTDOWN:
inputAsText = "I_SHUTDOWN";
break;
/* case I_SHUTDOWN_REQ: */
/* inputAsText = "I_SHUTDOWN_REQ"; */
/* break; */
case I_STARTUP:
inputAsText = "I_STARTUP";
break;
case I_SUCCESS:
inputAsText = "I_SUCCESS";
break;
case I_TERMINATE:
inputAsText = "I_TERMINATE";
break;
case I_WELCOME:
inputAsText = "I_WELCOME";
break;
case I_WELCOME_ACK:
inputAsText = "I_WELCOME_ACK";
break;
case I_DC_HEARTBEAT:
inputAsText = "I_DC_HEARTBEAT";
break;
case I_WAIT_FOR_EVENT:
inputAsText = "I_WAIT_FOR_EVENT";
break;
case I_ILLEGAL:
inputAsText = "I_ILLEGAL";
break;
}
if(inputAsText == NULL) {
cl_log(LOG_ERR, "Input %d is unknown", input);
inputAsText = "<UNKNOWN_INPUT>";
}
return inputAsText;
}
const char *
fsa_state2string(int state)
{
const char *stateAsText = NULL;
switch(state){
case S_IDLE:
stateAsText = "S_IDLE";
break;
case S_ELECTION:
stateAsText = "S_ELECTION";
break;
case S_INTEGRATION:
stateAsText = "S_INTEGRATION";
break;
case S_NOT_DC:
stateAsText = "S_NOT_DC";
break;
case S_POLICY_ENGINE:
stateAsText = "S_POLICY_ENGINE";
break;
case S_RECOVERY:
stateAsText = "S_RECOVERY";
break;
case S_RECOVERY_DC:
stateAsText = "S_RECOVERY_DC";
break;
case S_RELEASE_DC:
stateAsText = "S_RELEASE_DC";
break;
case S_PENDING:
stateAsText = "S_PENDING";
break;
case S_STOPPING:
stateAsText = "S_STOPPING";
break;
case S_TERMINATE:
stateAsText = "S_TERMINATE";
break;
case S_TRANSITION_ENGINE:
stateAsText = "S_TRANSITION_ENGINE";
break;
case S_ILLEGAL:
stateAsText = "S_ILLEGAL";
break;
}
if(stateAsText == NULL) {
cl_log(LOG_ERR, "State %d is unknown", state);
stateAsText = "<UNKNOWN_STATE>";
}
return stateAsText;
}
const char *
fsa_cause2string(int cause)
{
const char *causeAsText = NULL;
switch(cause){
case C_UNKNOWN:
causeAsText = "C_UNKNOWN";
break;
case C_STARTUP:
causeAsText = "C_STARTUP";
break;
case C_IPC_MESSAGE:
causeAsText = "C_IPC_MESSAGE";
break;
case C_HA_MESSAGE:
causeAsText = "C_HA_MESSAGE";
break;
case C_CCM_CALLBACK:
causeAsText = "C_CCM_CALLBACK";
break;
case C_TIMER_POPPED:
causeAsText = "C_TIMER_POPPED";
break;
case C_SHUTDOWN:
causeAsText = "C_SHUTDOWN";
break;
case C_HEARTBEAT_FAILED:
causeAsText = "C_HEARTBEAT_FAILED";
break;
case C_SUBSYSTEM_CONNECT:
causeAsText = "C_SUBSYSTEM_CONNECT";
break;
case C_ILLEGAL:
causeAsText = "C_ILLEGAL";
break;
}
if(causeAsText == NULL) {
cl_log(LOG_ERR, "Cause %d is unknown", cause);
causeAsText = "<UNKNOWN_CAUSE>";
}
return causeAsText;
}
const char *
fsa_action2string(long long action)
{
const char *actionAsText = NULL;
switch(action){
case A_NOTHING:
actionAsText = "A_NOTHING";
break;
case O_SHUTDOWN:
actionAsText = "O_SHUTDOWN";
break;
case O_RELEASE:
actionAsText = "O_RELEASE";
break;
case A_STARTUP:
actionAsText = "A_STARTUP";
break;
case A_STARTED:
actionAsText = "A_STARTED";
break;
case A_HA_CONNECT:
actionAsText = "A_HA_CONNECT";
break;
case A_HA_DISCONNECT:
actionAsText = "A_HA_DISCONNECT";
break;
case A_LRM_CONNECT:
actionAsText = "A_LRM_CONNECT";
break;
case A_LRM_DISCONNECT:
actionAsText = "A_LRM_DISCONNECT";
break;
case O_DC_TIMER_RESTART:
actionAsText = "O_DC_TIMER_RESTART";
break;
case A_DC_TIMER_STOP:
actionAsText = "A_DC_TIMER_STOP";
break;
case A_DC_TIMER_START:
actionAsText = "A_DC_TIMER_START";
break;
case A_ELECTION_COUNT:
actionAsText = "A_ELECTION_COUNT";
break;
case A_ELECTION_TIMEOUT:
actionAsText = "A_ELECTION_TIMEOUT";
break;
case A_ELECT_TIMER_START:
actionAsText = "A_ELECT_TIMER_START";
break;
case A_ELECT_TIMER_STOP:
actionAsText = "A_ELECT_TIMER_STOP";
break;
case A_ELECTION_VOTE:
actionAsText = "A_ELECTION_VOTE";
break;
case A_ANNOUNCE:
actionAsText = "A_ANNOUNCE";
break;
case A_JOIN_ACK:
actionAsText = "A_JOIN_ACK";
break;
case A_JOIN_WELCOME:
actionAsText = "A_JOIN_WELCOME";
break;
case A_JOIN_WELCOME_ALL:
actionAsText = "A_JOIN_WELCOME_ALL";
break;
case A_JOIN_PROCESS_ACK:
actionAsText = "A_JOIN_PROCESS_ACK";
break;
case A_MSG_PROCESS:
actionAsText = "A_MSG_PROCESS";
break;
case A_MSG_ROUTE:
actionAsText = "A_MSG_ROUTE";
break;
case A_MSG_STORE:
actionAsText = "A_MSG_STORE";
break;
case A_RECOVER:
actionAsText = "A_RECOVER";
break;
case A_DC_RELEASE:
actionAsText = "A_DC_RELEASE";
break;
case A_DC_RELEASED:
actionAsText = "A_DC_RELEASED";
break;
case A_DC_TAKEOVER:
actionAsText = "A_DC_TAKEOVER";
break;
case A_SHUTDOWN:
actionAsText = "A_SHUTDOWN";
break;
case A_SHUTDOWN_REQ:
actionAsText = "A_SHUTDOWN_REQ";
break;
case A_STOP:
actionAsText = "A_STOP ";
break;
case A_EXIT_0:
actionAsText = "A_EXIT_0";
break;
case A_EXIT_1:
actionAsText = "A_EXIT_1";
break;
case A_CCM_CONNECT:
actionAsText = "A_CCM_CONNECT";
break;
case A_CCM_DISCONNECT:
actionAsText = "A_CCM_DISCONNECT";
break;
case A_CCM_EVENT:
actionAsText = "A_CCM_EVENT";
break;
case A_CCM_UPDATE_CACHE:
actionAsText = "A_CCM_UPDATE_CACHE";
break;
case A_CIB_BUMPGEN:
actionAsText = "A_CIB_BUMPGEN";
break;
case A_CIB_INVOKE:
actionAsText = "A_CIB_INVOKE";
break;
case O_CIB_RESTART:
actionAsText = "O_CIB_RESTART";
break;
case A_CIB_START:
actionAsText = "A_CIB_START";
break;
case A_CIB_STOP:
actionAsText = "A_CIB_STOP";
break;
case A_TE_INVOKE:
actionAsText = "A_TE_INVOKE";
break;
case O_TE_RESTART:
actionAsText = "O_TE_RESTART";
break;
case A_TE_START:
actionAsText = "A_TE_START";
break;
case A_TE_STOP:
actionAsText = "A_TE_STOP";
break;
+ case A_TE_COPYTO:
+ actionAsText = "A_TE_COPYTO";
+ break;
case A_PE_INVOKE:
actionAsText = "A_PE_INVOKE";
break;
case O_PE_RESTART:
actionAsText = "O_PE_RESTART";
break;
case A_PE_START:
actionAsText = "A_PE_START";
break;
case A_PE_STOP:
actionAsText = "A_PE_STOP";
break;
case A_NODE_BLOCK:
actionAsText = "A_NODE_BLOCK";
break;
case A_UPDATE_NODESTATUS:
actionAsText = "A_UPDATE_NODESTATUS";
break;
case A_LOG:
actionAsText = "A_LOG ";
break;
case A_ERROR:
actionAsText = "A_ERROR ";
break;
case A_WARN:
actionAsText = "A_WARN ";
break;
}
if(actionAsText == NULL) {
cl_log(LOG_ERR, "Action %.16llx is unknown", action);
actionAsText = "<UNKNOWN_ACTION>";
}
return actionAsText;
}
long long
do_state_transition(long long actions,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_state next_state,
enum crmd_fsa_input current_input,
void *data)
{
long long tmp = A_NOTHING;
if(current_input != I_NULL
&& (current_input != I_DC_HEARTBEAT || cur_state != S_NOT_DC)){
const char *state_from = fsa_state2string(cur_state);
const char *state_to = fsa_state2string(next_state);
const char *input = fsa_input2string(current_input);
time_t now = time(NULL);
fprintf(dot_strm,
"\t\"%s\" -> \"%s\" [ label =\"%s\" ] // %s",
state_from, state_to, input,
asctime(localtime(&now)));
fflush(dot_strm);
}
switch(next_state) {
case S_PENDING:
case S_NOT_DC:
if(is_set(fsa_input_register, R_SHUTDOWN)){
tmp = set_bit(actions, A_SHUTDOWN_REQ);
}
tmp = clear_bit(actions, A_RECOVER);
break;
case S_RECOVERY_DC:
case S_RECOVERY:
tmp = set_bit(actions, A_RECOVER);
break;
default:
tmp = clear_bit(actions, A_RECOVER);
break;
}
if(tmp != actions) {
cl_log(LOG_INFO, "Action b4 %.16llx ", actions);
cl_log(LOG_INFO, "Action after %.16llx ", tmp);
actions = tmp;
}
return actions;
}
long long
clear_flags(long long actions,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input cur_input)
{
if(is_set(fsa_input_register, R_SHUTDOWN)){
clear_bit_inplace(&actions, A_DC_TIMER_START);
}
switch(cur_state) {
case S_IDLE:
break;
case S_ELECTION:
break;
case S_INTEGRATION:
break;
case S_NOT_DC:
break;
case S_POLICY_ENGINE:
break;
case S_RECOVERY:
break;
case S_RECOVERY_DC:
break;
case S_RELEASE_DC:
break;
case S_PENDING:
break;
case S_STOPPING:
break;
case S_TERMINATE:
break;
case S_TRANSITION_ENGINE:
break;
case S_ILLEGAL:
break;
}
return actions;
}
diff --git a/crm/crmd/messages.c b/crm/crmd/messages.c
index 2bceb701f5..b1e6036c35 100644
--- a/crm/crmd/messages.c
+++ b/crm/crmd/messages.c
@@ -1,865 +1,866 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.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 <portability.h>
#include <crm/crm.h>
#include <string.h>
#include <crmd_fsa.h>
#include <libxml/tree.h>
#include <crm/msg_xml.h>
#include <crm/common/xmlutils.h>
#include <crm/common/msgutils.h>
#include <crm/cib.h>
#include <crmd.h>
#include <crmd_messages.h>
#include <crm/dmalloc_wrapper.h>
FILE *msg_in_strm = NULL;
FILE *router_strm = NULL;
fsa_message_queue_t fsa_message_queue = NULL;
gboolean relay_message(xmlNodePtr xml_relay_message,
gboolean originated_locally);
#ifdef MSG_LOG
# define ROUTER_RESULT(x) char *msg_text = dump_xml(xml_relay_message);\
if(router_strm == NULL) { \
router_strm = fopen("/tmp/router.log", "w"); \
} \
fprintf(router_strm, "[%d RESULT (%s)]\t%s\t%s\n", \
AM_I_DC, \
xmlGetProp(xml_relay_message, XML_ATTR_REFERENCE),\
x, msg_text); \
fflush(router_strm); \
cl_free(msg_text);
#else
# define ROUTER_RESULT(x) CRM_DEBUG(x);
#endif
/* returns the current head of the FIFO queue */
fsa_message_queue_t
put_message(xmlNodePtr new_message)
{
fsa_message_queue_t next_message = (fsa_message_queue_t)
cl_malloc(sizeof(struct fsa_message_queue_s));
CRM_DEBUG("Adding msg to queue");
-
- next_message->message = new_message;
+
+ // make sure to free it properly later
+ next_message->message = copy_xml_node_recursive(new_message);
next_message->next = NULL;
if(fsa_message_queue == NULL) {
fsa_message_queue = next_message;
} else {
fsa_message_queue->next = next_message;
}
CRM_DEBUG("Added msg to queue");
return fsa_message_queue;
}
/* returns the next message */
fsa_message_queue_t
get_message(void)
{
fsa_message_queue_t next_message = NULL;
if(fsa_message_queue != NULL) {
next_message = fsa_message_queue;
fsa_message_queue = fsa_message_queue->next;
next_message->next = NULL;
}
return next_message;
}
/* returns the current head of the FIFO queue */
gboolean
is_message(void)
{
return (fsa_message_queue != NULL
&& fsa_message_queue->message != NULL);
}
/* A_MSG_STORE */
enum crmd_fsa_input
do_msg_store(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
// xmlNodePtr new_message = (xmlNodePtr)data;
FNIN();
// put_message(new_message);
FNRET(I_NULL);
}
/* A_MSG_ROUTE */
enum crmd_fsa_input
do_msg_route(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
enum crmd_fsa_input result = I_NULL;
xmlNodePtr xml_message = (xmlNodePtr)data;
gboolean routed = FALSE, defer = TRUE, do_process = TRUE;
FNIN();
#if 0
// if(cause == C_IPC_MESSAGE) {
if (crmd_authorize_message(root_xml_node,
msg,
curr_client) == FALSE) {
CRM_DEBUG("Message not authorized");
do_process = FALSE;
}
// }
#endif
if(do_process) {
/* try passing the buck first */
routed = relay_message(xml_message, cause==C_IPC_MESSAGE);
if(routed == FALSE) {
defer = TRUE;
/* calculate defer */
result = handle_message(xml_message);
switch(result) {
case I_NULL:
defer = FALSE;
break;
case I_DC_HEARTBEAT:
defer = FALSE;
break;
/* what else should go here? */
default:
CRM_DEBUG("Defering local processing of message");
put_message(xml_message);
result = I_REQUEST;
break;
}
}
}
FNRET(result);
}
void
crmd_ha_input_callback(const struct ha_msg* msg, void* private_data)
{
const char *from = ha_msg_value(msg, F_ORIG);
const char *to = NULL;
xmlNodePtr root_xml_node;
FNIN();
#ifdef MSG_LOG
if(msg_in_strm == NULL) {
msg_in_strm = fopen("/tmp/inbound.log", "w");
}
#endif
if(from == NULL || strcmp(from, fsa_our_uname) == 0) {
#ifdef MSG_LOG
fprintf(msg_in_strm,
"Discarded message [F_SEQ=%s] from ourselves.\n",
ha_msg_value(msg, F_SEQ));
#endif
FNOUT();
}
#ifdef MSG_LOG
fprintf(msg_in_strm, "[%s (%s:%s)]\t%s\n",
from,
ha_msg_value(msg, F_SEQ),
ha_msg_value(msg, F_TYPE),
ha_msg_value(msg, "xml")
);
fflush(msg_in_strm);
#endif
root_xml_node = find_xml_in_hamessage(msg);
to = xmlGetProp(root_xml_node, XML_ATTR_HOSTTO);
if(to != NULL && strlen(to) > 0 && strcmp(to, fsa_our_uname) != 0) {
#ifdef MSG_LOG
fprintf(msg_in_strm,
"Discarding message [F_SEQ=%s] for someone else.",
ha_msg_value(msg, F_SEQ));
#endif
FNOUT();
}
set_xml_property_copy(root_xml_node, XML_ATTR_HOSTFROM, from);
s_crmd_fsa(C_HA_MESSAGE, I_ROUTER, root_xml_node);
free_xml(root_xml_node);
FNOUT();
}
/*
* Apparently returning TRUE means "stay connected, keep doing stuff".
* Returning FALSE means "we're all done, close the connection"
*/
gboolean
crmd_ipc_input_callback(IPC_Channel *client, gpointer user_data)
{
int lpc = 0;
char *buffer = NULL;
IPC_Message *msg = NULL;
gboolean hack_return_good = TRUE;
xmlNodePtr root_xml_node;
crmd_client_t *curr_client = (crmd_client_t*)user_data;
FNIN();
CRM_DEBUG("Processing IPC message from %s",
curr_client->table_key);
while(client->ops->is_message_pending(client)) {
if (client->ch_status == IPC_DISCONNECT) {
/* The message which was pending for us is that
* the IPC status is now IPC_DISCONNECT */
break;
}
if (client->ops->recv(client, &msg) != IPC_OK) {
perror("Receive failure:");
FNRET(!hack_return_good);
}
if (msg == NULL) {
CRM_DEBUG("No message this time");
continue;
}
lpc++;
buffer = (char*)msg->msg_body;
CRM_DEBUG("Processing xml from %s [text=%s]",
curr_client->table_key, buffer);
root_xml_node =
find_xml_in_ipcmessage(msg, FALSE);
if (root_xml_node != NULL) {
if (crmd_authorize_message(root_xml_node,
msg,
curr_client)) {
CRM_DEBUG("Message authorized,about to relay");
s_crmd_fsa(C_IPC_MESSAGE,
I_ROUTER,
root_xml_node);
} else {
CRM_DEBUG("Message not authorized");
}
} else {
cl_log(LOG_INFO,
"IPC Message was not valid... discarding.");
}
free_xml(root_xml_node);
msg->msg_done(msg);
msg = NULL;
buffer = NULL;
root_xml_node = NULL;
}
CRM_DEBUG("Processed %d messages", lpc);
if (client->ch_status == IPC_DISCONNECT)
{
cl_log(LOG_INFO,
"received HUP from %s",
curr_client->table_key);
if (curr_client != NULL) {
struct crm_subsystem_s *the_subsystem = NULL;
if (curr_client->sub_sys == NULL)
CRM_DEBUG("Client had not registered with us yet");
else if (strcmp(CRM_SYSTEM_PENGINE, curr_client->sub_sys) == 0)
the_subsystem = pe_subsystem;
else if (strcmp(CRM_SYSTEM_TENGINE, curr_client->sub_sys) == 0)
the_subsystem = te_subsystem;
else if (strcmp(CRM_SYSTEM_CIB, curr_client->sub_sys) == 0)
the_subsystem = cib_subsystem;
if(the_subsystem != NULL) {
cleanup_subsystem(the_subsystem);
} // else that was a transient client
if (curr_client->table_key != NULL) {
/*
* Key is destroyed below: curr_client->table_key
* Value is cleaned up by G_main_del_IPC_Channel
*/
g_hash_table_remove(ipc_clients,
curr_client->table_key);
}
if(curr_client->client_source != NULL) {
gboolean det =
G_main_del_IPC_Channel(curr_client->client_source);
CRM_DEBUG("crm_client was %s detached",
det?"successfully":"not");
}
cl_free(curr_client->table_key);
cl_free(curr_client->sub_sys);
cl_free(curr_client->uuid);
cl_free(curr_client);
}
CRM_DEBUG("this client has now left the building.");
FNRET(!hack_return_good);
}
FNRET(hack_return_good);
}
/*
* This method adds a copy of xml_response_data
*/
gboolean
send_request(xmlNodePtr msg_options, xmlNodePtr msg_data,
const char *operation, const char *host_to, const char *sys_to)
{
gboolean was_sent = FALSE;
xmlNodePtr request = NULL;
FNIN();
msg_options = set_xml_attr(msg_options, XML_TAG_OPTIONS,
XML_ATTR_OP, operation, TRUE);
request = create_request(msg_options,
msg_data,
host_to,
sys_to,
AM_I_DC?CRM_SYSTEM_DC:CRM_SYSTEM_CRMD,
NULL,
NULL);
// xml_message_debug(request, "Final request...");
was_sent = relay_message(request, TRUE);
if(was_sent == FALSE) {
put_message(request);
}
free_xml(request);
FNRET(was_sent);
}
/*
* This method adds a copy of xml_response_data
*/
gboolean
store_request(xmlNodePtr msg_options, xmlNodePtr msg_data,
const char *operation, const char *host_to, const char *sys_to)
{
xmlNodePtr request = NULL;
FNIN();
msg_options = set_xml_attr(msg_options, XML_TAG_OPTIONS,
XML_ATTR_OP, operation, TRUE);
request = create_request(msg_options,
msg_data,
host_to,
sys_to,
AM_I_DC?CRM_SYSTEM_DC:CRM_SYSTEM_CRMD,
NULL,
NULL);
put_message(request);
FNRET(TRUE);
}
gboolean
relay_message(xmlNodePtr xml_relay_message, gboolean originated_locally)
{
int is_for_dc = 0;
int is_for_dcib = 0;
int is_for_crm = 0;
int is_for_cib = 0;
int is_local = 0;
gboolean dont_cc= TRUE;
gboolean processing_complete = FALSE;
const char *host_to = xmlGetProp(xml_relay_message,XML_ATTR_HOSTTO);
const char *sys_to = xmlGetProp(xml_relay_message,XML_ATTR_SYSTO);
FNIN();
if(xml_relay_message == NULL) {
cl_log(LOG_ERR, "Cannot route empty message");
FNRET(TRUE);
}
if(strcmp("hello", xml_relay_message->name) == 0) {
/* quietly ignore */
FNRET(TRUE);
}
if(strcmp(XML_MSG_TAG, xml_relay_message->name) != 0) {
xml_message_debug(xml_relay_message,
"Bad message type, should be crm_message");
cl_log(LOG_ERR, "Ignoring message of type %s",
xml_relay_message->name);
FNRET(TRUE);
}
if(sys_to == NULL) {
xml_message_debug(xml_relay_message,
"Message did not have any value for sys_to");
cl_log(LOG_ERR, "Message did not have any value for %s",
XML_ATTR_SYSTO);
FNRET(TRUE);
}
is_for_dc = (strcmp(CRM_SYSTEM_DC, sys_to) == 0);
is_for_dcib = (strcmp(CRM_SYSTEM_DCIB, sys_to) == 0);
is_for_cib = (strcmp(CRM_SYSTEM_CIB, sys_to) == 0);
is_for_crm = (strcmp(CRM_SYSTEM_CRMD, sys_to) == 0);
is_local = 0;
if(host_to == NULL || strlen(host_to) == 0) {
if(is_for_dc)
is_local = 0;
else if(is_for_crm && originated_locally)
is_local = 0;
else
is_local = 1;
} else if(strcmp(fsa_our_uname, host_to) == 0) {
is_local=1;
}
#if 0
CRM_DEBUG("is_local %d", is_local);
CRM_DEBUG("is_for_dcib %d", is_for_dcib);
CRM_DEBUG("is_for_dc %d", is_for_dc);
CRM_DEBUG("is_for_crm %d", is_for_crm);
CRM_DEBUG("AM_I_DC %d", AM_I_DC);
CRM_DEBUG("sys_to %s", sys_to);
CRM_DEBUG("host_to %s", host_to);
#endif
if(is_for_dc || is_for_dcib) {
if(AM_I_DC) {
ROUTER_RESULT("Message result: DC/CRMd process");
processing_complete = FALSE; // more to be done by caller
} else if(originated_locally) {
ROUTER_RESULT("Message result: External relay to DC");
send_msg_via_ha(xml_relay_message, NULL);
processing_complete = TRUE;
} else {
ROUTER_RESULT("Message result: Discard, not DC");
processing_complete = TRUE; // discard
}
} else if(is_local && (is_for_crm || is_for_cib)) {
ROUTER_RESULT("Message result: CRMd process");
} else if(is_local) {
if(dont_cc) {
ROUTER_RESULT("Message result: Local relay");
} else {
/* The DC should also get this message */
ROUTER_RESULT("Message result: Local relay with CC");
}
send_msg_via_ipc(xml_relay_message, sys_to);
processing_complete = TRUE & dont_cc;
} else {
if(dont_cc) {
ROUTER_RESULT("Message result: External relay");
} else {
/* The DC should also get this message */
ROUTER_RESULT("Message result: External relay with CC");
}
send_msg_via_ha(xml_relay_message, host_to);
processing_complete = TRUE & dont_cc;
}
FNRET(processing_complete);
}
void
send_msg_via_ha(xmlNodePtr action, const char *dest_node)
{
FNIN();
if (action == NULL) FNOUT();
if (validate_crm_message(action, NULL, NULL, NULL) == NULL)
{
cl_log(LOG_ERR,
"Relay message to (%s) via HA was invalid, ignoring",
dest_node);
FNOUT();
}
// CRM_DEBUG("Relaying message to (%s) via HA", dest_node);
set_xml_property_copy(action, XML_ATTR_HOSTTO, dest_node);
send_xmlha_message(fsa_cluster_conn, action);
FNOUT();
}
void
send_msg_via_ipc(xmlNodePtr action, const char *sys)
{
IPC_Channel *client_channel;
FNIN();
// cl_log(LOG_DEBUG, "relaying msg to sub_sys=%s via IPC", sys);
client_channel =
(IPC_Channel*)g_hash_table_lookup (ipc_clients, sys);
if (client_channel != NULL) {
cl_log(LOG_DEBUG, "Sending message via channel %s.", sys);
send_xmlipc_message(client_channel, action);
} else if(sys != NULL && strcmp(sys, CRM_SYSTEM_CIB) == 0) {
cl_log(LOG_ERR,
"Sub-system (%s) has been incorporated into the CRMd.",
sys);
xml_message_debug(action, "Change the way we handle");
relay_message(process_cib_message(action, TRUE), TRUE);
} else if(sys != NULL && strcmp(sys, CRM_SYSTEM_LRMD) == 0) {
do_lrm_invoke(A_LRM_INVOKE, C_IPC_MESSAGE,
fsa_state, I_MESSAGE, action);
} else {
cl_log(LOG_ERR,
"Unknown Sub-system (%s)... discarding message.",
sys);
}
FNOUT();
}
gboolean
crmd_authorize_message(xmlNodePtr root_xml_node,
IPC_Message *client_msg,
crmd_client_t *curr_client)
{
// check the best case first
const char *sys_from = xmlGetProp(root_xml_node,
XML_ATTR_SYSFROM);
char *uuid = NULL;
char *client_name = NULL;
char *major_version = NULL;
char *minor_version = NULL;
const char *filtered_from;
gpointer table_key = NULL;
gboolean result;
FNIN();
if (sys_from != NULL) {
gboolean can_reply = FALSE; // no-one has registered with this id
filtered_from = sys_from;
/* The CIB can have two names on the DC */
if(strcmp(sys_from, CRM_SYSTEM_DCIB) == 0)
filtered_from = CRM_SYSTEM_CIB;
if (g_hash_table_lookup (ipc_clients, filtered_from) != NULL)
can_reply = TRUE; // reply can be routed
CRM_DEBUG("Message reply can%s be routed from %s.",
can_reply?"":" not", sys_from);
FNRET(can_reply);
}
// otherwise, check if it was a hello message
cl_log(LOG_INFO,
"received client join msg: %s",
(char*)client_msg->msg_body);
result = process_hello_message(client_msg,
&uuid,
&client_name,
&major_version,
&minor_version);
if (result == TRUE) {
// check version
int mav = atoi(major_version);
int miv = atoi(minor_version);
if (mav < 0 || miv < 0) {
cl_log(LOG_ERR,
"Client version (%d:%d) is not acceptable",
mav,
miv);
result = FALSE;
}
cl_free(major_version);
cl_free(minor_version);
}
struct crm_subsystem_s *the_subsystem = NULL;
if (result == TRUE) {
/* if we already have one of those clients
* only applies to te, pe etc. not admin clients
*/
if (client_name == NULL)
CRM_DEBUG("Client had not registered with us yet");
else if (strcmp(CRM_SYSTEM_PENGINE, client_name) == 0)
the_subsystem = pe_subsystem;
else if (strcmp(CRM_SYSTEM_TENGINE, client_name) == 0)
the_subsystem = te_subsystem;
else if (strcmp(CRM_SYSTEM_CIB, client_name) == 0)
the_subsystem = cib_subsystem;
if (the_subsystem != NULL) {
// do we already have one?
result =(fsa_input_register & the_subsystem->flag)==0;
if(result) {
the_subsystem->ipc =
curr_client->client_channel;
} // else we didnt ask for the client to start
} else if(client_name != NULL && uuid != NULL) {
table_key = (gpointer)
generate_hash_key(client_name, uuid);
} else {
result = FALSE;
cl_log(LOG_ERR,
"Bad client details (client_name=%s, uuid=%s)",
client_name, uuid);
}
}
if(result == TRUE && table_key == NULL)
table_key = (gpointer)cl_strdup(client_name);
if (result == TRUE) {
cl_log(LOG_INFO, "Accepted client %s", (char*)table_key);
curr_client->table_key = table_key;
curr_client->sub_sys = cl_strdup(client_name);
curr_client->uuid = cl_strdup(uuid);
g_hash_table_insert (ipc_clients,
table_key,
curr_client->client_channel);
send_hello_message(curr_client->client_channel,
"n/a", CRM_SYSTEM_CRMD,
"0", "1");
cl_log(LOG_INFO, "Updated client list with %s",
(char*)table_key);
if(the_subsystem != NULL) {
set_bit_inplace(&fsa_input_register,
the_subsystem->flag);
}
s_crmd_fsa(C_SUBSYSTEM_CONNECT, I_NULL, NULL);
} else {
cl_log(LOG_ERR, "Rejected client logon request");
curr_client->client_channel->ch_status = IPC_DISC_PENDING;
}
if(uuid != NULL) cl_free(uuid);
if(client_name != NULL) cl_free(client_name);
/* hello messages should never be processed further */
FNRET(FALSE);
}
enum crmd_fsa_input
handle_message(xmlNodePtr stored_msg)
{
enum crmd_fsa_input next_input = I_NULL;
const char *sys_to = get_xml_attr(stored_msg, NULL,
XML_ATTR_SYSTO, TRUE);
// const char *sys_from = get_xml_attr(stored_msg, NULL,
// XML_ATTR_SYSFROM, TRUE);
const char *type = get_xml_attr(stored_msg, NULL,
XML_ATTR_MSGTYPE, TRUE);
const char *op = get_xml_attr(stored_msg, XML_TAG_OPTIONS,
XML_ATTR_OP, TRUE);
// xml_message_debug(stored_msg, "Processing message");
if(type == NULL || op == NULL) {
cl_log(LOG_ERR, "Ignoring message (type=%s), (op=%s)",
type, op);
xml_message_debug(stored_msg, "Bad message");
} else if(strcmp(type, XML_ATTR_REQUEST) == 0){
if(strcmp(op, CRM_OPERATION_VOTE) == 0) {
next_input = I_ELECTION;
} else if(AM_I_DC && strcmp(op, "te_abort") == 0) {
next_input = I_PE_CALC;
} else if(AM_I_DC
&& fsa_state == S_TRANSITION_ENGINE
&& strcmp(op, "te_complete") == 0) {
next_input = I_SUCCESS;
} else if(strcmp(op, CRM_OPERATION_HBEAT) == 0) {
next_input = I_DC_HEARTBEAT;
} else if(strcmp(op, CRM_OPERATION_WELCOME) == 0) {
next_input = I_WELCOME;
} else if(strcmp(op, CRM_OPERATION_SHUTDOWN_REQ) == 0) {
next_input = I_CIB_OP;
} else if(strcmp(op, CRM_OPERATION_SHUTDOWN) == 0) {
next_input = I_TERMINATE;
} else if(strcmp(op, CRM_OPERATION_ANNOUNCE) == 0) {
next_input = I_NODE_JOIN;
} else if(strcmp(op, CRM_OPERATION_REPLACE) == 0
|| strcmp(op, CRM_OPERATION_ERASE) == 0) {
next_input = I_CIB_OP;
fprintf(router_strm, "Message result: CIB Op\n");
} else if(AM_I_DC
&& (strcmp(op, CRM_OPERATION_CREATE) == 0
|| strcmp(op, CRM_OPERATION_UPDATE) == 0
|| strcmp(op, CRM_OPERATION_DELETE) == 0)) {
/* updates should only be performed on the DC */
next_input = I_CIB_OP;
} else if(strcmp(op, CRM_OPERATION_PING) == 0) {
/* eventually do some stuff to figure out
* if we /are/ ok
*/
xmlNodePtr ping =
createPingAnswerFragment(sys_to, "ok");
xmlNodePtr wrapper = create_reply(stored_msg, ping);
relay_message(wrapper, TRUE);
free_xml(wrapper);
} else {
cl_log(LOG_ERR,
"Unexpected request (op=%s) sent to the %s",
op, AM_I_DC?"DC":"CRMd");
}
} else if(strcmp(type, XML_ATTR_RESPONSE) == 0) {
if(strcmp(op, CRM_OPERATION_WELCOME) == 0) {
next_input = I_WELCOME_ACK;
} else if(strcmp(op, "pecalc") == 0) {
// results int eh TE being invoked
next_input = I_SUCCESS;
} else if(strcmp(op, CRM_OPERATION_VOTE) == 0
|| strcmp(op, CRM_OPERATION_HBEAT) == 0
|| strcmp(op, CRM_OPERATION_WELCOME) == 0
|| strcmp(op, CRM_OPERATION_SHUTDOWN_REQ) == 0
|| strcmp(op, CRM_OPERATION_SHUTDOWN) == 0
|| strcmp(op, CRM_OPERATION_ANNOUNCE) == 0) {
next_input = I_NULL;
/* } else if(AM_I_DC */
/* && (strcmp(op, CRM_OPERATION_CREATE) == 0 */
/* || strcmp(op, CRM_OPERATION_UPDATE) == 0 */
/* || strcmp(op, CRM_OPERATION_DELETE) == 0 */
/* || strcmp(op, CRM_OPERATION_REPLACE) == 0 */
/* || strcmp(op, CRM_OPERATION_ERASE) == 0)) { */
/* // perhaps we should do somethign with these replies */
/* fprintf(router_strm, "Message result: CIB Reply\n"); */
} else {
cl_log(LOG_ERR,
"Unexpected response (op=%s) sent to the %s",
op, AM_I_DC?"DC":"CRMd");
next_input = I_NULL;
}
} else {
cl_log(LOG_ERR, "Unexpected message type %s", type);
}
/* CRM_DEBUG("%s: Next input is %s", __FUNCTION__, */
/* fsa_input2string(next_input)); */
return next_input;
}
void lrm_op_callback (lrm_op_t* op)
{
CRM_DEBUG("In lrm_op_callback()");
s_crmd_fsa(C_LRM_OP_CALLBACK, I_LRM_EVENT, op);
}
void lrm_monitor_callback (lrm_mon_t* monitor)
{
CRM_DEBUG("In lrm_monitor_callback()");
s_crmd_fsa(C_LRM_MONITOR_CALLBACK, I_LRM_EVENT, monitor);
}
diff --git a/crm/crmd/subsystems.c b/crm/crmd/subsystems.c
index f28c27abff..8509d4ed1a 100644
--- a/crm/crmd/subsystems.c
+++ b/crm/crmd/subsystems.c
@@ -1,1174 +1,1176 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.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 <portability.h>
#include <crm/crm.h>
#include <crmd_fsa.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h> // for access
#include <clplumbing/cl_signal.h>
#include <clplumbing/realtime.h>
#include <sys/types.h> // for calls to open
#include <sys/stat.h> // for calls to open
#include <fcntl.h> // for calls to open
#include <pwd.h> // for getpwuid
#include <grp.h> // for initgroups
#include <sys/time.h> // for getrlimit
#include <sys/resource.h>// for getrlimit
#include <crm/common/crmutils.h>
#include <crm/common/ipcutils.h>
#include <crm/common/msgutils.h>
#include <crm/msg_xml.h>
#include <crm/common/xmlutils.h>
#include <crm/cib.h>
#include <crmd.h>
#include <crmd_messages.h>
#include <string.h>
#include <errno.h>
#include <crm/dmalloc_wrapper.h>
#define CLIENT_EXIT_WAIT 10
static gboolean stop_subsystem (struct crm_subsystem_s *centry);
static gboolean start_subsystem(struct crm_subsystem_s *centry);
static gboolean run_command (struct crm_subsystem_s *centry,
const char *options,
gboolean update_pid);
xmlNodePtr do_lrm_query(void);
GHashTable *xml2list(xmlNodePtr parent, const char **attr_path, int depth);
gboolean lrm_dispatch(int fd, gpointer user_data);
void do_update_resource(lrm_rsc_t *rsc, int status, int rc, const char *op_type);
struct crm_subsystem_s *cib_subsystem = NULL;
struct crm_subsystem_s *te_subsystem = NULL;
struct crm_subsystem_s *pe_subsystem = NULL;
void
cleanup_subsystem(struct crm_subsystem_s *the_subsystem)
{
int pid_status = -1;
the_subsystem->ipc = NULL;
clear_bit_inplace(&fsa_input_register,
the_subsystem->flag);
/* Forcing client to die */
kill(the_subsystem->pid, -9);
// cleanup the ps entry
waitpid(the_subsystem->pid, &pid_status, WNOHANG);
the_subsystem->pid = -1;
}
/* A_CIB_STOP, A_CIB_START, A_CIB_RESTART, */
enum crmd_fsa_input
do_cib_control(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
enum crmd_fsa_input result = I_NULL;
struct crm_subsystem_s *this_subsys = cib_subsystem;
long long stop_actions = A_CIB_STOP;
long long start_actions = A_CIB_START;
FNIN();
if(action & stop_actions) {
// dont do anything, its embedded now
}
if(action & start_actions) {
if(cur_state != S_STOPPING) {
if(startCib(CIB_FILENAME) == FALSE)
result = I_FAIL;
} else {
cl_log(LOG_INFO,
"Ignoring request to start %s after shutdown",
this_subsys->command);
}
}
FNRET(result);
}
/* A_CIB_INVOKE, A_CIB_BUMPGEN, A_UPDATE_NODESTATUS */
enum crmd_fsa_input
do_cib_invoke(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
xmlNodePtr cib_msg = NULL;
xmlNodePtr answer = NULL;
xmlNodePtr tmp1 = NULL;
xmlNodePtr tmp2 = NULL;
xmlNodePtr new_options = NULL;
const char *req_from;
const char *section = NULL;
FNIN();
if(data != NULL)
cib_msg = (xmlNodePtr)data;
if(action & A_CIB_INVOKE) {
- const char *op = xmlGetProp(cib_msg, XML_ATTR_OP);
+ const char *op = get_xml_attr(cib_msg, XML_TAG_OPTIONS,
+ XML_ATTR_OP, TRUE);
+
if(safe_str_eq(op, CRM_OPERATION_SHUTDOWN_REQ)){
// create update section
tmp2 =
create_xml_node(NULL, XML_CIB_TAG_STATE);
req_from =
xmlGetProp(cib_msg, XML_ATTR_HOSTFROM);
set_xml_property_copy(tmp1, "id", req_from);
set_xml_property_copy(tmp1, "exp_state", "shutdown");
// create fragment
tmp1 = create_cib_fragment(tmp2, NULL);
// add to cib_msg
add_node_copy(cib_msg, tmp1);
free_xml(tmp2);
free_xml(tmp1);
}
set_xml_property_copy(cib_msg, XML_ATTR_SYSTO, "cib");
answer = process_cib_message(cib_msg, TRUE);
if(relay_message(answer, TRUE) == FALSE) {
cl_log(LOG_ERR, "Confused what to do with cib result");
xml_message_debug(answer, "Couldnt route: ");
}
if(op != NULL && AM_I_DC
&& (strcmp(op, CRM_OPERATION_CREATE) == 0
|| strcmp(op, CRM_OPERATION_UPDATE) == 0
|| strcmp(op, CRM_OPERATION_DELETE) == 0
|| strcmp(op, CRM_OPERATION_REPLACE) == 0
|| strcmp(op, CRM_OPERATION_WELCOME) == 0
|| strcmp(op, CRM_OPERATION_SHUTDOWN_REQ) == 0
|| strcmp(op, CRM_OPERATION_ERASE) == 0)) {
FNRET(I_CIB_UPDATE);
}
if(op == NULL) {
xml_message_debug(cib_msg, "Invalid CIB Message");
}
// check the answer, see if we are interested in it also
#if 0
if(interested in reply) {
put_message(answer);
FNRET(I_REQUEST);
}
#endif
free_xml(answer);
/* experimental */
} else if(action & A_CIB_INVOKE_LOCAL) {
answer = process_cib_message(cib_msg, TRUE);
put_message(answer);
FNRET(I_REQUEST);
} else if(action & A_CIB_BUMPGEN) {
// check if the response was ok before next bit
section = get_xml_attr(cib_msg, XML_TAG_OPTIONS,
XML_ATTR_FILTER_TYPE, FALSE);
/* set the section so that we dont always send the
* whole thing
*/
if(section != NULL) {
new_options = set_xml_attr(NULL, XML_TAG_OPTIONS,
XML_ATTR_FILTER_TYPE,
section, TRUE);
}
answer = process_cib_request(CRM_OPERATION_BUMP,
new_options, NULL);
free_xml(new_options);
if(answer == NULL) {
cl_log(LOG_ERR, "Result of BUMP in %s was NULL",
__FUNCTION__);
FNRET(I_FAIL);
}
send_request(NULL, answer, CRM_OPERATION_REPLACE,
NULL, CRM_SYSTEM_CRMD);
free_xml(answer);
} else {
cl_log(LOG_ERR, "Unexpected action %s in %s",
fsa_action2string(action), __FUNCTION__);
}
FNRET(I_NULL);
}
/* A_PE_START, A_PE_STOP, A_TE_RESTART */
enum crmd_fsa_input
do_pe_control(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
enum crmd_fsa_input result = I_NULL;
struct crm_subsystem_s *this_subsys = pe_subsystem;
long long stop_actions = A_PE_STOP;
long long start_actions = A_PE_START;
FNIN();
if(action & stop_actions) {
if(stop_subsystem(this_subsys) == FALSE)
result = I_FAIL;
else if(this_subsys->pid > 0){
int lpc = CLIENT_EXIT_WAIT;
int pid_status = -1;
while(lpc-- > 0
&& this_subsys->pid > 0
&& CL_PID_EXISTS(this_subsys->pid)) {
sleep(1);
waitpid(this_subsys->pid, &pid_status, WNOHANG);
}
if(CL_PID_EXISTS(this_subsys->pid)) {
cl_log(LOG_ERR,
"Process %s is still active with pid=%d",
this_subsys->command, this_subsys->pid);
result = I_FAIL;
}
}
cleanup_subsystem(this_subsys);
}
if(action & start_actions) {
if(cur_state != S_STOPPING) {
if(start_subsystem(this_subsys) == FALSE) {
result = I_FAIL;
cleanup_subsystem(this_subsys);
}
} else {
cl_log(LOG_INFO,
"Ignoring request to start %s while shutting down",
this_subsys->command);
}
}
FNRET(result);
}
/* A_PE_INVOKE */
enum crmd_fsa_input
do_pe_invoke(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
FNIN();
stopTimer(integration_timer);
if(is_set(fsa_input_register, R_PE_CONNECTED) == FALSE){
cl_log(LOG_INFO, "Waiting for the PE to connect");
FNRET(I_WAIT_FOR_EVENT);
}
xmlNodePtr local_cib = get_cib_copy();
CRM_DEBUG("Invoking %s with %p", CRM_SYSTEM_PENGINE, local_cib);
send_request(NULL, local_cib, "pecalc",
NULL, CRM_SYSTEM_PENGINE);
FNRET(I_NULL);
}
/* A_TE_START, A_TE_STOP, A_TE_RESTART */
enum crmd_fsa_input
do_te_control(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
enum crmd_fsa_input result = I_NULL;
struct crm_subsystem_s *this_subsys = te_subsystem;
long long stop_actions = A_TE_STOP;
long long start_actions = A_TE_START;
FNIN();
/* if(action & stop_actions && cur_state != S_STOPPING */
/* && is_set(fsa_input_register, R_TE_PEND)) { */
/* result = I_WAIT_FOR_EVENT; */
/* FNRET(result); */
/* } */
if(action & stop_actions) {
if(stop_subsystem(this_subsys) == FALSE)
result = I_FAIL;
else if(this_subsys->pid > 0){
int lpc = CLIENT_EXIT_WAIT;
int pid_status = -1;
while(lpc-- > 0
&& this_subsys->pid > 0
&& CL_PID_EXISTS(this_subsys->pid)) {
sleep(1);
waitpid(this_subsys->pid, &pid_status, WNOHANG);
}
if(CL_PID_EXISTS(this_subsys->pid)) {
cl_log(LOG_ERR,
"Process %s is still active with pid=%d",
this_subsys->command, this_subsys->pid);
result = I_FAIL;
}
}
cleanup_subsystem(this_subsys);
}
if(action & start_actions) {
if(cur_state != S_STOPPING) {
if(start_subsystem(this_subsys) == FALSE) {
result = I_FAIL;
cleanup_subsystem(this_subsys);
}
} else {
cl_log(LOG_INFO,
"Ignoring request to start %s while shutting down",
this_subsys->command);
}
}
FNRET(result);
}
/* A_TE_COPYTO */
enum crmd_fsa_input
do_te_copyto(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
xmlNodePtr message = copy_xml_node_recursive((xmlNodePtr)data);
xmlNodePtr opts = find_xml_node(message, "options");
const char *true_op = xmlGetProp(opts, XML_ATTR_OP);
FNIN();
set_xml_property_copy(opts, XML_ATTR_OP, "event");
set_xml_property_copy(message, XML_ATTR_SYSTO, CRM_SYSTEM_TENGINE);
set_xml_property_copy(opts, "true_op", true_op);
relay_message(message, FALSE);
free_xml(message);
FNRET(I_NULL);
}
static xmlNodePtr te_last_input = NULL;
/* A_TE_INVOKE, A_TE_CANCEL */
enum crmd_fsa_input
do_te_invoke(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
xmlNodePtr graph = NULL;
xmlNodePtr msg = (xmlNodePtr)data;
FNIN();
if(is_set(fsa_input_register, R_TE_CONNECTED) == FALSE){
cl_log(LOG_INFO, "Waiting for the TE to connect");
if(data != NULL) {
free_xml(te_last_input);
te_last_input = copy_xml_node_recursive(msg);
}
FNRET(I_WAIT_FOR_EVENT);
}
if(msg == NULL) {
msg = te_last_input;
} else {
free_xml(te_last_input);
}
if(action & A_TE_INVOKE) {
graph = find_xml_node(msg, "transition_graph");
if(graph == NULL) {
FNRET(I_FAIL);
}
send_request(NULL, graph, "transition",
NULL, CRM_SYSTEM_TENGINE);
} else {
send_request(NULL, graph, "abort",
NULL, CRM_SYSTEM_TENGINE);
}
FNRET(I_NULL);
}
gboolean
crmd_client_connect(IPC_Channel *client_channel, gpointer user_data)
{
FNIN();
CRM_DEBUG("A client tried to connect... and there was much rejoicing.");
if (client_channel == NULL) {
cl_log(LOG_ERR, "Channel was NULL");
} else if (client_channel->ch_status == IPC_DISCONNECT) {
cl_log(LOG_ERR, "Channel was disconnected");
} else {
crmd_client_t *blank_client =
(crmd_client_t *)cl_malloc(sizeof(crmd_client_t));
if (blank_client == NULL) {
cl_log(LOG_ERR,
"Could not allocate memory for a blank crmd_client_t");
FNRET(FALSE);
}
client_channel->ops->set_recv_qlen(client_channel, 100);
client_channel->ops->set_send_qlen(client_channel, 100);
blank_client->client_channel = client_channel;
blank_client->sub_sys = NULL;
blank_client->uuid = NULL;
blank_client->table_key = NULL;
CRM_DEBUG("Adding IPC Channel to main thread.");
blank_client->client_source =
G_main_add_IPC_Channel(G_PRIORITY_LOW,
client_channel,
FALSE,
crmd_ipc_input_callback,
blank_client,
default_ipc_input_destroy);
}
FNRET(TRUE);
}
static gboolean
stop_subsystem(struct crm_subsystem_s* centry)
{
cl_log(LOG_INFO, "Stopping sub-system \"%s\"", centry->name);
if (centry->pid <= 0) {
cl_log(LOG_ERR,
"OOPS! client %s not running yet",
centry->command);
} else {
cl_log(LOG_INFO, "Sending quit message to %s.", centry->name);
send_request(NULL, NULL, "quit", NULL, centry->name);
}
return TRUE;
}
static gboolean
start_subsystem(struct crm_subsystem_s* centry)
{
cl_log(LOG_INFO, "Starting sub-system \"%s\"", centry->command);
if (centry->pid != 0) {
cl_log(LOG_ERR, "OOPS! client %s already running as pid %d"
, centry->command, (int) centry->pid);
}
return run_command(centry, "-r", TRUE);
}
static gboolean
run_command(struct crm_subsystem_s *centry,
const char *options,
gboolean update_pid)
{
pid_t pid;
struct stat buf;
int s_res,size;
char *cmd_with_options = NULL;
/*
* We need to ensure that the exec will succeed before
* we bother forking. We don't want to respawn something that
* won't exec in the first place.
*/
if (access(centry->path, F_OK|X_OK) != 0) {
cl_perror("Cannot (access) exec %s", centry->path);
return FALSE;
}
s_res = stat(centry->command, &buf);
if(s_res != 0) {
cl_perror("Cannot (stat) exec %s", centry->command);
return FALSE;
}
/* We need to fork so we can make child procs not real time */
switch(pid=fork()) {
case -1: cl_log(LOG_ERR
, "start_a_child_client: Cannot fork.");
return FALSE;
default: /* Parent */
#if 0
NewTrackedProc(pid, 1, PT_LOGVERBOSE
, centry, &ManagedChildTrackOps);
#else
if(update_pid)
centry->pid = pid;
#endif
return TRUE;
case 0: /* Child */
break;
}
/* Child process: start the managed child */
cl_make_normaltime();
setpgid(0,0);
/* Limit peak resource usage, maximize success chances */
if (centry->shortrcount > 0) {
alarm(0);
sleep(1);
}
size = strlen(options);
size += strlen(centry->command);
size += 2; // ' ' + \0
cmd_with_options = cl_malloc((1+size)*sizeof(char));
sprintf(cmd_with_options, "%s %s", centry->command, options);
cmd_with_options[size] = 0;
cl_log(LOG_INFO, "Executing \"%s\" (pid %d)",
cmd_with_options, (int) getpid());
if(CL_SIGINTERRUPT(SIGALRM, 0) < 0) {
cl_perror("Cannot set interrupt for child process %s",
cmd_with_options);
}else{
const char * devnull = "/dev/null";
unsigned int j;
struct rlimit oflimits;
CL_SIGNAL(SIGCHLD, SIG_DFL);
alarm(0);
CL_IGNORE_SIG(SIGALRM);
/* A precautionary measure */
getrlimit(RLIMIT_NOFILE, &oflimits);
for (j=0; j < oflimits.rlim_cur; ++j) {
close(j);
}
(void)devnull;
(void)open(devnull, O_RDONLY); /* Stdin: fd 0 */
(void)open(devnull, O_WRONLY); /* Stdout: fd 1 */
(void)open(devnull, O_WRONLY); /* Stderr: fd 2 */
(void)execl("/bin/sh", "sh", "-c", cmd_with_options, (const char *)NULL);
/* Should not happen */
cl_perror("Cannot exec %s", cmd_with_options);
}
/* Suppress respawning */
exit(100);
// never reached
return TRUE;
}
/* A_LRM_CONNECT */
enum crmd_fsa_input
do_lrm_control(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
enum crmd_fsa_input failed = I_NULL;//I_FAIL;
int ret = HA_OK;
FNIN();
if(action & A_LRM_DISCONNECT) {
fsa_lrm_conn->lrm_ops->signoff(fsa_lrm_conn);
}
if(action & A_LRM_CONNECT) {
CRM_DEBUG("LRM: connect...");
fsa_lrm_conn = ll_lrm_new("lrm");
if(NULL == fsa_lrm_conn) {
return failed;
}
CRM_DEBUG("LRM: sigon...");
ret = fsa_lrm_conn->lrm_ops->signon(fsa_lrm_conn,
"crmd");
if(ret != HA_OK) {
cl_log(LOG_ERR, "Failed to sign on to the LRM");
return failed;
}
CRM_DEBUG("LRM: set_lrm_callback...");
ret = fsa_lrm_conn->lrm_ops->set_lrm_callback(fsa_lrm_conn,
lrm_op_callback,
lrm_monitor_callback);
if(ret != HA_OK) {
cl_log(LOG_ERR, "Failed to set LRM callbacks");
return failed;
}
/* TODO: create a destroy handler that causes
* some recovery to happen
*/
G_main_add_fd(G_PRIORITY_LOW,
fsa_lrm_conn->lrm_ops->inputfd(fsa_lrm_conn),
FALSE,
lrm_dispatch, fsa_lrm_conn,
default_ipc_input_destroy);
}
if(action & ~(A_LRM_CONNECT|A_LRM_DISCONNECT)) {
cl_log(LOG_ERR, "Unexpected action %s in %s",
fsa_action2string(action), __FUNCTION__);
}
FNRET(I_NULL);
}
gboolean lrm_dispatch(int fd, gpointer user_data)
{
ll_lrm_t *lrm = (ll_lrm_t*)user_data;
lrm->lrm_ops->rcvmsg(lrm, FALSE);
return TRUE;
}
xmlNodePtr
do_lrm_query(void)
{
GList* lrm_list = NULL;
GList* element = NULL;
GList* op_list = NULL;
xmlNodePtr agent = NULL;
xmlNodePtr data = create_xml_node(NULL, "lrm");
xmlNodePtr agent_list = create_xml_node(data, "lrm_agents");
xmlNodePtr rsc_list;
char *rsc_type = NULL;
state_flag_t cur_state = 0;
const char *this_op = NULL;
GList* node = NULL;
lrm_list = fsa_lrm_conn->lrm_ops->get_ra_supported(fsa_lrm_conn);
if (NULL != lrm_list) {
GList* element = g_list_first(lrm_list);
while (NULL != element) {
rsc_type = (char*)element->data;
agent =
create_xml_node(agent_list, "lrm_agent");
set_xml_property_copy(agent, "class", rsc_type);
/* we dont have these yet */
set_xml_property_copy(agent, "type", NULL);
set_xml_property_copy(agent, "version", NULL);
element = g_list_next(element);
}
}
g_list_free(lrm_list);
lrm_list = fsa_lrm_conn->lrm_ops->get_all_rscs(fsa_lrm_conn);
rsc_list = create_xml_node(data, "lrm_resources");
if (NULL != lrm_list) {
element = g_list_first(lrm_list);
}
while (NULL != element) {
lrm_rsc_t *the_rsc = (lrm_rsc_t*)element->data;
/* const char* ra_type; */
/* GHashTable* params; */
xmlNodePtr xml_rsc = create_xml_node(rsc_list, "rsc_state");
set_xml_property_copy(xml_rsc, "id", the_rsc->id);
set_xml_property_copy(xml_rsc, "rsc_id", the_rsc->name);
set_xml_property_copy(xml_rsc, "node_id",fsa_our_uname);
CRM_DEBUG("get_cur_state...");
op_list = the_rsc->ops->get_cur_state(the_rsc,
&cur_state);
CRM_DEBUG("\tcurrent state:%s\n",
cur_state==LRM_RSC_IDLE?"Idel":"Busy");
node = g_list_first(op_list);
while(NULL != node){
lrm_op_t* op = (lrm_op_t*)node->data;
this_op = op->op_type;
if(this_op == NULL
|| strcmp(this_op, "status") != 0){
const char *status_text = "<unknown>";
switch(op->status) {
case LRM_OP_DONE:
status_text = "done";
break;
case LRM_OP_CANCELLED:
status_text = "cancelled";
break;
case LRM_OP_TIMEOUT:
status_text = "timeout";
break;
case LRM_OP_NOTSUPPORTED:
status_text = "not suported";
break;
case LRM_OP_ERROR:
status_text = "error";
break;
}
set_xml_property_copy(xml_rsc,
"op_result",
status_text);
set_xml_property_copy(xml_rsc,
"rsc_op",
this_op);
// we only want the last one
break;
}
node = g_list_next(node);
}
element = g_list_next(element);
}
if (NULL != lrm_list) {
g_list_free(lrm_list);
}
return data;
}
/* A_LRM_INVOKE */
enum crmd_fsa_input
do_lrm_invoke(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
void *data)
{
enum crmd_fsa_input next_input = I_NULL;
xmlNodePtr fragment, tmp1;
xmlNodePtr msg;
const char *rsc_path[] =
{
"msg_data",
"rsc_op",
"resource",
"instance_attributes",
"parameters"
};
const char *operation = NULL;
rsc_id_t rid;
const char *id_from_cib = NULL;
const char *crm_op = NULL;
lrm_rsc_t *rsc = NULL;
lrm_mon_t* mon = NULL;
lrm_op_t* op = NULL;
FNIN();
#ifdef USE_FAKE_LRM
msg = (xmlNodePtr)data;
operation = get_xml_attr_nested(msg, rsc_path, DIMOF(rsc_path) -3,
- XML_ATTR_OP, TRUE);
+ "task", TRUE);
id_from_cib = get_xml_attr_nested(msg, rsc_path, DIMOF(rsc_path) -2,
"id", TRUE);
crm_op = get_xml_attr(msg, XML_TAG_OPTIONS, XML_ATTR_OP, TRUE);
if(safe_str_eq(crm_op, "rsc_op")) {
CRM_DEBUG("performing op %s...", operation);
- xmlNodePtr iter = create_xml_node(iter, "lrm_resource");
+ xmlNodePtr iter = create_xml_node(NULL, "lrm_resource");
set_xml_property_copy(iter, XML_ATTR_ID, id_from_cib);
set_xml_property_copy(iter, "last_op", operation);
if(safe_str_eq(operation, "start")){
set_xml_property_copy(iter, "op_status", "started");
} else {
set_xml_property_copy(iter, "op_status", "stopped");
}
set_xml_property_copy(iter, "op_code", "0");
set_xml_property_copy(iter, "op_node", fsa_our_uname);
send_request(NULL, iter, "event",
NULL, CRM_SYSTEM_TENGINE);
}
FNRET(I_NULL);
#endif
if(action & A_UPDATE_NODESTATUS) {
xmlNodePtr data = do_lrm_query();
set_xml_property_copy(data, "replace_lrm", "true");
tmp1 = create_xml_node(NULL, XML_CIB_TAG_STATE);
set_xml_property_copy(tmp1, XML_ATTR_ID, fsa_our_uname);
fragment = create_cib_fragment(tmp1, NULL);
set_xml_property_copy(data, "replace_lrm", "true");
add_node_copy(tmp1, data);
send_request(NULL, fragment, CRM_OPERATION_UPDATE,
NULL, CRM_SYSTEM_DC);
free_xml(fragment);
free_xml(tmp1);
free_xml(data);
FNRET(next_input);
}
cl_log(LOG_WARNING, "Action %s (%.16llx) only kind of supported\n",
fsa_action2string(action), action);
msg = (xmlNodePtr)data;
operation = get_xml_attr_nested(msg, rsc_path, DIMOF(rsc_path) -3,
XML_ATTR_OP, TRUE);
id_from_cib = get_xml_attr_nested(msg, rsc_path, DIMOF(rsc_path) -2,
"id", TRUE);
// only the first 16 chars are used by the LRM
strncpy(rid, id_from_cib, 16);
crm_op = get_xml_attr(msg, XML_TAG_OPTIONS, XML_ATTR_OP, TRUE);
rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid);
if(crm_op != NULL && strcmp(crm_op, "lrm_query") == 0) {
xmlNodePtr data, tmp1, tmp2, reply;
tmp1 = create_xml_node(NULL, XML_CIB_TAG_STATE);
set_xml_property_copy(tmp1, XML_ATTR_ID, fsa_our_uname);
data = create_cib_fragment(tmp1, NULL);
tmp2 = do_lrm_query();
add_node_copy(tmp1, tmp2);
reply = create_reply(msg, data);
relay_message(reply, TRUE);
free_xml(data);
free_xml(reply);
free_xml(tmp2);
free_xml(tmp1);
} else if(operation != NULL && strcmp(operation, "monitor") == 0) {
if(rsc == NULL) {
cl_log(LOG_ERR, "Could not find resource to monitor");
FNRET(I_FAIL);
}
mon = g_new(lrm_mon_t, 1);
mon->op_type = "status";
mon->params = NULL;
mon->timeout = 0;
mon->user_data = rsc;
mon->mode = LRM_MONITOR_SET;
mon->interval = 2;
mon->target = 1;
rsc->ops->set_monitor(rsc,mon);
mon = g_new(lrm_mon_t, 1);
} else if(operation != NULL) {
if(rsc == NULL) {
// add it to the list
CRM_DEBUG("add_rsc...");
fsa_lrm_conn->lrm_ops->add_rsc(
fsa_lrm_conn, rid,
get_xml_attr_nested(msg,
rsc_path,
DIMOF(rsc_path) -2,
"class", TRUE),
get_xml_attr_nested(msg,
rsc_path,
DIMOF(rsc_path) -2,
"type", TRUE),
NULL);
rsc = fsa_lrm_conn->lrm_ops->get_rsc(
fsa_lrm_conn, rid);
}
if(rsc == NULL) {
cl_log(LOG_ERR, "Could not add resource to LRM");
FNRET(I_FAIL);
}
// now do the op
CRM_DEBUG("performing op %s...", operation);
op = g_new(lrm_op_t, 1);
op->op_type = operation;
op->params = xml2list(msg, rsc_path, DIMOF(rsc_path));
op->timeout = 0;
op->user_data = rsc;
rsc->ops->perform_op(rsc, op);
}
FNRET(next_input);
}
GHashTable *
xml2list(xmlNodePtr parent, const char**attr_path, int depth)
{
xmlNodePtr node_iter = NULL;
GHashTable *nvpair_hash =
g_hash_table_new(&g_str_hash, &g_str_equal);
xmlNodePtr nvpair_list =
find_xml_node_nested(parent, attr_path, depth);
if(nvpair_list != NULL){
node_iter = nvpair_list->children;
while(node_iter != NULL) {
const char *key = xmlGetProp(node_iter, "name");
const char *value = xmlGetProp(node_iter, "value");
CRM_DEBUG("Added %s=%s", key, value);
g_hash_table_insert (nvpair_hash,
cl_strdup(key),
cl_strdup(value));
node_iter = node_iter->next;
}
}
return nvpair_hash;
}
void
do_update_resource(lrm_rsc_t *rsc, int status, int rc, const char *op_type)
{
/*
<status>
<nodes_status id=uname>
<lrm>
<lrm_resources>
<lrm_resource id=>
</...>
*/
xmlNodePtr update, iter;
char *tmp = NULL;
xmlNodePtr fragment, tmp1;
update = create_xml_node(NULL, "node_state");
set_xml_property_copy(update, XML_ATTR_ID, fsa_our_uname);
iter = create_xml_node(update, "lrm");
iter = create_xml_node(iter, "lrm_resources");
iter = create_xml_node(iter, "lrm_resource");
set_xml_property_copy(iter, XML_ATTR_ID, rsc->id);
set_xml_property_copy(iter, "last_op", op_type);
tmp = crm_itoa(status);
set_xml_property_copy(iter, "op_status", tmp);
cl_free(tmp);
tmp = crm_itoa(rc);
set_xml_property_copy(iter, "op_code", tmp);
cl_free(tmp);
set_xml_property_copy(iter, "op_node", fsa_our_uname);
tmp1 = create_xml_node(NULL, XML_CIB_TAG_STATE);
set_xml_property_copy(tmp1, XML_ATTR_ID, fsa_our_uname);
add_node_copy(tmp1, update);
fragment = create_cib_fragment(tmp1, NULL);
send_request(NULL, fragment, CRM_OPERATION_UPDATE,
NULL, CRM_SYSTEM_DCIB);
free_xml(fragment);
free_xml(update);
free_xml(tmp1);
}
enum crmd_fsa_input
do_lrm_event(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input cur_input,
void *data)
{
FNIN();
if(cause == C_LRM_MONITOR_CALLBACK) {
lrm_mon_t* monitor = (lrm_mon_t*)data;
lrm_rsc_t* rsc = monitor->rsc;
switch(monitor->status) {
case LRM_OP_DONE:
CRM_DEBUG("An LRM monitor operation passed");
FNRET(I_NULL);
break;
case LRM_OP_CANCELLED:
case LRM_OP_TIMEOUT:
case LRM_OP_NOTSUPPORTED:
case LRM_OP_ERROR:
cl_log(LOG_ERR,
"An LRM monitor operation failed"
" or was aborted");
do_update_resource(rsc,
monitor->status,
monitor->rc,
monitor->op_type);
break;
}
} else if(cause == C_LRM_OP_CALLBACK) {
lrm_op_t* op = (lrm_op_t*)data;
lrm_rsc_t* rsc = op->rsc;
switch(op->status) {
case LRM_OP_CANCELLED:
case LRM_OP_TIMEOUT:
case LRM_OP_NOTSUPPORTED:
case LRM_OP_ERROR:
cl_log(LOG_ERR,
"An LRM operation failed"
" or was aborted");
// keep going
case LRM_OP_DONE:
do_update_resource(rsc,
op->status,
op->rc,
op->op_type);
break;
}
} else {
FNRET(I_FAIL);
}
FNRET(I_NULL);
}
diff --git a/crm/pengine/pengine.c b/crm/pengine/pengine.c
index 33d8636f1f..3cb03e4e61 100755
--- a/crm/pengine/pengine.c
+++ b/crm/pengine/pengine.c
@@ -1,1888 +1,1890 @@
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/xmlutils.h>
#include <crm/common/crmutils.h>
#include <crm/common/msgutils.h>
#include <crm/cib.h>
#include <glib.h>
#include <libxml/tree.h>
#include <pengine.h>
#include <pe_utils.h>
xmlNodePtr do_calculations(xmlNodePtr cib_object);
gboolean process_pe_message(xmlNodePtr msg, IPC_Channel *sender);
void color_resource(resource_t *lh_resource,
GSListPtr *colors,
GSListPtr resources);
gboolean create_rsc_to_rsc(const char *id, enum con_strength strength,
resource_t *rsc_lh, resource_t *rsc_rh);
gboolean create_ordering(const char *id, enum con_strength strength,
resource_t *rsc_lh, resource_t *rsc_rh,
GSListPtr *action_constraints);
gboolean unpack_constraints(xmlNodePtr xml_constraints,
GSListPtr nodes, GSListPtr resources,
GSListPtr *node_constraints,
GSListPtr *action_constraints);
gboolean unpack_resources(xmlNodePtr xml_resources,
GSListPtr *resources,
GSListPtr *actions,
GSListPtr *action_cons,
GSListPtr all_nodes);
gboolean unpack_nodes(xmlNodePtr xml_nodes, GSListPtr *nodes);
gboolean unpack_status(xmlNodePtr status,
GSListPtr nodes,
GSListPtr rsc_list,
GSListPtr *node_constraints);
gboolean apply_node_constraints(GSListPtr constraints,
GSListPtr resources,
GSListPtr nodes);
gboolean is_active(rsc_to_node_t *cons);
gboolean choose_node_from_list(GSListPtr colors,
color_t *color,
GSListPtr nodes);
gboolean unpack_rsc_to_attr(xmlNodePtr xml_obj,
GSListPtr rsc_list,
GSListPtr node_list,
GSListPtr *node_constraints);
gboolean unpack_rsc_to_node(xmlNodePtr xml_obj,
GSListPtr rsc_list,
GSListPtr node_list,
GSListPtr *node_constraints);
gboolean unpack_rsc_to_rsc(xmlNodePtr xml_obj,
GSListPtr rsc_list,
GSListPtr *action_constraints);
gboolean choose_color(resource_t *lh_resource, GSListPtr candidate_colors);
gboolean strict_postproc(rsc_to_rsc_t *constraint,
color_t *local_color,
color_t *other_color,
GSListPtr *colors,
GSListPtr resources);
gboolean strict_preproc(rsc_to_rsc_t *constraint,
color_t *local_color,
color_t *other_color,
GSListPtr *colors,
GSListPtr resources);
gboolean update_node_weight(rsc_to_node_t *cons, char *id, GSListPtr nodes);
gboolean process_node_lrm_state(node_t *node,
xmlNodePtr lrm_state,
GSListPtr rsc_list,
GSListPtr nodes,
GSListPtr *node_constraints);
GSListPtr match_attrs(xmlNodePtr attr_exp, GSListPtr node_list);
gboolean update_runnable(GSListPtr actions);
GSListPtr create_action_set(action_t *action);
/*
GSListPtr rsc_list = NULL;
GSListPtr node_list = NULL;
GSListPtr node_cons_list = NULL;
GSListPtr rsc_cons_list = NULL;
GSListPtr action_list = NULL;
GSListPtr action_set_list = NULL;
GSListPtr action_cons_list = NULL;
GSListPtr colors = NULL;
GSListPtr stonith_list = NULL;
GSListPtr shutdown_list = NULL;
color_t *current_color = NULL;
xmlNodePtr xml_set_of_sets = NULL;
*/
color_t *no_color = NULL;
int max_valid_nodes = 0;
int order_id = 1;
int action_id = 1;
gboolean pe_debug = FALSE;
gboolean pe_debug_saved = FALSE;
/*
* Unpack everything
* At the end you'll have:
* - A list of nodes
* - A list of resources (each with any dependancies 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 dependancies)
*/
gboolean
stage0(xmlNodePtr cib,
GSListPtr *resources,
GSListPtr *nodes, GSListPtr *node_constraints,
GSListPtr *actions, GSListPtr *action_constraints,
GSListPtr *stonith_list, GSListPtr *shutdown_list)
{
int lpc = 0;
xmlNodePtr cib_nodes = get_object_root("nodes", cib);
xmlNodePtr cib_status = get_object_root("status", cib);
xmlNodePtr cib_resources = get_object_root("resources", cib);
xmlNodePtr cib_constraints = get_object_root("constraints", cib);
unpack_nodes(safe_val(NULL, cib_nodes, children), nodes);
unpack_resources(safe_val(NULL, cib_resources, children),
resources, actions, action_constraints, *nodes);
unpack_status(safe_val(NULL, cib_status, children),
*nodes, *resources, node_constraints);
unpack_constraints(safe_val(NULL, cib_constraints, children),
*nodes, *resources,
node_constraints, action_constraints);
slist_iter(
node, node_t, *nodes, lpc,
if(node->details->shutdown) {
*shutdown_list = g_slist_append(*shutdown_list, node);
} else if(node->details->unclean) {
*stonith_list = g_slist_append(*stonith_list, node);
}
);
return TRUE;
}
/*
* Count how many valid nodes we have (so we know the maximum number of
* colors we can resolve).
*
* Apply node constraints (ie. filter the "allowed_nodes" part of resources
*/
gboolean
stage1(GSListPtr node_constraints, GSListPtr nodes, GSListPtr resources)
{
int lpc = 0;
slist_iter(
node, node_t, nodes, lpc,
if(node == NULL) {
// error
} else if(node->weight >= 0.0
&& node->details->online
&& node->details->type == node_member) {
max_valid_nodes++;
}
);
apply_node_constraints(node_constraints, nodes, resources);
return TRUE;
}
/*
* Choose a color for all resources from highest priority and "must"
* dependancies to lowest, creating new colors as necessary (returned
* as "colors").
*
* Some nodes may be colored as a "no_color" meaning that it was unresolvable
* given the current node stati and constraints.
*/
gboolean
stage2(GSListPtr sorted_rscs, GSListPtr sorted_nodes, GSListPtr *colors)
{
int lpc = 0;
color_t *current_color = NULL;
// Set initial color
// Set color.candidate_nodes = all active nodes
no_color = create_color(colors, NULL, NULL);
current_color = create_color(colors, sorted_nodes, sorted_rscs);
// Set resource.color = color (all resources)
// Set resource.provisional = TRUE (all resources)
slist_iter(
this_resource, resource_t, sorted_rscs, lpc,
this_resource->color = current_color;
this_resource->provisional = TRUE;
);
pdebug("initialized resources to default color");
// Take (next) highest resource
slist_iter(
lh_resource, resource_t, sorted_rscs, lpc,
// if resource.provisional == FALSE, repeat
if(lh_resource->provisional == FALSE) {
// already processed this resource
continue;
}
color_resource(lh_resource, colors, sorted_rscs);
// next resource
);
return TRUE;
}
/*
* not sure if this is a good idea or not, but eventually we might like
* to utilize as many nodes as possible... and this might be a convienient
* hook
*/
gboolean
stage3(GSListPtr colors)
{
// not sure if this is a good idea or not
if(g_slist_length(colors) > max_valid_nodes) {
// we need to consolidate some
} else if(g_slist_length(colors) < max_valid_nodes) {
// we can create a few more
}
return TRUE;
}
#define color_n_nodes color_n->details->candidate_nodes
#define color_n_plus_1_nodes color_n_plus_1->details->candidate_nodes
/*
* Choose a node for each (if possible) color
*/
gboolean
stage4(GSListPtr colors)
{
int lpc = 0;
color_t *color_n = NULL;
color_t *color_n_plus_1 = NULL;
for(lpc = 0; lpc < g_slist_length(colors); lpc++) {
color_n = color_n_plus_1;
color_n_plus_1 = (color_t*)g_slist_nth_data(colors, lpc);
pdebug_action(print_color("Choose node for...", color_n, FALSE));
if(color_n == NULL) {
continue;
}
GSListPtr xor = node_list_xor(color_n_nodes,
color_n_plus_1_nodes);
GSListPtr minus = node_list_minus(color_n_nodes,
color_n_plus_1_nodes);
if(g_slist_length(xor) == 0 || g_slist_length(minus) == 0) {
pdebug("Choose any node from our list");
choose_node_from_list(colors, color_n, color_n_nodes);
} else {
pdebug("Choose a node not in n+1");
choose_node_from_list(colors, color_n, minus);
}
}
// choose last color
if(color_n_plus_1 != NULL) {
pdebug_action(print_color("Choose node for last color...",
color_n_plus_1,
FALSE));
choose_node_from_list(colors,
color_n_plus_1,
color_n_plus_1_nodes);
}
pdebug("done %s", __FUNCTION__);
return TRUE;
}
/*
* Attach nodes to the actions that need to be taken
*
* Mark actions "optional" if possible (Ie. if the start and stop are
* for the same node)
*
* Mark unrunnable actions
*/
gboolean
stage5(GSListPtr resources)
{
pdebug("filling in the nodes to perform the actions on");
int lpc = 0;
slist_iter(
rsc, resource_t, resources, lpc,
print_resource("Processing", rsc, FALSE);
if(safe_val(NULL, rsc, stop) == NULL
|| safe_val(NULL, rsc, start) == NULL) {
// error
continue;
}
if(safe_val4(NULL, rsc, color, details, chosen_node) == NULL) {
rsc->stop->node = safe_val(NULL, rsc, cur_node);
rsc->start->node = NULL;
} else if(safe_str_eq(safe_val4(NULL, rsc, cur_node, details, id),
safe_val6(NULL, rsc, color ,details,
chosen_node, details, id))){
cl_log(LOG_DEBUG,
"No change for Resource %s (%s)",
safe_val(NULL, rsc, id),
safe_val4(NULL, rsc, cur_node, details, id));
rsc->stop->optional = TRUE;
rsc->start->optional = TRUE;
rsc->stop->node = safe_val(NULL, rsc, cur_node);
rsc->start->node = safe_val4(NULL, rsc, color,
details, chosen_node);
} else if(safe_val4(NULL, rsc, cur_node, details, id) == NULL) {
rsc->stop->optional = TRUE;
rsc->start->node = safe_val4(NULL, rsc, color,
details, chosen_node);
} else {
rsc->stop->node = safe_val(NULL, rsc, cur_node);
rsc->start->node = safe_val4(NULL, rsc, color,
details, chosen_node);
}
if(rsc->stop->node != NULL) {
rsc->stop->runnable = TRUE;
}
if(rsc->start->node != NULL) {
rsc->start->runnable = TRUE;
}
);
return TRUE;
}
/*
* Create dependacies for stonith and shutdown operations
*/
gboolean
stage6(GSListPtr *actions, GSListPtr *action_constraints,
GSListPtr stonith_nodes, GSListPtr shutdown_nodes)
{
int lpc = 0;
int llpc = 0;
slist_iter(
node, node_t, shutdown_nodes, lpc,
action_t *down_node =
action_new(action_id++, NULL, shutdown_crm);
down_node->node = node;
down_node->runnable = TRUE;
*actions = g_slist_append(*actions, down_node);
slist_iter(
rsc, resource_t, node->details->running_rsc, llpc,
order_constraint_t *order = (order_constraint_t*)
cl_malloc(sizeof(order_constraint_t));
/* stop resources before shutdown */
order->id = order_id++;
order->lh_action = rsc->stop;
order->rh_action = down_node;
order->strength = must;
*action_constraints =
g_slist_append(*action_constraints, order);
);
);
slist_iter(
node, node_t, stonith_nodes, lpc,
action_t *stonith_node =
action_new(action_id++, NULL, stonith_op);
stonith_node->node = node;
stonith_node->runnable = TRUE;
*actions = g_slist_append(*actions, stonith_node);
slist_iter(
rsc, resource_t, node->details->running_rsc, llpc,
order_constraint_t *order = (order_constraint_t*)
cl_malloc(sizeof(order_constraint_t));
/* try stopping the resource before stonithing the node
*
* if the stop succeeds, the transitioner can then
* decided if stonith is needed
*/
order->id = order_id++;
order->lh_action = rsc->stop;
order->rh_action = stonith_node;
order->strength = must;
*action_constraints =
g_slist_append(*action_constraints, order);
/* stonith before start */
order = (order_constraint_t*)
cl_malloc(sizeof(order_constraint_t));
// try stopping the node first
order->id = order_id++;
order->lh_action = stonith_node;
order->rh_action = rsc->start;
order->strength = must;
*action_constraints =
g_slist_append(*action_constraints, order);
);
);
return TRUE;
}
/*
* Determin the sets of independant actions and the correct order for the
* actions in each set.
*
* Mark dependancies f un-runnable actions un-runnable
*
*/
gboolean
stage7(GSListPtr resources, GSListPtr actions, GSListPtr action_constraints,
GSListPtr *action_sets)
{
int lpc = 0;
slist_iter(
order, order_constraint_t, action_constraints, lpc,
action_wrapper_t *wrapper = (action_wrapper_t*)
cl_malloc(sizeof(action_wrapper_t));
wrapper->action = order->rh_action;
wrapper->strength = order->strength;
order->lh_action->actions_after =
g_slist_append(order->lh_action->actions_after,
wrapper);
wrapper = (action_wrapper_t*)
cl_malloc(sizeof(action_wrapper_t));
wrapper->action = order->lh_action;
wrapper->strength = order->strength;
order->rh_action->actions_before =
g_slist_append(order->rh_action->actions_before,
wrapper);
);
update_runnable(actions);
slist_iter(
rsc, resource_t, resources, lpc,
GSListPtr action_set = NULL;
if(rsc->stop->runnable) {
action_set = create_action_set(rsc->stop);
if(action_set != NULL) {
*action_sets = g_slist_append(*action_sets,
action_set);
} else {
pdebug("No actions resulting from %s->stop",
rsc->id);
}
}
if(rsc->start->runnable) {
action_set = create_action_set(rsc->start);
if(action_set != NULL) {
*action_sets = g_slist_append(*action_sets,
action_set);
} else {
pdebug("No actions resulting from %s->start",
rsc->id);
}
}
);
return TRUE;
}
/*
* Create a dependancy graph to send to the transitioner (via the CRMd)
*/
gboolean
stage8(GSListPtr action_sets, xmlNodePtr *graph)
{
int lpc = 0;
xmlNodePtr xml_action_set = NULL;
*graph = create_xml_node(NULL, "transition_graph");
/* errors...
slist_iter(action, action_t, action_list, lpc,
if(action->optional == FALSE && action->runnable == FALSE) {
print_action("Ignoring", action, TRUE);
}
);
*/
int lpc2;
slist_iter(action_set, GSList, action_sets, lpc,
pdebug("Processing Action Set %d", lpc);
xml_action_set = create_xml_node(NULL, "actions");
set_xml_property_copy(xml_action_set, "id", crm_itoa(lpc));
slist_iter(action, action_t, action_set, lpc2,
xmlNodePtr xml_action = action2xml(action);
xmlAddChild(xml_action_set, xml_action);
)
xmlAddChild(*graph, xml_action_set);
);
xml_message_debug(*graph, "created action list");
return TRUE;
}
/*
* Print a nice human readable high-level summary of what we're going to do
*/
gboolean
summary(GSListPtr resources)
{
int lpc = 0;
slist_iter(
rsc, resource_t, resources, lpc,
char *rsc_id = safe_val(NULL, rsc, id);
char *node_id = safe_val4(NULL, rsc, cur_node, details, id);
char *new_node_id = safe_val6(NULL, rsc, color, details,
chosen_node, details, id);
if(rsc->runnable == FALSE) {
cl_log(LOG_ERR,
"Resource %s was not runnable",
rsc_id);
if(node_id != NULL) {
cl_log(LOG_WARNING,
"Stopping Resource (%s) on node %s",
rsc_id,
node_id);
}
} else if(safe_val4(NULL, rsc, color, details, chosen_node) == NULL) {
cl_log(LOG_ERR,
"Could not allocate Resource %s",
rsc_id);
if(node_id != NULL) {
cl_log(LOG_WARNING,
"Stopping Resource (%s) on node %s",
rsc_id,
node_id);
}
} else if(safe_str_eq(node_id, new_node_id)){
cl_log(LOG_DEBUG,
"No change for Resource %s (%s)",
rsc_id,
safe_val4(NULL, rsc, cur_node, details, id));
} else if(node_id == NULL) {
cl_log(LOG_INFO,
"Starting Resource %s on %s",
rsc_id,
new_node_id);
} else {
cl_log(LOG_INFO,
"Moving Resource %s from %s to %s",
rsc_id,
node_id,
new_node_id);
}
);
return TRUE;
}
gboolean
choose_node_from_list(GSListPtr colors, color_t *color, GSListPtr nodes)
{
/*
1. Sort by weight
2. color.chosen_node = highest wieghted node
3. remove color.chosen_node from all other colors
*/
int lpc = 0;
nodes = g_slist_sort(nodes, sort_node_weight);
color->details->chosen_node = (node_t*)g_slist_nth_data(nodes, 0);
if(color->details->chosen_node == NULL) {
cl_log(LOG_ERR, "Could not allocate a node for color %d", color->id);
return FALSE;
}
slist_iter(
color_n, color_t, colors, lpc,
node_t *other_node = pe_find_node(color_n->details->candidate_nodes,
color->details->chosen_node->details->id);
color_n->details->candidate_nodes =
g_slist_remove(color_n->details->candidate_nodes,
other_node);
);
return TRUE;
}
gboolean
unpack_nodes(xmlNodePtr xml_nodes, GSListPtr *nodes)
{
pdebug("Begining unpack... %s", __FUNCTION__);
while(xml_nodes != NULL) {
pdebug("Processing node...");
xmlNodePtr xml_obj = xml_nodes;
xmlNodePtr attrs = xml_obj->children;
const char *id = xmlGetProp(xml_obj, "id");
const char *type = xmlGetProp(xml_obj, "type");
if(attrs != NULL) {
attrs = attrs->children;
}
xml_nodes = xml_nodes->next;
if(id == NULL) {
cl_log(LOG_ERR, "Must specify id tag in <node>");
continue;
}
if(type == NULL) {
cl_log(LOG_ERR, "Must specify type tag in <node>");
continue;
}
node_t *new_node = cl_malloc(sizeof(node_t));
new_node->weight = 1.0;
new_node->fixed = FALSE;
new_node->details = (struct node_shared_s*)
cl_malloc(sizeof(struct node_shared_s*));
new_node->details->online = FALSE;
new_node->details->unclean = FALSE;
new_node->details->shutdown = FALSE;
new_node->details->running_rsc = NULL;
new_node->details->id = cl_strdup(id);
new_node->details->attrs =
g_hash_table_new(g_str_hash, g_str_equal);
new_node->details->type = node_ping;
if(safe_str_eq(type, "node")) {
new_node->details->type = node_member;
}
while(attrs != NULL){
const char *name = xmlGetProp(attrs, "name");
const char *value = xmlGetProp(attrs, "value");
if(name != NULL && value != NULL) {
g_hash_table_insert(new_node->details->attrs,
cl_strdup(name),
cl_strdup(value));
}
attrs = attrs->next;
}
- pdebug("Adding node id... %s (%p)", id, new_node);
+ pdebug_action(print_node("Added", new_node, FALSE));
*nodes = g_slist_append(*nodes, new_node);
}
*nodes = g_slist_sort(*nodes, sort_node_weight);
return TRUE;
}
gboolean
unpack_resources(xmlNodePtr xml_resources,
GSListPtr *resources,
GSListPtr *actions,
GSListPtr *action_cons,
GSListPtr all_nodes)
{
pdebug("Begining unpack... %s", __FUNCTION__);
while(xml_resources != NULL) {
xmlNodePtr xml_obj = xml_resources;
const char *id = xmlGetProp(xml_obj, "id");
const char *priority = xmlGetProp(xml_obj, "priority");
float priority_f = atof(priority);
xml_resources = xml_resources->next;
pdebug("Processing resource...");
if(id == NULL) {
cl_log(LOG_ERR, "Must specify id tag in <resource>");
continue;
}
resource_t *new_rsc = cl_malloc(sizeof(resource_t));
new_rsc->xml = xml_obj; // copy first
new_rsc->priority = priority_f;
new_rsc->candidate_colors = NULL;
new_rsc->color = NULL;
new_rsc->runnable = TRUE;
new_rsc->provisional = TRUE;
new_rsc->allowed_nodes = node_list_dup(all_nodes);
new_rsc->rsc_cons = NULL;
new_rsc->node_cons = NULL;
new_rsc->id = cl_strdup(id);
-
+ new_rsc->cur_node = NULL;
action_t *action_stop = action_new(action_id++, new_rsc,
stop_rsc);
action_t *action_start = action_new(action_id++, new_rsc,
start_rsc);
new_rsc->stop = action_stop;
*actions = g_slist_append(*actions, action_stop);
new_rsc->start = action_start;
*actions = g_slist_append(*actions, action_start);
order_constraint_t *order = (order_constraint_t*)
cl_malloc(sizeof(order_constraint_t));
order->id = order_id++;
order->lh_action = action_stop;
order->rh_action = action_start;
order->strength = startstop;
*action_cons = g_slist_append(*action_cons, order);
pdebug_action(print_resource("Added", new_rsc, FALSE));
*resources = g_slist_append(*resources, new_rsc);
}
*resources = g_slist_sort(*resources, sort_rsc_priority);
return TRUE;
}
gboolean
unpack_constraints(xmlNodePtr xml_constraints,
GSListPtr nodes, GSListPtr resources,
GSListPtr *node_constraints,
GSListPtr *action_constraints)
{
pdebug("Begining unpack... %s", __FUNCTION__);
while(xml_constraints != NULL) {
const char *id = xmlGetProp(xml_constraints, "id");
xmlNodePtr xml_obj = xml_constraints;
xml_constraints = xml_constraints->next;
if(id == NULL) {
cl_log(LOG_ERR, "Constraint must have an id");
continue;
}
pdebug("Processing constraint %s %s",
xml_obj->name,id);
if(safe_str_eq("rsc_to_rsc", xml_obj->name)) {
unpack_rsc_to_rsc(xml_obj, resources,
action_constraints);
} else if(safe_str_eq("rsc_to_node", xml_obj->name)) {
unpack_rsc_to_node(xml_obj, resources, nodes,
node_constraints);
} else if(safe_str_eq("rsc_to_attr", xml_obj->name)) {
unpack_rsc_to_attr(xml_obj, resources, nodes,
node_constraints);
} else {
cl_log(LOG_ERR, "Unsupported constraint type: %s",
xml_obj->name);
}
}
return TRUE;
}
gboolean
apply_node_constraints(GSListPtr constraints,
GSListPtr resources,
GSListPtr nodes)
{
pdebug("Applying constraints... %s", __FUNCTION__);
int lpc = 0;
slist_iter(
cons, rsc_to_node_t, constraints, lpc,
pdebug_action(print_rsc_to_node("Applying", cons, FALSE));
// take "lifetime" into account
if(cons == NULL) {
cl_log(LOG_ERR, "Constraint (%d) is NULL", lpc);
continue;
} else if(is_active(cons) == FALSE) {
cl_log(LOG_INFO, "Constraint (%d) is not active", lpc);
// warning
continue;
}
resource_t *rsc_lh = cons->rsc_lh;
if(rsc_lh == NULL) {
cl_log(LOG_ERR, "LHS of rsc_to_node (%s) is NULL", cons->id);
continue;
}
cons->rsc_lh->node_cons =
g_slist_append(cons->rsc_lh->node_cons, cons);
if(cons->node_list_rh == NULL) {
cl_log(LOG_ERR,
"RHS of rsc_to_node (%s) is NULL",
cons->id);
continue;
} else {
int llpc = 0;
slist_iter(node_rh, node_t, cons->node_list_rh, llpc,
update_node_weight(cons,
node_rh->details->id,
nodes));
}
/* dont add it to the resource,
* the information is in the resouce's node list
*/
);
return TRUE;
}
// remove nodes that are down, stopping
// create +ve rsc_to_node constraints between resources and the nodes they are running on
// anything else?
gboolean
unpack_status(xmlNodePtr status,
GSListPtr nodes,
GSListPtr rsc_list,
GSListPtr *node_constraints)
{
pdebug("Begining unpack %s", __FUNCTION__);
while(status != NULL) {
const char *id = xmlGetProp(status, "id");
const char *state = xmlGetProp(status, "state");
const char *exp_state = xmlGetProp(status, "exp_state");
xmlNodePtr lrm_state = find_xml_node(status, "lrm");
xmlNodePtr attrs = find_xml_node(status, "attributes");
lrm_state = find_xml_node(lrm_state, "lrm_resources");
lrm_state = find_xml_node(lrm_state, "rsc_state");
status = status->next;
pdebug("Processing node %s", id);
if(id == NULL){
// error
continue;
}
pdebug("Processing node attrs");
node_t *this_node = pe_find_node(nodes, id);
while(attrs != NULL){
const char *name = xmlGetProp(attrs, "name");
const char *value = xmlGetProp(attrs, "value");
if(name != NULL && value != NULL
&& safe_val(NULL, this_node, details) != NULL) {
pdebug("Adding %s => %s",
name, value);
g_hash_table_insert(this_node->details->attrs,
cl_strdup(name),
cl_strdup(value));
}
attrs = attrs->next;
}
pdebug("determining node state");
if(safe_str_eq(exp_state, "active")
&& safe_str_eq(state, "active")) {
// process resource, make +ve preference
this_node->details->online = TRUE;
} else {
pdebug("remove %s", __FUNCTION__);
// remove node from contention
this_node->weight = -1;
this_node->fixed = TRUE;
pdebug("state %s, expected %s",
state, exp_state);
if(safe_str_eq(state, "shutdown")){
// create shutdown req
this_node->details->shutdown = TRUE;
} else if(safe_str_eq(exp_state, "active")
&& safe_str_neq(state, "active")) {
// mark unclean in the xml
this_node->details->unclean = TRUE;
// remove any running resources from being allocated
}
}
pdebug("Processing node lrm state");
process_node_lrm_state(this_node, lrm_state,
rsc_list, nodes,
node_constraints);
}
return TRUE;
}
gboolean
is_active(rsc_to_node_t *cons)
{
return TRUE;
}
gboolean
strict_preproc(rsc_to_rsc_t *constraint,
color_t *local_color,
color_t *other_color,
GSListPtr *colors,
GSListPtr resources)
{
resource_t * lh_resource = constraint->rsc_lh;
switch(constraint->strength) {
case must:
if(constraint->rsc_rh->runnable == FALSE) {
cl_log(LOG_WARNING,
"Resource %s must run on the same node"
" as %s (cons %s), but %s is not"
" runnable.",
constraint->rsc_lh->id,
constraint->rsc_rh->id,
constraint->id,
constraint->rsc_rh->id);
constraint->rsc_lh->runnable = FALSE;
}
break;
// x * should * should_not = x
case should:
if(constraint->rsc_rh->provisional == FALSE) {
local_color->local_weight =
local_color->local_weight * 2.0;
}
break;
case should_not:
if(constraint->rsc_rh->provisional == FALSE) {
local_color->local_weight =
local_color->local_weight * 0.5;
}
pdebug("# Colors %d, Nodes %d",
g_slist_length(*colors),
max_valid_nodes);
if(g_slist_length(*colors) < max_valid_nodes
// && g_slist_length(lh_resource->candidate_colors)==1
) {
create_color(colors,
lh_resource->allowed_nodes,
resources);
}
break;
case must_not:
if(constraint->rsc_rh->provisional == FALSE) {
lh_resource->candidate_colors =
g_slist_remove(
lh_resource->candidate_colors,
local_color);
}
break;
default:
// error
break;
}
return TRUE;
}
gboolean
strict_postproc(rsc_to_rsc_t *constraint,
color_t *local_color,
color_t *other_color,
GSListPtr *colors,
GSListPtr resources)
{
print_rsc_to_rsc("Post processing", constraint, FALSE);
switch(constraint->strength) {
case must:
if(constraint->rsc_rh->provisional == TRUE) {
constraint->rsc_rh->color = other_color;
constraint->rsc_rh->provisional = FALSE;
color_resource(constraint->rsc_rh,
colors, resources);
}
// else check for error
if(constraint->rsc_lh->runnable == FALSE) {
cl_log(LOG_WARNING,
"Resource %s must run on the same node"
" as %s (cons %s), but %s is not"
" runnable.",
constraint->rsc_rh->id,
constraint->rsc_lh->id,
constraint->id,
constraint->rsc_lh->id);
constraint->rsc_rh->runnable = FALSE;
}
break;
case should:
break;
case should_not:
break;
case must_not:
if(constraint->rsc_rh->provisional == TRUE) {
// check for error
}
break;
default:
// error
break;
}
return TRUE;
}
gboolean
choose_color(resource_t *lh_resource, GSListPtr candidate_colors)
{
int lpc = 0;
if(lh_resource->runnable == FALSE) {
lh_resource->color = no_color;
lh_resource->provisional = FALSE;
} else {
GSListPtr sorted_colors = g_slist_sort(candidate_colors,
sort_color_weight);
lh_resource->candidate_colors = sorted_colors;
pdebug( "Choose a color from %d possibilities",
g_slist_length(sorted_colors));
}
if(lh_resource->provisional) {
slist_iter(
this_color, color_t,lh_resource->candidate_colors, lpc,
GSListPtr intersection = node_list_and(
this_color->details->candidate_nodes,
lh_resource->allowed_nodes);
if(g_slist_length(intersection) != 0) {
// TODO: merge node weights
g_slist_free(this_color->details->candidate_nodes);
this_color->details->candidate_nodes = intersection;
lh_resource->color = this_color;
lh_resource->provisional = FALSE;
break;
}
);
}
return !lh_resource->provisional;
}
gboolean
unpack_rsc_to_node(xmlNodePtr xml_obj,
GSListPtr rsc_list,
GSListPtr node_list,
GSListPtr *node_constraints)
{
xmlNodePtr node_ref = xml_obj->children;
rsc_to_node_t *new_con = cl_malloc(sizeof(rsc_to_node_t));
const char *id_lh = xmlGetProp(xml_obj, "from");
const char *id = xmlGetProp(xml_obj, "id");
const char *mod = xmlGetProp(xml_obj, "modifier");
const char *weight = xmlGetProp(xml_obj, "weight");
float weight_f = atof(weight);
resource_t *rsc_lh = pe_find_resource(rsc_list, id_lh);
if(rsc_lh == NULL) {
cl_log(LOG_ERR, "No resource (con=%s, rsc=%s)",
id, id_lh);
}
new_con->id = cl_strdup(id);
new_con->rsc_lh = rsc_lh;
new_con->weight = weight_f;
if(safe_str_eq(mod, "set")){
new_con->modifier = set;
} else if(safe_str_eq(mod, "inc")){
new_con->modifier = inc;
} else if(safe_str_eq(mod, "dec")){
new_con->modifier = dec;
} else {
// error
}
/*
<rsc_to_node>
<node_ref id= type= name=/>
<node_ref id= type= name=/>
<node_ref id= type= name=/>
*/
//
while(node_ref != NULL) {
const char *id_rh = xmlGetProp(node_ref, "name");
node_t *node_rh = pe_find_node(node_list, id_rh);
if(node_rh == NULL) {
// error
cl_log(LOG_ERR,
"node %s (from %s) not found",
id_rh, node_ref->name);
continue;
}
new_con->node_list_rh =
g_slist_append(new_con->node_list_rh,
node_rh);
/* dont add it to the resource,
* the information is in the resouce's node list
*/
node_ref = node_ref->next;
}
*node_constraints = g_slist_append(*node_constraints, new_con);
return TRUE;
}
gboolean
unpack_rsc_to_attr(xmlNodePtr xml_obj,
GSListPtr rsc_list,
GSListPtr node_list,
GSListPtr *node_constraints)
{
/*
<rsc_to_attr id="cons4" from="rsc2" weight="20.0" modifier="inc">
<attr_expression id="attr_exp_1"/>
<node_match id="node_match_1" type="has_attr" target="cpu"/>
<node_match id="node_match_2" type="attr_value" target="kernel" value="2.6"/>
</attr_expression>
<attr_expression id="attr_exp_2"/>
<node_match id="node_match_3" type="has_attr" target="hdd"/>
<node_match id="node_match_4" type="attr_value" target="kernel" value="2.4"/>
</attr_expression>
Translation:
give any node a +ve weight of 20.0 to run rsc2 if:
attr "cpu" is set _and_ "kernel"="2.6", _or_
attr "hdd" is set _and_ "kernel"="2.4"
Further translation:
2 constraints that give any node a +ve weight of 20.0 to run rsc2
cons1: attr "cpu" is set and "kernel"="2.6"
cons2: attr "hdd" is set and "kernel"="2.4"
*/
xmlNodePtr attr_exp = xml_obj->children;
const char *id_lh = xmlGetProp(xml_obj, "from");
const char *mod = xmlGetProp(xml_obj, "modifier");
const char *weight = xmlGetProp(xml_obj, "weight");
const char *id = xmlGetProp(attr_exp, "id");
float weight_f = atof(weight);
enum con_modifier a_modifier = modifier_none;
resource_t *rsc_lh = pe_find_resource(rsc_list, id_lh);
if(rsc_lh == NULL) {
cl_log(LOG_ERR, "No resource (con=%s, rsc=%s)",
id, id_lh);
return FALSE;
}
if(safe_str_eq(mod, "set")){
a_modifier = set;
} else if(safe_str_eq(mod, "inc")){
a_modifier = inc;
} else if(safe_str_eq(mod, "dec")){
a_modifier = dec;
} else {
// error
}
if(attr_exp == NULL) {
cl_log(LOG_WARNING, "no attrs for constraint %s", id);
}
while(attr_exp != NULL) {
const char *id_rh = xmlGetProp(attr_exp, "name");
const char *id = xmlGetProp(attr_exp, "id");
rsc_to_node_t *new_con = cl_malloc(sizeof(rsc_to_node_t));
new_con->id = cl_strdup(id);
new_con->rsc_lh = rsc_lh;
new_con->weight = weight_f;
new_con->modifier = a_modifier;
new_con->node_list_rh = match_attrs(attr_exp, node_list);
if(new_con->node_list_rh == NULL) {
// error
cl_log(LOG_ERR,
"node %s (from %s) not found",
id_rh, attr_exp->name);
}
pdebug_action(print_rsc_to_node("Added", new_con, FALSE));
*node_constraints = g_slist_append(*node_constraints, new_con);
/* dont add it to the resource,
* the information is in the resouce's node list
*/
attr_exp = attr_exp->next;
}
return TRUE;
}
gboolean
update_node_weight(rsc_to_node_t *cons, char *id, GSListPtr nodes)
{
node_t *node_rh = pe_find_node(cons->rsc_lh->allowed_nodes, id);
if(node_rh == NULL) {
node_t *node_tmp = pe_find_node(nodes, id);
node_rh = node_copy(node_tmp);
cons->rsc_lh->allowed_nodes =
g_slist_append(cons->rsc_lh->allowed_nodes,
node_rh);
}
if(node_rh == NULL) {
// error
return FALSE;
}
if(node_rh->fixed) {
// warning
cl_log(LOG_WARNING,
"Constraint %s is irrelevant as the"
" weight of node %s is fixed as %f.",
cons->id,
node_rh->details->id,
node_rh->weight);
return TRUE;
}
pdebug( "Constraint %s: node %s weight %s %f.",
cons->id,
node_rh->details->id,
modifier2text(cons->modifier),
node_rh->weight);
switch(cons->modifier) {
case set:
node_rh->weight = cons->weight;
node_rh->fixed = TRUE;
break;
case inc:
node_rh->weight += cons->weight;
break;
case dec:
node_rh->weight -= cons->weight;
break;
case modifier_none:
// warning
break;
}
return TRUE;
}
gboolean
process_node_lrm_state(node_t *node, xmlNodePtr lrm_state,
GSListPtr rsc_list, GSListPtr nodes,
GSListPtr *node_constraints)
{
pdebug("here %s", __FUNCTION__);
while(lrm_state != NULL) {
const char *id = xmlGetProp(lrm_state, "id");
const char *rsc_id = xmlGetProp(lrm_state, "rsc_id");
const char *node_id = xmlGetProp(lrm_state, "node_id");
const char *rsc_state = xmlGetProp(lrm_state, "rsc_state");
resource_t *rsc_lh = pe_find_resource(rsc_list, rsc_id);
rsc_lh->cur_node = node;
node->details->running_rsc =
g_slist_append(node->details->running_rsc, rsc_lh);
/* it is runnable, but depends on a stonith op
if(safe_val3(FALSE, node, details, unclean)) {
rsc_lh->runnable = FALSE;
}
*/
if((safe_str_eq(rsc_state, "starting"))
|| (safe_str_eq(rsc_state, "started"))) {
node_t *node_rh;
rsc_to_node_t *new_cons =
cl_malloc(sizeof(rsc_to_node_t));
new_cons->id = cl_strdup(id); // genereate one
new_cons->weight = 100.0;
new_cons->modifier = inc;
new_cons->rsc_lh = rsc_lh;
node_rh = pe_find_node(nodes, node_id);
new_cons->node_list_rh = g_slist_append(NULL, node_rh);
*node_constraints =
g_slist_append(*node_constraints, new_cons);
pdebug_action(print_rsc_to_node(
"Added", new_cons, FALSE));
} else if(safe_str_eq(rsc_state, "stop_fail")) {
// do soemthing
} // else no preference
lrm_state = lrm_state->next;
}
return TRUE;
}
GSListPtr
match_attrs(xmlNodePtr attr_exp, GSListPtr node_list)
{
int lpc = 0;
GSListPtr result = NULL;
slist_iter(
node, node_t, node_list, lpc,
xmlNodePtr node_match = attr_exp->children;
gboolean accept = TRUE;
while(accept && node_match != NULL) {
const char *type =xmlGetProp(node_match, "type");
const char *value=xmlGetProp(node_match, "value");
const char *name =xmlGetProp(node_match, "target");
node_match = node_match->next;
if(name == NULL || type == NULL) {
// error
continue;
}
const char *h_val = (const char*)
g_hash_table_lookup(node->details->attrs, name);
if(h_val != NULL && safe_str_eq(type, "has_attr")){
accept = TRUE;
} else if(h_val == NULL
&& safe_str_eq(type, "not_attr")) {
accept = TRUE;
} else if(h_val != NULL
&& safe_str_eq(type, "attr_value")
&& safe_str_eq(h_val, value)) {
accept = TRUE;
} else {
accept = FALSE;
}
}
if(accept) {
result = g_slist_append(result, node);
}
);
return result;
}
gboolean
create_rsc_to_rsc(const char *id, enum con_strength strength,
resource_t *rsc_lh, resource_t *rsc_rh)
{
if(rsc_lh == NULL || rsc_rh == NULL){
// error
return FALSE;
}
rsc_to_rsc_t *new_con = cl_malloc(sizeof(rsc_to_node_t));
rsc_to_rsc_t *inverted_con = NULL;
new_con->id = cl_strdup(id);
new_con->rsc_lh = rsc_lh;
new_con->rsc_rh = rsc_rh;
new_con->strength = strength;
inverted_con = invert_constraint(new_con);
rsc_lh->rsc_cons = g_slist_insert_sorted(rsc_lh->rsc_cons,
inverted_con, sort_cons_strength);
rsc_rh->rsc_cons = g_slist_insert_sorted(rsc_rh->rsc_cons,
new_con, sort_cons_strength);
return TRUE;
}
gboolean
create_ordering(const char *id, enum con_strength strength,
resource_t *rsc_lh, resource_t *rsc_rh,
GSListPtr *action_constraints)
{
if(rsc_lh == NULL || rsc_rh == NULL){
// error
return FALSE;
}
action_t *lh_stop = rsc_lh->stop;
action_t *lh_start = rsc_lh->start;
action_t *rh_stop = rsc_rh->stop;
action_t *rh_start = rsc_rh->start;
order_constraint_t *order = (order_constraint_t*)
cl_malloc(sizeof(order_constraint_t));
order->id = order_id++;
order->lh_action = lh_stop;
order->rh_action = rh_stop;
order->strength = strength;
*action_constraints = g_slist_append(*action_constraints, order);
order = (order_constraint_t*)
cl_malloc(sizeof(order_constraint_t));
order->id = order_id++;
order->lh_action = rh_start;
order->rh_action = lh_start;
order->strength = strength;
*action_constraints = g_slist_append(*action_constraints, order);
return TRUE;
}
gboolean
unpack_rsc_to_rsc(xmlNodePtr xml_obj,
GSListPtr rsc_list,
GSListPtr *action_constraints)
{
const char *id_lh = xmlGetProp(xml_obj, "from");
const char *id = xmlGetProp(xml_obj, "id");
resource_t *rsc_lh = pe_find_resource(rsc_list, id_lh);
const char *id_rh = xmlGetProp(xml_obj, "to");
resource_t *rsc_rh = pe_find_resource(rsc_list, id_rh);
const char *strength = xmlGetProp(xml_obj, "strength");
const char *type = xmlGetProp(xml_obj, "type");
enum con_strength strength_e = ignore;
if(rsc_lh == NULL) {
cl_log(LOG_ERR, "No resource (con=%s, rsc=%s)",
id, id_lh);
return FALSE;
}
if(safe_str_eq(strength, "must")) {
strength_e = must;
} else if(safe_str_eq(strength, "should")) {
strength_e = should;
} else if(safe_str_eq(strength, "should_not")) {
strength_e = should_not;
} else if(safe_str_eq(strength, "must_not")) {
strength_e = must_not;
} else {
// error
}
if(safe_str_eq(type, "ordering")) {
// make an action_cons instead
return create_ordering(id, strength_e, rsc_lh, rsc_rh,
action_constraints);
}
return create_rsc_to_rsc(id, strength_e, rsc_lh, rsc_rh);
}
GSListPtr
create_action_set(action_t *action)
{
int lpc = 0;
GSListPtr result = NULL;
GSListPtr tmp = NULL;
if(action->processed) {
return NULL;
}
pdebug_action(print_action("Create action set for", action, FALSE));
// process actions_before
if(action->seen_count == 0) {
pdebug("Processing \"before\" for action %d", action->id);
slist_iter(
other, action_wrapper_t, action->actions_before, lpc,
tmp = create_action_set(other->action);
pdebug("%d (%d total) \"before\" actions for %d)",
g_slist_length(tmp), g_slist_length(result),action->id);
result = g_slist_concat(result, tmp);
);
// add ourselves
pdebug("Adding self %d", action->id);
if(action->processed == FALSE) {
result = g_slist_append(result, action);
action->processed = TRUE;
}
} else {
pdebug("Already seen action %d", action->id);
pdebug("Processing \"before\" for action %d", action->id);
slist_iter(
other, action_wrapper_t, action->actions_before, lpc,
if(other->action->seen_count > action->seen_count
&& other->strength == must) {
tmp = create_action_set(other->action);
pdebug("%d (%d total) \"before\" actions for %d)",
g_slist_length(tmp), g_slist_length(result),action->id);
result = g_slist_concat(result, tmp);
}
);
// add ourselves
pdebug("Adding self %d", action->id);
if(action->processed == FALSE) {
result = g_slist_append(result, action);
action->processed = TRUE;
}
// add strength == !MUST
slist_iter(
other, action_wrapper_t, action->actions_before, lpc,
tmp = create_action_set(other->action);
pdebug("%d (%d total) post-self \"before\" actions for %d)",
g_slist_length(tmp), g_slist_length(result),action->id);
result = g_slist_concat(result, tmp);
);
}
action->seen_count = action->seen_count + 1;
// process actions_after
pdebug("Processing \"after\" for action %d", action->id);
slist_iter(
other, action_wrapper_t, action->actions_after, lpc,
tmp = create_action_set(other->action);
pdebug("%d (%d total) \"after\" actions for %d)",
g_slist_length(tmp), g_slist_length(result),action->id);
result = g_slist_concat(result, tmp);
);
return result;
}
gboolean
update_runnable(GSListPtr actions)
{
int lpc = 0, lpc2 = 0;
gboolean change = TRUE;
while(change) {
change = FALSE;
slist_iter(
action, action_t, actions, lpc,
if(action->runnable) {
continue;
} else if(action->optional) {
continue;
}
slist_iter(
other, action_wrapper_t, action->actions_before, lpc2,
if(other->action->runnable) {
change = TRUE;
}
other->action->runnable = FALSE;
);
);
}
return TRUE;
}
void
color_resource(resource_t *lh_resource, GSListPtr *colors, GSListPtr resources)
{
int lpc = 0;
pdebug_action(print_resource("Coloring", lh_resource, FALSE));
if(lh_resource->provisional == FALSE) {
// already processed this resource
return;
}
lh_resource->rsc_cons = g_slist_sort(lh_resource->rsc_cons,
sort_cons_strength);
pdebug("=== Pre-processing");
//------ Pre-processing
slist_iter(
constraint, rsc_to_rsc_t, lh_resource->rsc_cons, lpc,
color_t *other_color = NULL;
color_t *local_color = NULL;
if(lh_resource->runnable == FALSE) {
break;
}
pdebug_action(print_rsc_to_rsc(
"Processing constraint",
constraint, FALSE));
if(constraint->rsc_rh == NULL) {
cl_log(LOG_ERR,
"rsc_rh was NULL for %s",
constraint->id);
continue;
}
other_color = constraint->rsc_rh->color;
local_color = find_color(lh_resource->candidate_colors,
other_color);
strict_preproc(constraint, local_color, other_color,
colors, resources);
);
// filter out nodes with a negative weight
filter_nodes(lh_resource);
/* Choose a color from the candidates or,
* create a new one if no color is suitable
* (this may need modification pending further napkin drawings)
*/
choose_color(lh_resource, lh_resource->candidate_colors);
pdebug("* Colors %d, Nodes %d",
g_slist_length(*colors),
max_valid_nodes);
if(lh_resource->provisional
&& g_slist_length(*colors) < max_valid_nodes) {
// Create new color
pdebug("Create a new color");
lh_resource->color = create_color(colors,
lh_resource->allowed_nodes,
resources);
lh_resource->provisional = FALSE;
} else if(lh_resource->provisional) {
cl_log(LOG_ERR, "Could not color resource %s", lh_resource->id);
print_resource("ERROR: No color", lh_resource, FALSE);
lh_resource->color = no_color;
lh_resource->provisional = FALSE;
}
pdebug_action(print_resource("Post-processing", lh_resource, FALSE));
//------ Post-processing
color_t *local_color = lh_resource->color;
slist_iter(
constraint, rsc_to_rsc_t, lh_resource->rsc_cons, lpc,
color_t *other_color =
find_color(constraint->rsc_rh->candidate_colors,
local_color);
strict_postproc(constraint, local_color, other_color,
colors, resources);
);
pdebug_action(print_resource("Colored", lh_resource, FALSE));
}
gboolean
process_pe_message(xmlNodePtr msg, IPC_Channel *sender)
{
const char *op = get_xml_attr (msg, XML_TAG_OPTIONS,
XML_ATTR_OP, TRUE);
const char *ref = xmlGetProp(msg, XML_ATTR_REFERENCE);
CRM_DEBUG("Processing %s op (ref=%s)...", op, ref);
const char *sys_to = xmlGetProp(msg, XML_ATTR_SYSTO);
if(op == NULL){
// error
} else if(sys_to == NULL || strcmp(sys_to, "pengine") != 0) {
CRM_DEBUG("Bad sys-to %s", sys_to);
return FALSE;
} else if(strcmp(op, "pecalc") == 0) {
xmlNodePtr input_cib = find_xml_node(msg, XML_TAG_CIB);
if (send_ipc_reply(sender, msg,
do_calculations(input_cib)) ==FALSE) {
cl_log(LOG_WARNING,
"Answer could not be sent");
}
} else if(strcmp(op, "quit") == 0) {
cl_log(LOG_WARNING, "Received quit message, terminating");
exit(0);
}
return TRUE;
}
xmlNodePtr
do_calculations(xmlNodePtr cib_object)
{
int lpc, lpc2;
GSListPtr resources = NULL;
GSListPtr nodes = NULL;
GSListPtr node_constraints = NULL;
GSListPtr actions = NULL;
GSListPtr action_constraints = NULL;
GSListPtr stonith_list = NULL;
GSListPtr shutdown_list = NULL;
GSListPtr colors = NULL;
GSListPtr action_sets = NULL;
xmlNodePtr graph = NULL;
+// pe_debug_on();
+
pdebug("=#=#=#=#= Stage 0 =#=#=#=#=");
stage0(cib_object,
&resources,
&nodes, &node_constraints,
&actions, &action_constraints,
&stonith_list, &shutdown_list);
pdebug("=#=#=#=#= Stage 1 =#=#=#=#=");
stage1(node_constraints, nodes, resources);
pdebug("=#=#=#=#= Stage 2 =#=#=#=#=");
stage2(resources, nodes, &colors);
pdebug("========= Nodes =========");
pdebug_action(
slist_iter(node, node_t, nodes, lpc,
print_node(NULL, node, TRUE)
)
);
pdebug("========= Resources =========");
pdebug_action(
slist_iter(resource, resource_t, resources, lpc,
print_resource(NULL, resource, TRUE)
)
);
pdebug("=#=#=#=#= Stage 3 =#=#=#=#=");
stage3(colors);
pdebug("=#=#=#=#= Stage 4 =#=#=#=#=");
stage4(colors);
pdebug("========= Colors =========");
pdebug_action(
slist_iter(color, color_t, colors, lpc,
print_color(NULL, color, FALSE)
)
);
pdebug("=#=#=#=#= Stage 5 =#=#=#=#=");
stage5(resources);
pdebug("=#=#=#=#= Stage 6 =#=#=#=#=");
stage6(&actions, &action_constraints,
stonith_list, shutdown_list);
pdebug("========= Action List =========");
pdebug_action(
slist_iter(action, action_t, actions, lpc,
print_action(NULL, action, TRUE)
)
);
pdebug("=#=#=#=#= Stage 7 =#=#=#=#=");
stage7(resources, actions, action_constraints, &action_sets);
pdebug("=#=#=#=#= Summary =#=#=#=#=");
summary(resources);
pdebug("========= Action Sets =========");
pdebug("\t========= Set %d (Un-runnable) =========", -1);
pdebug_action(
slist_iter(action, action_t, actions, lpc,
if(action->optional == FALSE
&& action->runnable == FALSE) {
print_action("\t", action, TRUE);
}
)
);
pdebug_action(
slist_iter(action_set, GSList, action_sets, lpc,
pdebug("\t========= Set %d =========", lpc);
slist_iter(action, action_t, action_set, lpc2,
print_action("\t", action, TRUE);
)
)
);
pdebug("========= Stonith List =========");
pdebug_action(
slist_iter(node, node_t, stonith_list, lpc,
print_node(NULL, node, FALSE);
)
);
pdebug("========= Shutdown List =========");
pdebug_action(
slist_iter(node, node_t, shutdown_list, lpc,
print_node(NULL, node, FALSE);
)
);
pdebug("=#=#=#=#= Stage 8 =#=#=#=#=");
stage8(action_sets, &graph);
return graph;
}
diff --git a/crm/tengine/tengine.c b/crm/tengine/tengine.c
index 7cb6c94360..cbf5522336 100644
--- a/crm/tengine/tengine.c
+++ b/crm/tengine/tengine.c
@@ -1,488 +1,543 @@
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/msgutils.h>
#include <crm/common/xmlutils.h>
#include <crm/cib.h>
#include <tengine.h>
GSListPtr graph = NULL;
IPC_Channel *crm_ch = NULL;
typedef struct action_list_s
{
int index;
int index_max;
GSListPtr actions;
} action_list_t;
gboolean initialize_graph(void);
gboolean unpack_graph(xmlNodePtr xml_graph);
gboolean extract_event(xmlNodePtr msg);
gboolean initiate_transition(void);
-gboolean initiate_action(xmlNodePtr xml_action);
+gboolean initiate_action(action_list_t *list);
gboolean process_graph_event(const char *event_node,
const char *event_rsc,
const char *event_action,
const char *event_status,
const char *event_rc);
void send_success(void);
void send_abort(void);
gboolean process_fake_event(xmlNodePtr msg);
gboolean
initialize_graph(void)
{
while(g_slist_length(graph) > 0) {
action_list_t *action_list = g_slist_nth_data(graph, 0);
while(g_slist_length(action_list->actions) > 0) {
- GSListPtr action = g_slist_nth(action_list->actions, 0);
- g_slist_remove(action_list->actions, action);
- cl_free(action->data);
+ xmlNodePtr action =
+ g_slist_nth_data(action_list->actions, 0);
+ action_list->actions =
+ g_slist_remove(action_list->actions, action);
+ free_xml(action);
}
- g_slist_remove(graph, action_list);
+ graph = g_slist_remove(graph, action_list);
+ cl_free(action_list);
}
graph = NULL;
return TRUE;
}
gboolean
unpack_graph(xmlNodePtr xml_graph)
{
/*
<transition_graph>
<actions id="0">
<rsc_op id="5" runnable="false" optional="true" task="stop">
<resource id="rsc3" priority="3.0"/>
</rsc_op>
*/
xmlNodePtr xml_action_list = xml_graph?xml_graph->children:NULL;
if(xml_action_list == NULL) {
// nothing to do
return FALSE;
}
while(xml_action_list != NULL) {
xmlNodePtr xml_obj = xml_action_list;
xmlNodePtr xml_action = xml_obj->children;
action_list_t *action_list = (action_list_t*)
cl_malloc(sizeof(action_list_t));
xml_action_list = xml_action_list->next;
- action_list->index = 0;
+ action_list->index = -1;
action_list->index_max = 0;
+ action_list->actions = NULL;
while(xml_action != NULL) {
- xmlNodePtr action = copy_xml_node_recursive(xml_action);
+ xmlNodePtr action =
+ copy_xml_node_recursive(xml_action);
action_list->actions =
g_slist_append(action_list->actions, action);
-
+
action_list->index_max++;
+ xml_action = xml_action->next;
}
graph = g_slist_append(graph, action_list);
}
return TRUE;
}
gboolean
process_fake_event(xmlNodePtr msg)
{
+
xmlNodePtr data = find_xml_node(msg, "lrm_resource");
+ CRM_DEBUG("Processing fake LRM event...");
+ const char *last_op = "start";
+ if(safe_str_eq("stopped", xmlGetProp(data, "last_op"))) {
+ last_op = "stop";
+ }
+ CRM_DEBUG("Fake LRM event... %s, %s, %s, %s, %s",
+ xmlGetProp(data, "op_node"),
+ xmlGetProp(data, "id"),
+ last_op,
+ xmlGetProp(data, "op_status"),
+ xmlGetProp(data, "op_code"));
+
return process_graph_event(xmlGetProp(data, "op_node"),
xmlGetProp(data, "id"),
xmlGetProp(data, "last_op"),
xmlGetProp(data, "op_status"),
xmlGetProp(data, "op_code"));
}
gboolean
extract_event(xmlNodePtr msg)
{
gboolean abort = FALSE;
xmlNodePtr iter = NULL;
const char *section = NULL;
const char *event_action = NULL;
const char *event_node = NULL;
const char *event_rsc = NULL;
const char *event_status = NULL;
const char *event_rc = NULL;
/*
<status>
<node_state id="node1" state="active" exp_state="active">
<lrm>
<lrm_resources>
<rsc_state id="" rsc_id="rsc4" node_id="node1" rsc_state="stopped"/>
*/
xml_message_debug(msg, "TE Event");
iter = find_xml_node(msg, XML_TAG_FRAGMENT);
section = xmlGetProp(iter, "section");
if(safe_str_neq(section, XML_CIB_TAG_STATUS)) {
// these too are never expected
send_abort();
return FALSE;
}
iter = find_xml_node(msg, XML_TAG_CIB);
iter = get_object_root(XML_CIB_TAG_STATUS, iter);
iter = iter->children;
while(abort == FALSE && iter != NULL) {
xmlNodePtr node_state = iter;
xmlNodePtr child = iter->children;
const char *state = xmlGetProp(node_state, "state");
iter = iter->next;
if(state != NULL && child == NULL) {
/* node state update,
* possibly from a shutdown we requested
*/
event_status = state;
event_node = xmlGetProp(node_state, XML_ATTR_ID);
if(safe_str_eq(event_status, "down")) {
event_action = "shutdown";
}
abort = !process_graph_event(event_node,
event_rsc,
event_action,
event_status,
event_rc);
} else if(state != NULL && child != NULL) {
/* this is a complex eventand could not be completely
* due to any request we made
*/
send_abort();
abort = TRUE;
} else {
child = find_xml_node(node_state, "lrm");
child = find_xml_node(child, "lrm_resources");
child = child->children;
while(abort == FALSE && child != NULL) {
event_action = xmlGetProp(child, "last_op");
event_node = xmlGetProp(child, "op_node");
event_rsc = xmlGetProp(child, "id");
event_status = xmlGetProp(child, "op_status");
event_rc = xmlGetProp(child, "op_code");
abort = !process_graph_event(event_node,
event_rsc,
event_action,
event_status,
event_rc);
child = child->next;
}
}
}
return !abort;
}
gboolean
process_graph_event(const char *event_node,
const char *event_rsc,
const char *event_action,
const char *event_status,
const char *event_rc)
{
int lpc;
xmlNodePtr action = NULL; // <rsc_op> or <crm_event>
xmlNodePtr next_action = NULL;
+ action_list_t *matched_action_list = NULL;
// Find the action corresponding to this event
slist_iter(
action_list, action_list_t, graph, lpc,
action = g_slist_nth_data(action_list->actions,
action_list->index);
/*
<rsc_op id= runnable= optional= task= on_node= >
<resource id="rsc3" priority="3.0"/>
</rsc_op>
*/
const char *this_action = xmlGetProp(action, "task");
const char *this_node = xmlGetProp(action, "on_node");
const char *this_rsc = xmlGetProp(action->children, "id");
if(safe_str_neq(this_node, event_node)) {
continue;
} else if(safe_str_neq(this_action, event_action)) {
continue;
} else if(safe_str_eq(action->name, "rsc_op")
&& safe_str_eq(this_rsc, event_rsc)) {
- action_list->index++;
- next_action = g_slist_nth_data(action_list->actions,
- action_list->index);
+ matched_action_list = action_list;
} else if(safe_str_eq(action->name, "crm_event")) {
- action_list->index++;
- next_action = g_slist_nth_data(action_list->actions,
- action_list->index);
-
+ matched_action_list = action_list;
}
- );
+ );
// for the moment all actions succeed
- if(action == NULL) {
+ if(matched_action_list == NULL) {
// unexpected event, trigger a pe-recompute
// possibly do this only for certain types of actions
send_abort();
-
- } else if(next_action == NULL) {
- /* last action in that list, check if there are
- * anymore actions at all
- */
- gboolean more_to_do = FALSE;
- slist_iter(
- action_list, action_list_t, graph, lpc,
- if(action_list->index <= action_list->index_max){
- more_to_do = TRUE;
- break;
- }
- );
- if(more_to_do == FALSE) {
- // indicate to the CRMd that we're done
- xmlNodePtr options = create_xml_node(NULL, "options");
- set_xml_property_copy(options, XML_ATTR_OP, "te_complete");
+ return FALSE;
+ }
- send_ipc_request(crm_ch, options, NULL,
- NULL, "dc", "tengine",
- NULL, NULL);
-
- free_xml(options);
+ gboolean more_to_do = FALSE;
+ while(matched_action_list->index < matched_action_list->index_max) {
+ next_action = g_slist_nth_data(matched_action_list->actions,
+ matched_action_list->index);
- return TRUE;
- } // else wait for the next event
+ gboolean failed = initiate_action(matched_action_list);
+
+ if(failed) {
+ send_abort();
+
+ } else if(matched_action_list->index >
+ matched_action_list->index_max) {
+ /* last action in that list, check if there are
+ * anymore actions at all
+ */
+ slist_iter(
+ action_list, action_list_t, graph, lpc,
+ if(action_list->index <=
+ action_list->index_max){
+ more_to_do = TRUE;
+ break;
+ }
+ );
+ } else {
+ more_to_do = TRUE;
+
+ }
- } else {
- return initiate_action(next_action);
}
+ if(more_to_do == FALSE) {
+ // indicate to the CRMd that we're done
+ xmlNodePtr options = create_xml_node(NULL, "options");
+ set_xml_property_copy(options, XML_ATTR_OP,
+ "te_complete");
+
+ send_ipc_request(crm_ch, options, NULL,
+ NULL, "dc", "tengine",
+ NULL, NULL);
+
+ free_xml(options);
+
+ return TRUE;
+ } // else wait for the next event
+
return FALSE;
}
gboolean
initiate_transition(void)
{
int lpc;
gboolean anything = FALSE;
- xmlNodePtr action = NULL;
FNIN();
slist_iter(
action_list, action_list_t, graph, lpc,
- action = g_slist_nth_data(action_list->actions,
- action_list->index);
-
- if(action != NULL) {
+ if(initiate_action(action_list) == FALSE) {
anything = TRUE;
- initiate_action(action);
}
-
- action_list->index++;
);
FNRET(anything);
}
gboolean
-initiate_action(xmlNodePtr xml_action)
+initiate_action(action_list_t *list)
{
- // initiate the next action
-
- const char *on_node = xmlGetProp(xml_action, "on_node");
- const char *id = xmlGetProp(xml_action, "id");
-// const char *runnable = xmlGetProp(xml_action, "runnable");
-// const char *optional = xmlGetProp(xml_action, "optional");
- const char *task = xmlGetProp(xml_action, "task");
-
- FNIN();
-
-
- cl_log(LOG_INFO, "Invoking action %s (id=%s) on %s", task, id, on_node);
-
-
- if(id == NULL || strlen(id) == 0
- || on_node == NULL || strlen(on_node) == 0
- || task == NULL || strlen(task) == 0) {
- // error
- cl_log(LOG_ERR,
- "Command: \"%s (id=%s) on %s\" was corrupted.",
+ gboolean was_error = FALSE;
+ xmlNodePtr xml_action = NULL;
+ const char *on_node = NULL;
+ const char *id = NULL;
+ const char *runnable = NULL;
+ const char *optional = NULL;
+ const char *task = NULL;
+
+ while(TRUE) {
+
+ list->index++;
+ xml_action = g_slist_nth_data(list->actions, list->index);
+
+ if(xml_action == NULL) {
+ cl_log(LOG_INFO, "No tasks left on this list");
+ list->index = list->index_max + 1;
+
+ return was_error;
+ }
+
+
+ on_node = xmlGetProp(xml_action, "on_node");
+ id = xmlGetProp(xml_action, "id");
+ runnable = xmlGetProp(xml_action, "runnable");
+ optional = xmlGetProp(xml_action, "optional");
+ task = xmlGetProp(xml_action, "task");
+
+ cl_log(LOG_INFO,
+ "Invoking action %s (id=%s) on %s",
task, id, on_node);
- FNRET(FALSE);
+ if(safe_str_eq(optional, "true")) {
+ cl_log(LOG_INFO, "Skipping optional command");
+
+ } else if(safe_str_eq(runnable, "false")) {
+ cl_log(LOG_ERR, "Skipping un-runnable command");
+ return !was_error;
+
+ } else if(id == NULL || strlen(id) == 0
+ || on_node == NULL || strlen(on_node) == 0
+ || task == NULL || strlen(task) == 0) {
+ // error
+ cl_log(LOG_ERR,
+ "Command: \"%s (id=%s) on %s\" was corrupted.",
+ task, id, on_node);
+
+ return !was_error;
// } else if(safe_str_eq(xml_action->name, "pseduo_event")){
- } else if(safe_str_eq(xml_action->name, "crm_event")){
- /*
- <crm_msg op="task" to="on_node">
- */
- xmlNodePtr options = create_xml_node(NULL, "options");
- set_xml_property_copy(options, XML_ATTR_OP, task);
-
- send_ipc_request(crm_ch, options, NULL,
- on_node, "crmd", "tengine",
- NULL, NULL);
+ } else if(safe_str_eq(xml_action->name, "crm_event")){
+ /*
+ <crm_msg op="task" to="on_node">
+ */
+ xmlNodePtr options = create_xml_node(NULL, "options");
+ set_xml_property_copy(options, XML_ATTR_OP, task);
- free_xml(options);
+ send_ipc_request(crm_ch, options, NULL,
+ on_node, "crmd", "tengine",
+ NULL, NULL);
+
+ free_xml(options);
+ return was_error;
- } else if(safe_str_eq(xml_action->name, "rsc_op")){
- /*
- <msg_data>
+ } else if(safe_str_eq(xml_action->name, "rsc_op")){
+ /*
+ <msg_data>
<rsc_op id="operation number" on_node="" task="">
- <resource>...</resource>
- */
- xmlNodePtr options = create_xml_node(NULL, "options");
- xmlNodePtr data = create_xml_node(NULL, "msg_data");
- xmlNodePtr rsc_op = create_xml_node(data, "rsc_op");
-
- set_xml_property_copy(options, XML_ATTR_OP, "rsc_op");
-
- set_xml_property_copy(rsc_op, "id", id);
- set_xml_property_copy(rsc_op, "task", task);
- set_xml_property_copy(rsc_op, "on_node", on_node);
-
- add_node_copy(rsc_op, xml_action->children);
-
- send_ipc_request(crm_ch, options, data,
- on_node, "lrmd", "tengine",
- NULL, NULL);
+ <resource>...</resource>
+ */
+ xmlNodePtr options = create_xml_node(NULL, "options");
+ xmlNodePtr data = create_xml_node(NULL, "msg_data");
+ xmlNodePtr rsc_op = create_xml_node(data, "rsc_op");
- free_xml(options);
- free_xml(data);
+ set_xml_property_copy(options, XML_ATTR_OP, "rsc_op");
- } else {
- // error
- cl_log(LOG_ERR, "Action %s is not (yet?) supported",
- xml_action->name);
-
- FNRET(FALSE);
+ set_xml_property_copy(rsc_op, "id", id);
+ set_xml_property_copy(rsc_op, "task", task);
+ set_xml_property_copy(rsc_op, "on_node", on_node);
+
+ add_node_copy(rsc_op, xml_action->children);
+
+ send_ipc_request(crm_ch, options, data,
+ on_node, "lrmd", "tengine",
+ NULL, NULL);
+
+ free_xml(options);
+ free_xml(data);
+ return was_error;
+
+ } else {
+ // error
+ cl_log(LOG_ERR, "Action %s is not (yet?) supported",
+ xml_action->name);
+
+ }
}
-
- FNRET(TRUE);
-
+
+ return !was_error;
}
gboolean
process_te_message(xmlNodePtr msg, IPC_Channel *sender)
{
const char *op = get_xml_attr (msg, XML_TAG_OPTIONS,
XML_ATTR_OP, TRUE);
const char *sys_to = xmlGetProp(msg, XML_ATTR_SYSTO);
cl_log(LOG_DEBUG, "Processing %s message", op);
if(op == NULL){
// error
} else if(sys_to == NULL || strcmp(sys_to, "tengine") != 0) {
CRM_DEBUG("Bad sys-to %s", sys_to);
return FALSE;
} else if(strcmp(op, "transition") == 0) {
+
+ CRM_DEBUG("Initializing graph...");
initialize_graph();
xmlNodePtr graph = find_xml_node(msg, "transition_graph");
+ CRM_DEBUG("Unpacking graph...");
unpack_graph(graph);
+ CRM_DEBUG("Initiating transition...");
if(initiate_transition() == FALSE) {
// nothing to be done.. means we're done.
cl_log(LOG_INFO, "No actions to be taken..."
" transition compelte.");
send_success();
}
+ CRM_DEBUG("Processing complete...");
} else if(strcmp(op, "event") == 0) {
const char *true_op = get_xml_attr (msg, XML_TAG_OPTIONS,
"true_op", TRUE);
if(true_op == NULL) {
#ifdef USE_FAKE_LRM
process_fake_event(msg);
#else
// error
#endif
} else if(strcmp(op, CRM_OPERATION_CREATE) == 0
|| strcmp(op, CRM_OPERATION_DELETE) == 0
|| strcmp(op, CRM_OPERATION_REPLACE) == 0
|| strcmp(op, CRM_OPERATION_WELCOME) == 0
|| strcmp(op, CRM_OPERATION_SHUTDOWN_REQ) == 0
|| strcmp(op, CRM_OPERATION_ERASE) == 0) {
// these are always unexpected, trigger the PE
send_abort();
} else if(strcmp(op, CRM_OPERATION_UPDATE) == 0) {
// this may not be un-expected
extract_event(msg);
} else {
cl_log(LOG_ERR,
"Did not expect copy of action %s", op);
}
} else if(strcmp(op, "abort") == 0) {
initialize_graph();
} else if(strcmp(op, "quit") == 0) {
cl_log(LOG_WARNING, "Received quit message, terminating");
exit(0);
}
return TRUE;
}
void
send_abort(void)
{
xmlNodePtr options = create_xml_node(NULL, "options");
CRM_DEBUG("Sending \"abort\" message");
set_xml_property_copy(options, XML_ATTR_OP, "te_abort");
send_ipc_request(crm_ch, options, NULL,
NULL, "dc", "tengine",
NULL, NULL);
free_xml(options);
}
void
send_success(void)
{
xmlNodePtr options = create_xml_node(NULL, "options");
CRM_DEBUG("Sending \"complete\" message");
set_xml_property_copy(options, XML_ATTR_OP, "te_complete");
send_ipc_request(crm_ch, options, NULL,
NULL, "dc", "tengine",
NULL, NULL);
free_xml(options);
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jun 25, 4:15 AM (1 d, 11 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1952113
Default Alt Text
(180 KB)

Event Timeline