Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/daemons/execd/cts-exec-helper.c b/daemons/execd/cts-exec-helper.c
index a74f5993a6..9cd7b056dd 100644
--- a/daemons/execd/cts-exec-helper.c
+++ b/daemons/execd/cts-exec-helper.c
@@ -1,626 +1,631 @@
/*
* Copyright 2012-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <glib.h>
#include <unistd.h>
#include <crm/crm.h>
#include <crm/services.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/mainloop.h>
#include <crm/pengine/status.h>
#include <crm/pengine/internal.h>
#include <crm/cib.h>
#include <crm/cib/internal.h>
#include <crm/lrmd.h>
#define SUMMARY "cts-exec-helper - inject commands into the Pacemaker executor and watch for events"
static int exec_call_id = 0;
static gboolean start_test(gpointer user_data);
static void try_connect(void);
static char *key = NULL;
static char *val = NULL;
static struct {
int verbose;
int quiet;
guint interval_ms;
int timeout;
int start_delay;
int cancel_call_id;
gboolean no_wait;
gboolean is_running;
gboolean no_connect;
int exec_call_opts;
const char *api_call;
const char *rsc_id;
const char *provider;
const char *class;
const char *type;
const char *action;
const char *listen;
gboolean use_tls;
lrmd_key_value_t *params;
} options;
static gboolean
interval_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
return pcmk_parse_interval_spec(optarg,
&options.interval_ms) == pcmk_rc_ok;
}
static gboolean
notify_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
if (pcmk__str_any_of(option_name, "--notify-orig", "-n", NULL)) {
options.exec_call_opts = lrmd_opt_notify_orig_only;
} else if (pcmk__str_any_of(option_name, "--notify-changes", "-o", NULL)) {
options.exec_call_opts = lrmd_opt_notify_changes_only;
}
return TRUE;
}
static gboolean
param_key_val_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
if (pcmk__str_any_of(option_name, "--param-key", "-k", NULL)) {
pcmk__str_update(&key, optarg);
} else if (pcmk__str_any_of(option_name, "--param-val", "-v", NULL)) {
pcmk__str_update(&val, optarg);
}
if (key != NULL && val != NULL) {
options.params = lrmd_key_value_add(options.params, key, val);
pcmk__str_update(&key, NULL);
pcmk__str_update(&val, NULL);
}
return TRUE;
}
static GOptionEntry basic_entries[] = {
{ "api-call", 'c', 0, G_OPTION_ARG_STRING, &options.api_call,
"Directly relates to executor API functions",
NULL },
{ "is-running", 'R', 0, G_OPTION_ARG_NONE, &options.is_running,
"Determine if a resource is registered and running",
NULL },
{ "listen", 'l', 0, G_OPTION_ARG_STRING, &options.listen,
"Listen for a specific event string",
NULL },
{ "no-wait", 'w', 0, G_OPTION_ARG_NONE, &options.no_wait,
"Make api call and do not wait for result",
NULL },
{ "notify-changes", 'o', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, notify_cb,
"Only notify client changes to recurring operations",
NULL },
{ "notify-orig", 'n', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, notify_cb,
"Only notify this client of the results of an API action",
NULL },
{ "tls", 'S', 0, G_OPTION_ARG_NONE, &options.use_tls,
"Use TLS backend for local connection",
NULL },
{ NULL }
};
static GOptionEntry api_call_entries[] = {
{ "action", 'a', 0, G_OPTION_ARG_STRING, &options.action,
NULL, NULL },
{ "cancel-call-id", 'x', 0, G_OPTION_ARG_INT, &options.cancel_call_id,
NULL, NULL },
{ "class", 'C', 0, G_OPTION_ARG_STRING, &options.class,
NULL, NULL },
{ "interval", 'i', 0, G_OPTION_ARG_CALLBACK, interval_cb,
NULL, NULL },
{ "param-key", 'k', 0, G_OPTION_ARG_CALLBACK, param_key_val_cb,
NULL, NULL },
{ "param-val", 'v', 0, G_OPTION_ARG_CALLBACK, param_key_val_cb,
NULL, NULL },
{ "provider", 'P', 0, G_OPTION_ARG_STRING, &options.provider,
NULL, NULL },
{ "rsc-id", 'r', 0, G_OPTION_ARG_STRING, &options.rsc_id,
NULL, NULL },
{ "start-delay", 's', 0, G_OPTION_ARG_INT, &options.start_delay,
NULL, NULL },
{ "timeout", 't', 0, G_OPTION_ARG_INT, &options.timeout,
NULL, NULL },
{ "type", 'T', 0, G_OPTION_ARG_STRING, &options.type,
NULL, NULL },
{ NULL }
};
static GMainLoop *mainloop = NULL;
static lrmd_t *lrmd_conn = NULL;
static char event_buf_v0[1024];
static crm_exit_t
test_exit(crm_exit_t exit_code)
{
lrmd_api_delete(lrmd_conn);
return crm_exit(exit_code);
}
#define print_result(fmt, args...) \
if (!options.quiet) { \
printf(fmt "\n" , ##args); \
}
#define report_event(event) \
snprintf(event_buf_v0, sizeof(event_buf_v0), "NEW_EVENT event_type:%s rsc_id:%s action:%s rc:%s op_status:%s", \
lrmd_event_type2str(event->type), \
event->rsc_id, \
event->op_type ? event->op_type : "none", \
crm_exit_str((crm_exit_t) event->rc), \
pcmk_exec_status_str(event->op_status)); \
crm_info("%s", event_buf_v0);
static void
test_shutdown(int nsig)
{
lrmd_api_delete(lrmd_conn);
lrmd_conn = NULL;
}
static void
read_events(lrmd_event_data_t * event)
{
report_event(event);
if (options.listen) {
if (pcmk__str_eq(options.listen, event_buf_v0, pcmk__str_casei)) {
print_result("LISTEN EVENT SUCCESSFUL");
test_exit(CRM_EX_OK);
}
}
if (exec_call_id && (event->call_id == exec_call_id)) {
if (event->op_status == 0 && event->rc == 0) {
print_result("API-CALL SUCCESSFUL for 'exec'");
} else {
print_result("API-CALL FAILURE for 'exec', rc:%d lrmd_op_status:%s",
event->rc, pcmk_exec_status_str(event->op_status));
test_exit(CRM_EX_ERROR);
}
if (!options.listen) {
test_exit(CRM_EX_OK);
}
}
}
static gboolean
timeout_err(gpointer data)
{
print_result("LISTEN EVENT FAILURE - timeout occurred, never found");
test_exit(CRM_EX_TIMEOUT);
return FALSE;
}
static void
connection_events(lrmd_event_data_t * event)
{
int rc = event->connection_rc;
if (event->type != lrmd_event_connect) {
/* ignore */
return;
}
if (!rc) {
crm_info("Executor client connection established");
start_test(NULL);
return;
} else {
sleep(1);
try_connect();
crm_notice("Executor client connection failed");
}
}
static void
try_connect(void)
{
int tries = 10;
static int num_tries = 0;
int rc = 0;
lrmd_conn->cmds->set_callback(lrmd_conn, connection_events);
for (; num_tries < tries; num_tries++) {
rc = lrmd_conn->cmds->connect_async(lrmd_conn, crm_system_name, 3000);
if (!rc) {
return; /* we'll hear back in async callback */
}
sleep(1);
}
print_result("API CONNECTION FAILURE");
test_exit(CRM_EX_ERROR);
}
static gboolean
start_test(gpointer user_data)
{
int rc = 0;
if (!options.no_connect) {
if (!lrmd_conn->cmds->is_connected(lrmd_conn)) {
try_connect();
/* async connect -- this function will get called back into */
return 0;
}
}
lrmd_conn->cmds->set_callback(lrmd_conn, read_events);
if (options.timeout) {
pcmk__create_timer(options.timeout, timeout_err, NULL);
}
if (!options.api_call) {
return 0;
}
if (pcmk__str_eq(options.api_call, "exec", pcmk__str_casei)) {
rc = lrmd_conn->cmds->exec(lrmd_conn,
options.rsc_id,
options.action,
NULL,
options.interval_ms,
options.timeout,
options.start_delay,
options.exec_call_opts,
options.params);
if (rc > 0) {
exec_call_id = rc;
print_result("API-CALL 'exec' action pending, waiting on response");
}
} else if (pcmk__str_eq(options.api_call, "register_rsc", pcmk__str_casei)) {
rc = lrmd_conn->cmds->register_rsc(lrmd_conn,
options.rsc_id,
options.class, options.provider, options.type, 0);
} else if (pcmk__str_eq(options.api_call, "get_rsc_info", pcmk__str_casei)) {
lrmd_rsc_info_t *rsc_info;
rsc_info = lrmd_conn->cmds->get_rsc_info(lrmd_conn, options.rsc_id, 0);
if (rsc_info) {
print_result("RSC_INFO: id:%s class:%s provider:%s type:%s",
rsc_info->id, rsc_info->standard,
(rsc_info->provider? rsc_info->provider : "<none>"),
rsc_info->type);
lrmd_free_rsc_info(rsc_info);
rc = pcmk_ok;
} else {
rc = -1;
}
} else if (pcmk__str_eq(options.api_call, "unregister_rsc", pcmk__str_casei)) {
rc = lrmd_conn->cmds->unregister_rsc(lrmd_conn, options.rsc_id, 0);
} else if (pcmk__str_eq(options.api_call, "cancel", pcmk__str_casei)) {
rc = lrmd_conn->cmds->cancel(lrmd_conn, options.rsc_id, options.action,
options.interval_ms);
} else if (pcmk__str_eq(options.api_call, "metadata", pcmk__str_casei)) {
char *output = NULL;
rc = lrmd_conn->cmds->get_metadata(lrmd_conn,
options.class,
options.provider, options.type, &output, 0);
if (rc == pcmk_ok) {
print_result("%s", output);
free(output);
}
} else if (pcmk__str_eq(options.api_call, "list_agents", pcmk__str_casei)) {
lrmd_list_t *list = NULL;
lrmd_list_t *iter = NULL;
rc = lrmd_conn->cmds->list_agents(lrmd_conn, &list, options.class, options.provider);
if (rc > 0) {
print_result("%d agents found", rc);
for (iter = list; iter != NULL; iter = iter->next) {
print_result("%s", iter->val);
}
lrmd_list_freeall(list);
rc = 0;
} else {
print_result("API_CALL FAILURE - no agents found");
rc = -1;
}
} else if (pcmk__str_eq(options.api_call, "list_ocf_providers", pcmk__str_casei)) {
lrmd_list_t *list = NULL;
lrmd_list_t *iter = NULL;
rc = lrmd_conn->cmds->list_ocf_providers(lrmd_conn, options.type, &list);
if (rc > 0) {
print_result("%d providers found", rc);
for (iter = list; iter != NULL; iter = iter->next) {
print_result("%s", iter->val);
}
lrmd_list_freeall(list);
rc = 0;
} else {
print_result("API_CALL FAILURE - no providers found");
rc = -1;
}
} else if (pcmk__str_eq(options.api_call, "list_standards", pcmk__str_casei)) {
lrmd_list_t *list = NULL;
lrmd_list_t *iter = NULL;
rc = lrmd_conn->cmds->list_standards(lrmd_conn, &list);
if (rc > 0) {
print_result("%d standards found", rc);
for (iter = list; iter != NULL; iter = iter->next) {
print_result("%s", iter->val);
}
lrmd_list_freeall(list);
rc = 0;
} else {
print_result("API_CALL FAILURE - no providers found");
rc = -1;
}
} else if (pcmk__str_eq(options.api_call, "get_recurring_ops", pcmk__str_casei)) {
GList *op_list = NULL;
GList *op_item = NULL;
rc = lrmd_conn->cmds->get_recurring_ops(lrmd_conn, options.rsc_id, 0, 0,
&op_list);
for (op_item = op_list; op_item != NULL; op_item = op_item->next) {
lrmd_op_info_t *op_info = op_item->data;
print_result("RECURRING_OP: %s_%s_%s timeout=%sms",
op_info->rsc_id, op_info->action,
op_info->interval_ms_s, op_info->timeout_ms_s);
lrmd_free_op_info(op_info);
}
g_list_free(op_list);
} else if (options.api_call) {
print_result("API-CALL FAILURE unknown action '%s'", options.action);
test_exit(CRM_EX_ERROR);
}
if (rc < 0) {
print_result("API-CALL FAILURE for '%s' api_rc:%d",
options.api_call, rc);
test_exit(CRM_EX_ERROR);
}
if (options.api_call && rc == pcmk_ok) {
print_result("API-CALL SUCCESSFUL for '%s'", options.api_call);
if (!options.listen) {
test_exit(CRM_EX_OK);
}
}
if (options.no_wait) {
/* just make the call and exit regardless of anything else. */
test_exit(CRM_EX_OK);
}
return 0;
}
/*!
* \internal
* \brief Generate resource parameters from CIB if none explicitly given
*
* \return Standard Pacemaker return code
*/
static int
generate_params(void)
{
int rc = pcmk_rc_ok;
pcmk_scheduler_t *scheduler = NULL;
xmlNode *cib_xml_copy = NULL;
pcmk_resource_t *rsc = NULL;
GHashTable *params = NULL;
GHashTable *meta = NULL;
GHashTableIter iter;
char *key = NULL;
char *value = NULL;
if (options.params != NULL) {
return pcmk_rc_ok; // User specified parameters explicitly
}
// Retrieve and update CIB
rc = cib__signon_query(NULL, NULL, &cib_xml_copy);
if (rc != pcmk_rc_ok) {
return rc;
}
rc = pcmk__update_configured_schema(&cib_xml_copy, false);
if (rc != pcmk_rc_ok) {
return rc;
}
// Calculate cluster status
scheduler = pe_new_working_set();
if (scheduler == NULL) {
crm_crit("Could not allocate scheduler data");
return ENOMEM;
}
pcmk__set_scheduler_flags(scheduler, pcmk__sched_no_counts);
scheduler->input = cib_xml_copy;
scheduler->priv->now = crm_time_new(NULL);
- cluster_status(scheduler);
+
+ if (!cluster_status(scheduler)) {
+ /* cluster_status -> pcmk__config_err will already log an error */
+ pe_free_working_set(scheduler);
+ return pcmk_rc_schema_validation;
+ }
// Find resource in CIB
rsc = pe_find_resource_with_flags(scheduler->priv->resources,
options.rsc_id,
pcmk_rsc_match_history
|pcmk_rsc_match_basename);
if (rsc == NULL) {
crm_err("Resource does not exist in config");
pe_free_working_set(scheduler);
return EINVAL;
}
// Add resource instance parameters to options.params
params = pe_rsc_params(rsc, NULL, scheduler);
if (params != NULL) {
g_hash_table_iter_init(&iter, params);
while (g_hash_table_iter_next(&iter, (gpointer *) &key,
(gpointer *) &value)) {
options.params = lrmd_key_value_add(options.params, key, value);
}
}
// Add resource meta-attributes to options.params
meta = pcmk__strkey_table(free, free);
get_meta_attributes(meta, rsc, NULL, scheduler);
g_hash_table_iter_init(&iter, meta);
while (g_hash_table_iter_next(&iter, (gpointer *) &key,
(gpointer *) &value)) {
char *crm_name = crm_meta_name(key);
options.params = lrmd_key_value_add(options.params, crm_name, value);
free(crm_name);
}
g_hash_table_destroy(meta);
pe_free_working_set(scheduler);
return rc;
}
static GOptionContext *
build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
GOptionContext *context = NULL;
context = pcmk__build_arg_context(args, NULL, group, NULL);
pcmk__add_main_args(context, basic_entries);
pcmk__add_arg_group(context, "api-call", "API Call Options:",
"Parameters for api-call option", api_call_entries);
return context;
}
int
main(int argc, char **argv)
{
GError *error = NULL;
crm_exit_t exit_code = CRM_EX_OK;
crm_trigger_t *trig = NULL;
pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
/* Typically we'd pass all the single character options that take an argument
* as the second parameter here (and there's a bunch of those in this tool).
* However, we control how this program is called so we can just not call it
* in a way where the preprocessing ever matters.
*/
gchar **processed_args = pcmk__cmdline_preproc(argv, NULL);
GOptionContext *context = build_arg_context(args, NULL);
if (!g_option_context_parse_strv(context, &processed_args, &error)) {
exit_code = CRM_EX_USAGE;
goto done;
}
/* We have to use crm_log_init here to set up the logging because there's
* different handling for daemons vs. command line programs, and
* pcmk__cli_init_logging is set up to only handle the latter.
*/
crm_log_init(NULL, LOG_INFO, TRUE, (args->verbosity? TRUE : FALSE), argc,
argv, FALSE);
for (int i = 0; i < args->verbosity; i++) {
crm_bump_log_level(argc, argv);
}
if (!options.listen && pcmk__strcase_any_of(options.api_call, "metadata", "list_agents",
"list_standards", "list_ocf_providers", NULL)) {
options.no_connect = TRUE;
}
if (options.is_running) {
int rc = pcmk_rc_ok;
if (options.rsc_id == NULL) {
exit_code = CRM_EX_USAGE;
g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
"--is-running requires --rsc-id");
goto done;
}
options.interval_ms = 0;
if (options.timeout == 0) {
options.timeout = 30000;
}
rc = generate_params();
if (rc != pcmk_rc_ok) {
exit_code = pcmk_rc2exitc(rc);
g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
"Can not determine resource status: "
"unable to get parameters from CIB");
goto done;
}
options.api_call = "exec";
options.action = PCMK_ACTION_MONITOR;
options.exec_call_opts = lrmd_opt_notify_orig_only;
}
if (!options.api_call && !options.listen) {
exit_code = CRM_EX_USAGE;
g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
"Must specify at least one of --api-call, --listen, "
"or --is-running");
goto done;
}
if (options.use_tls) {
lrmd_conn = lrmd_remote_api_new(NULL, "localhost", 0);
} else {
lrmd_conn = lrmd_api_new();
}
trig = mainloop_add_trigger(G_PRIORITY_HIGH, start_test, NULL);
mainloop_set_trigger(trig);
mainloop_add_signal(SIGTERM, test_shutdown);
crm_info("Starting");
mainloop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(mainloop);
done:
g_strfreev(processed_args);
pcmk__free_arg_context(context);
free(key);
free(val);
pcmk__output_and_clear_error(&error, NULL);
return test_exit(exit_code);
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Jan 25, 6:20 AM (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1321499
Default Alt Text
(20 KB)

Event Timeline