blob: b105a58d034f3217931471c4614efe8b49c81e8e [file] [log] [blame] [edit]
/*
* Copyright (c) 2015 David Vossel <davidvossel@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <crm_internal.h>
#include <glib.h>
#include <unistd.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/services.h>
#include <crm/common/mainloop.h>
#include <crm/pengine/status.h>
#include <crm/cib.h>
#include <crm/lrmd.h>
extern GHashTable *proxy_table;
void lrmd_internal_set_proxy_callback(lrmd_t * lrmd, void *userdata, void (*callback)(lrmd_t *lrmd, void *userdata, xmlNode *msg));
/* *INDENT-OFF* */
static struct crm_option long_options[] = {
{"help", 0, 0, '?'},
{"verbose", 0, 0, 'V', "\t\tPrint out logs and events to screen"},
{"quiet", 0, 0, 'Q', "\t\tSuppress all output to screen"},
{"tls", 1, 0, 'S', "\t\tSet tls host to contact"},
{"tls-port", 1, 0, 'p', "\t\tUse custom tls port"},
{"node", 1, 0, 'n', "\tNode name to use for ipc proxy"},
{"api-call", 1, 0, 'c', "\tDirectly relates to lrmd api functions"},
{"-spacer-", 1, 0, '-', "\nParameters for api-call option"},
{"action", 1, 0, 'a'},
{"rsc-id", 1, 0, 'r'},
{"provider", 1, 0, 'P'},
{"class", 1, 0, 'C'},
{"type", 1, 0, 'T'},
{"timeout", 1, 0, 't'},
{"param-key", 1, 0, 'k'},
{"param-val", 1, 0, 'v'},
{"-spacer-", 1, 0, '-'},
{0, 0, 0, 0}
};
/* *INDENT-ON* */
static int wait_poke = 0;
static int exec_call_id = 0;
static gboolean client_start(gpointer user_data);
static void try_connect(void);
static struct {
int verbose;
int quiet;
int print;
int interval;
int timeout;
int port;
const char *node_name;
const char *api_call;
const char *rsc_id;
const char *provider;
const char *class;
const char *type;
const char *action;
const char *listen;
const char *tls_host;
lrmd_key_value_t *params;
} options;
GMainLoop *mainloop = NULL;
lrmd_t *lrmd_conn = NULL;
static void
client_exit(int rc)
{
lrmd_api_delete(lrmd_conn);
if (proxy_table) {
g_hash_table_destroy(proxy_table); proxy_table = NULL;
}
exit(rc);
}
static void
client_shutdown(int nsig)
{
lrmd_api_delete(lrmd_conn);
lrmd_conn = NULL;
}
static void
read_events(lrmd_event_data_t * event)
{
if (wait_poke && event->type == lrmd_event_poke) {
client_exit(PCMK_OCF_OK);
}
if ((event->call_id == exec_call_id) && (event->type == lrmd_event_exec_complete)) {
if (event->output) {
crm_info("%s", event->output);
}
if (event->exit_reason) {
fprintf(stderr, "%s%s\n", PCMK_OCF_REASON_PREFIX, event->exit_reason);
}
client_exit(event->rc);
}
}
static gboolean
timeout_err(gpointer data)
{
crm_err("timed out in remote_client");
client_exit(PCMK_OCF_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) {
client_start(NULL);
return;
} else {
sleep(1);
try_connect();
}
}
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, "lrmd", 10000);
if (!rc) {
num_tries++;
return; /* we'll hear back in async callback */
}
sleep(1);
}
crm_err("Failed to connect to pacemaker remote.");
client_exit(PCMK_OCF_UNKNOWN_ERROR);
}
static gboolean
client_start(gpointer user_data)
{
int rc = 0;
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 (safe_str_eq(options.api_call, "ipc_debug")) {
/* Do nothing, leave connection up just for debugging ipc proxy */
return 0;
}
if (options.timeout) {
g_timeout_add(options.timeout, timeout_err, NULL);
}
if (safe_str_eq(options.api_call, "metadata")) {
char *output = NULL;
/* This is broken when the agent is installed only in the
* remote environment and not on the cluster node handling the proxy.
*/
rc = lrmd_conn->cmds->get_metadata(lrmd_conn,
options.class,
options.provider, options.type, &output, 0);
if (rc == pcmk_ok) {
printf("%s", output);
free(output);
client_exit(PCMK_OCF_OK);
}
client_exit(PCMK_OCF_UNKNOWN_ERROR);
} else if (safe_str_eq(options.api_call, "poke")) {
rc = lrmd_conn->cmds->poke_connection(lrmd_conn);
if (rc != pcmk_ok) {
client_exit(PCMK_OCF_UNKNOWN_ERROR);
}
wait_poke = 1;
} else {
lrmd_rsc_info_t *rsc_info = NULL;
rsc_info = lrmd_conn->cmds->get_rsc_info(lrmd_conn, options.rsc_id, 0);
if (rsc_info == NULL) {
rc = lrmd_conn->cmds->register_rsc(lrmd_conn, options.rsc_id,
options.class, options.provider, options.type, 0);
if (rc != 0){
crm_err("failed to register resource %s with pacemaker_remote. rc: %d", options.rsc_id, rc);
client_exit(1);
}
}
lrmd_free_rsc_info(rsc_info);
rc = lrmd_conn->cmds->exec(lrmd_conn,
options.rsc_id,
options.action,
NULL,
options.interval,
options.timeout,
0, 0, options.params);
if (rc > 0) {
exec_call_id = rc;
} else {
crm_err("execution of rsc %s failed. rc = %d", options.rsc_id, rc);
client_exit(PCMK_OCF_UNKNOWN_ERROR);
}
}
return 0;
}
static void
ctl_remote_proxy_cb(lrmd_t *lrmd, void *userdata, xmlNode *msg)
{
const char *op = crm_element_value(msg, F_LRMD_IPC_OP);
const char *session = crm_element_value(msg, F_LRMD_IPC_SESSION);
if (safe_str_eq(op, LRMD_IPC_OP_NEW)) {
const char *channel = crm_element_value(msg, F_LRMD_IPC_IPC_SERVER);
static struct ipc_client_callbacks proxy_callbacks = {
.dispatch = remote_proxy_dispatch,
.destroy = remote_proxy_disconnected
};
remote_proxy_new(lrmd, &proxy_callbacks, options.node_name, session, channel);
} else {
remote_proxy_cb(lrmd, options.node_name, msg);
}
}
int
main(int argc, char **argv)
{
int option_index = 0;
int argerr = 0;
int flag;
char *key = NULL;
char *val = NULL;
gboolean use_tls = FALSE;
crm_trigger_t *trig;
crm_set_options(NULL, "mode [options]", long_options,
"Inject commands into the lrmd and watch for events\n");
while (1) {
flag = crm_get_option(argc, argv, &option_index);
if (flag == -1)
break;
switch (flag) {
case '?':
crm_help(flag, EX_OK);
break;
case 'V':
options.verbose = 1;
break;
case 'Q':
options.quiet = 1;
options.verbose = 0;
break;
case 'n':
options.node_name = optarg;
break;
case 'c':
options.api_call = optarg;
break;
case 'a':
options.action = optarg;
break;
case 'r':
options.rsc_id = optarg;
break;
case 'P':
options.provider = optarg;
break;
case 'C':
options.class = optarg;
break;
case 'T':
options.type = optarg;
break;
case 't':
if(optarg) {
options.timeout = atoi(optarg);
}
break;
case 'k':
key = optarg;
if (key && val) {
options.params = lrmd_key_value_add(options.params, key, val);
key = val = NULL;
}
break;
case 'v':
val = optarg;
if (key && val) {
options.params = lrmd_key_value_add(options.params, key, val);
key = val = NULL;
}
break;
case 'S':
options.tls_host = optarg;
use_tls = TRUE;
break;
case 'p':
if(optarg) {
options.port = atoi(optarg);
}
use_tls = TRUE;
break;
default:
++argerr;
break;
}
}
if (argerr) {
crm_help('?', EX_USAGE);
}
if (optind > argc) {
++argerr;
}
crm_log_init("remote_client", LOG_INFO, FALSE, options.verbose ? TRUE : FALSE, argc, argv, FALSE);
/* if we can't perform an api_call or listen for events,
* there is nothing to do */
if (!options.api_call ) {
crm_err("Nothing to be done. Please specify 'api-call'");
return PCMK_OCF_UNKNOWN_ERROR;
}
if (!options.timeout ) {
options.timeout = 20000;
}
if (use_tls) {
if (options.node_name == NULL) {
crm_err("\"node\" option required when tls is in use.");
return PCMK_OCF_UNKNOWN_ERROR;
}
proxy_table =
g_hash_table_new_full(crm_strcase_hash, crm_strcase_equal, NULL, remote_proxy_free);
lrmd_conn = lrmd_remote_api_new(NULL, options.tls_host ? options.tls_host : "localhost", options.port);
lrmd_internal_set_proxy_callback(lrmd_conn, NULL, ctl_remote_proxy_cb);
} else {
lrmd_conn = lrmd_api_new();
}
trig = mainloop_add_trigger(G_PRIORITY_HIGH, client_start, NULL);
mainloop_set_trigger(trig);
mainloop_add_signal(SIGTERM, client_shutdown);
mainloop = g_main_new(FALSE);
g_main_run(mainloop);
client_exit(0);
return 0;
}