Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F1841675
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
49 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c
index 9df6cacc6d..db668c3d7a 100644
--- a/lib/cib/cib_utils.c
+++ b/lib/cib/cib_utils.c
@@ -1,771 +1,772 @@
/*
* Original copyright 2004 International Business Machines
* Later changes copyright 2008-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>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <sys/utsname.h>
#include <glib.h>
#include <crm/crm.h>
#include <crm/cib/internal.h>
#include <crm/msg_xml.h>
#include <crm/common/iso8601_internal.h>
#include <crm/common/xml.h>
#include <crm/pengine/rules.h>
struct config_root_s {
const char *name;
const char *parent;
const char *path;
};
/*
* "//crm_config" will also work in place of "/cib/configuration/crm_config"
* The / prefix means find starting from the root, whereas the // prefix means
* find anywhere and risks multiple matches
*/
/* *INDENT-OFF* */
struct config_root_s known_paths[] = {
- { NULL, NULL, "//cib" },
- { XML_TAG_CIB, NULL, "//cib" },
- { XML_CIB_TAG_STATUS, "/cib", "//cib/status" },
- { XML_CIB_TAG_CONFIGURATION,"/cib", "//cib/configuration" },
- { XML_CIB_TAG_CRMCONFIG, "/cib/configuration", "//cib/configuration/crm_config" },
- { XML_CIB_TAG_NODES, "/cib/configuration", "//cib/configuration/nodes" },
- { XML_CIB_TAG_DOMAINS, "/cib/configuration", "//cib/configuration/domains" },
- { XML_CIB_TAG_RESOURCES, "/cib/configuration", "//cib/configuration/resources" },
- { XML_CIB_TAG_CONSTRAINTS, "/cib/configuration", "//cib/configuration/constraints" },
- { XML_CIB_TAG_OPCONFIG, "/cib/configuration", "//cib/configuration/op_defaults" },
- { XML_CIB_TAG_RSCCONFIG, "/cib/configuration", "//cib/configuration/rsc_defaults" },
- { XML_CIB_TAG_ACLS, "/cib/configuration", "//cib/configuration/acls" },
- { XML_TAG_FENCING_TOPOLOGY, "/cib/configuration", "//cib/configuration/fencing-topology" },
- { XML_CIB_TAG_SECTION_ALL, NULL, "//cib" },
+ { NULL, NULL, "//cib" },
+ { XML_TAG_CIB, NULL, "//cib" },
+ { XML_CIB_TAG_STATUS, "/cib", "//cib/status" },
+ { XML_CIB_TAG_CONFIGURATION, "/cib", "//cib/configuration" },
+ { XML_CIB_TAG_CRMCONFIG, "/cib/configuration", "//cib/configuration/crm_config" },
+ { XML_CIB_TAG_NODES, "/cib/configuration", "//cib/configuration/nodes" },
+ { XML_CIB_TAG_RESOURCES, "/cib/configuration", "//cib/configuration/resources" },
+ { XML_CIB_TAG_CONSTRAINTS, "/cib/configuration", "//cib/configuration/constraints" },
+ { XML_CIB_TAG_OPCONFIG, "/cib/configuration", "//cib/configuration/op_defaults" },
+ { XML_CIB_TAG_RSCCONFIG, "/cib/configuration", "//cib/configuration/rsc_defaults" },
+ { XML_CIB_TAG_ACLS, "/cib/configuration", "//cib/configuration/acls" },
+ { XML_TAG_FENCING_TOPOLOGY, "/cib/configuration", "//cib/configuration/fencing-topology" },
+ { XML_CIB_TAG_TAGS, "/cib/configuration", "//cib/configuration/tags" },
+ { XML_CIB_TAG_ALERTS, "/cib/configuration", "//cib/configuration/alerts" },
+ { XML_CIB_TAG_SECTION_ALL, NULL, "//cib" },
};
/* *INDENT-ON* */
xmlNode *
cib_get_generation(cib_t * cib)
{
xmlNode *the_cib = NULL;
xmlNode *generation = create_xml_node(NULL, XML_CIB_TAG_GENERATION_TUPPLE);
cib->cmds->query(cib, NULL, &the_cib, cib_scope_local | cib_sync_call);
if (the_cib != NULL) {
copy_in_properties(generation, the_cib);
free_xml(the_cib);
}
return generation;
}
gboolean
cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates)
{
*epoch = -1;
*updates = -1;
*admin_epoch = -1;
if (cib == NULL) {
return FALSE;
} else {
crm_element_value_int(cib, XML_ATTR_GENERATION, epoch);
crm_element_value_int(cib, XML_ATTR_NUMUPDATES, updates);
crm_element_value_int(cib, XML_ATTR_GENERATION_ADMIN, admin_epoch);
}
return TRUE;
}
gboolean
cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *updates,
int *_admin_epoch, int *_epoch, int *_updates)
{
int add[] = { 0, 0, 0 };
int del[] = { 0, 0, 0 };
xml_patch_versions(diff, add, del);
*admin_epoch = add[0];
*epoch = add[1];
*updates = add[2];
*_admin_epoch = del[0];
*_epoch = del[1];
*_updates = del[2];
return TRUE;
}
/*
* The caller should never free the return value
*/
const char *
get_object_path(const char *object_type)
{
int lpc = 0;
int max = DIMOF(known_paths);
for (; lpc < max; lpc++) {
if ((object_type == NULL && known_paths[lpc].name == NULL)
|| safe_str_eq(object_type, known_paths[lpc].name)) {
return known_paths[lpc].path;
}
}
return NULL;
}
const char *
get_object_parent(const char *object_type)
{
int lpc = 0;
int max = DIMOF(known_paths);
for (; lpc < max; lpc++) {
if (safe_str_eq(object_type, known_paths[lpc].name)) {
return known_paths[lpc].parent;
}
}
return NULL;
}
xmlNode *
get_object_root(const char *object_type, xmlNode * the_root)
{
const char *xpath = get_object_path(object_type);
if (xpath == NULL) {
return the_root; /* or return NULL? */
}
return get_xpath_object(xpath, the_root, LOG_TRACE);
}
/*
* It is the callers responsibility to free both the new CIB (output)
* and the new CIB (input)
*/
xmlNode *
createEmptyCib(int admin_epoch)
{
xmlNode *cib_root = NULL, *config = NULL;
cib_root = create_xml_node(NULL, XML_TAG_CIB);
crm_xml_add(cib_root, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
crm_xml_add(cib_root, XML_ATTR_VALIDATION, xml_latest_schema());
crm_xml_add_int(cib_root, XML_ATTR_GENERATION, admin_epoch);
crm_xml_add_int(cib_root, XML_ATTR_NUMUPDATES, 0);
crm_xml_add_int(cib_root, XML_ATTR_GENERATION_ADMIN, 0);
config = create_xml_node(cib_root, XML_CIB_TAG_CONFIGURATION);
create_xml_node(cib_root, XML_CIB_TAG_STATUS);
create_xml_node(config, XML_CIB_TAG_CRMCONFIG);
create_xml_node(config, XML_CIB_TAG_NODES);
create_xml_node(config, XML_CIB_TAG_RESOURCES);
create_xml_node(config, XML_CIB_TAG_CONSTRAINTS);
return cib_root;
}
static bool
cib_acl_enabled(xmlNode *xml, const char *user)
{
bool rc = FALSE;
#if ENABLE_ACL
if(pcmk_acl_required(user)) {
const char *value = NULL;
GHashTable *options = crm_str_table_new();
cib_read_config(options, xml);
value = cib_pref(options, "enable-acl");
rc = crm_is_true(value);
g_hash_table_destroy(options);
}
crm_trace("CIB ACL is %s", rc ? "enabled" : "disabled");
#endif
return rc;
}
int
cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_query,
const char *section, xmlNode * req, xmlNode * input,
gboolean manage_counters, gboolean * config_changed,
xmlNode * current_cib, xmlNode ** result_cib, xmlNode ** diff, xmlNode ** output)
{
int rc = pcmk_ok;
gboolean check_schema = TRUE;
xmlNode *top = NULL;
xmlNode *scratch = NULL;
xmlNode *local_diff = NULL;
const char *new_version = NULL;
static struct qb_log_callsite *diff_cs = NULL;
const char *user = crm_element_value(req, F_CIB_USER);
bool with_digest = FALSE;
crm_trace("Begin %s%s%s op", is_set(call_options, cib_dryrun)?"dry-run of ":"", is_query ? "read-only " : "", op);
CRM_CHECK(output != NULL, return -ENOMSG);
CRM_CHECK(result_cib != NULL, return -ENOMSG);
CRM_CHECK(config_changed != NULL, return -ENOMSG);
if(output) {
*output = NULL;
}
*result_cib = NULL;
*config_changed = FALSE;
if (fn == NULL) {
return -EINVAL;
}
if (is_query) {
xmlNode *cib_ro = current_cib;
xmlNode *cib_filtered = NULL;
if(cib_acl_enabled(cib_ro, user)) {
if(xml_acl_filtered_copy(user, current_cib, current_cib, &cib_filtered)) {
if (cib_filtered == NULL) {
crm_debug("Pre-filtered the entire cib");
return -EACCES;
}
cib_ro = cib_filtered;
crm_log_xml_trace(cib_ro, "filtered");
}
}
rc = (*fn) (op, call_options, section, req, input, cib_ro, result_cib, output);
if(output == NULL || *output == NULL) {
/* nothing */
} else if(cib_filtered == *output) {
cib_filtered = NULL; /* Let them have this copy */
} else if(*output == current_cib) {
/* They already know not to free it */
} else if(cib_filtered && (*output)->doc == cib_filtered->doc) {
/* We're about to free the document of which *output is a part */
*output = copy_xml(*output);
} else if((*output)->doc == current_cib->doc) {
/* Give them a copy they can free */
*output = copy_xml(*output);
}
free_xml(cib_filtered);
return rc;
}
if (is_set(call_options, cib_zero_copy)) {
/* Conditional on v2 patch style */
scratch = current_cib;
/* Create a shallow copy of current_cib for the version details */
current_cib = create_xml_node(NULL, (const char *)scratch->name);
copy_in_properties(current_cib, scratch);
top = current_cib;
xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
rc = (*fn) (op, call_options, section, req, input, scratch, &scratch, output);
} else {
scratch = copy_xml(current_cib);
xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
rc = (*fn) (op, call_options, section, req, input, current_cib, &scratch, output);
if(scratch && xml_tracking_changes(scratch) == FALSE) {
crm_trace("Inferring changes after %s op", op);
xml_track_changes(scratch, user, current_cib, cib_acl_enabled(current_cib, user));
xml_calculate_changes(current_cib, scratch);
}
CRM_CHECK(current_cib != scratch, return -EINVAL);
}
xml_acl_disable(scratch); /* Allow the system to make any additional changes */
if (rc == pcmk_ok && scratch == NULL) {
rc = -EINVAL;
goto done;
} else if(rc == pcmk_ok && xml_acl_denied(scratch)) {
crm_trace("ACL rejected part or all of the proposed changes");
rc = -EACCES;
goto done;
} else if (rc != pcmk_ok) {
goto done;
}
if (scratch) {
new_version = crm_element_value(scratch, XML_ATTR_CRM_VERSION);
if (new_version && compare_version(new_version, CRM_FEATURE_SET) > 0) {
crm_err("Discarding update with feature set '%s' greater than our own '%s'",
new_version, CRM_FEATURE_SET);
rc = -EPROTONOSUPPORT;
goto done;
}
}
if (current_cib) {
int old = 0;
int new = 0;
crm_element_value_int(scratch, XML_ATTR_GENERATION_ADMIN, &new);
crm_element_value_int(current_cib, XML_ATTR_GENERATION_ADMIN, &old);
if (old > new) {
crm_err("%s went backwards: %d -> %d (Opts: 0x%x)",
XML_ATTR_GENERATION_ADMIN, old, new, call_options);
crm_log_xml_warn(req, "Bad Op");
crm_log_xml_warn(input, "Bad Data");
rc = -pcmk_err_old_data;
} else if (old == new) {
crm_element_value_int(scratch, XML_ATTR_GENERATION, &new);
crm_element_value_int(current_cib, XML_ATTR_GENERATION, &old);
if (old > new) {
crm_err("%s went backwards: %d -> %d (Opts: 0x%x)",
XML_ATTR_GENERATION, old, new, call_options);
crm_log_xml_warn(req, "Bad Op");
crm_log_xml_warn(input, "Bad Data");
rc = -pcmk_err_old_data;
}
}
}
crm_trace("Massaging CIB contents");
strip_text_nodes(scratch);
fix_plus_plus_recursive(scratch);
if (is_set(call_options, cib_zero_copy)) {
/* At this point, current_cib is just the 'cib' tag and its properties,
*
* The v1 format would barf on this, but we know the v2 patch
* format only needs it for the top-level version fields
*/
local_diff = xml_create_patchset(2, current_cib, scratch, (bool*)config_changed, manage_counters);
} else {
static time_t expires = 0;
time_t tm_now = time(NULL);
if (expires < tm_now) {
expires = tm_now + 60; /* Validate clients are correctly applying v2-style diffs at most once a minute */
with_digest = TRUE;
}
local_diff = xml_create_patchset(0, current_cib, scratch, (bool*)config_changed, manage_counters);
}
xml_log_changes(LOG_TRACE, __FUNCTION__, scratch);
xml_accept_changes(scratch);
if (diff_cs == NULL) {
diff_cs = qb_log_callsite_get(__PRETTY_FUNCTION__, __FILE__, "diff-validation", LOG_DEBUG, __LINE__, crm_trace_nonlog);
}
if(local_diff) {
patchset_process_digest(local_diff, current_cib, scratch, with_digest);
xml_log_patchset(LOG_INFO, __FUNCTION__, local_diff);
crm_log_xml_trace(local_diff, "raw patch");
}
if (is_not_set(call_options, cib_zero_copy) /* The original to compare against doesn't exist */
&& local_diff
&& crm_is_callsite_active(diff_cs, LOG_TRACE, 0)) {
/* Validate the calculated patch set */
int test_rc, format = 1;
xmlNode * c = copy_xml(current_cib);
crm_element_value_int(local_diff, "format", &format);
test_rc = xml_apply_patchset(c, local_diff, manage_counters);
if(test_rc != pcmk_ok) {
save_xml_to_file(c, "PatchApply:calculated", NULL);
save_xml_to_file(current_cib, "PatchApply:input", NULL);
save_xml_to_file(scratch, "PatchApply:actual", NULL);
save_xml_to_file(local_diff, "PatchApply:diff", NULL);
crm_err("v%d patchset error, patch failed to apply: %s (%d)", format, pcmk_strerror(test_rc), test_rc);
}
free_xml(c);
}
if (safe_str_eq(section, XML_CIB_TAG_STATUS)) {
/* Throttle the amount of costly validation we perform due to status updates
* a) we don't really care whats in the status section
* b) we don't validate any of its contents at the moment anyway
*/
check_schema = FALSE;
}
/* === scratch must not be modified after this point ===
* Exceptions, anything in:
static filter_t filter[] = {
{ 0, XML_ATTR_ORIGIN },
{ 0, XML_CIB_ATTR_WRITTEN },
{ 0, XML_ATTR_UPDATE_ORIG },
{ 0, XML_ATTR_UPDATE_CLIENT },
{ 0, XML_ATTR_UPDATE_USER },
};
*/
if (*config_changed && is_not_set(call_options, cib_no_mtime)) {
const char *schema = crm_element_value(scratch, XML_ATTR_VALIDATION);
crm_xml_add_last_written(scratch);
if (schema) {
static int minimum_schema = 0;
int current_schema = get_schema_version(schema);
if (minimum_schema == 0) {
minimum_schema = get_schema_version("pacemaker-1.2");
}
/* Does the CIB support the "update-*" attributes... */
if (current_schema >= minimum_schema) {
const char *origin = crm_element_value(req, F_ORIG);
CRM_LOG_ASSERT(origin != NULL);
crm_xml_replace(scratch, XML_ATTR_UPDATE_ORIG, origin);
crm_xml_replace(scratch, XML_ATTR_UPDATE_CLIENT,
crm_element_value(req, F_CIB_CLIENTNAME));
#if ENABLE_ACL
crm_xml_replace(scratch, XML_ATTR_UPDATE_USER, crm_element_value(req, F_CIB_USER));
#endif
}
}
}
crm_trace("Perform validation: %s", (check_schema? "true" : "false"));
if ((rc == pcmk_ok) && check_schema && !validate_xml(scratch, NULL, TRUE)) {
const char *current_schema = crm_element_value(scratch,
XML_ATTR_VALIDATION);
crm_warn("Updated CIB does not validate against %s schema",
crm_str(current_schema));
rc = -pcmk_err_schema_validation;
}
done:
*result_cib = scratch;
#if ENABLE_ACL
if(rc != pcmk_ok && cib_acl_enabled(current_cib, user)) {
if(xml_acl_filtered_copy(user, current_cib, scratch, result_cib)) {
if (*result_cib == NULL) {
crm_debug("Pre-filtered the entire cib result");
}
free_xml(scratch);
}
}
#endif
if(diff) {
*diff = local_diff;
} else {
free_xml(local_diff);
}
free_xml(top);
crm_trace("Done");
return rc;
}
xmlNode *
cib_create_op(int call_id, const char *token, const char *op, const char *host, const char *section,
xmlNode * data, int call_options, const char *user_name)
{
xmlNode *op_msg = create_xml_node(NULL, "cib_command");
CRM_CHECK(op_msg != NULL, return NULL);
CRM_CHECK(token != NULL, return NULL);
crm_xml_add(op_msg, F_XML_TAGNAME, "cib_command");
crm_xml_add(op_msg, F_TYPE, T_CIB);
crm_xml_add(op_msg, F_CIB_CALLBACK_TOKEN, token);
crm_xml_add(op_msg, F_CIB_OPERATION, op);
crm_xml_add(op_msg, F_CIB_HOST, host);
crm_xml_add(op_msg, F_CIB_SECTION, section);
crm_xml_add_int(op_msg, F_CIB_CALLID, call_id);
#if ENABLE_ACL
if (user_name) {
crm_xml_add(op_msg, F_CIB_USER, user_name);
}
#endif
crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options);
crm_xml_add_int(op_msg, F_CIB_CALLOPTS, call_options);
if (data != NULL) {
add_message_xml(op_msg, F_CIB_CALLDATA, data);
}
if (call_options & cib_inhibit_bcast) {
CRM_CHECK((call_options & cib_scope_local), return NULL);
}
return op_msg;
}
void
cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc)
{
xmlNode *output = NULL;
cib_callback_client_t *blob = NULL;
if (msg != NULL) {
crm_element_value_int(msg, F_CIB_RC, &rc);
crm_element_value_int(msg, F_CIB_CALLID, &call_id);
output = get_message_xml(msg, F_CIB_CALLDATA);
}
blob = g_hash_table_lookup(cib_op_callback_table, GINT_TO_POINTER(call_id));
if (blob == NULL) {
crm_trace("No callback found for call %d", call_id);
}
if (cib == NULL) {
crm_debug("No cib object supplied");
}
if (rc == -pcmk_err_diff_resync) {
/* This is an internal value that clients do not and should not care about */
rc = pcmk_ok;
}
if (blob && blob->callback && (rc == pcmk_ok || blob->only_success == FALSE)) {
crm_trace("Invoking callback %s for call %d", crm_str(blob->id), call_id);
blob->callback(msg, call_id, rc, output, blob->user_data);
} else if (cib && cib->op_callback == NULL && rc != pcmk_ok) {
crm_warn("CIB command failed: %s", pcmk_strerror(rc));
crm_log_xml_debug(msg, "Failed CIB Update");
}
/* This may free user_data, so do it after the callback */
if (blob) {
remove_cib_op_callback(call_id, FALSE);
}
if (cib && cib->op_callback != NULL) {
crm_trace("Invoking global callback for call %d", call_id);
cib->op_callback(msg, call_id, rc, output);
}
crm_trace("OP callback activated for %d", call_id);
}
void
cib_native_notify(gpointer data, gpointer user_data)
{
xmlNode *msg = user_data;
cib_notify_client_t *entry = data;
const char *event = NULL;
if (msg == NULL) {
crm_warn("Skipping callback - NULL message");
return;
}
event = crm_element_value(msg, F_SUBTYPE);
if (entry == NULL) {
crm_warn("Skipping callback - NULL callback client");
return;
} else if (entry->callback == NULL) {
crm_warn("Skipping callback - NULL callback");
return;
} else if (safe_str_neq(entry->event, event)) {
crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event);
return;
}
crm_trace("Invoking callback for %p/%s event...", entry, event);
entry->callback(event, msg);
crm_trace("Callback invoked...");
}
static pcmk__cluster_option_t cib_opts[] = {
/* name, legacy name, type, allowed values,
* default value, validator,
* short description,
* long description
*/
{
"enable-acl", NULL, "boolean", NULL,
"false", pcmk__valid_boolean,
"Enable Access Control Lists (ACLs) for the CIB",
NULL
},
{
"cluster-ipc-limit", NULL, "integer", NULL,
"500", pcmk__valid_positive_number,
"Maximum IPC message backlog before disconnecting a cluster daemon",
"Raise this if log has \"Evicting client\" messages for cluster daemon"
" PIDs (a good value is the number of resources in the cluster"
" multiplied by the number of nodes)."
},
};
void
cib_metadata(void)
{
pcmk__print_option_metadata("pacemaker-based", "1.0",
"Cluster Information Base manager options",
"Cluster options used by Pacemaker's "
"Cluster Information Base manager",
cib_opts, DIMOF(cib_opts));
}
void
verify_cib_options(GHashTable * options)
{
pcmk__validate_cluster_options(options, cib_opts, DIMOF(cib_opts));
}
const char *
cib_pref(GHashTable * options, const char *name)
{
return pcmk__cluster_option(options, cib_opts, DIMOF(cib_opts), name);
}
gboolean
cib_read_config(GHashTable * options, xmlNode * current_cib)
{
xmlNode *config = NULL;
crm_time_t *now = NULL;
if (options == NULL || current_cib == NULL) {
return FALSE;
}
now = crm_time_new(NULL);
g_hash_table_remove_all(options);
config = get_object_root(XML_CIB_TAG_CRMCONFIG, current_cib);
if (config) {
pe_unpack_nvpairs(current_cib, config, XML_CIB_TAG_PROPSET, NULL,
options, CIB_OPTIONS_FIRST, TRUE, now, NULL);
}
verify_cib_options(options);
crm_time_free(now);
return TRUE;
}
/* v2 and v2 patch formats */
#define XPATH_CONFIG_CHANGE \
"//" XML_CIB_TAG_CRMCONFIG " | " \
"//" XML_DIFF_CHANGE "[contains(@" XML_DIFF_PATH ",'/" XML_CIB_TAG_CRMCONFIG "/')]"
gboolean
cib_internal_config_changed(xmlNode *diff)
{
gboolean changed = FALSE;
if (diff) {
xmlXPathObject *xpathObj = xpath_search(diff, XPATH_CONFIG_CHANGE);
if (numXpathResults(xpathObj) > 0) {
changed = TRUE;
}
freeXpathObject(xpathObj);
}
return changed;
}
int
cib_internal_op(cib_t * cib, const char *op, const char *host,
const char *section, xmlNode * data,
xmlNode ** output_data, int call_options, const char *user_name)
{
int (*delegate) (cib_t * cib, const char *op, const char *host,
const char *section, xmlNode * data,
xmlNode ** output_data, int call_options, const char *user_name) =
cib->delegate_fn;
#if ENABLE_ACL
if(user_name == NULL) {
user_name = getenv("CIB_user");
}
#endif
return delegate(cib, op, host, section, data, output_data, call_options, user_name);
}
// Deprecated functions kept only for backward API compatibility
int cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output,
int level);
/*!
* \deprecated
*/
int
cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output,
int level)
{
int rc = pcmk_err_generic;
xmlNode *diff = NULL;
CRM_ASSERT(event);
CRM_ASSERT(input);
CRM_ASSERT(output);
crm_element_value_int(event, F_CIB_RC, &rc);
diff = get_message_xml(event, F_CIB_UPDATE_RESULT);
if (rc < pcmk_ok || diff == NULL) {
return rc;
}
if (level > LOG_CRIT) {
xml_log_patchset(level, "Config update", diff);
}
if (input != NULL) {
rc = cib_process_diff(NULL, cib_none, NULL, event, diff, input, output,
NULL);
if (rc != pcmk_ok) {
crm_debug("Update didn't apply: %s (%d) %p",
pcmk_strerror(rc), rc, *output);
if (rc == -pcmk_err_old_data) {
crm_trace("Masking error, we already have the supplied update");
return pcmk_ok;
}
free_xml(*output);
*output = NULL;
return rc;
}
}
return rc;
}
diff --git a/tools/cibadmin.c b/tools/cibadmin.c
index d53a816c0e..6e1a9941d6 100644
--- a/tools/cibadmin.c
+++ b/tools/cibadmin.c
@@ -1,772 +1,773 @@
/*
* 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 General Public License version 2
* or later (GPLv2+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <stdio.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/ipc.h>
#include <crm/cib/internal.h>
static int message_timeout_ms = 30;
static int command_options = 0;
static int request_id = 0;
static int bump_log_num = 0;
static const char *host = NULL;
static const char *cib_user = NULL;
static const char *cib_action = NULL;
static const char *obj_type = NULL;
static cib_t *the_cib = NULL;
static GMainLoop *mainloop = NULL;
static gboolean force_flag = FALSE;
static crm_exit_t exit_code = CRM_EX_OK;
int do_init(void);
int do_work(xmlNode *input, int command_options, xmlNode **output);
void cibadmin_op_callback(xmlNode *msg, int call_id, int rc, xmlNode *output,
void *user_data);
static pcmk__cli_option_t long_options[] = {
// long option, argument type, storage, short option, description, flags
{
"help", no_argument, NULL, '?',
"\tThis text", pcmk__option_default
},
{
"version", no_argument, NULL, '$',
"\tVersion information", pcmk__option_default
},
{
"verbose", no_argument, NULL, 'V',
"\tIncrease debug output\n", pcmk__option_default
},
{
"-spacer-", no_argument, NULL, '-',
"Commands:", pcmk__option_default
},
{
"upgrade", no_argument, NULL, 'u',
"\tUpgrade the configuration to the latest syntax", pcmk__option_default
},
{
"query", no_argument, NULL, 'Q',
"\tQuery the contents of the CIB", pcmk__option_default
},
{
"erase", no_argument, NULL, 'E',
"\tErase the contents of the whole CIB", pcmk__option_default
},
{
"bump", no_argument, NULL, 'B',
"\tIncrease the CIB's epoch value by 1", pcmk__option_default
},
{
"create", no_argument, NULL, 'C',
"\tCreate an object in the CIB (will fail if object already exists)",
pcmk__option_default
},
{
"modify", no_argument, NULL, 'M',
"\tFind object somewhere in CIB's XML tree and update it "
"(fails if object does not exist unless -c is also specified)",
pcmk__option_default
},
{
"patch", no_argument, NULL, 'P',
"\tSupply an update in the form of an XML diff (see crm_diff(8))",
pcmk__option_default
},
{
"replace", no_argument, NULL, 'R',
"\tRecursively replace an object in the CIB", pcmk__option_default
},
{
"delete", no_argument, NULL, 'D',
"\tDelete first object matching supplied criteria "
"(for example, <op id=\"rsc1_op1\" name=\"monitor\"/>)",
pcmk__option_default
},
{
"-spacer-", no_argument, NULL, '-',
"\n\tThe XML element name and all attributes must match "
"in order for the element to be deleted.\n",
pcmk__option_default
},
{
"delete-all", no_argument, NULL, 'd',
"When used with --xpath, remove all matching objects in the "
"configuration instead of just the first one",
pcmk__option_default
},
{
"empty", no_argument, NULL, 'a',
"\tOutput an empty CIB", pcmk__option_default
},
{
"md5-sum", no_argument, NULL, '5',
"\tCalculate the on-disk CIB digest", pcmk__option_default
},
{
"md5-sum-versioned", no_argument, NULL, '6',
"Calculate an on-the-wire versioned CIB digest", pcmk__option_default
},
{
"blank", no_argument, NULL, '-',
NULL, pcmk__option_hidden
},
{
"-spacer-", required_argument, NULL, '-',
"\nAdditional options:", pcmk__option_default
},
{
"force", no_argument, NULL, 'f',
NULL, pcmk__option_default
},
{
"timeout", required_argument, NULL, 't',
"Time (in seconds) to wait before declaring the operation failed",
pcmk__option_default
},
{
"user", required_argument, NULL, 'U',
"Run the command with permissions of the named user (valid only for "
"the root and " CRM_DAEMON_USER " accounts)",
pcmk__option_default
},
{
"sync-call", no_argument, NULL, 's',
"Wait for call to complete before returning", pcmk__option_default
},
{
"local", no_argument, NULL, 'l',
"\tCommand takes effect locally (should be used only for queries)",
pcmk__option_default
},
{
"allow-create", no_argument, NULL, 'c',
"(Advanced) Allow target of --modify/-M to be created "
"if it does not exist",
pcmk__option_default
},
{
"no-children", no_argument, NULL, 'n',
"(Advanced) When querying an object, do not include its children "
"in the result",
pcmk__option_default
},
{
"no-bcast", no_argument, NULL, 'b',
NULL, pcmk__option_hidden
},
{
"-spacer-", no_argument, NULL, '-',
"\nData:", pcmk__option_default
},
{
"xml-text", required_argument, NULL, 'X',
"Retrieve XML from the supplied string", pcmk__option_default
},
{
"xml-file", required_argument, NULL, 'x',
"Retrieve XML from the named file", pcmk__option_default
},
{
"xml-pipe", no_argument, NULL, 'p',
"Retrieve XML from stdin\n", pcmk__option_default
},
{
"scope", required_argument, NULL, 'o',
"Limit scope of operation to specific section of CIB",
pcmk__option_default
},
{
"-spacer-", no_argument, NULL, '-',
"\tValid values: configuration, nodes, resources, constraints, "
- "crm_config, rsc_defaults, op_defaults, acls, fencing-topology",
+ "crm_config, rsc_defaults, op_defaults, acls, fencing-topology, "
+ "tags, alerts",
pcmk__option_default
},
{
"xpath", required_argument, NULL, 'A',
"A valid XPath to use instead of --scope/-o", pcmk__option_default
},
{
"node-path", no_argument, NULL, 'e',
"When performing XPath queries, return path of any matches found",
pcmk__option_default
},
{
"-spacer-", no_argument, NULL, '-',
"\t(for example, \"/cib/configuration/resources/clone[@id='ms_RH1_SCS']"
"/primitive[@id='prm_RH1_SCS']\")",
pcmk__option_paragraph
},
{
"node", required_argument, NULL, 'N',
"(Advanced) Send command to the specified host", pcmk__option_default
},
{
"-spacer-", no_argument, NULL, '!',
NULL, pcmk__option_hidden
},
{
"-spacer-", no_argument, NULL, '-',
"\n\nExamples:\n", pcmk__option_default
},
{
"-spacer-", no_argument, NULL, '-',
"Query the configuration from the local node:", pcmk__option_paragraph
},
{
"-spacer-", no_argument, NULL, '-',
" cibadmin --query --local", pcmk__option_example
},
{
"-spacer-", no_argument, NULL, '-',
"Query just the cluster options configuration:", pcmk__option_paragraph
},
{
"-spacer-", no_argument, NULL, '-',
" cibadmin --query --scope crm_config", pcmk__option_example
},
{
"-spacer-", no_argument, NULL, '-',
"Query all 'target-role' settings:", pcmk__option_paragraph
},
{
"-spacer-", no_argument, NULL, '-',
" cibadmin --query --xpath \"//nvpair[@name='target-role']\"",
pcmk__option_example
},
{
"-spacer-", no_argument, NULL, '-',
"Remove all 'is-managed' settings:", pcmk__option_paragraph
},
{
"-spacer-", no_argument, NULL, '-',
" cibadmin --delete-all --xpath \"//nvpair[@name='is-managed']\"",
pcmk__option_example
},
{
"-spacer-", no_argument, NULL, '-',
"Remove the resource named 'old':", pcmk__option_paragraph
},
{
"-spacer-", no_argument, NULL, '-',
" cibadmin --delete --xml-text '<primitive id=\"old\"/>'",
pcmk__option_example
},
{
"-spacer-", no_argument, NULL, '-',
"Remove all resources from the configuration:", pcmk__option_paragraph
},
{
"-spacer-", no_argument, NULL, '-',
" cibadmin --replace --scope resources --xml-text '<resources/>'",
pcmk__option_example
},
{
"-spacer-", no_argument, NULL, '-',
"Replace complete configuration with contents of $HOME/pacemaker.xml:",
pcmk__option_paragraph
},
{
"-spacer-", no_argument, NULL, '-',
" cibadmin --replace --xml-file $HOME/pacemaker.xml",
pcmk__option_example
},
{
"-spacer-", no_argument, NULL, '-',
"Replace constraints section of configuration with contents of "
"$HOME/constraints.xml:",
pcmk__option_paragraph
},
{
"-spacer-", no_argument, NULL, '-',
" cibadmin --replace --scope constraints --xml-file "
"$HOME/constraints.xml",
pcmk__option_example
},
{
"-spacer-", no_argument, NULL, '-',
"Increase configuration version to prevent old configurations from "
"being loaded accidentally:",
pcmk__option_paragraph
},
{
"-spacer-", no_argument, NULL, '-',
" cibadmin --modify --xml-text '<cib admin_epoch=\"admin_epoch++\"/>'",
pcmk__option_example
},
{
"-spacer-", no_argument, NULL, '-',
"Edit the configuration with your favorite $EDITOR:",
pcmk__option_paragraph
},
{
"-spacer-", no_argument, NULL, '-',
" cibadmin --query > $HOME/local.xml", pcmk__option_example
},
{
"-spacer-", no_argument, NULL, '-',
" $EDITOR $HOME/local.xml", pcmk__option_example
},
{
"-spacer-", no_argument, NULL, '-',
" cibadmin --replace --xml-file $HOME/local.xml", pcmk__option_example
},
{
"-spacer-", no_argument, NULL, '-',
"SEE ALSO:", pcmk__option_default
},
{
"-spacer-", no_argument, NULL, '-',
" crm(8), pcs(8), crm_shadow(8), crm_diff(8)", pcmk__option_default
},
{
"host", required_argument, NULL, 'h',
"deprecated", pcmk__option_hidden
},
{ 0, 0, 0, 0 }
};
static void
print_xml_output(xmlNode * xml)
{
char *buffer;
if (!xml) {
return;
} else if (xml->type != XML_ELEMENT_NODE) {
return;
}
if (command_options & cib_xpath_address) {
const char *id = crm_element_value(xml, XML_ATTR_ID);
if (safe_str_eq((const char *)xml->name, "xpath-query")) {
xmlNode *child = NULL;
for (child = xml->children; child; child = child->next) {
print_xml_output(child);
}
} else if (id) {
printf("%s\n", id);
}
} else {
buffer = dump_xml_formatted(xml);
fprintf(stdout, "%s", crm_str(buffer));
free(buffer);
}
}
// Upgrade requested but already at latest schema
static void
report_schema_unchanged(void)
{
const char *err = pcmk_strerror(pcmk_err_schema_unchanged);
crm_info("Upgrade unnecessary: %s\n", err);
printf("Upgrade unnecessary: %s\n", err);
exit_code = CRM_EX_OK;
}
int
main(int argc, char **argv)
{
int argerr = 0;
int rc = pcmk_ok;
int flag;
const char *source = NULL;
const char *admin_input_xml = NULL;
const char *admin_input_file = NULL;
gboolean dangerous_cmd = FALSE;
gboolean admin_input_stdin = FALSE;
xmlNode *output = NULL;
xmlNode *input = NULL;
int option_index = 0;
crm_log_cli_init("cibadmin");
set_crm_log_level(LOG_CRIT);
pcmk__set_cli_options(NULL, "<command> [options]", long_options,
"query and edit the Pacemaker configuration");
if (argc < 2) {
pcmk__cli_help('?', CRM_EX_USAGE);
}
while (1) {
flag = pcmk__next_cli_option(argc, argv, &option_index, NULL);
if (flag == -1)
break;
switch (flag) {
case 't':
message_timeout_ms = atoi(optarg);
if (message_timeout_ms < 1) {
message_timeout_ms = 30;
}
break;
case 'A':
obj_type = optarg;
command_options |= cib_xpath;
break;
case 'e':
command_options |= cib_xpath_address;
break;
case 'u':
cib_action = CIB_OP_UPGRADE;
dangerous_cmd = TRUE;
break;
case 'E':
cib_action = CIB_OP_ERASE;
dangerous_cmd = TRUE;
break;
case 'Q':
cib_action = CIB_OP_QUERY;
break;
case 'P':
cib_action = CIB_OP_APPLY_DIFF;
break;
case 'U':
cib_user = optarg;
break;
case 'M':
cib_action = CIB_OP_MODIFY;
break;
case 'R':
cib_action = CIB_OP_REPLACE;
break;
case 'C':
cib_action = CIB_OP_CREATE;
break;
case 'D':
cib_action = CIB_OP_DELETE;
break;
case '5':
cib_action = "md5-sum";
break;
case '6':
cib_action = "md5-sum-versioned";
break;
case 'c':
command_options |= cib_can_create;
break;
case 'n':
command_options |= cib_no_children;
break;
case 'B':
cib_action = CIB_OP_BUMP;
crm_log_args(argc, argv);
break;
case 'V':
command_options = command_options | cib_verbose;
bump_log_num++;
break;
case '?':
case '$':
case '!':
pcmk__cli_help(flag, CRM_EX_OK);
break;
case 'o':
crm_trace("Option %c => %s", flag, optarg);
obj_type = optarg;
break;
case 'X':
crm_trace("Option %c => %s", flag, optarg);
admin_input_xml = optarg;
crm_log_args(argc, argv);
break;
case 'x':
crm_trace("Option %c => %s", flag, optarg);
admin_input_file = optarg;
crm_log_args(argc, argv);
break;
case 'p':
admin_input_stdin = TRUE;
crm_log_args(argc, argv);
break;
case 'N':
case 'h':
host = strdup(optarg);
break;
case 'l':
command_options |= cib_scope_local;
break;
case 'd':
cib_action = CIB_OP_DELETE;
command_options |= cib_multiple;
dangerous_cmd = TRUE;
break;
case 'b':
dangerous_cmd = TRUE;
command_options |= cib_inhibit_bcast;
command_options |= cib_scope_local;
break;
case 's':
command_options |= cib_sync_call;
break;
case 'f':
force_flag = TRUE;
command_options |= cib_quorum_override;
crm_log_args(argc, argv);
break;
case 'a':
output = createEmptyCib(1);
if (optind < argc) {
crm_xml_add(output, XML_ATTR_VALIDATION, argv[optind]);
}
admin_input_xml = dump_xml_formatted(output);
fprintf(stdout, "%s\n", crm_str(admin_input_xml));
crm_exit(CRM_EX_OK);
break;
default:
printf("Argument code 0%o (%c)" " is not (?yet?) supported\n", flag, flag);
++argerr;
break;
}
}
while (bump_log_num > 0) {
crm_bump_log_level(argc, argv);
bump_log_num--;
}
if (optind < argc) {
printf("non-option ARGV-elements: ");
while (optind < argc)
printf("%s ", argv[optind++]);
printf("\n");
pcmk__cli_help('?', CRM_EX_USAGE);
}
if (optind > argc || cib_action == NULL) {
++argerr;
}
if (argerr) {
pcmk__cli_help('?', CRM_EX_USAGE);
}
if (dangerous_cmd && force_flag == FALSE) {
fprintf(stderr, "The supplied command is considered dangerous."
" To prevent accidental destruction of the cluster,"
" the --force flag is required in order to proceed.\n");
fflush(stderr);
crm_exit(CRM_EX_UNSAFE);
}
if (admin_input_file != NULL) {
input = filename2xml(admin_input_file);
source = admin_input_file;
} else if (admin_input_xml != NULL) {
source = "input string";
input = string2xml(admin_input_xml);
} else if (admin_input_stdin) {
source = "STDIN";
input = stdin2xml();
}
if (input != NULL) {
crm_log_xml_debug(input, "[admin input]");
} else if (source) {
fprintf(stderr, "Couldn't parse input from %s.\n", source);
crm_exit(CRM_EX_CONFIG);
}
if (safe_str_eq(cib_action, "md5-sum")) {
char *digest = NULL;
if (input == NULL) {
fprintf(stderr, "Please supply XML to process with -X, -x or -p\n");
crm_exit(CRM_EX_USAGE);
}
digest = calculate_on_disk_digest(input);
fprintf(stderr, "Digest: ");
fprintf(stdout, "%s\n", crm_str(digest));
free(digest);
free_xml(input);
crm_exit(CRM_EX_OK);
} else if (safe_str_eq(cib_action, "md5-sum-versioned")) {
char *digest = NULL;
const char *version = NULL;
if (input == NULL) {
fprintf(stderr, "Please supply XML to process with -X, -x or -p\n");
crm_exit(CRM_EX_USAGE);
}
version = crm_element_value(input, XML_ATTR_CRM_VERSION);
digest = calculate_xml_versioned_digest(input, FALSE, TRUE, version);
fprintf(stderr, "Versioned (%s) digest: ", version);
fprintf(stdout, "%s\n", crm_str(digest));
free(digest);
free_xml(input);
crm_exit(CRM_EX_OK);
}
rc = do_init();
if (rc != pcmk_ok) {
crm_err("Init failed, could not perform requested operations");
fprintf(stderr, "Init failed, could not perform requested operations\n");
free_xml(input);
crm_exit(crm_errno2exit(rc));
}
rc = do_work(input, command_options, &output);
if (rc > 0) {
/* wait for the reply by creating a mainloop and running it until
* the callbacks are invoked...
*/
request_id = rc;
the_cib->cmds->register_callback(the_cib, request_id, message_timeout_ms, FALSE, NULL,
"cibadmin_op_callback", cibadmin_op_callback);
mainloop = g_main_loop_new(NULL, FALSE);
crm_trace("%s waiting for reply from the local CIB", crm_system_name);
crm_info("Starting mainloop");
g_main_loop_run(mainloop);
} else if ((rc == -pcmk_err_schema_unchanged)
&& crm_str_eq(cib_action, CIB_OP_UPGRADE, TRUE)) {
report_schema_unchanged();
} else if (rc < 0) {
crm_err("Call failed: %s", pcmk_strerror(rc));
fprintf(stderr, "Call failed: %s\n", pcmk_strerror(rc));
if (rc == -pcmk_err_schema_validation) {
if (crm_str_eq(cib_action, CIB_OP_UPGRADE, TRUE)) {
xmlNode *obj = NULL;
int version = 0, rc = 0;
rc = the_cib->cmds->query(the_cib, NULL, &obj, command_options);
if (rc == pcmk_ok) {
update_validation(&obj, &version, 0, TRUE, FALSE);
}
} else if (output) {
validate_xml_verbose(output);
}
}
exit_code = crm_errno2exit(rc);
}
if (output != NULL) {
print_xml_output(output);
free_xml(output);
}
crm_trace("%s exiting normally", crm_system_name);
free_xml(input);
rc = the_cib->cmds->signoff(the_cib);
if (exit_code == CRM_EX_OK) {
exit_code = crm_errno2exit(rc);
}
cib_delete(the_cib);
crm_exit(exit_code);
}
int
do_work(xmlNode * input, int call_options, xmlNode ** output)
{
/* construct the request */
the_cib->call_timeout = message_timeout_ms;
if (strcasecmp(CIB_OP_REPLACE, cib_action) == 0
&& safe_str_eq(crm_element_name(input), XML_TAG_CIB)) {
xmlNode *status = get_object_root(XML_CIB_TAG_STATUS, input);
if (status == NULL) {
create_xml_node(input, XML_CIB_TAG_STATUS);
}
}
if (cib_action != NULL) {
crm_trace("Passing \"%s\" to variant_op...", cib_action);
return cib_internal_op(the_cib, cib_action, host, obj_type, input, output, call_options, cib_user);
} else {
crm_err("You must specify an operation");
}
return -EINVAL;
}
int
do_init(void)
{
int rc = pcmk_ok;
the_cib = cib_new();
rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
if (rc != pcmk_ok) {
crm_err("Could not connect to the CIB: %s", pcmk_strerror(rc));
fprintf(stderr, "Could not connect to the CIB: %s\n",
pcmk_strerror(rc));
}
return rc;
}
void
cibadmin_op_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
{
exit_code = crm_errno2exit(rc);
if (rc == -pcmk_err_schema_unchanged) {
report_schema_unchanged();
} else if (rc != pcmk_ok) {
crm_warn("Call %s failed (%d): %s", cib_action, rc, pcmk_strerror(rc));
fprintf(stderr, "Call %s failed (%d): %s\n", cib_action, rc, pcmk_strerror(rc));
print_xml_output(output);
} else if (safe_str_eq(cib_action, CIB_OP_QUERY) && output == NULL) {
crm_err("Query returned no output");
crm_log_xml_err(msg, "no output");
} else if (output == NULL) {
crm_info("Call passed");
} else {
crm_info("Call passed");
print_xml_output(output);
}
if (call_id == request_id) {
g_main_loop_quit(mainloop);
} else {
crm_info("Message was not the response we were looking for (%d vs. %d)",
call_id, request_id);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 23, 6:56 AM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1018363
Default Alt Text
(49 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment