blob: 4c02869e9945cd701fb10c775cd10bf5017c35ca [file] [log] [blame] [edit]
/*
* Copyright (C) 2011-2017 Andrew Beekhof <andrew@beekhof.net>
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <crm_internal.h>
#include <stdio.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/attrd.h>
/*!
* \internal
* \brief Create a generic attrd operation
*
* \param[in] user_name If not NULL, ACL user to set for operation
*
* \return XML of attrd operation
*/
static xmlNode *
create_attrd_op(const char *user_name)
{
xmlNode *attrd_op = create_xml_node(NULL, __FUNCTION__);
crm_xml_add(attrd_op, F_TYPE, T_ATTRD);
crm_xml_add(attrd_op, F_ORIG, (crm_system_name? crm_system_name: "unknown"));
#if ENABLE_ACL
crm_xml_add(attrd_op, F_ATTRD_USER, user_name);
#endif
return attrd_op;
}
/*!
* \internal
* \brief Send an operation to attrd via IPC
*
* \param[in] ipc Connection to attrd (or NULL to use a local connection)
* \param[in] attrd_op XML of attrd operation to send
*
* \return pcmk_ok on success, -errno otherwise
*/
static int
send_attrd_op(crm_ipc_t *ipc, xmlNode *attrd_op)
{
int rc = -ENOTCONN;
int max = 5;
static gboolean connected = TRUE;
static crm_ipc_t *local_ipc = NULL;
static enum crm_ipc_flags flags = crm_ipc_flags_none;
if (ipc == NULL && local_ipc == NULL) {
local_ipc = crm_ipc_new(T_ATTRD, 0);
flags |= crm_ipc_client_response;
connected = FALSE;
}
if (ipc == NULL) {
ipc = local_ipc;
}
while (max > 0) {
if (connected == FALSE) {
crm_info("Connecting to cluster... %d retries remaining", max);
connected = crm_ipc_connect(ipc);
}
if (connected) {
rc = crm_ipc_send(ipc, attrd_op, flags, 0, NULL);
} else {
crm_perror(LOG_INFO, "Connection to cluster attribute manager failed");
}
if (ipc != local_ipc) {
break;
} else if (rc > 0) {
break;
} else if (rc == -EAGAIN || rc == -EALREADY) {
sleep(5 - max);
max--;
} else {
crm_ipc_close(ipc);
connected = FALSE;
sleep(5 - max);
max--;
}
}
if (rc > 0) {
rc = pcmk_ok;
}
return rc;
}
/*!
* \brief Send a request to attrd
*
* \param[in] ipc Connection to attrd (or NULL to use a local connection)
* \param[in] command A character indicating the type of attrd request:
* U or v: update attribute (or refresh if name is NULL)
* u: update attributes matching regular expression in name
* D: delete attribute (value must be NULL)
* R: refresh
* B: update both attribute and its dampening
* Y: update attribute dampening only
* Q: query attribute
* C: remove peer specified by host
* \param[in] host Affect only this host (or NULL for all hosts)
* \param[in] name Name of attribute to affect
* \param[in] value Attribute value to set
* \param[in] section Status or nodes
* \param[in] set ID of attribute set to use (or NULL to choose first)
* \param[in] dampen Attribute dampening to use with B/Y, and U/v if creating
* \param[in] user_name ACL user to pass to attrd
* \param[in] options Bitmask that may include:
* attrd_opt_remote: host is a Pacemaker Remote node
* attrd_opt_private: attribute is private (not kept in CIB)
*
* \return pcmk_ok if request was successfully submitted to attrd, else -errno
*/
int
attrd_update_delegate(crm_ipc_t *ipc, char command, const char *host,
const char *name, const char *value, const char *section,
const char *set, const char *dampen,
const char *user_name, int options)
{
int rc = pcmk_ok;
const char *task = NULL;
const char *name_as = NULL;
const char *display_host = (host ? host : "localhost");
const char *display_command = NULL; /* for commands without name/value */
xmlNode *update = create_attrd_op(user_name);
/* remap common aliases */
if (safe_str_eq(section, "reboot")) {
section = XML_CIB_TAG_STATUS;
} else if (safe_str_eq(section, "forever")) {
section = XML_CIB_TAG_NODES;
}
if (name == NULL && command == 'U') {
command = 'R';
}
switch (command) {
case 'u':
task = ATTRD_OP_UPDATE;
name_as = F_ATTRD_REGEX;
break;
case 'D':
case 'U':
case 'v':
task = ATTRD_OP_UPDATE;
name_as = F_ATTRD_ATTRIBUTE;
break;
case 'R':
task = ATTRD_OP_REFRESH;
display_command = "refresh";
break;
case 'B':
task = ATTRD_OP_UPDATE_BOTH;
name_as = F_ATTRD_ATTRIBUTE;
break;
case 'Y':
task = ATTRD_OP_UPDATE_DELAY;
name_as = F_ATTRD_ATTRIBUTE;
break;
case 'Q':
task = ATTRD_OP_QUERY;
name_as = F_ATTRD_ATTRIBUTE;
break;
case 'C':
task = ATTRD_OP_PEER_REMOVE;
display_command = "purge";
break;
}
if (name_as != NULL) {
if (name == NULL) {
rc = -EINVAL;
goto done;
}
crm_xml_add(update, name_as, name);
}
crm_xml_add(update, F_ATTRD_TASK, task);
crm_xml_add(update, F_ATTRD_VALUE, value);
crm_xml_add(update, F_ATTRD_DAMPEN, dampen);
crm_xml_add(update, F_ATTRD_SECTION, section);
crm_xml_add(update, F_ATTRD_HOST, host);
crm_xml_add(update, F_ATTRD_SET, set);
crm_xml_add_int(update, F_ATTRD_IS_REMOTE, is_set(options, attrd_opt_remote));
crm_xml_add_int(update, F_ATTRD_IS_PRIVATE, is_set(options, attrd_opt_private));
rc = send_attrd_op(ipc, update);
done:
free_xml(update);
if (display_command) {
crm_debug("Asked attrd to %s %s: %s (%d)",
display_command, display_host, pcmk_strerror(rc), rc);
} else {
crm_debug("Asked attrd to update %s=%s for %s: %s (%d)",
name, value, display_host, pcmk_strerror(rc), rc);
}
return rc;
}
/*!
* \brief Send a request to attrd to clear resource failure
*
* \param[in] ipc Connection to attrd (or NULL to use a local connection)
* \param[in] host Affect only this host (or NULL for all hosts)
* \param[in] name Name of resource to clear
* \param[in] user_name ACL user to pass to attrd
* \param[in] options attrd_opt_remote if host is a Pacemaker Remote node
*
* \return pcmk_ok if request was successfully submitted to attrd, else -errno
*/
int
attrd_clear_delegate(crm_ipc_t *ipc, const char *host, const char *resource,
const char *operation, const char *interval,
const char *user_name, int options)
{
int rc = pcmk_ok;
xmlNode *clear_op = create_attrd_op(user_name);
crm_xml_add(clear_op, F_ATTRD_TASK, ATTRD_OP_CLEAR_FAILURE);
crm_xml_add(clear_op, F_ATTRD_HOST, host);
crm_xml_add(clear_op, F_ATTRD_RESOURCE, resource);
crm_xml_add(clear_op, F_ATTRD_OPERATION, operation);
crm_xml_add(clear_op, F_ATTRD_INTERVAL, interval);
crm_xml_add_int(clear_op, F_ATTRD_IS_REMOTE, is_set(options, attrd_opt_remote));
rc = send_attrd_op(ipc, clear_op);
free_xml(clear_op);
crm_debug("Asked attrd to clear failure of %s (interval %s) for %s on %s: %s (%d)",
(operation? operation : "all operations"),
(interval? interval : "0"),
(resource? resource : "all resources"),
(host? host : "all nodes"), pcmk_strerror(rc), rc);
return rc;
}
#define LRM_TARGET_ENV "OCF_RESKEY_" CRM_META "_" XML_LRM_ATTR_TARGET
const char *
attrd_get_target(const char *name)
{
if(safe_str_eq(name, "auto") || safe_str_eq(name, "localhost")) {
name = NULL;
}
if(name != NULL) {
return name;
} else {
char *target_var = crm_meta_name(XML_RSC_ATTR_TARGET);
char *phys_var = crm_meta_name(PCMK_ENV_PHYSICAL_HOST);
const char *target = getenv(target_var);
const char *host_physical = getenv(phys_var);
/* It is important we use the names by which the PE knows us */
if (host_physical && safe_str_eq(target, "host")) {
name = host_physical;
} else {
const char *host_pcmk = getenv(LRM_TARGET_ENV);
if (host_pcmk) {
name = host_pcmk;
}
}
free(target_var);
free(phys_var);
}
// TODO? Call get_local_node_name() if name == NULL
// (currently would require linkage against libcrmcluster)
return name;
}