1    	/*
2    	 * Copyright 2009-2023 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 <stdio.h>
13   	#include <unistd.h>
14   	#include <stdlib.h>
15   	
16   	#include <sys/stat.h>
17   	#include <sys/param.h>
18   	#include <sys/types.h>
19   	#include <dirent.h>
20   	
21   	#include <crm/crm.h>
22   	#include <crm/cib.h>
23   	#include <crm/cib/internal.h>
24   	#include <crm/common/util.h>
25   	#include <crm/common/iso8601.h>
26   	#include <crm/common/xml_internal.h>
27   	#include <crm/lrmd_events.h>            // lrmd_event_data_t, etc.
28   	#include <crm/lrmd_internal.h>
29   	#include <crm/pengine/status.h>
30   	#include <pacemaker-internal.h>
31   	
32   	#include "libpacemaker_private.h"
33   	
34   	bool pcmk__simulate_node_config = false;
35   	
36   	#define XPATH_NODE_CONFIG   "//" XML_CIB_TAG_NODE "[@" XML_ATTR_UNAME "='%s']"
37   	#define XPATH_NODE_STATE    "//" XML_CIB_TAG_STATE "[@" XML_ATTR_UNAME "='%s']"
38   	#define XPATH_NODE_STATE_BY_ID "//" XML_CIB_TAG_STATE "[@" XML_ATTR_ID "='%s']"
39   	#define XPATH_RSC_HISTORY   XPATH_NODE_STATE \
40   	                            "//" XML_LRM_TAG_RESOURCE "[@" XML_ATTR_ID "='%s']"
41   	
42   	
43   	/*!
44   	 * \internal
45   	 * \brief Inject a fictitious transient node attribute into scheduler input
46   	 *
47   	 * \param[in,out] out       Output object for displaying error messages
48   	 * \param[in,out] cib_node  node_state XML to inject attribute into
49   	 * \param[in]     name      Transient node attribute name to inject
50   	 * \param[in]     value     Transient node attribute value to inject
51   	 */
52   	static void
53   	inject_transient_attr(pcmk__output_t *out, xmlNode *cib_node,
54   	                      const char *name, const char *value)
55   	{
56   	    xmlNode *attrs = NULL;
57   	    xmlNode *instance_attrs = NULL;
58   	    const char *node_uuid = ID(cib_node);
59   	
60   	    out->message(out, "inject-attr", name, value, cib_node);
61   	
62   	    attrs = first_named_child(cib_node, XML_TAG_TRANSIENT_NODEATTRS);
63   	    if (attrs == NULL) {
64   	        attrs = create_xml_node(cib_node, XML_TAG_TRANSIENT_NODEATTRS);
65   	        crm_xml_add(attrs, XML_ATTR_ID, node_uuid);
66   	    }
67   	
68   	    instance_attrs = first_named_child(attrs, XML_TAG_ATTR_SETS);
69   	    if (instance_attrs == NULL) {
70   	        instance_attrs = create_xml_node(attrs, XML_TAG_ATTR_SETS);
71   	        crm_xml_add(instance_attrs, XML_ATTR_ID, node_uuid);
72   	    }
73   	
74   	    crm_create_nvpair_xml(instance_attrs, NULL, name, value);
75   	}
76   	
77   	/*!
78   	 * \internal
79   	 * \brief Inject a fictitious fail count into a scheduler input
80   	 *
81   	 * \param[in,out] out          Output object for displaying error messages
82   	 * \param[in,out] cib_node     Node state XML to inject into
83   	 * \param[in]     resource     ID of resource for fail count to inject
84   	 * \param[in]     task         Action name for fail count to inject
85   	 * \param[in]     interval_ms  Action interval (in milliseconds) for fail count
86   	 * \param[in]     rc           Action result for fail count to inject (if 0, or
87   	 *                             7 when interval_ms is 0, inject nothing)
88   	 */
89   	void
90   	pcmk__inject_failcount(pcmk__output_t *out, xmlNode *cib_node,
91   	                       const char *resource, const char *task,
92   	                       guint interval_ms, int rc)
93   	{
94   	    if (rc == 0) {
95   	        return;
96   	
97   	    } else if ((rc == 7) && (interval_ms == 0)) {
98   	        return;
99   	
100  	    } else {
101  	        char *name = NULL;
102  	        char *now = pcmk__ttoa(time(NULL));
103  	
104  	        name = pcmk__failcount_name(resource, task, interval_ms);
105  	        inject_transient_attr(out, cib_node, name, "value++");
106  	        free(name);
107  	
108  	        name = pcmk__lastfailure_name(resource, task, interval_ms);
109  	        inject_transient_attr(out, cib_node, name, now);
110  	        free(name);
111  	
112  	        free(now);
113  	    }
114  	}
115  	
116  	/*!
117  	 * \internal
118  	 * \brief Create a CIB configuration entry for a fictitious node
119  	 *
120  	 * \param[in,out] cib_conn  CIB object to use
121  	 * \param[in]     node      Node name to use
122  	 */
123  	static void
124  	create_node_entry(cib_t *cib_conn, const char *node)
125  	{
126  	    int rc = pcmk_ok;
127  	    char *xpath = crm_strdup_printf(XPATH_NODE_CONFIG, node);
128  	
129  	    rc = cib_conn->cmds->query(cib_conn, xpath, NULL,
130  	                               cib_xpath|cib_sync_call|cib_scope_local);
131  	
132  	    if (rc == -ENXIO) { // Only add if not already existing
133  	        xmlNode *cib_object = create_xml_node(NULL, XML_CIB_TAG_NODE);
134  	
135  	        crm_xml_add(cib_object, XML_ATTR_ID, node); // Use node name as ID
136  	        crm_xml_add(cib_object, XML_ATTR_UNAME, node);
137  	        cib_conn->cmds->create(cib_conn, XML_CIB_TAG_NODES, cib_object,
138  	                               cib_sync_call|cib_scope_local);
139  	        /* Not bothering with subsequent query to see if it exists,
140  	           we'll bomb out later in the call to query_node_uuid()... */
141  	
142  	        free_xml(cib_object);
143  	    }
144  	
145  	    free(xpath);
146  	}
147  	
148  	/*!
149  	 * \internal
150  	 * \brief Synthesize a fake executor event for an action
151  	 *
152  	 * \param[in] cib_resource  XML for any existing resource action history
153  	 * \param[in] task          Name of action to synthesize
154  	 * \param[in] interval_ms   Interval of action to synthesize
155  	 * \param[in] outcome       Result of action to synthesize
156  	 *
157  	 * \return Newly allocated executor event
158  	 * \note It is the caller's responsibility to free the result with
159  	 *       lrmd_free_event().
160  	 */
161  	static lrmd_event_data_t *
162  	create_op(const xmlNode *cib_resource, const char *task, guint interval_ms,
163  	          int outcome)
164  	{
165  	    lrmd_event_data_t *op = NULL;
166  	    xmlNode *xop = NULL;
167  	
168  	    op = lrmd_new_event(ID(cib_resource), task, interval_ms);
169  	    lrmd__set_result(op, outcome, PCMK_EXEC_DONE, "Simulated action result");
170  	    op->params = NULL; // Not needed for simulation purposes
CID (unavailable; MK=07385b63d5ef0f3105257701927f9e84) (#1 of 1): Use of 32-bit time_t (Y2K38_SAFETY):
(1) Event store_truncates_time_t: A "time_t" value is stored in an integer with too few bits to accommodate it. The expression "time(NULL)" is cast to "unsigned int".
171  	    op->t_run = (unsigned int) time(NULL);
172  	    op->t_rcchange = op->t_run;
173  	
174  	    // Use a call ID higher than any existing history entries
175  	    op->call_id = 0;
176  	    for (xop = pcmk__xe_first_child(cib_resource); xop != NULL;
177  	         xop = pcmk__xe_next(xop)) {
178  	
179  	        int tmp = 0;
180  	
181  	        crm_element_value_int(xop, XML_LRM_ATTR_CALLID, &tmp);
182  	        if (tmp > op->call_id) {
183  	            op->call_id = tmp;
184  	        }
185  	    }
186  	    op->call_id++;
187  	
188  	    return op;
189  	}
190  	
191  	/*!
192  	 * \internal
193  	 * \brief Inject a fictitious resource history entry into a scheduler input
194  	 *
195  	 * \param[in,out] cib_resource  Resource history XML to inject entry into
196  	 * \param[in,out] op            Action result to inject
197  	 * \param[in]     target_rc     Expected result for action to inject
198  	 *
199  	 * \return XML of injected resource history entry
200  	 */
201  	xmlNode *
202  	pcmk__inject_action_result(xmlNode *cib_resource, lrmd_event_data_t *op,
203  	                           int target_rc)
204  	{
205  	    return pcmk__create_history_xml(cib_resource, op, CRM_FEATURE_SET,
206  	                                    target_rc, NULL, crm_system_name);
207  	}
208  	
209  	/*!
210  	 * \internal
211  	 * \brief Inject a fictitious node into a scheduler input
212  	 *
213  	 * \param[in,out] cib_conn  Scheduler input CIB to inject node into
214  	 * \param[in]     node      Name of node to inject
215  	 * \param[in]     uuid      UUID of node to inject
216  	 *
217  	 * \return XML of node_state entry for new node
218  	 * \note If the global pcmk__simulate_node_config has been set to true, a
219  	 *       node entry in the configuration section will be added, as well as a
220  	 *       node state entry in the status section.
221  	 */
222  	xmlNode *
223  	pcmk__inject_node(cib_t *cib_conn, const char *node, const char *uuid)
224  	{
225  	    int rc = pcmk_ok;
226  	    xmlNode *cib_object = NULL;
227  	    char *xpath = crm_strdup_printf(XPATH_NODE_STATE, node);
228  	    bool duplicate = false;
229  	    char *found_uuid = NULL;
230  	
231  	    if (pcmk__simulate_node_config) {
232  	        create_node_entry(cib_conn, node);
233  	    }
234  	
235  	    rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object,
236  	                               cib_xpath|cib_sync_call|cib_scope_local);
237  	
238  	    if ((cib_object != NULL) && (ID(cib_object) == NULL)) {
239  	        crm_err("Detected multiple node_state entries for xpath=%s, bailing",
240  	                xpath);
241  	        duplicate = true;
242  	        goto done;
243  	    }
244  	
245  	    if (rc == -ENXIO) {
246  	        if (uuid == NULL) {
247  	            query_node_uuid(cib_conn, node, &found_uuid, NULL);
248  	        } else {
249  	            found_uuid = strdup(uuid);
250  	        }
251  	
252  	        if (found_uuid) {
253  	            char *xpath_by_uuid = crm_strdup_printf(XPATH_NODE_STATE_BY_ID,
254  	                                                    found_uuid);
255  	
256  	            // It's possible that a node_state entry doesn't have an uname yet.
257  	            rc = cib_conn->cmds->query(cib_conn, xpath_by_uuid, &cib_object,
258  	                                       cib_xpath|cib_sync_call|cib_scope_local);
259  	
260  	            if ((cib_object != NULL) && (ID(cib_object) == NULL)) {
261  	                crm_err("Can't inject node state for %s because multiple "
262  	                        "state entries found for ID %s", node, found_uuid);
263  	                duplicate = true;
264  	                free(xpath_by_uuid);
265  	                goto done;
266  	
267  	            } else if (cib_object != NULL) {
268  	                crm_xml_add(cib_object, XML_ATTR_UNAME, node);
269  	
270  	                rc = cib_conn->cmds->modify(cib_conn, XML_CIB_TAG_STATUS,
271  	                                            cib_object,
272  	                                            cib_sync_call|cib_scope_local);
273  	            }
274  	
275  	            free(xpath_by_uuid);
276  	        }
277  	    }
278  	
279  	    if (rc == -ENXIO) {
280  	        cib_object = create_xml_node(NULL, XML_CIB_TAG_STATE);
281  	        crm_xml_add(cib_object, XML_ATTR_ID, found_uuid);
282  	        crm_xml_add(cib_object, XML_ATTR_UNAME, node);
283  	        cib_conn->cmds->create(cib_conn, XML_CIB_TAG_STATUS, cib_object,
284  	                               cib_sync_call|cib_scope_local);
285  	        free_xml(cib_object);
286  	
287  	        rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object,
288  	                                   cib_xpath|cib_sync_call|cib_scope_local);
289  	        crm_trace("Injecting node state for %s (rc=%d)", node, rc);
290  	    }
291  	
292  	done:
293  	    free(found_uuid);
294  	    free(xpath);
295  	
296  	    if (duplicate) {
297  	        crm_log_xml_warn(cib_object, "Duplicates");
298  	        crm_exit(CRM_EX_SOFTWARE);
299  	        return NULL; // not reached, but makes static analysis happy
300  	    }
301  	
302  	    CRM_ASSERT(rc == pcmk_ok);
303  	    return cib_object;
304  	}
305  	
306  	/*!
307  	 * \internal
308  	 * \brief Inject a fictitious node state change into a scheduler input
309  	 *
310  	 * \param[in,out] cib_conn  Scheduler input CIB to inject into
311  	 * \param[in]     node      Name of node to inject change for
312  	 * \param[in]     up        If true, change state to online, otherwise offline
313  	 *
314  	 * \return XML of changed (or added) node state entry
315  	 */
316  	xmlNode *
317  	pcmk__inject_node_state_change(cib_t *cib_conn, const char *node, bool up)
318  	{
319  	    xmlNode *cib_node = pcmk__inject_node(cib_conn, node, NULL);
320  	
321  	    if (up) {
322  	        pcmk__xe_set_props(cib_node,
323  	                           PCMK__XA_IN_CCM, XML_BOOLEAN_YES,
324  	                           PCMK__XA_CRMD, ONLINESTATUS,
325  	                           PCMK__XA_JOIN, CRMD_JOINSTATE_MEMBER,
326  	                           PCMK__XA_EXPECTED, CRMD_JOINSTATE_MEMBER,
327  	                           NULL);
328  	    } else {
329  	        pcmk__xe_set_props(cib_node,
330  	                           PCMK__XA_IN_CCM, XML_BOOLEAN_NO,
331  	                           PCMK__XA_CRMD, OFFLINESTATUS,
332  	                           PCMK__XA_JOIN, CRMD_JOINSTATE_DOWN,
333  	                           PCMK__XA_EXPECTED, CRMD_JOINSTATE_DOWN,
334  	                           NULL);
335  	    }
336  	    crm_xml_add(cib_node, XML_ATTR_ORIGIN, crm_system_name);
337  	    return cib_node;
338  	}
339  	
340  	/*!
341  	 * \internal
342  	 * \brief Check whether a node has history for a given resource
343  	 *
344  	 * \param[in,out] cib_node  Node state XML to check
345  	 * \param[in]     resource  Resource name to check for
346  	 *
347  	 * \return Resource's lrm_resource XML entry beneath \p cib_node if found,
348  	 *         otherwise NULL
349  	 */
350  	static xmlNode *
351  	find_resource_xml(xmlNode *cib_node, const char *resource)
352  	{
353  	    const char *node = crm_element_value(cib_node, XML_ATTR_UNAME);
354  	    char *xpath = crm_strdup_printf(XPATH_RSC_HISTORY, node, resource);
355  	    xmlNode *match = get_xpath_object(xpath, cib_node, LOG_TRACE);
356  	
357  	    free(xpath);
358  	    return match;
359  	}
360  	
361  	/*!
362  	 * \internal
363  	 * \brief Inject a resource history element into a scheduler input
364  	 *
365  	 * \param[in,out] out       Output object for displaying error messages
366  	 * \param[in,out] cib_node  Node state XML to inject resource history entry into
367  	 * \param[in]     resource  ID (in configuration) of resource to inject
368  	 * \param[in]     lrm_name  ID as used in history (could be clone instance)
369  	 * \param[in]     rclass    Resource agent class of resource to inject
370  	 * \param[in]     rtype     Resource agent type of resource to inject
371  	 * \param[in]     rprovider Resource agent provider of resource to inject
372  	 *
373  	 * \return XML of injected resource history element
374  	 * \note If a history element already exists under either \p resource or
375  	 *       \p lrm_name, this will return it rather than injecting a new one.
376  	 */
377  	xmlNode *
378  	pcmk__inject_resource_history(pcmk__output_t *out, xmlNode *cib_node,
379  	                              const char *resource, const char *lrm_name,
380  	                              const char *rclass, const char *rtype,
381  	                              const char *rprovider)
382  	{
383  	    xmlNode *lrm = NULL;
384  	    xmlNode *container = NULL;
385  	    xmlNode *cib_resource = NULL;
386  	
387  	    cib_resource = find_resource_xml(cib_node, resource);
388  	    if (cib_resource != NULL) {
389  	        /* If an existing LRM history entry uses the resource name,
390  	         * continue using it, even if lrm_name is different.
391  	         */
392  	        return cib_resource;
393  	    }
394  	
395  	    // Check for history entry under preferred name
396  	    if (strcmp(resource, lrm_name) != 0) {
397  	        cib_resource = find_resource_xml(cib_node, lrm_name);
398  	        if (cib_resource != NULL) {
399  	            return cib_resource;
400  	        }
401  	    }
402  	
403  	    if ((rclass == NULL) || (rtype == NULL)) {
404  	        // @TODO query configuration for class, provider, type
405  	        out->err(out,
406  	                 "Resource %s not found in the status section of %s "
407  	                 "(supply class and type to continue)",
408  	                 resource, ID(cib_node));
409  	        return NULL;
410  	
411  	    } else if (!pcmk__strcase_any_of(rclass,
412  	                                     PCMK_RESOURCE_CLASS_OCF,
413  	                                     PCMK_RESOURCE_CLASS_STONITH,
414  	                                     PCMK_RESOURCE_CLASS_SERVICE,
415  	                                     PCMK_RESOURCE_CLASS_UPSTART,
416  	                                     PCMK_RESOURCE_CLASS_SYSTEMD,
417  	                                     PCMK_RESOURCE_CLASS_LSB, NULL)) {
418  	        out->err(out, "Invalid class for %s: %s", resource, rclass);
419  	        return NULL;
420  	
421  	    } else if (pcmk_is_set(pcmk_get_ra_caps(rclass), pcmk_ra_cap_provider)
422  	               && (rprovider == NULL)) {
423  	        // @TODO query configuration for provider
424  	        out->err(out, "Please specify the provider for resource %s", resource);
425  	        return NULL;
426  	    }
427  	
428  	    crm_info("Injecting new resource %s into node state '%s'",
429  	             lrm_name, ID(cib_node));
430  	
431  	    lrm = first_named_child(cib_node, XML_CIB_TAG_LRM);
432  	    if (lrm == NULL) {
433  	        const char *node_uuid = ID(cib_node);
434  	
435  	        lrm = create_xml_node(cib_node, XML_CIB_TAG_LRM);
436  	        crm_xml_add(lrm, XML_ATTR_ID, node_uuid);
437  	    }
438  	
439  	    container = first_named_child(lrm, XML_LRM_TAG_RESOURCES);
440  	    if (container == NULL) {
441  	        container = create_xml_node(lrm, XML_LRM_TAG_RESOURCES);
442  	    }
443  	
444  	    cib_resource = create_xml_node(container, XML_LRM_TAG_RESOURCE);
445  	
446  	    // If we're creating a new entry, use the preferred name
447  	    crm_xml_add(cib_resource, XML_ATTR_ID, lrm_name);
448  	
449  	    crm_xml_add(cib_resource, XML_AGENT_ATTR_CLASS, rclass);
450  	    crm_xml_add(cib_resource, XML_AGENT_ATTR_PROVIDER, rprovider);
451  	    crm_xml_add(cib_resource, XML_ATTR_TYPE, rtype);
452  	
453  	    return cib_resource;
454  	}
455  	
456  	static int
457  	find_ticket_state(pcmk__output_t *out, cib_t *the_cib, const char *ticket_id,
458  	                  xmlNode **ticket_state_xml)
459  	{
460  	    int rc = pcmk_ok;
461  	    xmlNode *xml_search = NULL;
462  	
463  	    GString *xpath = g_string_sized_new(256);
464  	
465  	    CRM_ASSERT(ticket_state_xml != NULL);
466  	    *ticket_state_xml = NULL;
467  	
468  	    g_string_append(xpath,
469  	                    "/" XML_TAG_CIB "/" XML_CIB_TAG_STATUS
470  	                    "/" XML_CIB_TAG_TICKETS);
471  	
472  	    if (ticket_id) {
473  	        pcmk__g_strcat(xpath,
474  	                       "/" XML_CIB_TAG_TICKET_STATE
475  	                       "[@" XML_ATTR_ID "=\"", ticket_id, "\"]", NULL);
476  	    }
477  	    rc = the_cib->cmds->query(the_cib, (const char *) xpath->str, &xml_search,
478  	                              cib_sync_call|cib_scope_local|cib_xpath);
479  	    g_string_free(xpath, TRUE);
480  	
481  	    if (rc != pcmk_ok) {
482  	        return rc;
483  	    }
484  	
485  	    crm_log_xml_debug(xml_search, "Match");
486  	    if ((xml_search->children != NULL) && (ticket_id != NULL)) {
487  	        out->err(out, "Multiple ticket_states match ticket_id=%s", ticket_id);
488  	    }
489  	    *ticket_state_xml = xml_search;
490  	
491  	    return rc;
492  	}
493  	
494  	/*!
495  	 * \internal
496  	 * \brief Inject a ticket attribute into ticket state
497  	 *
498  	 * \param[in,out] out          Output object for displaying error messages
499  	 * \param[in]     ticket_id    Ticket whose state should be changed
500  	 * \param[in]     attr_name    Ticket attribute name to inject
501  	 * \param[in]     attr_value   Boolean value of ticket attribute to inject
502  	 * \param[in,out] cib          CIB object to use
503  	 *
504  	 * \return Standard Pacemaker return code
505  	 */
506  	static int
507  	set_ticket_state_attr(pcmk__output_t *out, const char *ticket_id,
508  	                      const char *attr_name, bool attr_value, cib_t *cib)
509  	{
510  	    int rc = pcmk_rc_ok;
511  	    xmlNode *xml_top = NULL;
512  	    xmlNode *ticket_state_xml = NULL;
513  	
514  	    // Check for an existing ticket state entry
515  	    rc = find_ticket_state(out, cib, ticket_id, &ticket_state_xml);
516  	    rc = pcmk_legacy2rc(rc);
517  	
518  	    if (rc == pcmk_rc_ok) { // Ticket state found, use it
519  	        crm_debug("Injecting attribute into existing ticket state %s",
520  	                  ticket_id);
521  	        xml_top = ticket_state_xml;
522  	
523  	    } else if (rc == ENXIO) { // No ticket state, create it
524  	        xmlNode *xml_obj = NULL;
525  	
526  	        xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS);
527  	        xml_obj = create_xml_node(xml_top, XML_CIB_TAG_TICKETS);
528  	        ticket_state_xml = create_xml_node(xml_obj, XML_CIB_TAG_TICKET_STATE);
529  	        crm_xml_add(ticket_state_xml, XML_ATTR_ID, ticket_id);
530  	
531  	    } else { // Error
532  	        return rc;
533  	    }
534  	
535  	    // Add the attribute to the ticket state
536  	    pcmk__xe_set_bool_attr(ticket_state_xml, attr_name, attr_value);
537  	    crm_log_xml_debug(xml_top, "Update");
538  	
539  	    // Commit the change to the CIB
540  	    rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, xml_top,
541  	                           cib_sync_call|cib_scope_local);
542  	    rc = pcmk_legacy2rc(rc);
543  	
544  	    free_xml(xml_top);
545  	    return rc;
546  	}
547  	
548  	/*!
549  	 * \internal
550  	 * \brief Inject a fictitious action into the cluster
551  	 *
552  	 * \param[in,out] out       Output object for displaying error messages
553  	 * \param[in]     spec      Action specification to inject
554  	 * \param[in,out] cib       CIB object for scheduler input
555  	 * \param[in]     scheduler  Scheduler data
556  	 */
557  	static void
558  	inject_action(pcmk__output_t *out, const char *spec, cib_t *cib,
559  	              const pcmk_scheduler_t *scheduler)
560  	{
561  	    int rc;
562  	    int outcome = PCMK_OCF_OK;
563  	    guint interval_ms = 0;
564  	
565  	    char *key = NULL;
566  	    char *node = NULL;
567  	    char *task = NULL;
568  	    char *resource = NULL;
569  	
570  	    const char *rtype = NULL;
571  	    const char *rclass = NULL;
572  	    const char *rprovider = NULL;
573  	
574  	    xmlNode *cib_op = NULL;
575  	    xmlNode *cib_node = NULL;
576  	    xmlNode *cib_resource = NULL;
577  	    const pcmk_resource_t *rsc = NULL;
578  	    lrmd_event_data_t *op = NULL;
579  	
580  	    out->message(out, "inject-spec", spec);
581  	
582  	    key = calloc(1, strlen(spec) + 1);
583  	    node = calloc(1, strlen(spec) + 1);
584  	    rc = sscanf(spec, "%[^@]@%[^=]=%d", key, node, &outcome);
585  	    if (rc != 3) {
586  	        out->err(out, "Invalid operation spec: %s.  Only found %d fields",
587  	                 spec, rc);
588  	        goto done;
589  	    }
590  	
591  	    parse_op_key(key, &resource, &task, &interval_ms);
592  	
593  	    rsc = pe_find_resource(scheduler->resources, resource);
594  	    if (rsc == NULL) {
595  	        out->err(out, "Invalid resource name: %s", resource);
596  	        goto done;
597  	    }
598  	
599  	    rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
600  	    rtype = crm_element_value(rsc->xml, XML_ATTR_TYPE);
601  	    rprovider = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER);
602  	
603  	    cib_node = pcmk__inject_node(cib, node, NULL);
604  	    CRM_ASSERT(cib_node != NULL);
605  	
606  	    pcmk__inject_failcount(out, cib_node, resource, task, interval_ms, outcome);
607  	
608  	    cib_resource = pcmk__inject_resource_history(out, cib_node,
609  	                                                 resource, resource,
610  	                                                 rclass, rtype, rprovider);
611  	    CRM_ASSERT(cib_resource != NULL);
612  	
613  	    op = create_op(cib_resource, task, interval_ms, outcome);
614  	    CRM_ASSERT(op != NULL);
615  	
616  	    cib_op = pcmk__inject_action_result(cib_resource, op, 0);
617  	    CRM_ASSERT(cib_op != NULL);
618  	    lrmd_free_event(op);
619  	
620  	    rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, cib_node,
621  	                           cib_sync_call|cib_scope_local);
622  	    CRM_ASSERT(rc == pcmk_ok);
623  	
624  	done:
625  	    free(task);
626  	    free(node);
627  	    free(key);
628  	}
629  	
630  	/*!
631  	 * \internal
632  	 * \brief Inject fictitious scheduler inputs
633  	 *
634  	 * \param[in,out] scheduler   Scheduler data
635  	 * \param[in,out] cib         CIB object for scheduler input to modify
636  	 * \param[in]     injections  Injections to apply
637  	 */
638  	void
639  	pcmk__inject_scheduler_input(pcmk_scheduler_t *scheduler, cib_t *cib,
640  	                             const pcmk_injections_t *injections)
641  	{
642  	    int rc = pcmk_ok;
643  	    const GList *iter = NULL;
644  	    xmlNode *cib_node = NULL;
645  	    pcmk__output_t *out = scheduler->priv;
646  	
647  	    out->message(out, "inject-modify-config", injections->quorum,
648  	                 injections->watchdog);
649  	    if (injections->quorum != NULL) {
650  	        xmlNode *top = create_xml_node(NULL, XML_TAG_CIB);
651  	
652  	        /* crm_xml_add(top, XML_ATTR_DC_UUID, dc_uuid);      */
653  	        crm_xml_add(top, XML_ATTR_HAVE_QUORUM, injections->quorum);
654  	
655  	        rc = cib->cmds->modify(cib, NULL, top, cib_sync_call|cib_scope_local);
656  	        CRM_ASSERT(rc == pcmk_ok);
657  	    }
658  	
659  	    if (injections->watchdog != NULL) {
660  	        rc = cib__update_node_attr(out, cib, cib_sync_call|cib_scope_local,
661  	                                   XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL,
662  	                                   NULL, XML_ATTR_HAVE_WATCHDOG,
663  	                                   injections->watchdog, NULL, NULL);
664  	        CRM_ASSERT(rc == pcmk_rc_ok);
665  	    }
666  	
667  	    for (iter = injections->node_up; iter != NULL; iter = iter->next) {
668  	        const char *node = (const char *) iter->data;
669  	
670  	        out->message(out, "inject-modify-node", "Online", node);
671  	
672  	        cib_node = pcmk__inject_node_state_change(cib, node, true);
673  	        CRM_ASSERT(cib_node != NULL);
674  	
675  	        rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, cib_node,
676  	                               cib_sync_call|cib_scope_local);
677  	        CRM_ASSERT(rc == pcmk_ok);
678  	        free_xml(cib_node);
679  	    }
680  	
681  	    for (iter = injections->node_down; iter != NULL; iter = iter->next) {
682  	        const char *node = (const char *) iter->data;
683  	        char *xpath = NULL;
684  	
685  	        out->message(out, "inject-modify-node", "Offline", node);
686  	
687  	        cib_node = pcmk__inject_node_state_change(cib, node, false);
688  	        CRM_ASSERT(cib_node != NULL);
689  	
690  	        rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, cib_node,
691  	                               cib_sync_call|cib_scope_local);
692  	        CRM_ASSERT(rc == pcmk_ok);
693  	        free_xml(cib_node);
694  	
695  	        xpath = crm_strdup_printf("//node_state[@uname='%s']/%s",
696  	                                  node, XML_CIB_TAG_LRM);
697  	        cib->cmds->remove(cib, xpath, NULL,
698  	                          cib_xpath|cib_sync_call|cib_scope_local);
699  	        free(xpath);
700  	
701  	        xpath = crm_strdup_printf("//node_state[@uname='%s']/%s",
702  	                                  node, XML_TAG_TRANSIENT_NODEATTRS);
703  	        cib->cmds->remove(cib, xpath, NULL,
704  	                          cib_xpath|cib_sync_call|cib_scope_local);
705  	        free(xpath);
706  	    }
707  	
708  	    for (iter = injections->node_fail; iter != NULL; iter = iter->next) {
709  	        const char *node = (const char *) iter->data;
710  	
711  	        out->message(out, "inject-modify-node", "Failing", node);
712  	
713  	        cib_node = pcmk__inject_node_state_change(cib, node, true);
714  	        crm_xml_add(cib_node, PCMK__XA_IN_CCM, XML_BOOLEAN_NO);
715  	        CRM_ASSERT(cib_node != NULL);
716  	
717  	        rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, cib_node,
718  	                               cib_sync_call|cib_scope_local);
719  	        CRM_ASSERT(rc == pcmk_ok);
720  	        free_xml(cib_node);
721  	    }
722  	
723  	    for (iter = injections->ticket_grant; iter != NULL; iter = iter->next) {
724  	        const char *ticket_id = (const char *) iter->data;
725  	
726  	        out->message(out, "inject-modify-ticket", "Granting", ticket_id);
727  	
728  	        rc = set_ticket_state_attr(out, ticket_id, "granted", true, cib);
729  	        CRM_ASSERT(rc == pcmk_rc_ok);
730  	    }
731  	
732  	    for (iter = injections->ticket_revoke; iter != NULL; iter = iter->next) {
733  	        const char *ticket_id = (const char *) iter->data;
734  	
735  	        out->message(out, "inject-modify-ticket", "Revoking", ticket_id);
736  	
737  	        rc = set_ticket_state_attr(out, ticket_id, "granted", false, cib);
738  	        CRM_ASSERT(rc == pcmk_rc_ok);
739  	    }
740  	
741  	    for (iter = injections->ticket_standby; iter != NULL; iter = iter->next) {
742  	        const char *ticket_id = (const char *) iter->data;
743  	
744  	        out->message(out, "inject-modify-ticket", "Standby", ticket_id);
745  	
746  	        rc = set_ticket_state_attr(out, ticket_id, "standby", true, cib);
747  	        CRM_ASSERT(rc == pcmk_rc_ok);
748  	    }
749  	
750  	    for (iter = injections->ticket_activate; iter != NULL; iter = iter->next) {
751  	        const char *ticket_id = (const char *) iter->data;
752  	
753  	        out->message(out, "inject-modify-ticket", "Activating", ticket_id);
754  	
755  	        rc = set_ticket_state_attr(out, ticket_id, "standby", false, cib);
756  	        CRM_ASSERT(rc == pcmk_rc_ok);
757  	    }
758  	
759  	    for (iter = injections->op_inject; iter != NULL; iter = iter->next) {
760  	        inject_action(out, (const char *) iter->data, cib, scheduler);
761  	    }
762  	
763  	    if (!out->is_quiet(out)) {
764  	        out->end_list(out);
765  	    }
766  	}
767  	
768  	void
769  	pcmk_free_injections(pcmk_injections_t *injections)
770  	{
771  	    if (injections == NULL) {
772  	        return;
773  	    }
774  	
775  	    g_list_free_full(injections->node_up, g_free);
776  	    g_list_free_full(injections->node_down, g_free);
777  	    g_list_free_full(injections->node_fail, g_free);
778  	    g_list_free_full(injections->op_fail, g_free);
779  	    g_list_free_full(injections->op_inject, g_free);
780  	    g_list_free_full(injections->ticket_grant, g_free);
781  	    g_list_free_full(injections->ticket_revoke, g_free);
782  	    g_list_free_full(injections->ticket_standby, g_free);
783  	    g_list_free_full(injections->ticket_activate, g_free);
784  	    free(injections->quorum);
785  	    free(injections->watchdog);
786  	
787  	    free(injections);
788  	}
789