Line data Source code
1 : /* 2 : * Copyright 2023-2024 the Pacemaker project contributors 3 : * 4 : * The version control history for this file may have further details. 5 : * 6 : * This source code is licensed under the GNU Lesser General Public License 7 : * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. 8 : */ 9 : 10 : #include <crm_internal.h> 11 : #include <crm/cib/internal.h> 12 : #include <crm/common/output.h> 13 : #include <crm/common/results.h> 14 : #include <crm/common/scheduler.h> 15 : #include <pacemaker-internal.h> 16 : #include <pacemaker.h> 17 : 18 : #include <stdint.h> 19 : #include <sys/types.h> 20 : #include <sys/stat.h> 21 : #include <unistd.h> 22 : 23 : #include "libpacemaker_private.h" 24 : 25 : int 26 0 : pcmk__parse_cib(pcmk__output_t *out, const char *cib_source, xmlNodePtr *cib_object) 27 : { 28 : // @COMPAT Take an enum for cib_source instead of trying to figure it out? 29 0 : const char *first = cib_source; 30 : 31 0 : if (cib_source == NULL) { 32 0 : crm_info("Reading XML from: live cluster"); 33 0 : return cib__signon_query(out, NULL, cib_object); 34 : } 35 : 36 0 : while (isspace(*first)) { 37 0 : first++; 38 : } 39 : 40 0 : if (*first == '<') { 41 0 : *cib_object = pcmk__xml_parse(cib_source); 42 : } else { 43 0 : *cib_object = pcmk__xml_read(cib_source); 44 : } 45 : 46 0 : return (*cib_object == NULL)? ENODATA : pcmk_rc_ok; 47 : } 48 : 49 : int 50 0 : pcmk__verify(pcmk_scheduler_t *scheduler, pcmk__output_t *out, xmlNode *cib_object) 51 : { 52 0 : int rc = pcmk_rc_ok; 53 0 : xmlNode *status = NULL; 54 0 : xmlNode *cib_object_copy = NULL; 55 : 56 0 : if (!pcmk__xe_is(cib_object, PCMK_XE_CIB)) { 57 0 : rc = EBADMSG; 58 0 : out->err(out, "This tool can only check complete configurations (i.e. those starting with <cib>)."); 59 0 : goto verify_done; 60 : } 61 : 62 0 : status = pcmk_find_cib_element(cib_object, PCMK_XE_STATUS); 63 0 : if (status == NULL) { 64 0 : pcmk__xe_create(cib_object, PCMK_XE_STATUS); 65 : } 66 : 67 0 : if (!pcmk__validate_xml(cib_object, NULL, 68 0 : (xmlRelaxNGValidityErrorFunc) out->err, out)) { 69 0 : crm_config_error = TRUE; 70 0 : rc = pcmk_rc_schema_validation; 71 0 : goto verify_done; 72 : 73 0 : } else if (!pcmk__update_configured_schema(&cib_object, false)) { 74 0 : crm_config_error = TRUE; 75 0 : out->err(out, "The cluster will NOT be able to use this configuration.\n" 76 : "Please manually update the configuration to conform to the %s syntax.", 77 : pcmk__highest_schema_name()); 78 0 : rc = pcmk_rc_schema_validation; 79 0 : goto verify_done; 80 : } 81 : 82 : /* Process the configuration to set crm_config_error/crm_config_warning. 83 : * 84 : * @TODO Some parts of the configuration are unpacked only when needed (for 85 : * example, action configuration), so we aren't necessarily checking those. 86 : */ 87 0 : if (cib_object != NULL) { 88 0 : unsigned long long flags = pcmk_sched_no_counts|pcmk_sched_no_compat; 89 : 90 0 : if (status == NULL) { 91 : // No status available, so do minimal checks 92 0 : flags |= pcmk_sched_validate_only; 93 : } 94 0 : cib_object_copy = pcmk__xml_copy(NULL, cib_object); 95 : 96 : /* The scheduler takes ownership of the XML object and potentially 97 : * frees it later. We want the caller of pcmk__verify to retain 98 : * ownership of the passed-in XML object, hence we pass in a copy 99 : * to the scheduler. 100 : */ 101 0 : pcmk__schedule_actions(cib_object_copy, flags, scheduler); 102 : } 103 : 104 0 : verify_done: 105 0 : if (crm_config_error) { 106 0 : rc = pcmk_rc_schema_validation; 107 0 : pcmk__config_err("CIB did not pass schema validation"); 108 0 : } else if (crm_config_warning) { 109 0 : rc = pcmk_rc_schema_validation; 110 : } 111 0 : return rc; 112 : } 113 : 114 : int 115 0 : pcmk_verify(xmlNodePtr *xml, const char *cib_source) 116 : { 117 0 : pcmk_scheduler_t *scheduler = NULL; 118 0 : pcmk__output_t *out = NULL; 119 0 : int rc = pcmk_rc_ok; 120 : 121 0 : xmlNode *cib_object = NULL; 122 : 123 0 : rc = pcmk__xml_output_new(&out, xml); 124 0 : if (rc != pcmk_rc_ok) { 125 0 : return rc; 126 : } 127 : 128 0 : pe__register_messages(out); 129 0 : pcmk__register_lib_messages(out); 130 : 131 0 : rc = pcmk__parse_cib(out, cib_source, &cib_object); 132 0 : if (rc != pcmk_rc_ok) { 133 0 : out->err(out, "Couldn't parse input"); 134 0 : goto done; 135 : } 136 : 137 0 : scheduler = pe_new_working_set(); 138 0 : if (scheduler == NULL) { 139 0 : rc = errno; 140 0 : out->err(out, "Couldn't allocate scheduler data: %s", pcmk_rc_str(rc)); 141 0 : goto done; 142 : } 143 : 144 0 : scheduler->priv = out; 145 0 : rc = pcmk__verify(scheduler, out, cib_object); 146 : 147 0 : done: 148 0 : pe_free_working_set(scheduler); 149 0 : pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml); 150 0 : free_xml(cib_object); 151 0 : return rc; 152 : }