Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

powerman: support new --diag option #163

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions man/powerman.1.in
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ Retry connect to server up to N times with a 100ms delay after each failure.
.I "-d, --device"
Displays device status information for the device(s) that control the targets,
if specified, or all devices if not.
.Tp
.I "-D, --diag"
Display diagnostic errors for power control/query failures. Support dependent
on device support and not available on many devices.
.SH "TARGET SPECIFICATION"
.B powerman
target hostnames may be specified as comma separated or space separated
Expand Down
6 changes: 6 additions & 0 deletions man/powerman.dev.5.in
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,12 @@ the second argument, result in the plug state being set to off or on.
Yes we are applying regexes to regmatches!
If no off or on strings are provided, state will be unknown.
.TP
.I "setresult <regmatch> <regmatch> success=<string>"
Set the result state. The first argument, a <regmatch> from the
previous expect which contains the plug name. The success string is
a compiled regex, which if it matches the second argument, results in
the power operation being successful for this plug.
.TP
.I "ifoff, ifon"
Script statements enclosed in an ifon/ifoff block are conditional
executed based on the state of the plug passed in as an argument.
Expand Down
2 changes: 2 additions & 0 deletions src/powerman/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ AM_CFLAGS = @WARNING_CFLAGS@

AM_CPPFLAGS = \
-I$(top_srcdir)/src/liblsd \
-I$(top_srcdir)/src/libczmq \
-I$(top_srcdir)/src/libcommon

bin_PROGRAMS = \
Expand Down Expand Up @@ -59,6 +60,7 @@ powermand_SOURCES = \

powermand_LDADD = \
$(top_builddir)/src/liblsd/liblsd.la \
$(top_builddir)/src/libczmq/libczmq.la \
$(top_builddir)/src/libcommon/libcommon.la \
$(LIBWRAP)

Expand Down
5 changes: 5 additions & 0 deletions src/powerman/arglist.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@
#define PM_ARGLIST_H

typedef enum { ST_UNKNOWN, ST_OFF, ST_ON } InterpState;
/* result not required to be set after power operation,
* result defaults to RT_NONE in that case.
*/
typedef enum { RT_NONE, RT_UNKNOWN, RT_SUCCESS } InterpResult;

typedef struct {
char *node; /* node name (in) */
char *val; /* value as returned by the device (out) */
InterpState state; /* interpreted value, if appropriate (out) */
InterpResult result; /* interpreted result, if appropriate (out) */
} Arg;

typedef struct arglist_iterator *ArgListIterator;
Expand Down
117 changes: 113 additions & 4 deletions src/powerman/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include "device_private.h"
#include "fdutil.h"
#include "powerman.h"
#include "czmq.h"

#ifndef HAVE_SOCKLEN_T
typedef int socklen_t; /* socklen_t is uint32_t in Posix.1g */
Expand Down Expand Up @@ -80,6 +81,7 @@ typedef struct {
int client_id; /* client identifier */
bool telemetry; /* client wants telemetry debugging info */
bool exprange; /* client wants host ranges expanded */
bool diag; /* client wants extra node error diagnostics */
bool client_quit; /* set true after client quit command */
} Client;

Expand Down Expand Up @@ -363,13 +365,99 @@ static void _client_query_device_reply(Client * c, char *arg)
_client_printf(c, CP_RSP_QRY_COMPLETE);
}

/* zhashx_destructor_fn */
void hostlist_destroy_wrapper(void **data)
{
if (data) {
hostlist_t hl = *data;
hostlist_destroy(hl);
}
}

static void store_diag_msg(zhashx_t *diagmsgs, Arg *arg)
{
hostlist_t hl;
if (!(hl = zhashx_lookup(diagmsgs, arg->val))) {
if (!(hl = hostlist_create(NULL)))
err_exit(false, "hostlist_create");
zhashx_insert(diagmsgs, arg->val, hl);
}
hostlist_push(hl, arg->node);
}

static void return_diags(Client *c, zhashx_t *diagmsgs)
{
zlistx_t *keys = zhashx_keys(diagmsgs);
char *str;
if (!keys)
err_exit(false, "zhashx_keys");
str = zlistx_first(keys);
while (str) {
char *hosts;
char strbuf[1024 + 1] = {0};
hostlist_t hl = zhashx_lookup(diagmsgs, str);
assert(hl);
hosts = _xhostlist_ranged_string(hl);
snprintf(strbuf, 1024, "%s", str);
/* remove trailing carriage return or newline */
strbuf[strcspn(strbuf, "\r\n")] = '\0';
_client_printf(c, CP_INFO_DIAG, hosts, strbuf);
str = zlistx_next(keys);
xfree(hosts);
}
zlistx_destroy(&keys);
}

