Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F1841697
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
15 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/common/operations.c b/lib/common/operations.c
index ff2428401e..85ac404851 100644
--- a/lib/common/operations.c
+++ b/lib/common/operations.c
@@ -1,523 +1,547 @@
/*
* Copyright 2004-2020 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 <crm_internal.h>
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <regex.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <ctype.h>
#include <crm/crm.h>
#include <crm/lrmd.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/util.h>
static regex_t *notify_migrate_re = NULL;
/*!
* \brief Generate an operation key (RESOURCE_ACTION_INTERVAL)
*
* \param[in] rsc_id ID of resource being operated on
* \param[in] op_type Operation name
* \param[in] interval_ms Operation interval
*
* \return Newly allocated memory containing operation key as string
*
* \note This function asserts on errors, so it will never return NULL.
* The caller is responsible for freeing the result with free().
*/
char *
pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
{
CRM_ASSERT(rsc_id != NULL);
CRM_ASSERT(op_type != NULL);
return crm_strdup_printf(PCMK__OP_FMT, rsc_id, op_type, interval_ms);
}
+static inline gboolean
+convert_interval(const char *s, guint *interval_ms)
+{
+ unsigned long l;
+
+ errno = 0;
+ l = strtoul(s, NULL, 10);
+
+ if (errno != 0) {
+ return FALSE;
+ }
+
+ *interval_ms = (guint) l;
+ return TRUE;
+}
+
+static gboolean
+try_fast_match(const char *key, const char *underbar1, const char *underbar2,
+ char **rsc_id, char **op_type, guint *interval_ms)
+{
+ if (interval_ms) {
+ if (!convert_interval(underbar2+1, interval_ms)) {
+ return FALSE;
+ }
+ }
+
+ if (rsc_id) {
+ *rsc_id = strndup(key, underbar1-key);
+ }
+
+ if (op_type) {
+ *op_type = strndup(underbar1+1, underbar2-underbar1-1);
+ }
+
+ return TRUE;
+}
+
static gboolean
try_basic_match(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
{
size_t len = 0, offset = 0;
size_t op_len = 0;
len = strlen(key);
offset = len - 1;
// Parse interval at end of string
while ((offset > 0) && isdigit(key[offset])) {
offset--;
}
if (interval_ms) {
- unsigned long l;
-
- errno = 0;
- l = strtoul(&(key[offset+1]), NULL, 10);
-
- if (errno != 0) {
+ if (!convert_interval(&(key[offset+1]), interval_ms)) {
return FALSE;
}
-
- *interval_ms = (guint) l;
}
// Verify we're at the separator between the operation and interval.
if (offset == len-1 || key[offset] != '_') {
if (interval_ms) {
*interval_ms = 0;
}
return FALSE;
}
// We're already on the underscore before the interval. Back up one
// or this loop will never do anything.
offset--;
while ((offset > 0) && key[offset] != '_') {
offset--;
op_len++;
}
if (op_type) {
// Add one here to skip the leading underscore we landed on in the
// while loop.
*op_type = strndup(&(key[offset+1]), op_len);
}
// Verify we're at the separator between the resource and operation.
if (offset == len-1 || key[offset] != '_') {
if (interval_ms) {
*interval_ms = 0;
}
if (op_type) {
free(*op_type);
*op_type = NULL;
}
return FALSE;
}
// Everything else is the name of the resource.
if (rsc_id) {
*rsc_id = strndup(key, offset);
}
return TRUE;
}
static gboolean
try_migrate_notify_match(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
{
int rc = 0;
size_t nmatch = 8;
regmatch_t *pmatch = NULL;
if (notify_migrate_re == NULL) {
// cppcheck-suppress memleak
notify_migrate_re = calloc(1, sizeof(regex_t));
rc = regcomp(notify_migrate_re, "^(.*)_(migrate_(from|to)|(pre|post)_notify_([a-z]+|migrate_(from|to)))_([0-9]+)$",
REG_EXTENDED);
CRM_ASSERT(rc == 0);
}
pmatch = calloc(nmatch, sizeof(regmatch_t));
rc = regexec(notify_migrate_re, key, nmatch, pmatch, 0);
if (rc == REG_NOMATCH) {
free(pmatch);
return FALSE;
}
if (rsc_id) {
*rsc_id = strndup(key+pmatch[1].rm_so, pmatch[1].rm_eo-pmatch[1].rm_so);
}
if (op_type) {
*op_type = strndup(key+pmatch[2].rm_so, pmatch[2].rm_eo-pmatch[2].rm_so);
}
if (interval_ms) {
- unsigned long l;
-
- errno = 0;
- l = strtoul(key+pmatch[7].rm_so, NULL, 10);
-
- if (errno != 0) {
+ if (!convert_interval(key+pmatch[7].rm_so, interval_ms)) {
if (rsc_id) {
free(*rsc_id);
*rsc_id = NULL;
}
if (op_type) {
free(*op_type);
*op_type = NULL;
}
free(pmatch);
return FALSE;
}
-
- *interval_ms = (guint) l;
}
free(pmatch);
return TRUE;
}
gboolean
parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
{
char *underbar1 = NULL;
char *underbar2 = NULL;
char *underbar3 = NULL;
// Initialize output variables in case of early return
if (rsc_id) {
*rsc_id = NULL;
}
if (op_type) {
*op_type = NULL;
}
if (interval_ms) {
*interval_ms = 0;
}
CRM_CHECK(key && *key, return FALSE);
underbar1 = strchr(key, '_');
if (!underbar1) {
return FALSE;
}
underbar2 = strchr(underbar1+1, '_');
if (!underbar2) {
return FALSE;
}
underbar3 = strchr(underbar2+1, '_');
if (!underbar3) {
- return try_basic_match(key, rsc_id, op_type, interval_ms);
+ return try_fast_match(key, underbar1, underbar2,
+ rsc_id, op_type, interval_ms);
} else if (try_migrate_notify_match(key, rsc_id, op_type, interval_ms)) {
return TRUE;
} else {
return try_basic_match(key, rsc_id, op_type, interval_ms);
}
}
char *
pcmk__notify_key(const char *rsc_id, const char *notify_type,
const char *op_type)
{
CRM_CHECK(rsc_id != NULL, return NULL);
CRM_CHECK(op_type != NULL, return NULL);
CRM_CHECK(notify_type != NULL, return NULL);
return crm_strdup_printf("%s_%s_notify_%s_0",
rsc_id, notify_type, op_type);
}
/*!
* \brief Parse a transition magic string into its constituent parts
*
* \param[in] magic Magic string to parse (must be non-NULL)
* \param[out] uuid If non-NULL, where to store copy of parsed UUID
* \param[out] transition_id If non-NULL, where to store parsed transition ID
* \param[out] action_id If non-NULL, where to store parsed action ID
* \param[out] op_status If non-NULL, where to store parsed result status
* \param[out] op_rc If non-NULL, where to store parsed actual rc
* \param[out] target_rc If non-NULL, where to stored parsed target rc
*
* \return TRUE if key was valid, FALSE otherwise
* \note If uuid is supplied and this returns TRUE, the caller is responsible
* for freeing the memory for *uuid using free().
*/
gboolean
decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id,
int *op_status, int *op_rc, int *target_rc)
{
int res = 0;
char *key = NULL;
gboolean result = TRUE;
int local_op_status = -1;
int local_op_rc = -1;
CRM_CHECK(magic != NULL, return FALSE);
#ifdef SSCANF_HAS_M
res = sscanf(magic, "%d:%d;%ms", &local_op_status, &local_op_rc, &key);
#else
key = calloc(1, strlen(magic) - 3); // magic must have >=4 other characters
CRM_ASSERT(key);
res = sscanf(magic, "%d:%d;%s", &local_op_status, &local_op_rc, key);
#endif
if (res == EOF) {
crm_err("Could not decode transition information '%s': %s",
magic, pcmk_strerror(errno));
result = FALSE;
} else if (res < 3) {
crm_warn("Transition information '%s' incomplete (%d of 3 expected items)",
magic, res);
result = FALSE;
} else {
if (op_status) {
*op_status = local_op_status;
}
if (op_rc) {
*op_rc = local_op_rc;
}
result = decode_transition_key(key, uuid, transition_id, action_id,
target_rc);
}
free(key);
return result;
}
char *
pcmk__transition_key(int transition_id, int action_id, int target_rc,
const char *node)
{
CRM_CHECK(node != NULL, return NULL);
return crm_strdup_printf("%d:%d:%d:%-*s",
action_id, transition_id, target_rc, 36, node);
}
/*!
* \brief Parse a transition key into its constituent parts
*
* \param[in] key Transition key to parse (must be non-NULL)
* \param[out] uuid If non-NULL, where to store copy of parsed UUID
* \param[out] transition_id If non-NULL, where to store parsed transition ID
* \param[out] action_id If non-NULL, where to store parsed action ID
* \param[out] target_rc If non-NULL, where to stored parsed target rc
*
* \return TRUE if key was valid, FALSE otherwise
* \note If uuid is supplied and this returns TRUE, the caller is responsible
* for freeing the memory for *uuid using free().
*/
gboolean
decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id,
int *target_rc)
{
int local_transition_id = -1;
int local_action_id = -1;
int local_target_rc = -1;
char local_uuid[37] = { '\0' };
// Initialize any supplied output arguments
if (uuid) {
*uuid = NULL;
}
if (transition_id) {
*transition_id = -1;
}
if (action_id) {
*action_id = -1;
}
if (target_rc) {
*target_rc = -1;
}
CRM_CHECK(key != NULL, return FALSE);
if (sscanf(key, "%d:%d:%d:%36s", &local_action_id, &local_transition_id,
&local_target_rc, local_uuid) != 4) {
crm_err("Invalid transition key '%s'", key);
return FALSE;
}
if (strlen(local_uuid) != 36) {
crm_warn("Invalid UUID '%s' in transition key '%s'", local_uuid, key);
}
if (uuid) {
*uuid = strdup(local_uuid);
CRM_ASSERT(*uuid);
}
if (transition_id) {
*transition_id = local_transition_id;
}
if (action_id) {
*action_id = local_action_id;
}
if (target_rc) {
*target_rc = local_target_rc;
}
return TRUE;
}
/*!
* \internal
* \brief Remove XML attributes not needed for operation digest
*
* \param[in,out] param_set XML with operation parameters
*/
void
pcmk__filter_op_for_digest(xmlNode *param_set)
{
char *key = NULL;
char *timeout = NULL;
guint interval_ms = 0;
const char *attr_filter[] = {
XML_ATTR_ID,
XML_ATTR_CRM_VERSION,
XML_LRM_ATTR_OP_DIGEST,
XML_LRM_ATTR_TARGET,
XML_LRM_ATTR_TARGET_UUID,
"pcmk_external_ip"
};
const int meta_len = strlen(CRM_META);
if (param_set == NULL) {
return;
}
// Remove the specific attributes listed in attr_filter
for (int lpc = 0; lpc < DIMOF(attr_filter); lpc++) {
xml_remove_prop(param_set, attr_filter[lpc]);
}
key = crm_meta_name(XML_LRM_ATTR_INTERVAL_MS);
if (crm_element_value_ms(param_set, key, &interval_ms) != pcmk_ok) {
interval_ms = 0;
}
free(key);
key = crm_meta_name(XML_ATTR_TIMEOUT);
timeout = crm_element_value_copy(param_set, key);
// Remove all CRM_meta_* attributes
for (xmlAttrPtr xIter = param_set->properties; xIter != NULL; ) {
const char *prop_name = (const char *) (xIter->name);
xIter = xIter->next;
// @TODO Why is this case-insensitive?
if (strncasecmp(prop_name, CRM_META, meta_len) == 0) {
xml_remove_prop(param_set, prop_name);
}
}
if ((interval_ms != 0) && (timeout != NULL)) {
// Add the timeout back, it's useful for recurring operation digests
crm_xml_add(param_set, key, timeout);
}
free(timeout);
free(key);
}
int
rsc_op_expected_rc(lrmd_event_data_t * op)
{
int rc = 0;
if (op && op->user_data) {
decode_transition_key(op->user_data, NULL, NULL, NULL, &rc);
}
return rc;
}
gboolean
did_rsc_op_fail(lrmd_event_data_t * op, int target_rc)
{
switch (op->op_status) {
case PCMK_LRM_OP_CANCELLED:
case PCMK_LRM_OP_PENDING:
return FALSE;
case PCMK_LRM_OP_NOTSUPPORTED:
case PCMK_LRM_OP_TIMEOUT:
case PCMK_LRM_OP_ERROR:
case PCMK_LRM_OP_NOT_CONNECTED:
case PCMK_LRM_OP_INVALID:
return TRUE;
default:
if (target_rc != op->rc) {
return TRUE;
}
}
return FALSE;
}
/*!
* \brief Create a CIB XML element for an operation
*
* \param[in] parent If not NULL, make new XML node a child of this one
* \param[in] prefix Generate an ID using this prefix
* \param[in] task Operation task to set
* \param[in] interval_spec Operation interval to set
* \param[in] timeout If not NULL, operation timeout to set
*
* \return New XML object on success, NULL otherwise
*/
xmlNode *
crm_create_op_xml(xmlNode *parent, const char *prefix, const char *task,
const char *interval_spec, const char *timeout)
{
xmlNode *xml_op;
CRM_CHECK(prefix && task && interval_spec, return NULL);
xml_op = create_xml_node(parent, XML_ATTR_OP);
crm_xml_set_id(xml_op, "%s-%s-%s", prefix, task, interval_spec);
crm_xml_add(xml_op, XML_LRM_ATTR_INTERVAL, interval_spec);
crm_xml_add(xml_op, "name", task);
if (timeout) {
crm_xml_add(xml_op, XML_ATTR_TIMEOUT, timeout);
}
return xml_op;
}
/*!
* \brief Check whether an operation requires resource agent meta-data
*
* \param[in] rsc_class Resource agent class (or NULL to skip class check)
* \param[in] op Operation action (or NULL to skip op check)
*
* \return TRUE if operation needs meta-data, FALSE otherwise
* \note At least one of rsc_class and op must be specified.
*/
bool
crm_op_needs_metadata(const char *rsc_class, const char *op)
{
/* Agent meta-data is used to determine whether a reload is possible, and to
* evaluate versioned parameters -- so if this op is not relevant to those
* features, we don't need the meta-data.
*/
CRM_CHECK((rsc_class != NULL) || (op != NULL), return false);
if ((rsc_class != NULL)
&& !pcmk_is_set(pcmk_get_ra_caps(rsc_class), pcmk_ra_cap_params)) {
/* Meta-data is only needed for resource classes that use parameters */
return false;
}
if (op == NULL) {
return true;
}
/* Meta-data is only needed for these actions */
return pcmk__str_any_of(op, CRMD_ACTION_START, CRMD_ACTION_STATUS,
CRMD_ACTION_PROMOTE, CRMD_ACTION_DEMOTE,
CRMD_ACTION_RELOAD, CRMD_ACTION_MIGRATE,
CRMD_ACTION_MIGRATED, CRMD_ACTION_NOTIFY, NULL);
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 23, 7:01 AM (1 d, 19 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1018380
Default Alt Text
(15 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment