Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F2822651
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
20 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment