root/daemons/controld/controld_schedulerd.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pe_subsystem_free
  2. save_cib_contents
  3. pe_ipc_destroy
  4. pe_ipc_dispatch
  5. pe_subsystem_new
  6. pe_subsystem_send
  7. do_pe_control
  8. controld_sched_timeout
  9. controld_stop_sched_timer
  10. controld_expect_sched_reply
  11. controld_free_sched_timer
  12. do_pe_invoke
  13. force_local_option
  14. do_pe_invoke_callback

   1 /*
   2  * Copyright 2004-2020 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 General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <unistd.h>  /* pid_t, sleep, ssize_t */
  13 
  14 #include <crm/cib.h>
  15 #include <crm/cluster.h>
  16 #include <crm/common/xml.h>
  17 #include <crm/crm.h>
  18 #include <crm/msg_xml.h>
  19 #include <crm/common/xml_internal.h>
  20 
  21 #include <pacemaker-controld.h>
  22 
  23 static mainloop_io_t *pe_subsystem = NULL;
  24 
  25 /*!
  26  * \internal
  27  * \brief Close any scheduler connection and free associated memory
  28  */
  29 void
  30 pe_subsystem_free(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  31 {
  32     controld_clear_fsa_input_flags(R_PE_REQUIRED);
  33     if (pe_subsystem) {
  34         controld_expect_sched_reply(NULL);
  35         mainloop_del_ipc_client(pe_subsystem);
  36         pe_subsystem = NULL;
  37         controld_clear_fsa_input_flags(R_PE_CONNECTED);
  38     }
  39 }
  40 
  41 /*!
  42  * \internal
  43  * \brief Save CIB query result to file, raising FSA error
  44  *
  45  * \param[in] msg        Ignored
  46  * \param[in] call_id    Call ID of CIB query
  47  * \param[in] rc         Return code of CIB query
  48  * \param[in] output     Result of CIB query
  49  * \param[in] user_data  Unique identifier for filename (will be freed)
  50  *
  51  * \note This is intended to be called after a scheduler connection fails.
  52  */
  53 static void
  54 save_cib_contents(xmlNode *msg, int call_id, int rc, xmlNode *output,
     /* [previous][next][first][last][top][bottom][index][help] */
  55                   void *user_data)
  56 {
  57     char *id = user_data;
  58 
  59     register_fsa_error_adv(C_FSA_INTERNAL, I_ERROR, NULL, NULL, __func__);
  60     CRM_CHECK(id != NULL, return);
  61 
  62     if (rc == pcmk_ok) {
  63         char *filename = crm_strdup_printf(PE_STATE_DIR "/pe-core-%s.bz2", id);
  64 
  65         if (write_xml_file(output, filename, TRUE) < 0) {
  66             crm_err("Could not save Cluster Information Base to %s after scheduler crash",
  67                     filename);
  68         } else {
  69             crm_notice("Saved Cluster Information Base to %s after scheduler crash",
  70                        filename);
  71         }
  72         free(filename);
  73     }
  74 }
  75 
  76 /*!
  77  * \internal
  78  * \brief Respond to scheduler connection failure
  79  *
  80  * \param[in] user_data  Ignored
  81  */
  82 static void
  83 pe_ipc_destroy(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  84 {
  85     // If we aren't connected to the scheduler, we can't expect a reply
  86     controld_expect_sched_reply(NULL);
  87 
  88     if (pcmk_is_set(fsa_input_register, R_PE_REQUIRED)) {
  89         int rc = pcmk_ok;
  90         char *uuid_str = crm_generate_uuid();
  91 
  92         crm_crit("Connection to the scheduler failed "
  93                  CRM_XS " uuid=%s", uuid_str);
  94 
  95         /*
  96          * The scheduler died...
  97          *
  98          * Save the current CIB so that we have a chance of
  99          * figuring out what killed it.
 100          *
 101          * Delay raising the I_ERROR until the query below completes or
 102          * 5s is up, whichever comes first.
 103          *
 104          */
 105         rc = fsa_cib_conn->cmds->query(fsa_cib_conn, NULL, NULL, cib_scope_local);
 106         fsa_register_cib_callback(rc, FALSE, uuid_str, save_cib_contents);
 107 
 108     } else {
 109         crm_info("Connection to the scheduler released");
 110     }
 111 
 112     controld_clear_fsa_input_flags(R_PE_CONNECTED);
 113     pe_subsystem = NULL;
 114     mainloop_set_trigger(fsa_source);
 115     return;
 116 }
 117 
 118 /*!
 119  * \internal
 120  * \brief Handle message from scheduler connection
 121  *
 122  * \param[in] buffer    XML message (will be freed)
 123  * \param[in] length    Ignored
 124  * \param[in] userdata  Ignored
 125  *
 126  * \return 0
 127  */
 128 static int
 129 pe_ipc_dispatch(const char *buffer, ssize_t length, gpointer userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
 130 {
 131     xmlNode *msg = string2xml(buffer);
 132 
 133     if (msg) {
 134         route_message(C_IPC_MESSAGE, msg);
 135     }
 136     free_xml(msg);
 137     return 0;
 138 }
 139 
 140 /*!
 141  * \internal
 142  * \brief Make new connection to scheduler
 143  *
 144  * \return TRUE on success, FALSE otherwise
 145  */
 146 static bool
 147 pe_subsystem_new(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 148 {
 149     struct ipc_client_callbacks pe_callbacks = {
 150         .dispatch = pe_ipc_dispatch,
 151         .destroy = pe_ipc_destroy
 152     };
 153 
 154     controld_set_fsa_input_flags(R_PE_REQUIRED);
 155     pe_subsystem = mainloop_add_ipc_client(CRM_SYSTEM_PENGINE,
 156                                            G_PRIORITY_DEFAULT,
 157                                            5 * 1024 * 1024 /* 5MB */,
 158                                            NULL, &pe_callbacks);
 159     if (pe_subsystem == NULL) {
 160         return FALSE;
 161     }
 162     controld_set_fsa_input_flags(R_PE_CONNECTED);
 163     return TRUE;
 164 }
 165 
 166 /*!
 167  * \internal
 168  * \brief Send an XML message to the scheduler
 169  *
 170  * \param[in] cmd  XML message to send
 171  *
 172  * \return pcmk_ok on success, -errno otherwise
 173  */
 174 static int
 175 pe_subsystem_send(xmlNode *cmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 176 {
 177     if (pe_subsystem) {
 178         int sent = crm_ipc_send(mainloop_get_ipc_client(pe_subsystem), cmd,
 179                                 0, 0, NULL);
 180 
 181         if (sent == 0) {
 182             sent = -ENODATA;
 183         } else if (sent > 0) {
 184             sent = pcmk_ok;
 185         }
 186         return sent;
 187     }
 188     return -ENOTCONN;
 189 }
 190 
 191 static void do_pe_invoke_callback(xmlNode *msg, int call_id, int rc,
 192                                   xmlNode *output, void *user_data);
 193 
 194 /*       A_PE_START, A_PE_STOP, O_PE_RESTART    */
 195 void
 196 do_pe_control(long long action,
     /* [previous][next][first][last][top][bottom][index][help] */
 197               enum crmd_fsa_cause cause,
 198               enum crmd_fsa_state cur_state,
 199               enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 200 {
 201     if (action & A_PE_STOP) {
 202         pe_subsystem_free();
 203     }
 204     if ((action & A_PE_START)
 205         && !pcmk_is_set(fsa_input_register, R_PE_CONNECTED)) {
 206 
 207         if (cur_state == S_STOPPING) {
 208             crm_info("Ignoring request to connect to scheduler while shutting down");
 209 
 210         } else if (!pe_subsystem_new()) {
 211             crm_warn("Could not connect to scheduler");
 212             register_fsa_error(C_FSA_INTERNAL, I_FAIL, NULL);
 213         }
 214     }
 215 }
 216 
 217 int fsa_pe_query = 0;
 218 char *fsa_pe_ref = NULL;
 219 static mainloop_timer_t *controld_sched_timer = NULL;
 220 
 221 // @TODO Make this a configurable cluster option if there's demand for it
 222 #define SCHED_TIMEOUT_MS (120000)
 223 
 224 /*!
 225  * \internal
 226  * \brief Handle a timeout waiting for scheduler reply
 227  *
 228  * \param[in] user_data  Ignored
 229  *
 230  * \return FALSE (indicating that timer should not be restarted)
 231  */
 232 static gboolean
 233 controld_sched_timeout(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 234 {
 235     if (AM_I_DC) {
 236         /* If this node is the DC but can't communicate with the scheduler, just
 237          * exit (and likely get fenced) so this node doesn't interfere with any
 238          * further DC elections.
 239          *
 240          * @TODO We could try something less drastic first, like disconnecting
 241          * and reconnecting to the scheduler, but something is likely going
 242          * seriously wrong, so perhaps it's better to just fail as quickly as
 243          * possible.
 244          */
 245         crmd_exit(CRM_EX_FATAL);
 246     }
 247     return FALSE;
 248 }
 249 
 250 void
 251 controld_stop_sched_timer(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 252 {
 253     if (controld_sched_timer && fsa_pe_ref) {
 254         crm_trace("Stopping timer for scheduler reply %s", fsa_pe_ref);
 255     }
 256     mainloop_timer_stop(controld_sched_timer);
 257 }
 258 
 259 /*!
 260  * \internal
 261  * \brief Set the scheduler request currently being waited on
 262  *
 263  * \param[in] msg  Request to expect reply to (or NULL for none)
 264  */
 265 void
 266 controld_expect_sched_reply(xmlNode *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 267 {
 268     char *ref = NULL;
 269 
 270     if (msg) {
 271         ref = crm_element_value_copy(msg, XML_ATTR_REFERENCE);
 272         CRM_ASSERT(ref != NULL);
 273 
 274         if (controld_sched_timer == NULL) {
 275             controld_sched_timer = mainloop_timer_add("scheduler_reply_timer",
 276                                                       SCHED_TIMEOUT_MS, FALSE,
 277                                                       controld_sched_timeout,
 278                                                       NULL);
 279         }
 280         mainloop_timer_start(controld_sched_timer);
 281     } else {
 282         controld_stop_sched_timer();
 283     }
 284     free(fsa_pe_ref);
 285     fsa_pe_ref = ref;
 286 }
 287 
 288 /*!
 289  * \internal
 290  * \brief Free the scheduler reply timer
 291  */
 292 void
 293 controld_free_sched_timer(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 294 {
 295     if (controld_sched_timer != NULL) {
 296         mainloop_timer_del(controld_sched_timer);
 297         controld_sched_timer = NULL;
 298     }
 299 }
 300 
 301 /*       A_PE_INVOKE    */
 302 void
 303 do_pe_invoke(long long action,
     /* [previous][next][first][last][top][bottom][index][help] */
 304              enum crmd_fsa_cause cause,
 305              enum crmd_fsa_state cur_state,
 306              enum crmd_fsa_input current_input, fsa_data_t * msg_data)
 307 {
 308     if (AM_I_DC == FALSE) {
 309         crm_err("Not invoking scheduler because not DC: %s",
 310                 fsa_action2string(action));
 311         return;
 312     }
 313 
 314     if (!pcmk_is_set(fsa_input_register, R_PE_CONNECTED)) {
 315         if (pcmk_is_set(fsa_input_register, R_SHUTDOWN)) {
 316             crm_err("Cannot shut down gracefully without the scheduler");
 317             register_fsa_input_before(C_FSA_INTERNAL, I_TERMINATE, NULL);
 318 
 319         } else {
 320             crm_info("Waiting for the scheduler to connect");
 321             crmd_fsa_stall(FALSE);
 322             controld_set_fsa_action_flags(A_PE_START);
 323             trigger_fsa();
 324         }
 325         return;
 326     }
 327 
 328     if (cur_state != S_POLICY_ENGINE) {
 329         crm_notice("Not invoking scheduler because in state %s",
 330                    fsa_state2string(cur_state));
 331         return;
 332     }
 333     if (!pcmk_is_set(fsa_input_register, R_HAVE_CIB)) {
 334         crm_err("Attempted to invoke scheduler without consistent Cluster Information Base!");
 335 
 336         /* start the join from scratch */
 337         register_fsa_input_before(C_FSA_INTERNAL, I_ELECTION, NULL);
 338         return;
 339     }
 340 
 341     fsa_pe_query = fsa_cib_conn->cmds->query(fsa_cib_conn, NULL, NULL, cib_scope_local);
 342 
 343     crm_debug("Query %d: Requesting the current CIB: %s", fsa_pe_query,
 344               fsa_state2string(fsa_state));
 345 
 346     controld_expect_sched_reply(NULL);
 347     fsa_register_cib_callback(fsa_pe_query, FALSE, NULL, do_pe_invoke_callback);
 348 }
 349 
 350 static void
 351 force_local_option(xmlNode *xml, const char *attr_name, const char *attr_value)
     /* [previous][next][first][last][top][bottom][index][help] */
 352 {
 353     int max = 0;
 354     int lpc = 0;
 355     char *xpath_string = NULL;
 356     xmlXPathObjectPtr xpathObj = NULL;
 357 
 358     xpath_string = crm_strdup_printf("%.128s//%s//nvpair[@name='%.128s']",
 359                                      get_object_path(XML_CIB_TAG_CRMCONFIG),
 360                                      XML_CIB_TAG_PROPSET, attr_name);
 361     xpathObj = xpath_search(xml, xpath_string);
 362     max = numXpathResults(xpathObj);
 363     free(xpath_string);
 364 
 365     for (lpc = 0; lpc < max; lpc++) {
 366         xmlNode *match = getXpathResult(xpathObj, lpc);
 367         crm_trace("Forcing %s/%s = %s", ID(match), attr_name, attr_value);
 368         crm_xml_add(match, XML_NVPAIR_ATTR_VALUE, attr_value);
 369     }
 370 
 371     if(max == 0) {
 372         xmlNode *configuration = NULL;
 373         xmlNode *crm_config = NULL;
 374         xmlNode *cluster_property_set = NULL;
 375 
 376         crm_trace("Creating %s-%s for %s=%s",
 377                   CIB_OPTIONS_FIRST, attr_name, attr_name, attr_value);
 378 
 379         configuration = pcmk__xe_match(xml, XML_CIB_TAG_CONFIGURATION, NULL,
 380                                        NULL);
 381         if (configuration == NULL) {
 382             configuration = create_xml_node(xml, XML_CIB_TAG_CONFIGURATION);
 383         }
 384 
 385         crm_config = pcmk__xe_match(configuration, XML_CIB_TAG_CRMCONFIG, NULL,
 386                                     NULL);
 387         if (crm_config == NULL) {
 388             crm_config = create_xml_node(configuration, XML_CIB_TAG_CRMCONFIG);
 389         }
 390 
 391         cluster_property_set = pcmk__xe_match(crm_config, XML_CIB_TAG_PROPSET,
 392                                               NULL, NULL);
 393         if (cluster_property_set == NULL) {
 394             cluster_property_set = create_xml_node(crm_config, XML_CIB_TAG_PROPSET);
 395             crm_xml_add(cluster_property_set, XML_ATTR_ID, CIB_OPTIONS_FIRST);
 396         }
 397 
 398         xml = create_xml_node(cluster_property_set, XML_CIB_TAG_NVPAIR);
 399 
 400         crm_xml_set_id(xml, "%s-%s", CIB_OPTIONS_FIRST, attr_name);
 401         crm_xml_add(xml, XML_NVPAIR_ATTR_NAME, attr_name);
 402         crm_xml_add(xml, XML_NVPAIR_ATTR_VALUE, attr_value);
 403     }
 404     freeXpathObject(xpathObj);
 405 }
 406 
 407 static void
 408 do_pe_invoke_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 409 {
 410     xmlNode *cmd = NULL;
 411     pid_t watchdog = pcmk__locate_sbd();
 412 
 413     if (rc != pcmk_ok) {
 414         crm_err("Could not retrieve the Cluster Information Base: %s "
 415                 CRM_XS " rc=%d call=%d", pcmk_strerror(rc), rc, call_id);
 416         register_fsa_error_adv(C_FSA_INTERNAL, I_ERROR, NULL, NULL, __func__);
 417         return;
 418 
 419     } else if (call_id != fsa_pe_query) {
 420         crm_trace("Skipping superseded CIB query: %d (current=%d)", call_id, fsa_pe_query);
 421         return;
 422 
 423     } else if (!AM_I_DC || !pcmk_is_set(fsa_input_register, R_PE_CONNECTED)) {
 424         crm_debug("No need to invoke the scheduler anymore");
 425         return;
 426 
 427     } else if (fsa_state != S_POLICY_ENGINE) {
 428         crm_debug("Discarding scheduler request in state: %s",
 429                   fsa_state2string(fsa_state));
 430         return;
 431 
 432     /* this callback counts as 1 */
 433     } else if (num_cib_op_callbacks() > 1) {
 434         crm_debug("Re-asking for the CIB: %d other peer updates still pending",
 435                   (num_cib_op_callbacks() - 1));
 436         sleep(1);
 437         controld_set_fsa_action_flags(A_PE_INVOKE);
 438         trigger_fsa();
 439         return;
 440     }
 441 
 442     CRM_LOG_ASSERT(output != NULL);
 443 
 444     /* Refresh the remote node cache and the known node cache when the
 445      * scheduler is invoked */
 446     crm_peer_caches_refresh(output);
 447 
 448     crm_xml_add(output, XML_ATTR_DC_UUID, fsa_our_uuid);
 449     crm_xml_add_int(output, XML_ATTR_HAVE_QUORUM, fsa_has_quorum);
 450 
 451     force_local_option(output, XML_ATTR_HAVE_WATCHDOG, pcmk__btoa(watchdog));
 452 
 453     if (ever_had_quorum && crm_have_quorum == FALSE) {
 454         crm_xml_add_int(output, XML_ATTR_QUORUM_PANIC, 1);
 455     }
 456 
 457     cmd = create_request(CRM_OP_PECALC, output, NULL, CRM_SYSTEM_PENGINE, CRM_SYSTEM_DC, NULL);
 458 
 459     rc = pe_subsystem_send(cmd);
 460     if (rc < 0) {
 461         crm_err("Could not contact the scheduler: %s " CRM_XS " rc=%d",
 462                 pcmk_strerror(rc), rc);
 463         register_fsa_error_adv(C_FSA_INTERNAL, I_ERROR, NULL, NULL, __func__);
 464     } else {
 465         controld_expect_sched_reply(cmd);
 466         crm_debug("Invoking the scheduler: query=%d, ref=%s, seq=%llu, quorate=%d",
 467                   fsa_pe_query, fsa_pe_ref, crm_peer_seq, fsa_has_quorum);
 468     }
 469     free_xml(cmd);
 470 }

/* [previous][next][first][last][top][bottom][index][help] */