Skip to content

Commit

Permalink
powerman: support new setresult directive
Browse files Browse the repository at this point in the history
Problem: In many cases, there is no way for a power operation (i.e.
power on, but not power status) to inform powerman that an error has
occurred in the operation.  The user will always get a
"Command completed successfully" output and exit status 0 after
issuing a power operation.

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 subsequently inform 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 chaos#79
  • Loading branch information
chu11 committed Mar 26, 2024
1 parent be1a92d commit f6fdd70
Show file tree
Hide file tree
Showing 6 changed files with 213 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
74 changes: 72 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 @@ -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 @@ -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
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 f6fdd70

Please sign in to comment.