Skip to content

Commit

Permalink
powerman: support new setresult directive
Browse files Browse the repository at this point in the history
Problem: Powerman device files can be scripted to "expect" a string to indicate
success.  In the event of a failure (i.e. an unexpected string output), the device
script will ultimately time out.  This is undesirable as it can slow down powerman
when simple errors (e.g. "password failure") fail immediately.  The lack of the
ability to "fail fast" has lead to many device scripts being scripted to simply "succeed"
most of the time.  Powerman will complete with "Command completed successfully"
regardless if the power operation was a success or not.

Solution: Support a new "setresult" statement that can inform powerman
that a power operation did not succeed.  A regex can be used to determine
what output is expected of a successful power operation.  If any are not
successful, powerman can immediately report to the user an error has occurred,
leading to a "Command completed with errors" message and exit status 1.

Some example uses:

script on_all {
	send "on\n"
	foreachnode {
		expect "([^\n:]+): ([^\n]+\n)"
		setresult $1 $2 success="^ok\n"
	}
	expect "redfishpower> "
}

script on {
	send "on %s\n"
	expect "([^\n:]+): ([^\n]+\n)"
	setresult $1 $2 success="^ok\n"
	expect "redfishpower> "
}

Fixes #79
  • Loading branch information
chu11 committed Apr 4, 2024
1 parent 71ea403 commit cfc1972
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 25 deletions.
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
34 changes: 30 additions & 4 deletions src/powerman/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,35 @@ static void _client_query_device_reply(Client * c, char *arg)
_client_printf(c, CP_RSP_QRY_COMPLETE);
}

/*
* 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;

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++;
break;
}
}
arglist_iterator_destroy(itr);

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

/*
* Reply to client request for plug/soft status.
*/
Expand Down Expand Up @@ -717,10 +746,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
72 changes: 70 additions & 2 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 @@ -932,6 +936,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 @@ -954,8 +961,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 @@ -1113,6 +1135,52 @@ 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;
xfree(arg->val);
arg->val = xstrdup(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
12 changes: 12 additions & 0 deletions src/powerman/device_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,20 @@ typedef struct {
xregex_t re;
} StateInterp;

typedef struct {
InterpResult result;
char *str;
xregex_t re;
} ResultInterp;

/*
* A Script is a list of Stmts.
*/
typedef enum {
STMT_SEND,
STMT_EXPECT,
STMT_SETPLUGSTATE,
STMT_SETRESULT,
STMT_DELAY,
STMT_FOREACHPLUG,
STMT_FOREACHNODE,
Expand All @@ -81,6 +88,11 @@ typedef struct {
int stat_mp; /* regex subexp match pos of plug status */
List interps; /* list of possible interpretations */
} setplugstate;
struct { /* SETRESULT (regexs refer to prev expect) */
int plug_mp; /* regex subexp match pos of plug name if not */
int stat_mp; /* regex subexp match pos of plug status */
List interps; /* list of possible interpretations */
} setresult;
struct { /* DELAY */
struct timeval tv; /* delay at this point in the script */
} delay;
Expand Down
2 changes: 2 additions & 0 deletions src/powerman/parse_lex.l
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ pingperiod return TOK_PING_PERIOD;
specification return TOK_SPEC;
expect return TOK_EXPECT;
setplugstate return TOK_SETPLUGSTATE;
setresult return TOK_SETRESULT;
foreachnode return TOK_FOREACHNODE;
foreachplug return TOK_FOREACHPLUG;
ifoff return TOK_IFOFF;
Expand Down Expand Up @@ -169,6 +170,7 @@ plug[ \t]+name return TOK_PLUG_NAME;
node return TOK_NODE;
yes return TOK_YES;
no return TOK_NO;
success return TOK_SUCCESS;
\{ return TOK_BEGIN;
\} return TOK_END;
= return TOK_EQUALS;
Expand Down
Loading

0 comments on commit cfc1972

Please sign in to comment.