diff --git a/configure.ac b/configure.ac index 66faa47..5575696 100644 --- a/configure.ac +++ b/configure.ac @@ -2023,6 +2023,7 @@ AC_CONFIG_FILES(Makefile \ lib/common/tests/operations/Makefile \ lib/common/tests/strings/Makefile \ lib/common/tests/utils/Makefile \ + lib/common/tests/xpath/Makefile \ lib/cluster/Makefile \ lib/cib/Makefile \ lib/gnu/Makefile \ diff --git a/daemons/controld/controld_te_callbacks.c b/daemons/controld/controld_te_callbacks.c index 66fc645..4e3e4e6 100644 --- a/daemons/controld/controld_te_callbacks.c +++ b/daemons/controld/controld_te_callbacks.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2020 the Pacemaker project contributors + * Copyright 2004-2021 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -276,24 +276,6 @@ process_resource_updates(const char *node, xmlNode *xml, xmlNode *change, } } -#define NODE_PATT "/lrm[@id=" -static char *get_node_from_xpath(const char *xpath) -{ - char *nodeid = NULL; - char *tmp = strstr(xpath, NODE_PATT); - - if(tmp) { - tmp += strlen(NODE_PATT); - tmp += 1; - - nodeid = strdup(tmp); - tmp = strstr(nodeid, "\'"); - CRM_ASSERT(tmp); - tmp[0] = 0; - } - return nodeid; -} - static char *extract_node_uuid(const char *xpath) { char *mutable_path = strdup(xpath); @@ -522,19 +504,19 @@ te_update_diff_v2(xmlNode *diff) process_resource_updates(ID(match), match, change, op, xpath); } else if (strcmp(name, XML_LRM_TAG_RESOURCES) == 0) { - char *local_node = get_node_from_xpath(xpath); + char *local_node = pcmk__xpath_node_id(xpath, "lrm"); process_resource_updates(local_node, match, change, op, xpath); free(local_node); } else if (strcmp(name, XML_LRM_TAG_RESOURCE) == 0) { - char *local_node = get_node_from_xpath(xpath); + char *local_node = pcmk__xpath_node_id(xpath, "lrm"); process_lrm_resource_diff(match, local_node); free(local_node); } else if (strcmp(name, XML_LRM_TAG_RSC_OP) == 0) { - char *local_node = get_node_from_xpath(xpath); + char *local_node = pcmk__xpath_node_id(xpath, "lrm"); process_graph_event(match, local_node); free(local_node); diff --git a/include/crm/common/xml_internal.h b/include/crm/common/xml_internal.h index 1e80bc6..d8694ee 100644 --- a/include/crm/common/xml_internal.h +++ b/include/crm/common/xml_internal.h @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 the Pacemaker project contributors + * Copyright 2017-2021 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -273,4 +273,16 @@ pcmk__xe_first_attr(const xmlNode *xe) return (xe == NULL)? NULL : xe->properties; } +/*! + * \internal + * \brief Extract the ID attribute from an XML element + * + * \param[in] xpath String to search + * \param[in] node Node to get the ID for + * + * \return ID attribute of \p node in xpath string \p xpath + */ +char * +pcmk__xpath_node_id(const char *xpath, const char *node); + #endif // PCMK__XML_INTERNAL__H diff --git a/lib/common/tests/Makefile.am b/lib/common/tests/Makefile.am index 2c33cc5..4c6e8b4 100644 --- a/lib/common/tests/Makefile.am +++ b/lib/common/tests/Makefile.am @@ -1 +1 @@ -SUBDIRS = agents cmdline flags operations strings utils +SUBDIRS = agents cmdline flags operations strings utils xpath diff --git a/lib/common/tests/xpath/Makefile.am b/lib/common/tests/xpath/Makefile.am new file mode 100644 index 0000000..7a53683 --- /dev/null +++ b/lib/common/tests/xpath/Makefile.am @@ -0,0 +1,29 @@ +# +# Copyright 2021 the Pacemaker project contributors +# +# The version control history for this file may have further details. +# +# This source code is licensed under the GNU General Public License version 2 +# or later (GPLv2+) WITHOUT ANY WARRANTY. +# +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include +LDADD = $(top_builddir)/lib/common/libcrmcommon.la + +include $(top_srcdir)/mk/glib-tap.mk + +# Add each test program here. Each test should be written as a little standalone +# program using the glib unit testing functions. See the documentation for more +# information. +# +# https://developer.gnome.org/glib/unstable/glib-Testing.html +# +# Add "_test" to the end of all test program names to simplify .gitignore. +test_programs = pcmk__xpath_node_id_test + +# If any extra data needs to be added to the source distribution, add it to the +# following list. +dist_test_data = + +# If any extra data needs to be used by tests but should not be added to the +# source distribution, add it to the following list. +test_data = diff --git a/lib/common/tests/xpath/pcmk__xpath_node_id_test.c b/lib/common/tests/xpath/pcmk__xpath_node_id_test.c new file mode 100644 index 0000000..f6b5c10 --- /dev/null +++ b/lib/common/tests/xpath/pcmk__xpath_node_id_test.c @@ -0,0 +1,43 @@ +/* + * Copyright 2021 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include +#include + +static void +empty_input(void) { + g_assert_null(pcmk__xpath_node_id(NULL, "lrm")); + g_assert_null(pcmk__xpath_node_id("", "lrm")); + g_assert_null(pcmk__xpath_node_id("/blah/blah", NULL)); + g_assert_null(pcmk__xpath_node_id("/blah/blah", "")); + g_assert_null(pcmk__xpath_node_id(NULL, NULL)); +} + +static void +not_present(void) { + g_assert_null(pcmk__xpath_node_id("/some/xpath/string[@id='xyz']", "lrm")); + g_assert_null(pcmk__xpath_node_id("/some/xpath/containing[@id='lrm']", "lrm")); +} + +static void +present(void) { + g_assert_cmpint(strcmp(pcmk__xpath_node_id("/some/xpath/containing/lrm[@id='xyz']", "lrm"), "xyz"), ==, 0); + g_assert_cmpint(strcmp(pcmk__xpath_node_id("/some/other/lrm[@id='xyz']/xpath", "lrm"), "xyz"), ==, 0); +} + +int +main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/common/xpath/node_id/empty_input", empty_input); + g_test_add_func("/common/xpath/node_id/not_present", not_present); + g_test_add_func("/common/xpath/node_id/present", present); + return g_test_run(); +} diff --git a/lib/common/xpath.c b/lib/common/xpath.c index 6fa4941..7851a7c 100644 --- a/lib/common/xpath.c +++ b/lib/common/xpath.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2020 the Pacemaker project contributors + * Copyright 2004-2021 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -11,6 +11,7 @@ #include #include #include +#include #include "crmcommon_private.h" /* @@ -297,3 +298,34 @@ xml_get_path(xmlNode *xml) } return NULL; } + +char * +pcmk__xpath_node_id(const char *xpath, const char *node) +{ + char *retval = NULL; + char *patt = NULL; + char *start = NULL; + char *end = NULL; + + if (node == NULL || xpath == NULL) { + return retval; + } + + patt = crm_strdup_printf("/%s[@id=", node); + start = strstr(xpath, patt); + + if (!start) { + free(patt); + return retval; + } + + start += strlen(patt); + start++; + + end = strstr(start, "\'"); + CRM_ASSERT(end); + retval = strndup(start, end-start); + + free(patt); + return retval; +} diff --git a/tools/crm_mon.c b/tools/crm_mon.c index 8b47bbc..ff1b86b 100644 --- a/tools/crm_mon.c +++ b/tools/crm_mon.c @@ -1719,25 +1719,6 @@ mon_trigger_refresh(gpointer user_data) return FALSE; } -#define NODE_PATT "/lrm[@id=" -static char * -get_node_from_xpath(const char *xpath) -{ - char *nodeid = NULL; - char *tmp = strstr(xpath, NODE_PATT); - - if(tmp) { - tmp += strlen(NODE_PATT); - tmp += 1; - - nodeid = strdup(tmp); - tmp = strstr(nodeid, "\'"); - CRM_ASSERT(tmp); - tmp[0] = 0; - } - return nodeid; -} - static void crm_diff_update_v2(const char *event, xmlNode * msg) { @@ -1822,19 +1803,19 @@ crm_diff_update_v2(const char *event, xmlNode * msg) handle_rsc_op(match, node); } else if(strcmp(name, XML_LRM_TAG_RESOURCES) == 0) { - char *local_node = get_node_from_xpath(xpath); + char *local_node = pcmk__xpath_node_id(xpath, "lrm"); handle_rsc_op(match, local_node); free(local_node); } else if(strcmp(name, XML_LRM_TAG_RESOURCE) == 0) { - char *local_node = get_node_from_xpath(xpath); + char *local_node = pcmk__xpath_node_id(xpath, "lrm"); handle_rsc_op(match, local_node); free(local_node); } else if(strcmp(name, XML_LRM_TAG_RSC_OP) == 0) { - char *local_node = get_node_from_xpath(xpath); + char *local_node = pcmk__xpath_node_id(xpath, "lrm"); handle_rsc_op(match, local_node); free(local_node);