/*
* Reply to client power command (on/off/cycle/reset/beacon on/beacon off)
*/
static void _client_power_status_reply(Client * c, bool error)
{
Arg *arg;
ArgListIterator itr;
int error_found = 0;
zhashx_t *diagmsgs = NULL;

assert(c->cmd != NULL);

/* N.B. if result is RT_NONE, device script does not
* specify setresult interpretation.
*/
itr = arglist_iterator_create(c->cmd->arglist);
while ((arg = arglist_next(itr))) {
if (arg->result == RT_UNKNOWN) {
error_found++;
if (c->diag) {
if (!diagmsgs) {
if (!(diagmsgs = zhashx_new()))
err_exit(false, "zhashx_new");
zhashx_set_destructor(diagmsgs, hostlist_destroy_wrapper);
}
store_diag_msg(diagmsgs, arg);
}
}
}
arglist_iterator_destroy(itr);

if (c->diag && diagmsgs)
return_diags(c, diagmsgs);

if (c->cmd->error || error_found)
_client_printf(c, CP_ERR_COM_COMPLETE);
else
_client_printf(c, CP_RSP_COM_COMPLETE);

zhashx_destroy(&diagmsgs);
}

/*
* Reply to client request for plug/soft status.
*/
static void _client_query_status_reply(Client * c, bool error)
{
Arg *arg;
ArgListIterator itr;
zhashx_t *diagmsgs = NULL;

assert(c->cmd != NULL);

Expand All @@ -379,6 +467,16 @@ static void _client_query_status_reply(Client * c, bool error)
_client_printf(c, CP_INFO_XSTATUS, arg->node,
arg->state == ST_ON ? "on"
: arg->state == ST_OFF ? "off" : "unknown");
if (arg->state == ST_UNKNOWN) {
if (c->diag) {
if (!diagmsgs) {
if (!(diagmsgs = zhashx_new()))
err_exit(false, "zhashx_new");
zhashx_set_destructor(diagmsgs, hostlist_destroy_wrapper);
}
store_diag_msg(diagmsgs, arg);
}
}
}
arglist_iterator_destroy(itr);

Expand All @@ -395,6 +493,14 @@ static void _client_query_status_reply(Client * c, bool error)
switch (arg->state) {
case ST_UNKNOWN:
hostlist_push(hl_unknown, arg->node);
if (c->diag) {
if (!diagmsgs) {
if (!(diagmsgs = zhashx_new()))
err_exit(false, "zhashx_new");
zhashx_set_destructor(diagmsgs, hostlist_destroy_wrapper);
}
store_diag_msg(diagmsgs, arg);
}
break;
case ST_ON:
hostlist_push(hl_on, arg->node);
Expand All @@ -406,6 +512,9 @@ static void _client_query_status_reply(Client * c, bool error)
}
arglist_iterator_destroy(itr);

if (c->diag && diagmsgs)
return_diags(c, diagmsgs);

hostlist_sort(hl_unknown);
hostlist_sort(hl_on);
hostlist_sort(hl_off);
Expand Down Expand Up @@ -565,6 +674,9 @@ static void _parse_input(Client * c, char *input)
} else if (!strncasecmp(str, CP_EXPRANGE, strlen(CP_EXPRANGE))) {
c->exprange = !c->exprange; /* exprange */
_client_printf(c, CP_RSP_EXPRANGE, c->exprange ? "ON" : "OFF");
} else if (!strncasecmp(str, CP_DIAG, strlen(CP_DIAG))) {
c->diag = !c->diag; /* diag */
_client_printf(c, CP_RSP_DIAG, c->diag ? "ON" : "OFF");
} else if (!strncasecmp(str, CP_QUIT, strlen(CP_QUIT))) {
c->client_quit = true;
_client_printf(c, CP_RSP_QUIT); /* quit */
Expand Down Expand Up @@ -717,10 +829,7 @@ static void _act_finish(int client_id, ActError acterr, const char *fmt, ...)
case PM_BEACON_OFF: /* unflash */
case PM_POWER_CYCLE: /* cycle */
case PM_RESET: /* reset */
if (c->cmd->error)
_client_printf(c, CP_ERR_COM_COMPLETE);
else
_client_printf(c, CP_RSP_COM_COMPLETE);
_client_power_status_reply(c, c->cmd->error);
break;
default:
assert(false);
Expand Down
3 changes: 3 additions & 0 deletions src/powerman/client_proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#define CP_BEACON_OFF "unflash %s"
#define CP_TELEMETRY "telemetry"
#define CP_EXPRANGE "exprange"
#define CP_DIAG "diag"

/*
* Responses -
Expand All @@ -67,6 +68,7 @@
#define CP_RSP_QRY_COMPLETE "103 Query complete" CP_EOL
#define CP_RSP_TELEMETRY "104 Telemetry %s" CP_EOL
#define CP_RSP_EXPRANGE "105 Hostrange expansion %s" CP_EOL
#define CP_RSP_DIAG "106 Diag %s" CP_EOL

/* failure 2xx */
#define CP_ERR_UNKNOWN "201 Unknown command" CP_EOL
Expand Down Expand Up @@ -108,6 +110,7 @@
#define CP_INFO_NODES "306 %s" CP_EOL
#define CP_INFO_XNODES "307 %s" CP_EOL
#define CP_INFO_ACTERROR "308 %s" CP_EOL
#define CP_INFO_DIAG "309 %s: %s" CP_EOL

#endif /* PM_CLIENT_PROTO_H */

Expand Down
76 changes: 73 additions & 3 deletions src/powerman/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ typedef struct {
ListIterator stmtitr; /* next stmt in block */
Stmt *cur; /* current stmt */
PlugListIterator plugitr; /* used by foreach */
PlugList pluglist; /* pluglist if foreach is ranged */
bool processing; /* flag used by stmts, ifon/ifoff */
} ExecCtx;

Expand All @@ -104,6 +105,7 @@ static bool _process_stmt(Device *dev, Action *act, ExecCtx *e,
static bool _process_ifonoff(Device *dev, Action *act, ExecCtx *e);
static bool _process_foreach(Device *dev, Action *act, ExecCtx *e);
static bool _process_setplugstate(Device * dev, Action *act, ExecCtx *e);
static bool _process_setresult(Device * dev, Action *act, ExecCtx *e);
static bool _process_expect(Device * dev, Action *act, ExecCtx *e);
static bool _process_send(Device * dev, Action *act, ExecCtx *e);
static bool _process_delay(Device * dev, Action *act, ExecCtx *e,
Expand Down Expand Up @@ -229,6 +231,8 @@ static void _destroy_exec_ctx(ExecCtx *e)
e->cur = NULL;
if (e->plugs)
list_destroy(e->plugs);
if (e->pluglist)
pluglist_destroy(e->pluglist);
e->plugs = NULL;
xfree(e);
}
Expand Down Expand Up @@ -919,6 +923,9 @@ bool _process_stmt(Device *dev, Action *act, ExecCtx *e,
case STMT_SETPLUGSTATE:
finished = _process_setplugstate(dev, act, e);
break;
case STMT_SETRESULT:
finished = _process_setresult(dev, act, e);
break;
case STMT_DELAY:
finished = _process_delay(dev, act, e, timeout);
break;
Expand All @@ -941,8 +948,23 @@ static bool _process_foreach(Device *dev, Action *act, ExecCtx *e)
Plug *plug = NULL;

/* we store a plug iterator in the ExecCtx */
if (e->plugitr == NULL)
e->plugitr = pluglist_iterator_create(dev->plugs);
if (e->plugitr == NULL) {
if (act->com == PM_POWER_ON_RANGED
|| act->com == PM_POWER_OFF_RANGED
|| act->com == PM_POWER_CYCLE_RANGED
|| act->com == PM_RESET_RANGED
|| act->com == PM_BEACON_ON_RANGED
|| act->com == PM_BEACON_OFF_RANGED) {
assert(e->plugs);
if (!e->pluglist) {
if (!(e->pluglist = pluglist_copy_from_list(e->plugs)))
goto cleanup;
}
e->plugitr = pluglist_iterator_create(e->pluglist);
}
else
e->plugitr = pluglist_iterator_create(dev->plugs);
}

/* Each time the inner block is executed, its argument will be
* a new plug name. Pick that up here.
Expand Down Expand Up @@ -1074,7 +1096,7 @@ static bool _process_setplugstate(Device *dev, Action *act, ExecCtx *e)
if (str && plug && plug->node) {
InterpState state = ST_UNKNOWN;
ListIterator itr;
Interp *i;
StateInterp *i;
Arg *arg;

itr = list_iterator_create(e->cur->u.setplugstate.interps);
Expand Down Expand Up @@ -1102,6 +1124,54 @@ static bool _process_setplugstate(Device *dev, Action *act, ExecCtx *e)
return finished;
}

static bool _process_setresult(Device *dev, Action *act, ExecCtx *e)
{
bool finished = true;
char *plug_name;

/*
* Usage: setresult regex regexstatus interps
*/
plug_name = xregex_match_sub_strdup(dev->xmatch,
e->cur->u.setresult.plug_mp);

/* if no plug name, do nothing */
if (plug_name) {
char *str = xregex_match_sub_strdup(dev->xmatch,
e->cur->u.setresult.stat_mp);
Plug *plug = pluglist_find(dev->plugs, plug_name);

if (str && plug && plug->node) {
InterpResult result = RT_UNKNOWN;
ListIterator itr;
ResultInterp *i;
Arg *arg;

itr = list_iterator_create(e->cur->u.setresult.interps);
while ((i = list_next(itr))) {
if (xregex_exec(i->re, str, NULL)) {
result = i->result;
break;
}
}
list_iterator_destroy(itr);

if ((arg = arglist_find(act->arglist, plug->node))) {
arg->result = result;
if (arg->val)
xfree(arg->val);
arg->val = xstrdup(str);
}
}
if (str)
xfree(str);
/* if no match, do nothing */
xfree(plug_name);
}

return finished;
}

/* return true if expect is finished */
static bool _process_expect(Device *dev, Action *act, ExecCtx *e)
{
Expand Down
Loading
Loading