diff --git a/cts/schemas/test-3/ref/lifetime-1.ref-3 b/cts/schemas/test-3/ref/lifetime-1.ref-3 index 982c439a39..1eb33227dd 100644 --- a/cts/schemas/test-3/ref/lifetime-1.ref-3 +++ b/cts/schemas/test-3/ref/lifetime-1.ref-3 @@ -1,152 +1,136 @@ - - - - + + + + + + + + + + + + - - - - + - - - - - - + + + + + + + + + + + - - - - + - - + + + + + - + - - - - + + + + - + - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/cts/schemas/test-3/ref/lifetime-1.ref-99 b/cts/schemas/test-3/ref/lifetime-1.ref-99 index 1669699672..98eccc1c60 100644 --- a/cts/schemas/test-3/ref/lifetime-1.ref-99 +++ b/cts/schemas/test-3/ref/lifetime-1.ref-99 @@ -1,134 +1,126 @@ - - - - + + + + - - + + + + + + + - + - - + + + + + + + + - - - - - - + + + + + - + - - - - + + + + - + - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/cts/schemas/test-3/ref/lifetime-2.ref-3 b/cts/schemas/test-3/ref/lifetime-2.ref-3 index 8fb1725898..e3483efadb 100644 --- a/cts/schemas/test-3/ref/lifetime-2.ref-3 +++ b/cts/schemas/test-3/ref/lifetime-2.ref-3 @@ -1,70 +1,34 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + diff --git a/cts/schemas/test-3/ref/lifetime-2.ref-99 b/cts/schemas/test-3/ref/lifetime-2.ref-99 index 72e59f6ce5..9621fe4ea2 100644 --- a/cts/schemas/test-3/ref/lifetime-2.ref-99 +++ b/cts/schemas/test-3/ref/lifetime-2.ref-99 @@ -1,62 +1,34 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + diff --git a/cts/schemas/test-3/ref/multiple-location-rules.ref-3 b/cts/schemas/test-3/ref/multiple-location-rules.ref-3 index 7b72e5629e..e6372e2ba7 100644 --- a/cts/schemas/test-3/ref/multiple-location-rules.ref-3 +++ b/cts/schemas/test-3/ref/multiple-location-rules.ref-3 @@ -1,155 +1,163 @@ - - - - - - + + + - - + + + + + + + - + - - - - - - + + + - - + + + + + + + - + - - - - - - + + + - - + + + + + + + - + - - - - - - + + + - - + + + + + + + - + diff --git a/cts/schemas/test-3/ref/multiple-location-rules.ref-99 b/cts/schemas/test-3/ref/multiple-location-rules.ref-99 index 28f9636a6e..20495cffe1 100644 --- a/cts/schemas/test-3/ref/multiple-location-rules.ref-99 +++ b/cts/schemas/test-3/ref/multiple-location-rules.ref-99 @@ -1,131 +1,139 @@ - - - - - - + + + - - + + + + + + + - + - - + + + + + + + + - - - - - - + + + + + + + + - - - - - - + + + + + + + + - - - - diff --git a/lib/pacemaker/pcmk_sched_constraints.c b/lib/pacemaker/pcmk_sched_constraints.c index 8cefce8752..8ce17198dc 100644 --- a/lib/pacemaker/pcmk_sched_constraints.c +++ b/lib/pacemaker/pcmk_sched_constraints.c @@ -1,477 +1,478 @@ /* * Copyright 2004-2024 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libpacemaker_private.h" static bool evaluate_lifetime(xmlNode *lifetime, pcmk_scheduler_t *scheduler) { bool result = false; crm_time_t *next_change = crm_time_new_undefined(); pcmk_rule_input_t rule_input = { .now = scheduler->priv->now, }; result = (pcmk__evaluate_rules(lifetime, &rule_input, next_change) == pcmk_rc_ok); if (crm_time_is_defined(next_change)) { time_t recheck = (time_t) crm_time_get_seconds_since_epoch(next_change); pe__update_recheck_time(recheck, scheduler, "constraint lifetime"); } crm_time_free(next_change); return result; } /*! * \internal * \brief Unpack constraints from XML * * Given scheduler data, unpack all constraints from its input XML into * data structures. * * \param[in,out] scheduler Scheduler data */ void pcmk__unpack_constraints(pcmk_scheduler_t *scheduler) { xmlNode *xml_constraints = pcmk_find_cib_element(scheduler->input, PCMK_XE_CONSTRAINTS); for (xmlNode *xml_obj = pcmk__xe_first_child(xml_constraints, NULL, NULL, NULL); xml_obj != NULL; xml_obj = pcmk__xe_next(xml_obj)) { xmlNode *lifetime = NULL; const char *id = crm_element_value(xml_obj, PCMK_XA_ID); const char *tag = (const char *) xml_obj->name; if (id == NULL) { pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID, tag); continue; } crm_trace("Unpacking %s constraint '%s'", tag, id); lifetime = pcmk__xe_first_child(xml_obj, PCMK__XE_LIFETIME, NULL, NULL); if (lifetime != NULL) { + // @COMPAT Not possible with schema validation enabled pcmk__config_warn("Support for '" PCMK__XE_LIFETIME "' element " "(in %s) is deprecated and will be dropped " "in a later release", id); } if ((lifetime != NULL) && !evaluate_lifetime(lifetime, scheduler)) { crm_info("Constraint %s %s is not active", tag, id); } else if (pcmk__str_eq(PCMK_XE_RSC_ORDER, tag, pcmk__str_none)) { pcmk__unpack_ordering(xml_obj, scheduler); } else if (pcmk__str_eq(PCMK_XE_RSC_COLOCATION, tag, pcmk__str_none)) { pcmk__unpack_colocation(xml_obj, scheduler); } else if (pcmk__str_eq(PCMK_XE_RSC_LOCATION, tag, pcmk__str_none)) { pcmk__unpack_location(xml_obj, scheduler); } else if (pcmk__str_eq(PCMK_XE_RSC_TICKET, tag, pcmk__str_none)) { pcmk__unpack_rsc_ticket(xml_obj, scheduler); } else { pcmk__config_err("Unsupported constraint type: %s", tag); } } } pcmk_resource_t * pcmk__find_constraint_resource(GList *rsc_list, const char *id) { if (id == NULL) { return NULL; } for (GList *iter = rsc_list; iter != NULL; iter = iter->next) { pcmk_resource_t *parent = iter->data; pcmk_resource_t *match = NULL; match = parent->priv->fns->find_rsc(parent, id, NULL, pcmk_rsc_match_history); if (match != NULL) { if (!pcmk__str_eq(match->id, id, pcmk__str_none)) { /* We found an instance of a clone instead */ match = uber_parent(match); crm_debug("Found %s for %s", match->id, id); } return match; } } crm_trace("No match for %s", id); return NULL; } /*! * \internal * \brief Check whether an ID references a resource tag * * \param[in] scheduler Scheduler data * \param[in] id Tag ID to search for * \param[out] tag Where to store tag, if found * * \return true if ID refers to a tagged resource or resource set template, * otherwise false */ static bool find_constraint_tag(const pcmk_scheduler_t *scheduler, const char *id, pcmk__idref_t **tag) { *tag = NULL; // Check whether id refers to a resource set template if (g_hash_table_lookup_extended(scheduler->priv->templates, id, NULL, (gpointer *) tag)) { if (*tag == NULL) { crm_notice("No resource is derived from template '%s'", id); return false; } return true; } // If not, check whether id refers to a tag if (g_hash_table_lookup_extended(scheduler->priv->tags, id, NULL, (gpointer *) tag)) { if (*tag == NULL) { crm_notice("No resource is tagged with '%s'", id); return false; } return true; } pcmk__config_warn("No resource, template, or tag named '%s'", id); return false; } /*! * \internal * \brief Parse a role attribute from a constraint * * This is like pcmk_parse_role() except that started is treated as * pcmk_role_unknown (indicating any role), and the return value is * pcmk_rc_unpack_error for invalid specifications. * * \param[in] id ID of constraint being parsed (for logging only) * \param[in] role_spec Role specification * \param[in] role Where to store parsed role * * \return Standard Pacemaker return code */ int pcmk__parse_constraint_role(const char *id, const char *role_spec, enum rsc_role_e *role) { *role = pcmk_parse_role(role_spec); switch (*role) { case pcmk_role_unknown: if (role_spec != NULL) { pcmk__config_err("Ignoring constraint %s: Invalid role '%s'", id, role_spec); return pcmk_rc_unpack_error; } break; case pcmk_role_started: *role = pcmk_role_unknown; break; default: break; } return pcmk_rc_ok; } /*! * \brief * \internal Check whether an ID refers to a valid resource or tag * * \param[in] scheduler Scheduler data * \param[in] id ID to search for * \param[out] rsc Where to store resource, if found * (or NULL to skip searching resources) * \param[out] tag Where to store tag, if found * (or NULL to skip searching tags) * * \return true if id refers to a resource (possibly indirectly via a tag) */ bool pcmk__valid_resource_or_tag(const pcmk_scheduler_t *scheduler, const char *id, pcmk_resource_t **rsc, pcmk__idref_t **tag) { if (rsc != NULL) { *rsc = pcmk__find_constraint_resource(scheduler->priv->resources, id); if (*rsc != NULL) { return true; } } if ((tag != NULL) && find_constraint_tag(scheduler, id, tag)) { return true; } return false; } /*! * \internal * \brief Replace any resource tags with equivalent \C PCMK_XE_RESOURCE_REF * entries * * If a given constraint has resource sets, check each set for * \c PCMK_XE_RESOURCE_REF entries that list tags rather than resource IDs, and * replace any found with \c PCMK_XE_RESOURCE_REF entries for the corresponding * resource IDs. * * \param[in,out] xml_obj Constraint XML * \param[in] scheduler Scheduler data * * \return Equivalent XML with resource tags replaced (or NULL if none) * \note It is the caller's responsibility to free the return value with * \c pcmk__xml_free(). */ xmlNode * pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pcmk_scheduler_t *scheduler) { xmlNode *new_xml = NULL; bool any_refs = false; // Short-circuit if there are no sets if (pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL) == NULL) { return NULL; } new_xml = pcmk__xml_copy(NULL, xml_obj); for (xmlNode *set = pcmk__xe_first_child(new_xml, PCMK_XE_RESOURCE_SET, NULL, NULL); set != NULL; set = pcmk__xe_next_same(set)) { GList *tag_refs = NULL; GList *iter = NULL; for (xmlNode *xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL, NULL); xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) { pcmk_resource_t *rsc = NULL; pcmk__idref_t *tag = NULL; if (!pcmk__valid_resource_or_tag(scheduler, pcmk__xe_id(xml_rsc), &rsc, &tag)) { pcmk__config_err("Ignoring resource sets for constraint '%s' " "because '%s' is not a valid resource or tag", pcmk__xe_id(xml_obj), pcmk__xe_id(xml_rsc)); pcmk__xml_free(new_xml); return NULL; } else if (rsc) { continue; } else if (tag) { /* PCMK_XE_RESOURCE_REF under PCMK_XE_RESOURCE_SET references * template or tag */ xmlNode *last_ref = xml_rsc; /* For example, given the original XML: * * * * * * * * If rsc2 and rsc3 are tagged with tag1, we add them after it: * * * * * * * * */ for (iter = tag->refs; iter != NULL; iter = iter->next) { const char *obj_ref = iter->data; xmlNode *new_rsc_ref = NULL; new_rsc_ref = xmlNewDocRawNode(set->doc, NULL, (pcmkXmlStr) PCMK_XE_RESOURCE_REF, NULL); crm_xml_add(new_rsc_ref, PCMK_XA_ID, obj_ref); xmlAddNextSibling(last_ref, new_rsc_ref); last_ref = new_rsc_ref; } any_refs = true; /* Freeing the resource_ref now would break the XML child * iteration, so just remember it for freeing later. */ tag_refs = g_list_append(tag_refs, xml_rsc); } } /* Now free '', and finally get: */ for (iter = tag_refs; iter != NULL; iter = iter->next) { xmlNode *tag_ref = iter->data; pcmk__xml_free(tag_ref); } g_list_free(tag_refs); } if (!any_refs) { pcmk__xml_free(new_xml); new_xml = NULL; } return new_xml; } /*! * \internal * \brief Convert a tag into a resource set of tagged resources * * \param[in,out] xml_obj Constraint XML * \param[out] rsc_set Where to store resource set XML * \param[in] attr Name of XML attribute with resource or tag ID * \param[in] convert_rsc If true, convert to set even if \p attr * references a resource * \param[in] scheduler Scheduler data */ bool pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, bool convert_rsc, const pcmk_scheduler_t *scheduler) { const char *cons_id = NULL; const char *id = NULL; pcmk_resource_t *rsc = NULL; pcmk__idref_t *tag = NULL; *rsc_set = NULL; CRM_CHECK((xml_obj != NULL) && (attr != NULL), return false); cons_id = pcmk__xe_id(xml_obj); if (cons_id == NULL) { pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID, xml_obj->name); return false; } id = crm_element_value(xml_obj, attr); if (id == NULL) { return true; } if (!pcmk__valid_resource_or_tag(scheduler, id, &rsc, &tag)) { pcmk__config_err("Ignoring constraint '%s' because '%s' is not a " "valid resource or tag", cons_id, id); return false; } else if (tag) { /* The "attr" attribute (for a resource in a constraint) specifies a * template or tag. Add the corresponding PCMK_XE_RESOURCE_SET * containing the resources derived from or tagged with it. */ *rsc_set = pcmk__xe_create(xml_obj, PCMK_XE_RESOURCE_SET); crm_xml_add(*rsc_set, PCMK_XA_ID, id); for (GList *iter = tag->refs; iter != NULL; iter = iter->next) { const char *obj_ref = iter->data; xmlNode *rsc_ref = NULL; rsc_ref = pcmk__xe_create(*rsc_set, PCMK_XE_RESOURCE_REF); crm_xml_add(rsc_ref, PCMK_XA_ID, obj_ref); } // Set PCMK_XA_SEQUENTIAL=PCMK_VALUE_FALSE for the PCMK_XE_RESOURCE_SET pcmk__xe_set_bool_attr(*rsc_set, PCMK_XA_SEQUENTIAL, false); } else if ((rsc != NULL) && convert_rsc) { /* Even if a regular resource is referenced by "attr", convert it into a * PCMK_XE_RESOURCE_SET, because the other resource reference in the * constraint could be a template or tag. */ xmlNode *rsc_ref = NULL; *rsc_set = pcmk__xe_create(xml_obj, PCMK_XE_RESOURCE_SET); crm_xml_add(*rsc_set, PCMK_XA_ID, id); rsc_ref = pcmk__xe_create(*rsc_set, PCMK_XE_RESOURCE_REF); crm_xml_add(rsc_ref, PCMK_XA_ID, id); } else { return true; } /* Remove the "attr" attribute referencing the template/tag */ if (*rsc_set != NULL) { pcmk__xe_remove_attr(xml_obj, attr); } return true; } /*! * \internal * \brief Create constraints inherent to resource types * * \param[in,out] scheduler Scheduler data */ void pcmk__create_internal_constraints(pcmk_scheduler_t *scheduler) { crm_trace("Create internal constraints"); for (GList *iter = scheduler->priv->resources; iter != NULL; iter = iter->next) { pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data; rsc->priv->cmds->internal_constraints(rsc); } } diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c index fe962239fc..f0d216b80d 100644 --- a/tools/crm_resource_print.c +++ b/tools/crm_resource_print.c @@ -1,927 +1,927 @@ /* * Copyright 2004-2024 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 #include #include #include #include #include #define cons_string(x) x?x:"NA" static int print_constraint(xmlNode *xml_obj, void *userdata) { pcmk_scheduler_t *scheduler = (pcmk_scheduler_t *) userdata; pcmk__output_t *out = scheduler->priv->out; xmlNode *lifetime = NULL; const char *id = crm_element_value(xml_obj, PCMK_XA_ID); pcmk_rule_input_t rule_input = { .now = scheduler->priv->now, }; if (id == NULL) { return pcmk_rc_ok; } - // @COMPAT PCMK__XE_LIFETIME is deprecated + // @COMPAT Not possible with schema validation enabled lifetime = pcmk__xe_first_child(xml_obj, PCMK__XE_LIFETIME, NULL, NULL); if (pcmk__evaluate_rules(lifetime, &rule_input, NULL) != pcmk_rc_ok) { return pcmk_rc_ok; } if (!pcmk__xe_is(xml_obj, PCMK_XE_RSC_COLOCATION)) { return pcmk_rc_ok; } out->info(out, "Constraint %s %s %s %s %s %s %s", xml_obj->name, cons_string(crm_element_value(xml_obj, PCMK_XA_ID)), cons_string(crm_element_value(xml_obj, PCMK_XA_RSC)), cons_string(crm_element_value(xml_obj, PCMK_XA_WITH_RSC)), cons_string(crm_element_value(xml_obj, PCMK_XA_SCORE)), cons_string(crm_element_value(xml_obj, PCMK_XA_RSC_ROLE)), cons_string(crm_element_value(xml_obj, PCMK_XA_WITH_RSC_ROLE))); return pcmk_rc_ok; } void cli_resource_print_cts_constraints(pcmk_scheduler_t *scheduler) { pcmk__xe_foreach_child(pcmk_find_cib_element(scheduler->input, PCMK_XE_CONSTRAINTS), NULL, print_constraint, scheduler); } void cli_resource_print_cts(pcmk_resource_t *rsc, pcmk__output_t *out) { const char *host = NULL; bool needs_quorum = TRUE; const char *rtype = crm_element_value(rsc->priv->xml, PCMK_XA_TYPE); const char *rprov = crm_element_value(rsc->priv->xml, PCMK_XA_PROVIDER); const char *rclass = crm_element_value(rsc->priv->xml, PCMK_XA_CLASS); pcmk_node_t *node = pcmk__current_node(rsc); if (pcmk_is_set(rsc->flags, pcmk__rsc_fence_device)) { needs_quorum = FALSE; } else { // @TODO check requires in resource meta-data and rsc_defaults } if (node != NULL) { host = node->priv->name; } out->info(out, "Resource: %s %s %s %s %s %s %s %s %d %lld %#.16llx", rsc->priv->xml->name, rsc->id, pcmk__s(rsc->priv->history_id, rsc->id), ((rsc->priv->parent == NULL)? "NA" : rsc->priv->parent->id), rprov ? rprov : "NA", rclass, rtype, host ? host : "NA", needs_quorum, rsc->flags, rsc->flags); g_list_foreach(rsc->priv->children, (GFunc) cli_resource_print_cts, out); } // \return Standard Pacemaker return code int cli_resource_print_operations(const char *rsc_id, const char *host_uname, bool active, pcmk_scheduler_t *scheduler) { pcmk__output_t *out = scheduler->priv->out; int rc = pcmk_rc_no_output; GList *ops = find_operations(rsc_id, host_uname, active, scheduler); if (!ops) { return rc; } out->begin_list(out, NULL, NULL, "Resource Operations"); rc = pcmk_rc_ok; for (GList *lpc = ops; lpc != NULL; lpc = lpc->next) { xmlNode *xml_op = (xmlNode *) lpc->data; out->message(out, "node-and-op", scheduler, xml_op); } out->end_list(out); return rc; } // \return Standard Pacemaker return code int cli_resource_print(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler, bool expanded) { pcmk__output_t *out = scheduler->priv->out; uint32_t show_opts = pcmk_show_pending; GList *all = NULL; all = g_list_prepend(all, (gpointer) "*"); out->begin_list(out, NULL, NULL, "Resource Config"); out->message(out, pcmk__map_element_name(rsc->priv->xml), show_opts, rsc, all, all); out->message(out, "resource-config", rsc, !expanded); out->end_list(out); g_list_free(all); return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("attribute-changed", "attr_update_data_t *") static int attribute_changed_default(pcmk__output_t *out, va_list args) { attr_update_data_t *ud = va_arg(args, attr_update_data_t *); out->info(out, "Set '%s' option: " PCMK_XA_ID "=%s%s%s%s%s value=%s", ud->given_rsc_id, ud->found_attr_id, ((ud->attr_set_id == NULL)? "" : " " PCMK__XA_SET "="), pcmk__s(ud->attr_set_id, ""), ((ud->attr_name == NULL)? "" : " " PCMK_XA_NAME "="), pcmk__s(ud->attr_name, ""), ud->attr_value); return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("attribute-changed", "attr_update_data_t *") static int attribute_changed_xml(pcmk__output_t *out, va_list args) { attr_update_data_t *ud = va_arg(args, attr_update_data_t *); pcmk__output_xml_create_parent(out, (const char *) ud->rsc->priv->xml->name, PCMK_XA_ID, ud->rsc->id, NULL); pcmk__output_xml_create_parent(out, ud->attr_set_type, PCMK_XA_ID, ud->attr_set_id, NULL); pcmk__output_create_xml_node(out, PCMK_XE_NVPAIR, PCMK_XA_ID, ud->found_attr_id, PCMK_XA_VALUE, ud->attr_value, PCMK_XA_NAME, ud->attr_name, NULL); pcmk__output_xml_pop_parent(out); pcmk__output_xml_pop_parent(out); return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("attribute-changed-list", "GList *") static int attribute_changed_list_default(pcmk__output_t *out, va_list args) { GList *results = va_arg(args, GList *); if (results == NULL) { return pcmk_rc_no_output; } for (GList *iter = results; iter != NULL; iter = iter->next) { attr_update_data_t *ud = iter->data; out->message(out, "attribute-changed", ud); } return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("attribute-changed-list", "GList *") static int attribute_changed_list_xml(pcmk__output_t *out, va_list args) { GList *results = va_arg(args, GList *); if (results == NULL) { return pcmk_rc_no_output; } pcmk__output_xml_create_parent(out, PCMK__XE_RESOURCE_SETTINGS, NULL); for (GList *iter = results; iter != NULL; iter = iter->next) { attr_update_data_t *ud = iter->data; out->message(out, "attribute-changed", ud); } pcmk__output_xml_pop_parent(out); return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("attribute-list", "pcmk_resource_t *", "const char *", "const char *") static int attribute_list_default(pcmk__output_t *out, va_list args) { pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *); const char *attr = va_arg(args, char *); const char *value = va_arg(args, const char *); if (value != NULL) { out->begin_list(out, NULL, NULL, "Attributes"); out->list_item(out, attr, "%s", value); out->end_list(out); return pcmk_rc_ok; } else { out->err(out, "Attribute '%s' not found for '%s'", attr, rsc->id); } return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("agent-status", "int", "const char *", "const char *", "const char *", "const char *", "const char *", "crm_exit_t", "const char *") static int agent_status_default(pcmk__output_t *out, va_list args) { int status = va_arg(args, int); const char *action = va_arg(args, const char *); const char *name = va_arg(args, const char *); const char *class = va_arg(args, const char *); const char *provider = va_arg(args, const char *); const char *type = va_arg(args, const char *); crm_exit_t rc = va_arg(args, crm_exit_t); const char *exit_reason = va_arg(args, const char *); if (status == PCMK_EXEC_DONE) { /* Operation [for ] ([:]:) * returned ([: ]) */ out->info(out, "Operation %s%s%s (%s%s%s:%s) returned %d (%s%s%s)", action, ((name == NULL)? "" : " for "), ((name == NULL)? "" : name), class, ((provider == NULL)? "" : ":"), ((provider == NULL)? "" : provider), type, (int) rc, services_ocf_exitcode_str((int) rc), ((exit_reason == NULL)? "" : ": "), ((exit_reason == NULL)? "" : exit_reason)); } else { /* Operation [for ] ([:]:) * could not be executed ([: ]) */ out->err(out, "Operation %s%s%s (%s%s%s:%s) could not be executed (%s%s%s)", action, ((name == NULL)? "" : " for "), ((name == NULL)? "" : name), class, ((provider == NULL)? "" : ":"), ((provider == NULL)? "" : provider), type, pcmk_exec_status_str(status), ((exit_reason == NULL)? "" : ": "), ((exit_reason == NULL)? "" : exit_reason)); } return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("agent-status", "int", "const char *", "const char *", "const char *", "const char *", "const char *", "crm_exit_t", "const char *") static int agent_status_xml(pcmk__output_t *out, va_list args) { int status = va_arg(args, int); const char *action G_GNUC_UNUSED = va_arg(args, const char *); const char *name G_GNUC_UNUSED = va_arg(args, const char *); const char *class G_GNUC_UNUSED = va_arg(args, const char *); const char *provider G_GNUC_UNUSED = va_arg(args, const char *); const char *type G_GNUC_UNUSED = va_arg(args, const char *); crm_exit_t rc = va_arg(args, crm_exit_t); const char *exit_reason = va_arg(args, const char *); char *exit_s = pcmk__itoa(rc); const char *message = services_ocf_exitcode_str((int) rc); char *status_s = pcmk__itoa(status); const char *execution_message = pcmk_exec_status_str(status); pcmk__output_create_xml_node(out, PCMK_XE_AGENT_STATUS, PCMK_XA_CODE, exit_s, PCMK_XA_MESSAGE, message, PCMK_XA_EXECUTION_CODE, status_s, PCMK_XA_EXECUTION_MESSAGE, execution_message, PCMK_XA_REASON, exit_reason, NULL); free(exit_s); free(status_s); return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("attribute-list", "pcmk_resource_t *", "const char *", "const char *") static int attribute_list_text(pcmk__output_t *out, va_list args) { pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *); const char *attr = va_arg(args, char *); const char *value = va_arg(args, const char *); if (value != NULL) { pcmk__formatted_printf(out, "%s\n", value); return pcmk_rc_ok; } else { out->err(out, "Attribute '%s' not found for '%s'", attr, rsc->id); } return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("override", "const char *", "const char *", "const char *") static int override_default(pcmk__output_t *out, va_list args) { const char *rsc_name = va_arg(args, const char *); const char *name = va_arg(args, const char *); const char *value = va_arg(args, const char *); if (rsc_name == NULL) { out->list_item(out, NULL, "Overriding the cluster configuration with '%s' = '%s'", name, value); } else { out->list_item(out, NULL, "Overriding the cluster configuration for '%s' with '%s' = '%s'", rsc_name, name, value); } return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("override", "const char *", "const char *", "const char *") static int override_xml(pcmk__output_t *out, va_list args) { const char *rsc_name = va_arg(args, const char *); const char *name = va_arg(args, const char *); const char *value = va_arg(args, const char *); xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_OVERRIDE, PCMK_XA_NAME, name, PCMK_XA_VALUE, value, NULL); if (rsc_name != NULL) { crm_xml_add(node, PCMK_XA_RSC, rsc_name); } return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("property-list", "pcmk_resource_t *", "const char *") static int property_list_default(pcmk__output_t *out, va_list args) { pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *); const char *attr = va_arg(args, char *); const char *value = crm_element_value(rsc->priv->xml, attr); if (value != NULL) { out->begin_list(out, NULL, NULL, "Properties"); out->list_item(out, attr, "%s", value); out->end_list(out); } return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("property-list", "pcmk_resource_t *", "const char *") static int property_list_text(pcmk__output_t *out, va_list args) { pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *); const char *attr = va_arg(args, const char *); const char *value = crm_element_value(rsc->priv->xml, attr); if (value != NULL) { pcmk__formatted_printf(out, "%s\n", value); } return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("resource-agent-action", "int", "const char *", "const char *", "const char *", "const char *", "const char *", "GHashTable *", "crm_exit_t", "int", "const char *", "const char *", "const char *") static int resource_agent_action_default(pcmk__output_t *out, va_list args) { int verbose = va_arg(args, int); const char *class = va_arg(args, const char *); const char *provider = va_arg(args, const char *); const char *type = va_arg(args, const char *); const char *rsc_name = va_arg(args, const char *); const char *action = va_arg(args, const char *); GHashTable *overrides = va_arg(args, GHashTable *); crm_exit_t rc = va_arg(args, crm_exit_t); int status = va_arg(args, int); const char *exit_reason = va_arg(args, const char *); const char *stdout_data = va_arg(args, const char *); const char *stderr_data = va_arg(args, const char *); if (overrides) { GHashTableIter iter; const char *name = NULL; const char *value = NULL; out->begin_list(out, NULL, NULL, PCMK_XE_OVERRIDES); g_hash_table_iter_init(&iter, overrides); while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) { out->message(out, "override", rsc_name, name, value); } out->end_list(out); } out->message(out, "agent-status", status, action, rsc_name, class, provider, type, rc, exit_reason); /* hide output for validate-all if not in verbose */ if ((verbose == 0) && pcmk__str_eq(action, PCMK_ACTION_VALIDATE_ALL, pcmk__str_casei)) { return pcmk_rc_ok; } if (stdout_data || stderr_data) { xmlNodePtr doc = NULL; if (stdout_data != NULL) { doc = pcmk__xml_parse(stdout_data); } if (doc != NULL) { out->output_xml(out, PCMK_XE_COMMAND, stdout_data); xmlFreeNode(doc); } else { out->subprocess_output(out, rc, stdout_data, stderr_data); } } return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("resource-agent-action", "int", "const char *", "const char *", "const char *", "const char *", "const char *", "GHashTable *", "crm_exit_t", "int", "const char *", "const char *", "const char *") static int resource_agent_action_xml(pcmk__output_t *out, va_list args) { int verbose G_GNUC_UNUSED = va_arg(args, int); const char *class = va_arg(args, const char *); const char *provider = va_arg(args, const char *); const char *type = va_arg(args, const char *); const char *rsc_name = va_arg(args, const char *); const char *action = va_arg(args, const char *); GHashTable *overrides = va_arg(args, GHashTable *); crm_exit_t rc = va_arg(args, crm_exit_t); int status = va_arg(args, int); const char *exit_reason = va_arg(args, const char *); const char *stdout_data = va_arg(args, const char *); const char *stderr_data = va_arg(args, const char *); xmlNodePtr node = NULL; node = pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE_AGENT_ACTION, PCMK_XA_ACTION, action, PCMK_XA_CLASS, class, PCMK_XA_TYPE, type, NULL); if (rsc_name) { crm_xml_add(node, PCMK_XA_RSC, rsc_name); } crm_xml_add(node, PCMK_XA_PROVIDER, provider); if (overrides) { GHashTableIter iter; const char *name = NULL; const char *value = NULL; out->begin_list(out, NULL, NULL, PCMK_XE_OVERRIDES); g_hash_table_iter_init(&iter, overrides); while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) { out->message(out, "override", rsc_name, name, value); } out->end_list(out); } out->message(out, "agent-status", status, action, rsc_name, class, provider, type, rc, exit_reason); if (stdout_data || stderr_data) { xmlNodePtr doc = NULL; if (stdout_data != NULL) { doc = pcmk__xml_parse(stdout_data); } if (doc != NULL) { out->output_xml(out, PCMK_XE_COMMAND, stdout_data); xmlFreeNode(doc); } else { out->subprocess_output(out, rc, stdout_data, stderr_data); } } pcmk__output_xml_pop_parent(out); return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("resource-check-list", "resource_checks_t *") static int resource_check_list_default(pcmk__output_t *out, va_list args) { resource_checks_t *checks = va_arg(args, resource_checks_t *); const pcmk_resource_t *parent = pe__const_top_resource(checks->rsc, false); const pcmk_scheduler_t *scheduler = checks->rsc->priv->scheduler; if (checks->flags == 0) { return pcmk_rc_no_output; } out->begin_list(out, NULL, NULL, "Resource Checks"); if (pcmk_is_set(checks->flags, rsc_remain_stopped)) { out->list_item(out, "check", "Configuration specifies '%s' should remain stopped", parent->id); } if (pcmk_is_set(checks->flags, rsc_unpromotable)) { out->list_item(out, "check", "Configuration specifies '%s' should not be promoted", parent->id); } if (pcmk_is_set(checks->flags, rsc_unmanaged)) { out->list_item(out, "check", "Configuration prevents cluster from stopping or starting unmanaged '%s'", parent->id); } if (pcmk_is_set(checks->flags, rsc_locked)) { out->list_item(out, "check", "'%s' is locked to node %s due to shutdown", parent->id, checks->lock_node); } if (pcmk_is_set(checks->flags, rsc_node_health)) { out->list_item(out, "check", "'%s' cannot run on unhealthy nodes due to " PCMK_OPT_NODE_HEALTH_STRATEGY "='%s'", parent->id, pcmk__cluster_option(scheduler->priv->options, PCMK_OPT_NODE_HEALTH_STRATEGY)); } out->end_list(out); return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("resource-check-list", "resource_checks_t *") static int resource_check_list_xml(pcmk__output_t *out, va_list args) { resource_checks_t *checks = va_arg(args, resource_checks_t *); const pcmk_resource_t *parent = pe__const_top_resource(checks->rsc, false); xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_CHECK, PCMK_XA_ID, parent->id, NULL); if (pcmk_is_set(checks->flags, rsc_remain_stopped)) { pcmk__xe_set_bool_attr(node, PCMK_XA_REMAIN_STOPPED, true); } if (pcmk_is_set(checks->flags, rsc_unpromotable)) { pcmk__xe_set_bool_attr(node, PCMK_XA_PROMOTABLE, false); } if (pcmk_is_set(checks->flags, rsc_unmanaged)) { pcmk__xe_set_bool_attr(node, PCMK_XA_UNMANAGED, true); } if (pcmk_is_set(checks->flags, rsc_locked)) { crm_xml_add(node, PCMK_XA_LOCKED_TO_HYPHEN, checks->lock_node); } if (pcmk_is_set(checks->flags, rsc_node_health)) { pcmk__xe_set_bool_attr(node, PCMK_XA_UNHEALTHY, true); } return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("resource-search-list", "GList *", "const gchar *") static int resource_search_list_default(pcmk__output_t *out, va_list args) { GList *nodes = va_arg(args, GList *); const gchar *requested_name = va_arg(args, const gchar *); bool printed = false; int rc = pcmk_rc_no_output; if (!out->is_quiet(out) && nodes == NULL) { out->err(out, "resource %s is NOT running", requested_name); return rc; } for (GList *lpc = nodes; lpc != NULL; lpc = lpc->next) { node_info_t *ni = (node_info_t *) lpc->data; if (!printed) { out->begin_list(out, NULL, NULL, "Nodes"); printed = true; rc = pcmk_rc_ok; } if (out->is_quiet(out)) { out->list_item(out, "node", "%s", ni->node_name); } else { const char *role_text = ""; if (ni->promoted) { role_text = " " PCMK_ROLE_PROMOTED; } out->list_item(out, "node", "resource %s is running on: %s%s", requested_name, ni->node_name, role_text); } } if (printed) { out->end_list(out); } return rc; } PCMK__OUTPUT_ARGS("resource-search-list", "GList *", "const gchar *") static int resource_search_list_xml(pcmk__output_t *out, va_list args) { GList *nodes = va_arg(args, GList *); const gchar *requested_name = va_arg(args, const gchar *); pcmk__output_xml_create_parent(out, PCMK_XE_NODES, PCMK_XA_RESOURCE, requested_name, NULL); for (GList *lpc = nodes; lpc != NULL; lpc = lpc->next) { node_info_t *ni = (node_info_t *) lpc->data; xmlNodePtr sub_node = pcmk__output_create_xml_text_node(out, PCMK_XE_NODE, ni->node_name); if (ni->promoted) { crm_xml_add(sub_node, PCMK_XA_STATE, "promoted"); } } pcmk__output_xml_pop_parent(out); return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("resource-reasons-list", "GList *", "pcmk_resource_t *", "pcmk_node_t *") static int resource_reasons_list_default(pcmk__output_t *out, va_list args) { GList *resources = va_arg(args, GList *); pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *); pcmk_node_t *node = va_arg(args, pcmk_node_t *); const char *host_uname = (node == NULL)? NULL : node->priv->name; out->begin_list(out, NULL, NULL, "Resource Reasons"); if ((rsc == NULL) && (host_uname == NULL)) { GList *lpc = NULL; GList *hosts = NULL; for (lpc = resources; lpc != NULL; lpc = lpc->next) { pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data; rsc->priv->fns->location(rsc, &hosts, pcmk__rsc_node_current); if (hosts == NULL) { out->list_item(out, "reason", "Resource %s is not running", rsc->id); } else { out->list_item(out, "reason", "Resource %s is running", rsc->id); } cli_resource_check(out, rsc, NULL); g_list_free(hosts); hosts = NULL; } } else if ((rsc != NULL) && (host_uname != NULL)) { if (resource_is_running_on(rsc, host_uname)) { out->list_item(out, "reason", "Resource %s is running on host %s", rsc->id, host_uname); } else { out->list_item(out, "reason", "Resource %s is not running on host %s", rsc->id, host_uname); } cli_resource_check(out, rsc, node); } else if ((rsc == NULL) && (host_uname != NULL)) { const char* host_uname = node->priv->name; GList *allResources = node->priv->assigned_resources; GList *activeResources = node->details->running_rsc; GList *unactiveResources = pcmk__subtract_lists(allResources, activeResources, (GCompareFunc) strcmp); GList *lpc = NULL; for (lpc = activeResources; lpc != NULL; lpc = lpc->next) { pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data; out->list_item(out, "reason", "Resource %s is running on host %s", rsc->id, host_uname); cli_resource_check(out, rsc, node); } for(lpc = unactiveResources; lpc != NULL; lpc = lpc->next) { pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data; out->list_item(out, "reason", "Resource %s is assigned to host %s but not running", rsc->id, host_uname); cli_resource_check(out, rsc, node); } g_list_free(allResources); g_list_free(activeResources); g_list_free(unactiveResources); } else if ((rsc != NULL) && (host_uname == NULL)) { GList *hosts = NULL; rsc->priv->fns->location(rsc, &hosts, pcmk__rsc_node_current); out->list_item(out, "reason", "Resource %s is %srunning", rsc->id, (hosts? "" : "not ")); cli_resource_check(out, rsc, NULL); g_list_free(hosts); } out->end_list(out); return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("resource-reasons-list", "GList *", "pcmk_resource_t *", "pcmk_node_t *") static int resource_reasons_list_xml(pcmk__output_t *out, va_list args) { GList *resources = va_arg(args, GList *); pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *); pcmk_node_t *node = va_arg(args, pcmk_node_t *); const char *host_uname = (node == NULL)? NULL : node->priv->name; xmlNodePtr xml_node = pcmk__output_xml_create_parent(out, PCMK_XE_REASON, NULL); if ((rsc == NULL) && (host_uname == NULL)) { GList *lpc = NULL; GList *hosts = NULL; pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCES, NULL); for (lpc = resources; lpc != NULL; lpc = lpc->next) { pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data; const char *running = NULL; rsc->priv->fns->location(rsc, &hosts, pcmk__rsc_node_current); running = pcmk__btoa(hosts != NULL); pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE, PCMK_XA_ID, rsc->id, PCMK_XA_RUNNING, running, NULL); cli_resource_check(out, rsc, NULL); pcmk__output_xml_pop_parent(out); g_list_free(hosts); hosts = NULL; } pcmk__output_xml_pop_parent(out); } else if ((rsc != NULL) && (host_uname != NULL)) { if (resource_is_running_on(rsc, host_uname)) { crm_xml_add(xml_node, PCMK_XA_RUNNING_ON, host_uname); } cli_resource_check(out, rsc, node); } else if ((rsc == NULL) && (host_uname != NULL)) { const char* host_uname = node->priv->name; GList *allResources = node->priv->assigned_resources; GList *activeResources = node->details->running_rsc; GList *unactiveResources = pcmk__subtract_lists(allResources, activeResources, (GCompareFunc) strcmp); GList *lpc = NULL; pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCES, NULL); for (lpc = activeResources; lpc != NULL; lpc = lpc->next) { pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data; pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE, PCMK_XA_ID, rsc->id, PCMK_XA_RUNNING, PCMK_VALUE_TRUE, PCMK_XA_HOST, host_uname, NULL); cli_resource_check(out, rsc, node); pcmk__output_xml_pop_parent(out); } for(lpc = unactiveResources; lpc != NULL; lpc = lpc->next) { pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data; pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE, PCMK_XA_ID, rsc->id, PCMK_XA_RUNNING, PCMK_VALUE_FALSE, PCMK_XA_HOST, host_uname, NULL); cli_resource_check(out, rsc, node); pcmk__output_xml_pop_parent(out); } pcmk__output_xml_pop_parent(out); g_list_free(allResources); g_list_free(activeResources); g_list_free(unactiveResources); } else if ((rsc != NULL) && (host_uname == NULL)) { GList *hosts = NULL; rsc->priv->fns->location(rsc, &hosts, pcmk__rsc_node_current); crm_xml_add(xml_node, PCMK_XA_RUNNING, pcmk__btoa(hosts != NULL)); cli_resource_check(out, rsc, NULL); g_list_free(hosts); } pcmk__output_xml_pop_parent(out); return pcmk_rc_ok; } static void add_resource_name(pcmk_resource_t *rsc, pcmk__output_t *out) { if (rsc->priv->children == NULL) { /* Sometimes PCMK_XE_RESOURCE might act as a PCMK_XA_NAME instead of an * XML element name, depending on whether pcmk__output_enable_list_element * was called. */ out->list_item(out, PCMK_XE_RESOURCE, "%s", rsc->id); } else { g_list_foreach(rsc->priv->children, (GFunc) add_resource_name, out); } } PCMK__OUTPUT_ARGS("resource-names-list", "GList *") static int resource_names(pcmk__output_t *out, va_list args) { GList *resources = va_arg(args, GList *); if (resources == NULL) { out->err(out, "NO resources configured\n"); return pcmk_rc_no_output; } out->begin_list(out, NULL, NULL, "Resource Names"); g_list_foreach(resources, (GFunc) add_resource_name, out); out->end_list(out); return pcmk_rc_ok; } static pcmk__message_entry_t fmt_functions[] = { { "agent-status", "default", agent_status_default }, { "agent-status", "xml", agent_status_xml }, { "attribute-changed", "default", attribute_changed_default }, { "attribute-changed", "xml", attribute_changed_xml }, { "attribute-changed-list", "default", attribute_changed_list_default }, { "attribute-changed-list", "xml", attribute_changed_list_xml }, { "attribute-list", "default", attribute_list_default }, { "attribute-list", "text", attribute_list_text }, { "override", "default", override_default }, { "override", "xml", override_xml }, { "property-list", "default", property_list_default }, { "property-list", "text", property_list_text }, { "resource-agent-action", "default", resource_agent_action_default }, { "resource-agent-action", "xml", resource_agent_action_xml }, { "resource-check-list", "default", resource_check_list_default }, { "resource-check-list", "xml", resource_check_list_xml }, { "resource-search-list", "default", resource_search_list_default }, { "resource-search-list", "xml", resource_search_list_xml }, { "resource-reasons-list", "default", resource_reasons_list_default }, { "resource-reasons-list", "xml", resource_reasons_list_xml }, { "resource-names-list", "default", resource_names }, { NULL, NULL, NULL } }; void crm_resource_register_messages(pcmk__output_t *out) { pcmk__register_messages(out, fmt_functions); } diff --git a/xml/constraints-4.0.rng b/xml/constraints-4.0.rng index d43557f09f..7128842b5b 100644 --- a/xml/constraints-4.0.rng +++ b/xml/constraints-4.0.rng @@ -1,281 +1,248 @@ - - - - - - group listed - - - - - - - - - - - - stop demote fence freeze always never exclusive start promote demote stop Stopped Started Promoted Unpromoted Master Slave Optional Mandatory Serialize - - - - - - - - - - - - - - - diff --git a/xml/upgrade-3.10-3.xsl b/xml/upgrade-3.10-3.xsl index 76e8d5f9fa..ccf37c2d18 100644 --- a/xml/upgrade-3.10-3.xsl +++ b/xml/upgrade-3.10-3.xsl @@ -1,23 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + a^ + + + + + + + -INFINITY + + + + + + + + + + + + + + + + + + + + + + + #uname + eq + + + + + + + + + + + + + + + + + + + and + + + + + + + + + + + + + + + + + + + + + + + + + + + + or + + + + + + + + + + + + +