diff --git a/man/powerman.1.in b/man/powerman.1.in index a15ad96a..59638ae7 100644 --- a/man/powerman.1.in +++ b/man/powerman.1.in @@ -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 diff --git a/man/powerman.dev.5.in b/man/powerman.dev.5.in index 073c990e..877bb869 100644 --- a/man/powerman.dev.5.in +++ b/man/powerman.dev.5.in @@ -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 success=" +Set the result state. The first argument, a 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. diff --git a/src/powerman/Makefile.am b/src/powerman/Makefile.am index 312b0ffa..df53ec73 100644 --- a/src/powerman/Makefile.am +++ b/src/powerman/Makefile.am @@ -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 = \ @@ -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) diff --git a/src/powerman/arglist.h b/src/powerman/arglist.h index 34623c3e..176eea8b 100644 --- a/src/powerman/arglist.h +++ b/src/powerman/arglist.h @@ -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; diff --git a/src/powerman/client.c b/src/powerman/client.c index 0358f5bd..2dfe3124 100644 --- a/src/powerman/client.c +++ b/src/powerman/client.c @@ -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 */ @@ -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; @@ -363,6 +365,91 @@ 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. */ @@ -370,6 +457,7 @@ static void _client_query_status_reply(Client * c, bool error) { Arg *arg; ArgListIterator itr; + zhashx_t *diagmsgs = NULL; assert(c->cmd != NULL); @@ -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); @@ -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); @@ -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); @@ -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 */ @@ -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); diff --git a/src/powerman/client_proto.h b/src/powerman/client_proto.h index 17643888..8d734ff6 100644 --- a/src/powerman/client_proto.h +++ b/src/powerman/client_proto.h @@ -48,6 +48,7 @@ #define CP_BEACON_OFF "unflash %s" #define CP_TELEMETRY "telemetry" #define CP_EXPRANGE "exprange" +#define CP_DIAG "diag" /* * Responses - @@ -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 @@ -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 */ diff --git a/src/powerman/device.c b/src/powerman/device.c index f18757ea..e6e70cfe 100644 --- a/src/powerman/device.c +++ b/src/powerman/device.c @@ -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; @@ -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, @@ -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); } @@ -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; @@ -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. @@ -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); @@ -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) { diff --git a/src/powerman/device_private.h b/src/powerman/device_private.h index 6bbe617e..2413af23 100644 --- a/src/powerman/device_private.h +++ b/src/powerman/device_private.h @@ -50,7 +50,13 @@ typedef struct { InterpState state; char *str; xregex_t re; -} Interp; +} StateInterp; + +typedef struct { + InterpResult result; + char *str; + xregex_t re; +} ResultInterp; /* * A Script is a list of Stmts. @@ -59,6 +65,7 @@ typedef enum { STMT_SEND, STMT_EXPECT, STMT_SETPLUGSTATE, + STMT_SETRESULT, STMT_DELAY, STMT_FOREACHPLUG, STMT_FOREACHNODE, @@ -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; diff --git a/src/powerman/parse_lex.l b/src/powerman/parse_lex.l index 2570d40c..46991fc1 100644 --- a/src/powerman/parse_lex.l +++ b/src/powerman/parse_lex.l @@ -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; @@ -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; diff --git a/src/powerman/parse_tab.y b/src/powerman/parse_tab.y index eec8057c..d2a883e0 100644 --- a/src/powerman/parse_tab.y +++ b/src/powerman/parse_tab.y @@ -49,10 +49,11 @@ typedef struct { StmtType type; /* delay/expect/send */ char *str; /* expect string, send fmt, setplugstate plug */ struct timeval tv; /* delay value */ - int mp1; /* setplugstate plug match position */ - int mp2; /* setplugstate state match position */ + int mp1; /* setplugstate/result plug match position */ + int mp2; /* setplugstate/result state/result match position */ List prestmts; /* subblock */ - List interps; /* interpretations for setplugstate */ + List state_interps; /* interpretations for setplugstate */ + List result_interps; /* interpretations for setresult */ } PreStmt; typedef List PreScript; @@ -78,16 +79,20 @@ static void makeDevice(char *devstr, char *specstr, char *hoststr, /* device config */ static PreStmt *makePreStmt(StmtType type, char *str, char *tvstr, - char *mp1str, char *mp2str, List prestmts, List interps); + char *mp1str, char *mp2str, List prestmts, + List plugstate_interps, List result_interps); static void destroyPreStmt(PreStmt *p); static Spec *makeSpec(char *name); static Spec *findSpec(char *name); static int matchSpec(Spec * spec, void *key); static void destroySpec(Spec * spec); static void makeScript(int com, List stmts); -static void destroyInterp(Interp *i); -static Interp *makeInterp(InterpState state, char *str); -static List copyInterpList(List ilist); +static void destroyStateInterp(StateInterp *i); +static StateInterp *makeStateInterp(InterpState state, char *str); +static List copyStateInterpList(List ilist); +static void destroyResultInterp(ResultInterp *i); +static ResultInterp *makeResultInterp(InterpResult result, char *str); +static List copyResultInterpList(List ilist); /* utility functions */ static void _errormsg(char *msg); @@ -114,7 +119,7 @@ static Spec current_spec; /* Holds a Spec as it is built */ %token TOK_RESET TOK_RESET_RANGED TOK_RESET_ALL TOK_PING TOK_SPEC /* script statements */ -%token TOK_EXPECT TOK_SETPLUGSTATE TOK_SEND TOK_DELAY +%token TOK_EXPECT TOK_SETPLUGSTATE TOK_SETRESULT TOK_SEND TOK_DELAY %token TOK_FOREACHPLUG TOK_FOREACHNODE TOK_IFOFF TOK_IFON /* other device configuration stuff */ @@ -128,6 +133,7 @@ static Spec current_spec; /* Holds a Spec as it is built */ /* general */ %token TOK_MATCHPOS TOK_STRING_VAL TOK_NUMERIC_VAL TOK_YES TOK_NO %token TOK_BEGIN TOK_END TOK_UNRECOGNIZED TOK_EQUALS +%token TOK_SUCCESS %% /* Grammar Rules for the powerman.conf config file */ @@ -291,50 +297,71 @@ stmt_list : stmt_list stmt { } ; stmt : TOK_EXPECT TOK_STRING_VAL { - $$ = (char *)makePreStmt(STMT_EXPECT, $2, NULL, NULL, NULL, NULL, NULL); + $$ = (char *)makePreStmt(STMT_EXPECT, $2, NULL, NULL, NULL, NULL, NULL, + NULL); } | TOK_SEND TOK_STRING_VAL { - $$ = (char *)makePreStmt(STMT_SEND, $2, NULL, NULL, NULL, NULL, NULL); + $$ = (char *)makePreStmt(STMT_SEND, $2, NULL, NULL, NULL, NULL, NULL, NULL); } | TOK_DELAY TOK_NUMERIC_VAL { - $$ = (char *)makePreStmt(STMT_DELAY, NULL, $2, NULL, NULL, NULL, NULL); + $$ = (char *)makePreStmt(STMT_DELAY, NULL, $2, NULL, NULL, NULL, NULL, NULL); } | TOK_SETPLUGSTATE TOK_STRING_VAL regmatch { - $$ = (char *)makePreStmt(STMT_SETPLUGSTATE, $2, NULL, NULL, $3, NULL, NULL); -} | TOK_SETPLUGSTATE TOK_STRING_VAL regmatch interp_list { + $$ = (char *)makePreStmt(STMT_SETPLUGSTATE, $2, NULL, NULL, $3, NULL, NULL, + NULL); +} | TOK_SETPLUGSTATE TOK_STRING_VAL regmatch state_interp_list { $$ = (char *)makePreStmt(STMT_SETPLUGSTATE, $2, NULL, NULL, $3, NULL, - (List)$4); + (List)$4, NULL); } | TOK_SETPLUGSTATE regmatch regmatch { - $$ = (char *)makePreStmt(STMT_SETPLUGSTATE, NULL, NULL, $2, $3, NULL, NULL); -} | TOK_SETPLUGSTATE regmatch regmatch interp_list { - $$ = (char *)makePreStmt(STMT_SETPLUGSTATE, NULL, NULL, $2, $3, NULL,(List)$4); + $$ = (char *)makePreStmt(STMT_SETPLUGSTATE, NULL, NULL, $2, $3, NULL, NULL, + NULL); +} | TOK_SETPLUGSTATE regmatch regmatch state_interp_list { + $$ = (char *)makePreStmt(STMT_SETPLUGSTATE, NULL, NULL, $2, $3, NULL, + (List)$4, NULL); } | TOK_SETPLUGSTATE regmatch { - $$ = (char *)makePreStmt(STMT_SETPLUGSTATE, NULL, NULL, NULL, $2, NULL, NULL); -} | TOK_SETPLUGSTATE regmatch interp_list { - $$ = (char *)makePreStmt(STMT_SETPLUGSTATE, NULL, NULL, NULL,$2,NULL,(List)$3); + $$ = (char *)makePreStmt(STMT_SETPLUGSTATE, NULL, NULL, NULL, $2, NULL, NULL, + NULL); +} | TOK_SETPLUGSTATE regmatch state_interp_list { + $$ = (char *)makePreStmt(STMT_SETPLUGSTATE, NULL, NULL, NULL,$2,NULL, + (List)$3, NULL); +} | TOK_SETRESULT regmatch regmatch result_interp_list { + $$ = (char *)makePreStmt(STMT_SETRESULT, NULL, NULL, $2, $3, NULL, + NULL, (List)$4); } | TOK_FOREACHNODE stmt_block { $$ = (char *)makePreStmt(STMT_FOREACHNODE, NULL, NULL, NULL, NULL, - (List)$2, NULL); + (List)$2, NULL, NULL); } | TOK_FOREACHPLUG stmt_block { $$ = (char *)makePreStmt(STMT_FOREACHPLUG, NULL, NULL, NULL, NULL, - (List)$2, NULL); + (List)$2, NULL, NULL); } | TOK_IFOFF stmt_block { $$ = (char *)makePreStmt(STMT_IFOFF, NULL, NULL, NULL, NULL, - (List)$2, NULL); + (List)$2, NULL, NULL); } | TOK_IFON stmt_block { $$ = (char *)makePreStmt(STMT_IFON, NULL, NULL, NULL, NULL, - (List)$2, NULL); + (List)$2, NULL, NULL); } ; -interp_list : interp_list interp { +state_interp_list : state_interp_list state_interp { list_append((List)$1, $2); $$ = $1; -} | interp { - $$ = (char *)list_create((ListDelF)destroyInterp); +} | state_interp { + $$ = (char *)list_create((ListDelF)destroyStateInterp); list_append((List)$$, $1); } ; -interp : TOK_ON TOK_EQUALS TOK_STRING_VAL { - $$ = (char *)makeInterp(ST_ON, $3); +state_interp : TOK_ON TOK_EQUALS TOK_STRING_VAL { + $$ = (char *)makeStateInterp(ST_ON, $3); } | TOK_OFF TOK_EQUALS TOK_STRING_VAL { - $$ = (char *)makeInterp(ST_OFF, $3); + $$ = (char *)makeStateInterp(ST_OFF, $3); +} +; +result_interp_list : result_interp_list result_interp { + list_append((List)$1, $2); + $$ = $1; +} | result_interp { + $$ = (char *)list_create((ListDelF)destroyResultInterp); + list_append((List)$$, $1); +} +; +result_interp : TOK_SUCCESS TOK_EQUALS TOK_STRING_VAL { + $$ = (char *)makeResultInterp(RT_SUCCESS, $3); } ; regmatch : TOK_MATCHPOS TOK_NUMERIC_VAL { @@ -373,10 +400,11 @@ int parse_config_file (char *filename) return 0; } -/* makePreStmt(type, str, tv, mp1(plug), mp2(stat/node), prestmts, interps */ +/* makePreStmt(type, str, tv, mp1(plug), mp2(stat/node), prestmts, + * state_interps, result_interps */ static PreStmt *makePreStmt(StmtType type, char *str, char *tvstr, char *mp1str, char *mp2str, List prestmts, - List interps) + List state_interps, List result_interps) { PreStmt *new; @@ -390,7 +418,8 @@ static PreStmt *makePreStmt(StmtType type, char *str, char *tvstr, if (tvstr) _doubletotv(&new->tv, _strtodouble(tvstr)); new->prestmts = prestmts; - new->interps = interps; + new->state_interps = state_interps; + new->result_interps = result_interps; return new; } @@ -403,9 +432,9 @@ static void destroyPreStmt(PreStmt *p) if (p->prestmts) list_destroy(p->prestmts); p->prestmts = NULL; - if (p->interps) - list_destroy(p->interps); - p->interps = NULL; + if (p->state_interps) + list_destroy(p->state_interps); + p->state_interps = NULL; xfree(p); } @@ -464,9 +493,9 @@ static void makeScript(int com, List stmts) current_spec.prescripts[com] = stmts; } -static Interp *makeInterp(InterpState state, char *str) +static StateInterp *makeStateInterp(InterpState state, char *str) { - Interp *new = (Interp *)xmalloc(sizeof(Interp)); + StateInterp *new = (StateInterp *)xmalloc(sizeof(StateInterp)); new->str = xstrdup(str); new->re = xregex_create(); @@ -475,24 +504,62 @@ static Interp *makeInterp(InterpState state, char *str) return new; } -static void destroyInterp(Interp *i) +static void destroyStateInterp(StateInterp *i) +{ + xfree(i->str); + xregex_destroy(i->re); + xfree(i); +} + +static List copyStateInterpList(List il) +{ + ListIterator itr; + StateInterp *ip, *icpy; + List new = list_create((ListDelF) destroyStateInterp); + + if (il != NULL) { + + itr = list_iterator_create(il); + while((ip = list_next(itr))) { + icpy = makeStateInterp(ip->state, ip->str); + xregex_compile(icpy->re, icpy->str, false); + list_append(new, icpy); + } + list_iterator_destroy(itr); + } + + return new; +} + +static ResultInterp *makeResultInterp(InterpResult result, char *str) +{ + ResultInterp *new = (ResultInterp *)xmalloc(sizeof(ResultInterp)); + + new->str = xstrdup(str); + new->re = xregex_create(); + new->result = result; + + return new; +} + +static void destroyResultInterp(ResultInterp *i) { xfree(i->str); xregex_destroy(i->re); xfree(i); } -static List copyInterpList(List il) +static List copyResultInterpList(List il) { ListIterator itr; - Interp *ip, *icpy; - List new = list_create((ListDelF) destroyInterp); + ResultInterp *ip, *icpy; + List new = list_create((ListDelF) destroyResultInterp); if (il != NULL) { itr = list_iterator_create(il); while((ip = list_next(itr))) { - icpy = makeInterp(ip->state, ip->str); + icpy = makeResultInterp(ip->result, ip->str); xregex_compile(icpy->re, icpy->str, false); list_append(new, icpy); } @@ -523,6 +590,9 @@ static void destroyStmt(Stmt *stmt) list_destroy(stmt->u.setplugstate.interps); xfree (stmt->u.setplugstate.plug_name); break; + case STMT_SETRESULT: + list_destroy(stmt->u.setresult.interps); + break; case STMT_FOREACHNODE: case STMT_FOREACHPLUG: list_destroy(stmt->u.foreach.stmts); @@ -559,7 +629,12 @@ static Stmt *makeStmt(PreStmt *p) stmt->u.setplugstate.plug_name = xstrdup(p->str); else stmt->u.setplugstate.plug_mp = p->mp1; - stmt->u.setplugstate.interps = copyInterpList(p->interps); + stmt->u.setplugstate.interps = copyStateInterpList(p->state_interps); + break; + case STMT_SETRESULT: + stmt->u.setresult.stat_mp = p->mp2; + stmt->u.setresult.plug_mp = p->mp1; + stmt->u.setresult.interps = copyResultInterpList(p->result_interps); break; case STMT_DELAY: stmt->u.delay.tv = p->tv; diff --git a/src/powerman/pluglist.c b/src/powerman/pluglist.c index 2824e27a..2c607b0a 100644 --- a/src/powerman/pluglist.c +++ b/src/powerman/pluglist.c @@ -50,6 +50,18 @@ static Plug *_create_plug(char *name) return plug; } +static Plug *_copy_plug(Plug *p) +{ + Plug *plug = (Plug *) xmalloc(sizeof(Plug)); + + assert(p != NULL); + + plug->name = p->name ? xstrdup(p->name) : NULL; + plug->node = p->node ? xstrdup(p->node) : NULL; + + return plug; +} + static void _destroy_plug(Plug *plug) { assert(plug != NULL); @@ -82,6 +94,29 @@ PlugList pluglist_create(List plugnames) return pl; } +PlugList pluglist_copy_from_list(List plugs) +{ + PlugList pl = (PlugList) xmalloc(sizeof(struct pluglist)); + + pl->pluglist = list_create((ListDelF)_destroy_plug); + pl->hardwired = false; + + /* create plug from each plug in list */ + if (plugs) { + ListIterator itr; + Plug *p; + + itr = list_iterator_create(plugs); + while ((p = list_next(itr))) { + list_append(pl->pluglist, _copy_plug(p)); + } + list_iterator_destroy(itr); + pl->hardwired = true; + } + + return pl; +} + void pluglist_destroy(PlugList pl) { assert(pl != NULL); diff --git a/src/powerman/pluglist.h b/src/powerman/pluglist.h index 962e11cb..0746b8e9 100644 --- a/src/powerman/pluglist.h +++ b/src/powerman/pluglist.h @@ -33,6 +33,9 @@ typedef enum { EPL_SUCCESS, EPL_DUPNODE, EPL_UNKPLUG, EPL_DUPPLUG, */ PlugList pluglist_create(List plugnames); +/* Create a PlugList, copying plugs from an existing list of plugs */ +PlugList pluglist_copy_from_list(List plugs); + /* Destroy a PlugList and its Plugs */ void pluglist_destroy(PlugList pl); diff --git a/src/powerman/powerman.c b/src/powerman/powerman.c index d1ae913d..42783396 100644 --- a/src/powerman/powerman.c +++ b/src/powerman/powerman.c @@ -56,7 +56,7 @@ static void _set_command (const char **command, const char *value); static char *prog; -#define OPTIONS "01crfubqtldTxgh:VLR:H" +#define OPTIONS "01crfubqtldTxgh:VLR:DH" static const struct option longopts[] = { // command {"on", no_argument, 0, '1'}, @@ -78,6 +78,7 @@ static const struct option longopts[] = { {"version", no_argument, 0, 'V'}, {"license", no_argument, 0, 'L'}, {"retry-connect", required_argument, 0, 'R'}, + {"diag", no_argument, 0, 'D'}, {"help", no_argument, 0, 'H'}, {0, 0, 0, 0}, }; @@ -96,6 +97,7 @@ int main(int argc, char **argv) bool exprange = false; hostlist_t targets; bool targets_required = false; + bool diag = false; prog = basename(argv[0]); err_init(prog); @@ -179,6 +181,9 @@ int main(int argc, char **argv) if (errno != 0 || retry_connect < 1) err_exit(false, "invalid --retry-connect argument"); break; + case 'D': /* --diag */ + diag = true; + break; case 'H': /* --help */ _usage(); /*NOTREACHED*/ @@ -241,6 +246,13 @@ int main(int argc, char **argv) if (res != 0) goto done; } + if (diag) { + hfdprintf(server_fd, "%s%s", CP_DIAG, CP_EOL); + res = _process_response(server_fd); + _expect(server_fd, CP_PROMPT); + if (res != 0) + goto done; + } /* Send the main command. * Use 'command' as the format string if it contains '%s' for an argument. */ @@ -291,6 +303,7 @@ static void _usage(void) " -L,--license Show powerman license\n" " -T,--telemtery Show device conversation for debugging\n" " -R,--retry-connect=N Retry connect to server up to N times\n" +" -D,--diag Output diagnostics if available\n" ); exit(0); } @@ -403,6 +416,8 @@ static bool _suppress(int num) return true; if (strtol(CP_RSP_EXPRANGE, NULL, 10) == num) return true; + if (strtol(CP_RSP_DIAG, NULL, 10) == num) + return true; return false; } diff --git a/t/Makefile.am b/t/Makefile.am index 8086f228..36a4483a 100644 --- a/t/Makefile.am +++ b/t/Makefile.am @@ -41,7 +41,9 @@ TESTSCRIPTS = \ t0030-heartbeat-stonith.t \ t0031-llnl-mcr-cluster.t \ t0032-list.t \ - t0033-valgrind.t + t0033-valgrind.t \ + t0034-power-result.t \ + t0035-diag.t # make check runs these TAP tests directly (both scripts and programs) TESTS = \ @@ -59,7 +61,10 @@ EXTRA_DIST= \ sharness.sh \ etc/sierra_plugs.conf \ etc/mcr_plugs.conf \ - etc/vpc.dev + etc/vpc.dev \ + etc/redfishpower-setresult-all.dev \ + etc/redfishpower-setresult-range.dev \ + etc/redfishpower-setresult-singlet.dev AM_CFLAGS = @WARNING_CFLAGS@ diff --git a/t/etc/redfishpower-setresult-all.dev b/t/etc/redfishpower-setresult-all.dev new file mode 100644 index 00000000..157b9609 --- /dev/null +++ b/t/etc/redfishpower-setresult-all.dev @@ -0,0 +1,66 @@ +# Variant of redfishpower-cray-r272z30.dev that covers use setresult +# with "all" script variants +specification "redfishpower-setresult" { + timeout 60 + + script login { + expect "redfishpower> " + send "auth USER:PASS\n" + expect "redfishpower> " + send "setheader Content-Type:application/json\n" + expect "redfishpower> " + send "setstatpath redfish/v1/Systems/Self\n" + expect "redfishpower> " + send "setonpath redfish/v1/Systems/Self/Actions/ComputerSystem.Reset {\"ResetType\":\"On\"}\n" + expect "redfishpower> " + send "setoffpath redfish/v1/Systems/Self/Actions/ComputerSystem.Reset {\"ResetType\":\"ForceOff\"}\n" + expect "redfishpower> " + send "settimeout 60\n" + expect "redfishpower> " + } + script logout { + send "quit\n" + } + script status_all { + send "stat\n" + foreachnode { + expect "([^\n:]+): ([^\n]+\n)" + setplugstate $1 $2 on="^on\n" off="^off\n" + } + expect "redfishpower> " + } + script on_all { + send "on\n" + foreachnode { + expect "([^\n:]+): ([^\n]+\n)" + setresult $1 $2 success="^ok\n" + } + expect "redfishpower> " + } + script off_all { + send "off\n" + foreachnode { + expect "([^\n:]+): ([^\n]+\n)" + setresult $1 $2 success="^ok\n" + } + expect "redfishpower> " + } + # fake cycle for coverage testing, just does "on" + script cycle_all { + send "on\n" + foreachnode { + expect "([^\n:]+): ([^\n]+\n)" + setresult $1 $2 success="^ok\n" + } + expect "redfishpower> " + } + # fake reset for coverage testing, just does "on" + script reset_all { + send "on\n" + foreachnode { + expect "([^\n:]+): ([^\n]+\n)" + setresult $1 $2 success="^ok\n" + } + expect "redfishpower> " + } +} diff --git a/t/etc/redfishpower-setresult-range.dev b/t/etc/redfishpower-setresult-range.dev new file mode 100644 index 00000000..6b0f9b6c --- /dev/null +++ b/t/etc/redfishpower-setresult-range.dev @@ -0,0 +1,84 @@ +# Variant of redfishpower-cray-r272z30.dev that covers use setresult +# with "range" script variants +specification "redfishpower-setresult" { + timeout 60 + + script login { + expect "redfishpower> " + send "auth USER:PASS\n" + expect "redfishpower> " + send "setheader Content-Type:application/json\n" + expect "redfishpower> " + send "setstatpath redfish/v1/Systems/Self\n" + expect "redfishpower> " + send "setonpath redfish/v1/Systems/Self/Actions/ComputerSystem.Reset {\"ResetType\":\"On\"}\n" + expect "redfishpower> " + send "setoffpath redfish/v1/Systems/Self/Actions/ComputerSystem.Reset {\"ResetType\":\"ForceOff\"}\n" + expect "redfishpower> " + send "settimeout 60\n" + expect "redfishpower> " + } + script logout { + send "quit\n" + } + script status_all { + send "stat\n" + foreachnode { + expect "([^\n:]+): ([^\n]+\n)" + setplugstate $1 $2 on="^on\n" off="^off\n" + } + expect "redfishpower> " + } + script on_ranged { + send "on %s\n" + foreachnode { + expect "([^\n:]+): ([^\n]+\n)" + setresult $1 $2 success="^ok\n" + } + expect "redfishpower> " + } + script off_ranged { + send "off %s\n" + foreachnode { + expect "([^\n:]+): ([^\n]+\n)" + setresult $1 $2 success="^ok\n" + } + expect "redfishpower> " + } + # fake cycle for coverage testing, just does "on" + script cycle_ranged { + send "on %s\n" + foreachnode { + expect "([^\n:]+): ([^\n]+\n)" + setresult $1 $2 success="^ok\n" + } + expect "redfishpower> " + } + # fake reset for coverage testing, just does "on" + script reset_ranged { + send "on %s\n" + foreachnode { + expect "([^\n:]+): ([^\n]+\n)" + setresult $1 $2 success="^ok\n" + } + expect "redfishpower> " + } + # fake beacon on for coverage testing, just does "on" + script beacon_on_ranged { + send "on %s\n" + foreachnode { + expect "([^\n:]+): ([^\n]+\n)" + setresult $1 $2 success="^ok\n" + } + expect "redfishpower> " + } + # fake beacon off for coverage testing, just does "off" + script beacon_off_ranged { + send "off %s\n" + foreachnode { + expect "([^\n:]+): ([^\n]+\n)" + setresult $1 $2 success="^ok\n" + } + expect "redfishpower> " + } +} diff --git a/t/etc/redfishpower-setresult-singlet.dev b/t/etc/redfishpower-setresult-singlet.dev new file mode 100644 index 00000000..cfa0b9af --- /dev/null +++ b/t/etc/redfishpower-setresult-singlet.dev @@ -0,0 +1,72 @@ +# Variant of redfishpower-cray-r272z30.dev that covers use setresult +# with "singlet" script variants +specification "redfishpower-setresult" { + timeout 60 + + script login { + expect "redfishpower> " + send "auth USER:PASS\n" + expect "redfishpower> " + send "setheader Content-Type:application/json\n" + expect "redfishpower> " + send "setstatpath redfish/v1/Systems/Self\n" + expect "redfishpower> " + send "setonpath redfish/v1/Systems/Self/Actions/ComputerSystem.Reset {\"ResetType\":\"On\"}\n" + expect "redfishpower> " + send "setoffpath redfish/v1/Systems/Self/Actions/ComputerSystem.Reset {\"ResetType\":\"ForceOff\"}\n" + expect "redfishpower> " + send "settimeout 60\n" + expect "redfishpower> " + } + script logout { + send "quit\n" + } + script status_all { + send "stat\n" + foreachnode { + expect "([^\n:]+): ([^\n]+\n)" + setplugstate $1 $2 on="^on\n" off="^off\n" + } + expect "redfishpower> " + } + script on { + send "on %s\n" + expect "([^\n:]+): ([^\n]+\n)" + setresult $1 $2 success="^ok\n" + expect "redfishpower> " + } + script off { + send "off %s\n" + expect "([^\n:]+): ([^\n]+\n)" + setresult $1 $2 success="^ok\n" + expect "redfishpower> " + } + # fake cycle for coverage testing, just does "on" + script cycle { + send "on %s\n" + expect "([^\n:]+): ([^\n]+\n)" + setresult $1 $2 success="^ok\n" + expect "redfishpower> " + } + # fake reset for coverage testing, just does "on" + script reset { + send "on %s\n" + expect "([^\n:]+): ([^\n]+\n)" + setresult $1 $2 success="^ok\n" + expect "redfishpower> " + } + # fake beacon on for coverage testing, just does "on" + script beacon_on { + send "on %s\n" + expect "([^\n:]+): ([^\n]+\n)" + setresult $1 $2 success="^ok\n" + expect "redfishpower> " + } + # fake beacon off for coverage testing, just does "off" + script beacon_off { + send "off %s\n" + expect "([^\n:]+): ([^\n]+\n)" + setresult $1 $2 success="^ok\n" + expect "redfishpower> " + } +} diff --git a/t/t0034-power-result.t b/t/t0034-power-result.t new file mode 100755 index 00000000..dd74d941 --- /dev/null +++ b/t/t0034-power-result.t @@ -0,0 +1,561 @@ +#!/bin/sh + +test_description='Test Powerman power result' + +. `dirname $0`/sharness.sh + +powermand=$SHARNESS_BUILD_DIRECTORY/src/powerman/powermand +powerman=$SHARNESS_BUILD_DIRECTORY/src/powerman/powerman +redfishdir=$SHARNESS_BUILD_DIRECTORY/src/redfishpower +testdevicesdir=$SHARNESS_TEST_SRCDIR/etc + +# Use port = 11000 + test number +# That way there won't be port conflicts with make -j +testaddr=localhost:11034 + +makeoutput() { + printf "on: %s\n" $1 + printf "off: %s\n" $2 + printf "unknown: %s\n" $3 +} + +# +# we will use redfishpower for this testing because it +# contains an option for failing specific hosts +# +# note that redfishpower does not natively support power cycle, reset, +# beacon on, or beacon off. So in the test device files +# +# redfishpower-setresult-all.dev +# redfishpower-setresult-range.dev +# redfishpower-setresult-singlet.dev +# +# power cycle, power reset, beacon on, and beacon off are "fake" implemented +# by just re-using power on or power off. +# + +# +# "all" scripts +# +# on_all, off_all, cycle_all, reset_all +# + +test_expect_success 'create test powerman.conf (setresult all scripts)' ' + cat >powerman_all.conf <<-EOT + listen "$testaddr" + include "$testdevicesdir/redfishpower-setresult-all.dev" + device "test0" "redfishpower-setresult" "$redfishdir/redfishpower -h t[0-15] --test-mode |&" + node "t[0-15]" "test0" + EOT +' +test_expect_success 'start powerman daemon and wait for it to start (setresult all scripts)' ' + $powermand -c powerman_all.conf & + echo $! >powermand.pid && + $powerman --retry-connect=100 --server-host=$testaddr -q >/dev/null +' +test_expect_success 'powerman -q shows all off' ' + $powerman -h $testaddr -q >queryA1.out && + makeoutput "" "t[0-15]" "" >queryA1.exp && + test_cmp queryA1.exp queryA1.out +' +test_expect_success 'powerman -1 t[0-15] works' ' + $powerman -h $testaddr -1 t[0-15] >onA1.out && + echo Command completed successfully >onA1.exp && + test_cmp onA1.exp onA1.out +' +test_expect_success 'powerman -q shows all on' ' + $powerman -h $testaddr -q >queryA2.out && + makeoutput "t[0-15]" "" "" >queryA2.exp && + test_cmp queryA2.exp queryA2.out +' +test_expect_success 'powerman -0 t[0-15] works' ' + $powerman -h $testaddr -0 t[0-15] >offA1.out && + echo Command completed successfully >offA1.exp && + test_cmp offA1.exp offA1.out +' +test_expect_success 'powerman -q shows all off' ' + $powerman -h $testaddr -q >queryA3.out && + makeoutput "" "t[0-15]" "" >queryA3.exp && + test_cmp queryA3.exp queryA3.out +' +test_expect_success 'powerman -c t[0-15] works' ' + $powerman -h $testaddr -c t[0-15] >cycleA1.out && + echo Command completed successfully >cycleA1.exp && + test_cmp cycleA1.exp cycleA1.out +' +test_expect_success 'powerman -q shows all on' ' + $powerman -h $testaddr -q >queryA4.out && + makeoutput "t[0-15]" "" "" >queryA4.exp && + test_cmp queryA4.exp queryA4.out +' +test_expect_success 'powerman -0 t[0-15] works' ' + $powerman -h $testaddr -0 t[0-15] >offA2.out && + echo Command completed successfully >offA2.exp && + test_cmp offA2.exp offA2.out +' +test_expect_success 'powerman -q shows all off' ' + $powerman -h $testaddr -q >queryA5.out && + makeoutput "" "t[0-15]" "" >queryA5.exp && + test_cmp queryA5.exp queryA5.out +' +test_expect_success 'powerman -r t[0-15] works' ' + $powerman -h $testaddr -r t[0-15] >resetA1.out && + echo Command completed successfully >resetA1.exp && + test_cmp resetA1.exp resetA1.out +' +test_expect_success 'powerman -q shows all on' ' + $powerman -h $testaddr -q >queryA6.out && + makeoutput "t[0-15]" "" "" >queryA6.exp && + test_cmp queryA6.exp queryA6.out +' +test_expect_success 'stop powerman daemon (setresult all scripts)' ' + kill -15 $(cat powermand.pid) && + wait +' + +# +# "all" scripts +# +# on_all, off_all, cycle_all, reset_all +# +# t15 set to bad, so will not work with anything below +# + +test_expect_success 'create powerman.conf for 16 cray redfish nodes and bad host (setresult all scripts bad host)' ' + cat >powerman_all_bad_host.conf <<-EOT + listen "$testaddr" + include "$testdevicesdir/redfishpower-setresult-all.dev" + device "test0" "redfishpower-setresult" "$redfishdir/redfishpower -h t[0-15] --test-mode --test-fail-power-cmd-hosts=t15 |&" + node "t[0-15]" "test0" + EOT +' +test_expect_success 'start powerman daemon and wait for it to start (setresult all scripts bad host)' ' + $powermand -Y -c powerman_all_bad_host.conf & + echo $! >powermand.pid && + $powerman --retry-connect=100 --server-host=$testaddr -d +' +test_expect_success 'powerman -q shows t[0-14] off, t15 unknown' ' + $powerman -h $testaddr -q >queryAF1.out && + makeoutput "" "t[0-14]" "t15" >queryAF1.exp && + test_cmp queryAF1.exp queryAF1.out +' +test_expect_success 'powerman -1 t[0-15] fails' ' + test_must_fail $powerman -h $testaddr -1 t[0-15] >onAF1.out && + echo Command completed with errors >onAF1.exp && + test_cmp onAF1.exp onAF1.out +' +test_expect_success 'powerman -q shows t[0-14] on, t15 unknown' ' + $powerman -h $testaddr -q >queryAF2.out && + makeoutput "t[0-14]" "" "t15" >queryAF2.exp && + test_cmp queryAF2.exp queryAF2.out +' +test_expect_success 'powerman -0 t[0-15] fails' ' + test_must_fail $powerman -h $testaddr -0 t[0-15] >offAF1.out && + echo Command completed with errors >offAF1.exp && + test_cmp offAF1.exp offAF1.out +' +test_expect_success 'powerman -q shows t[0-14] off, t15 unknown' ' + $powerman -h $testaddr -q >queryAF3.out && + makeoutput "" "t[0-14]" "t15" >queryAF3.exp && + test_cmp queryAF3.exp queryAF3.out +' +test_expect_success 'powerman -c t[0-15] fails' ' + test_must_fail $powerman -h $testaddr -c t[0-15] >cycleAF1.out && + echo Command completed with errors >cycleAF1.exp && + test_cmp cycleAF1.exp cycleAF1.out +' +test_expect_success 'powerman -q shows t[0-14] on, t15 unknown' ' + $powerman -h $testaddr -q >queryAF4.out && + makeoutput "t[0-14]" "" "t15" >queryAF4.exp && + test_cmp queryAF4.exp queryAF4.out +' +test_expect_success 'powerman -0 t[0-15] fails' ' + test_must_fail $powerman -h $testaddr -0 t[0-15] >offAF2.out && + echo Command completed with errors >offAF2.exp && + test_cmp offAF2.exp offAF2.out +' +test_expect_success 'powerman -q shows t[0-14] off, t15 unknown' ' + $powerman -h $testaddr -q >queryAF5.out && + makeoutput "" "t[0-14]" "t15" >queryAF5.exp && + test_cmp queryAF5.exp queryAF5.out +' +test_expect_success 'powerman -r t[0-15] fails' ' + test_must_fail $powerman -h $testaddr -r t[0-15] >resetAF1.out && + echo Command completed with errors >resetAF1.exp && + test_cmp resetAF1.exp resetAF1.out +' +test_expect_success 'powerman -q shows t[0-14] on, t15 unknown' ' + $powerman -h $testaddr -q >queryAF6.out && + makeoutput "t[0-14]" "" "t15" >queryAF6.exp && + test_cmp queryAF6.exp queryAF6.out +' +test_expect_success 'stop powerman daemon (setresult all scripts bad host)' ' + kill -15 $(cat powermand.pid) && + wait +' + +# +# "range" scripts +# +# on_range, off_range, cycle_range, reset_range, beacon_on_range, beacon_off_range +# + +test_expect_success 'create test powerman.conf (setresult range scripts)' ' + cat >powerman_range.conf <<-EOT + listen "$testaddr" + include "$testdevicesdir/redfishpower-setresult-range.dev" + device "test0" "redfishpower-setresult" "$redfishdir/redfishpower -h t[0-15] --test-mode |&" + node "t[0-15]" "test0" + EOT +' +test_expect_success 'start powerman daemon and wait for it to start (setresult range scripts)' ' + $powermand -c powerman_range.conf & + echo $! >powermand.pid && + $powerman --retry-connect=100 --server-host=$testaddr -q >/dev/null +' +test_expect_success 'powerman -q shows all off' ' + $powerman -h $testaddr -q >queryR1.out && + makeoutput "" "t[0-15]" "" >queryR1.exp && + test_cmp queryR1.exp queryR1.out +' +test_expect_success 'powerman -1 t[8-15] works' ' + $powerman -h $testaddr -1 t[8-15] >onR1.out && + echo Command completed successfully >onR1.exp && + test_cmp onR1.exp onR1.out +' +test_expect_success 'powerman -q shows t[8-15] on' ' + $powerman -h $testaddr -q >queryR2.out && + makeoutput "t[8-15]" "t[0-7]" "" >queryR2.exp && + test_cmp queryR2.exp queryR2.out +' +test_expect_success 'powerman -0 t[8-15] works' ' + $powerman -h $testaddr -0 t[8-15] >offR1.out && + echo Command completed successfully >offR1.exp && + test_cmp offR1.exp offR1.out +' +test_expect_success 'powerman -q shows all off' ' + $powerman -h $testaddr -q >queryR3.out && + makeoutput "" "t[0-15]" "" >queryR3.exp && + test_cmp queryR3.exp queryR3.out +' +test_expect_success 'powerman -c t[8-15] works' ' + $powerman -h $testaddr -c t[8-15] >cycleR1.out && + echo Command completed successfully >cycleR1.exp && + test_cmp cycleR1.exp cycleR1.out +' +test_expect_success 'powerman -q shows t[8-15] on' ' + $powerman -h $testaddr -q >queryR4.out && + makeoutput "t[8-15]" "t[0-7]" "" >queryR4.exp && + test_cmp queryR4.exp queryR4.out +' +test_expect_success 'powerman -0 t[8-15] works' ' + $powerman -h $testaddr -0 t[8-15] >offR2.out && + echo Command completed successfully >offR2.exp && + test_cmp offR2.exp offR2.out +' +test_expect_success 'powerman -q shows all off' ' + $powerman -h $testaddr -q >queryR5.out && + makeoutput "" "t[0-15]" "" >queryR5.exp && + test_cmp queryR5.exp queryR5.out +' +test_expect_success 'powerman -r t[8-15] works' ' + $powerman -h $testaddr -r t[8-15] >resetR1.out && + echo Command completed successfully >resetR1.exp && + test_cmp resetR1.exp resetR1.out +' +test_expect_success 'powerman -q shows t[8-15] on' ' + $powerman -h $testaddr -q >queryR6.out && + makeoutput "t[8-15]" "t[0-7]" "" >queryR6.exp && + test_cmp queryR6.exp queryR6.out +' +test_expect_success 'powerman -f t[8-15] works' ' + $powerman -h $testaddr -f t[8-15] >flashR1.out && + echo Command completed successfully >flashR1.exp && + test_cmp flashR1.exp flashR1.out +' +test_expect_success 'powerman -u t[8-15] works' ' + $powerman -h $testaddr -u t[8-15] >unflashR1.out && + echo Command completed successfully >unflashR1.exp && + test_cmp unflashR1.exp unflashR1.out +' +test_expect_success 'stop powerman daemon (setresult range scripts)' ' + kill -15 $(cat powermand.pid) && + wait +' + +# +# "range" scripts +# +# on_range, off_range, cycle_range, reset_range, beacon_on_range, beacon_off_range +# +# t15 set to bad, so will not work with anything below +# + +test_expect_success 'create powerman.conf for 16 cray redfish nodes and bad host (setresult range scripts bad host)' ' + cat >powerman_range_bad_host.conf <<-EOT + listen "$testaddr" + include "$testdevicesdir/redfishpower-setresult-range.dev" + device "test0" "redfishpower-setresult" "$redfishdir/redfishpower -h t[0-15] --test-mode --test-fail-power-cmd-hosts=t15 |&" + node "t[0-15]" "test0" + EOT +' +test_expect_success 'start powerman daemon and wait for it to start (setresult range scripts bad host)' ' + $powermand -Y -c powerman_range_bad_host.conf & + echo $! >powermand.pid && + $powerman --retry-connect=100 --server-host=$testaddr -d +' +test_expect_success 'powerman -q shows t[0-14] off, t15 unknown' ' + $powerman -h $testaddr -q >queryRF1.out && + makeoutput "" "t[0-14]" "t15" >queryRF1.exp && + test_cmp queryRF1.exp queryRF1.out +' +test_expect_success 'powerman -1 t[8-15] fails' ' + test_must_fail $powerman -h $testaddr -1 t[8-15] >onRF1.out && + echo Command completed with errors >onRF1.exp && + test_cmp onRF1.exp onRF1.out +' +test_expect_success 'powerman -q shows t[8-14] on, t15 unknown' ' + $powerman -h $testaddr -q >queryRF2.out && + makeoutput "t[8-14]" "t[0-7]" "t15" >queryRF2.exp && + test_cmp queryRF2.exp queryRF2.out +' +test_expect_success 'powerman -0 t[8-15] fails' ' + test_must_fail $powerman -h $testaddr -0 t[8-15] >offRF1.out && + echo Command completed with errors >offRF1.exp && + test_cmp offRF1.exp offRF1.out +' +test_expect_success 'powerman -q shows t[0-14] off, t15 unknown' ' + $powerman -h $testaddr -q >queryRF3.out && + makeoutput "" "t[0-14]" "t15" >queryRF3.exp && + test_cmp queryRF3.exp queryRF3.out +' +test_expect_success 'powerman -c t[8-15] fails' ' + test_must_fail $powerman -h $testaddr -c t[8-15] >cycleRF1.out && + echo Command completed with errors >cycleRF1.exp && + test_cmp cycleRF1.exp cycleRF1.out +' +test_expect_success 'powerman -q shows t[8-14] on, t15 unknown' ' + $powerman -h $testaddr -q >queryRF4.out && + makeoutput "t[8-14]" "t[0-7]" "t15" >queryRF4.exp && + test_cmp queryRF4.exp queryRF4.out +' +test_expect_success 'powerman -0 t[8-15] fails' ' + test_must_fail $powerman -h $testaddr -0 t[8-15] >offRF2.out && + echo Command completed with errors >offRF2.exp && + test_cmp offRF2.exp offRF2.out +' +test_expect_success 'powerman -q shows t[0-14] off, t15 unknown' ' + $powerman -h $testaddr -q >queryRF5.out && + makeoutput "" "t[0-14]" "t15" >queryRF5.exp && + test_cmp queryRF5.exp queryRF5.out +' +test_expect_success 'powerman -r t[8-15] fails' ' + test_must_fail $powerman -h $testaddr -r t[8-15] >resetRF1.out && + echo Command completed with errors >resetRF1.exp && + test_cmp resetRF1.exp resetRF1.out +' +test_expect_success 'powerman -q shows t[8-14] on, t15 unknown' ' + $powerman -h $testaddr -q >queryRF6.out && + makeoutput "t[8-14]" "t[0-7]" "t15" >queryRF6.exp && + test_cmp queryRF6.exp queryRF6.out +' +test_expect_success 'powerman -f t[8-15] fails' ' + test_must_fail $powerman -h $testaddr -f t[8-15] >flashRF1.out && + echo Command completed with errors >flashRF1.exp && + test_cmp flashRF1.exp flashRF1.out +' +test_expect_success 'powerman -u t[8-15] fails' ' + test_must_fail $powerman -h $testaddr -u t[8-15] >unflashRF1.out && + echo Command completed with errors >unflashRF1.exp && + test_cmp unflashRF1.exp unflashRF1.out +' +test_expect_success 'stop powerman daemon (setresult range scripts bad host)' ' + kill -15 $(cat powermand.pid) && + wait +' + +# +# "singlet" scripts +# +# on, off, cycle, reset, beacon_on, beacon_off +# + +test_expect_success 'create test powerman.conf (setresult singlet scripts)' ' + cat >powerman_singlet.conf <<-EOT + listen "$testaddr" + include "$testdevicesdir/redfishpower-setresult-singlet.dev" + device "test0" "redfishpower-setresult" "$redfishdir/redfishpower -h t[0-15] --test-mode |&" + node "t[0-15]" "test0" + EOT +' +test_expect_success 'start powerman daemon and wait for it to start (setresult singlet scripts)' ' + $powermand -c powerman_singlet.conf & + echo $! >powermand.pid && + $powerman --retry-connect=100 --server-host=$testaddr -q >/dev/null +' +test_expect_success 'powerman -q shows all off' ' + $powerman -h $testaddr -q >queryS1.out && + makeoutput "" "t[0-15]" "" >queryS1.exp && + test_cmp queryS1.exp queryS1.out +' +test_expect_success 'powerman -1 t[8,15] works' ' + $powerman -h $testaddr -1 t[8,15] >onS1.out && + echo Command completed successfully >onS1.exp && + test_cmp onS1.exp onS1.out +' +test_expect_success 'powerman -q shows t[8,15] on' ' + $powerman -h $testaddr -q >queryS2.out && + makeoutput "t[8,15]" "t[0-7,9-14]" "" >queryS2.exp && + test_cmp queryS2.exp queryS2.out +' +test_expect_success 'powerman -0 t[8,15] works' ' + $powerman -h $testaddr -0 t[8,15] >offS1.out && + echo Command completed successfully >offS1.exp && + test_cmp offS1.exp offS1.out +' +test_expect_success 'powerman -q shows all off' ' + $powerman -h $testaddr -q >queryS3.out && + makeoutput "" "t[0-15]" "" >queryS3.exp && + test_cmp queryS3.exp queryS3.out +' +test_expect_success 'powerman -c t[8,15] works' ' + $powerman -h $testaddr -c t[8,15] >cycleS1.out && + echo Command completed successfully >cycleS1.exp && + test_cmp cycleS1.exp cycleS1.out +' +test_expect_success 'powerman -q shows t[8,15] on' ' + $powerman -h $testaddr -q >queryS4.out && + makeoutput "t[8,15]" "t[0-7,9-14]" "" >queryS4.exp && + test_cmp queryS4.exp queryS4.out +' +test_expect_success 'powerman -0 t[8,15] works' ' + $powerman -h $testaddr -0 t[8,15] >offS2.out && + echo Command completed successfully >offS2.exp && + test_cmp offS2.exp offS2.out +' +test_expect_success 'powerman -q shows all off' ' + $powerman -h $testaddr -q >queryS5.out && + makeoutput "" "t[0-15]" "" >queryS5.exp && + test_cmp queryS5.exp queryS5.out +' +test_expect_success 'powerman -r t[8,15] works' ' + $powerman -h $testaddr -r t[8,15] >resetS1.out && + echo Command completed successfully >resetS1.exp && + test_cmp resetS1.exp resetS1.out +' +test_expect_success 'powerman -q shows t[8,15] on' ' + $powerman -h $testaddr -q >queryS6.out && + makeoutput "t[8,15]" "t[0-7,9-14]" "" >queryS6.exp && + test_cmp queryS6.exp queryS6.out +' +test_expect_success 'powerman -f t[8,15] works' ' + $powerman -h $testaddr -f t[8,15] >flashS1.out && + echo Command completed successfully >flashS1.exp && + test_cmp flashS1.exp flashS1.out +' +test_expect_success 'powerman -u t[8,15] works' ' + $powerman -h $testaddr -u t[8,15] >unflashS1.out && + echo Command completed successfully >unflashS1.exp && + test_cmp unflashS1.exp unflashS1.out +' +test_expect_success 'stop powerman daemon (setresult singlet scripts)' ' + kill -15 $(cat powermand.pid) && + wait +' + +# +# "singlet" scripts +# +# on, off, cycle, reset, beacon_on, beacon_off +# +# t15 set to bad, so will not work with anything below +# + +test_expect_success 'create powerman.conf for 16 cray redfish nodes and bad host (setresult singlet scripts bad host)' ' + cat >powerman_singlet_bad_host.conf <<-EOT + listen "$testaddr" + include "$testdevicesdir/redfishpower-setresult-singlet.dev" + device "test0" "redfishpower-setresult" "$redfishdir/redfishpower -h t[0-15] --test-mode --test-fail-power-cmd-hosts=t15 |&" + node "t[0-15]" "test0" + EOT +' +test_expect_success 'start powerman daemon and wait for it to start (setresult singlet scripts bad host)' ' + $powermand -Y -c powerman_singlet_bad_host.conf & + echo $! >powermand.pid && + $powerman --retry-connect=100 --server-host=$testaddr -d +' +test_expect_success 'powerman -q shows t[0-14] off, t15 unknown' ' + $powerman -h $testaddr -q >querySF1.out && + makeoutput "" "t[0-14]" "t15" >querySF1.exp && + test_cmp querySF1.exp querySF1.out +' +test_expect_success 'powerman -1 t[8,15] fails' ' + test_must_fail $powerman -h $testaddr -1 t[8,15] >onSF1.out && + echo Command completed with errors >onSF1.exp && + test_cmp onSF1.exp onSF1.out +' +test_expect_success 'powerman -q shows t8 on, t15 unknown' ' + $powerman -h $testaddr -q >querySF2.out && + makeoutput "t8" "t[0-7,9-14]" "t15" >querySF2.exp && + test_cmp querySF2.exp querySF2.out +' +test_expect_success 'powerman -0 t[8,15] fails' ' + test_must_fail $powerman -h $testaddr -0 t[8,15] >offSF1.out && + echo Command completed with errors >offSF1.exp && + test_cmp offSF1.exp offSF1.out +' +test_expect_success 'powerman -q shows t[0-14] off, t15 unknown' ' + $powerman -h $testaddr -q >querySF3.out && + makeoutput "" "t[0-14]" "t15" >querySF3.exp && + test_cmp querySF3.exp querySF3.out +' +test_expect_success 'powerman -c t[8,15] fails' ' + test_must_fail $powerman -h $testaddr -c t[8,15] >cycleSF1.out && + echo Command completed with errors >cycleSF1.exp && + test_cmp cycleSF1.exp cycleSF1.out +' +test_expect_success 'powerman -q shows t[8-14] on, t15 unknown' ' + $powerman -h $testaddr -q >querySF4.out && + makeoutput "t8" "t[0-7,9-14]" "t15" >querySF4.exp && + test_cmp querySF4.exp querySF4.out +' +test_expect_success 'powerman -0 t[8,15] fails' ' + test_must_fail $powerman -h $testaddr -0 t[8,15] >offSF2.out && + echo Command completed with errors >offSF2.exp && + test_cmp offSF2.exp offSF2.out +' +test_expect_success 'powerman -q shows t[0-14] off, t15 unknown' ' + $powerman -h $testaddr -q >querySF5.out && + makeoutput "" "t[0-14]" "t15" >querySF5.exp && + test_cmp querySF5.exp querySF5.out +' +test_expect_success 'powerman -r t[8,15] fails' ' + test_must_fail $powerman -h $testaddr -r t[8,15] >resetSF1.out && + echo Command completed with errors >resetSF1.exp && + test_cmp resetSF1.exp resetSF1.out +' +test_expect_success 'powerman -q shows t[8-14] on, t15 unknown' ' + $powerman -h $testaddr -q >querySF6.out && + makeoutput "t8" "t[0-7,9-14]" "t15" >querySF6.exp && + test_cmp querySF6.exp querySF6.out +' +test_expect_success 'powerman -f t[8,15] fails' ' + test_must_fail $powerman -h $testaddr -f t[8,15] >flashSF1.out && + echo Command completed with errors >flashSF1.exp && + test_cmp flashSF1.exp flashSF1.out +' +test_expect_success 'powerman -u t[8,15] fails' ' + test_must_fail $powerman -h $testaddr -u t[8,15] >unflashSF1.out && + echo Command completed with errors >unflashSF1.exp && + test_cmp unflashSF1.exp unflashSF1.out +' +test_expect_success 'stop powerman daemon (setresult singlet scripts bad host)' ' + kill -15 $(cat powermand.pid) && + wait +' + + + +test_done + +# vi: set ft=sh diff --git a/t/t0035-diag.t b/t/t0035-diag.t new file mode 100755 index 00000000..8c63fe32 --- /dev/null +++ b/t/t0035-diag.t @@ -0,0 +1,178 @@ +#!/bin/sh + +test_description='Test Powerman diag' + +. `dirname $0`/sharness.sh + +powermand=$SHARNESS_BUILD_DIRECTORY/src/powerman/powermand +powerman=$SHARNESS_BUILD_DIRECTORY/src/powerman/powerman +redfishdir=$SHARNESS_BUILD_DIRECTORY/src/redfishpower +testdevicesdir=$SHARNESS_TEST_SRCDIR/etc + +# Use port = 11000 + test number +# That way there won't be port conflicts with make -j +testaddr=localhost:11035 + +makeoutput() { + printf "on: %s\n" $1 + printf "off: %s\n" $2 + printf "unknown: %s\n" $3 +} + +# +# we will use redfishpower for this testing because it +# contains an option for failing specific hosts +# +# note that redfishpower does not natively support power cycle, reset, +# beacon on, or beacon off. So in the test device files +# +# redfishpower-setresult-all.dev +# redfishpower-setresult-range.dev +# redfishpower-setresult-singlet.dev +# +# power cycle, power reset, beacon on, and beacon off are "fake" implemented +# by just re-using power on or power off. +# + +test_expect_success 'create test powerman.conf' ' + cat >powerman_all.conf <<-EOT + listen "$testaddr" + include "$testdevicesdir/redfishpower-setresult-range.dev" + device "test0" "redfishpower-setresult" "$redfishdir/redfishpower -h t[0-15] --test-mode |&" + node "t[0-15]" "test0" + EOT +' +test_expect_success 'start powerman daemon and wait for it to start' ' + $powermand -c powerman_all.conf & + echo $! >powermand.pid && + $powerman --retry-connect=100 --server-host=$testaddr -q >/dev/null +' +test_expect_success 'powerman -q shows all off' ' + $powerman -h $testaddr --diag -q >queryA1.out && + makeoutput "" "t[0-15]" "" >queryA1.exp && + test_cmp queryA1.exp queryA1.out +' +test_expect_success 'powerman --diag -1 t[0-15] works' ' + $powerman -h $testaddr --diag -1 t[0-15] >onA1.out && + echo Command completed successfully >onA1.exp && + test_cmp onA1.exp onA1.out +' +test_expect_success 'powerman -q shows all on' ' + $powerman -h $testaddr --diag -q >queryA2.out && + makeoutput "t[0-15]" "" "" >queryA2.exp && + test_cmp queryA2.exp queryA2.out +' +test_expect_success 'powerman -0 t[0-15] works' ' + $powerman -h $testaddr --diag -0 t[0-15] >offA1.out && + echo Command completed successfully >offA1.exp && + test_cmp offA1.exp offA1.out +' +test_expect_success 'powerman -q shows all off' ' + $powerman -h $testaddr --diag -q >queryA3.out && + makeoutput "" "t[0-15]" "" >queryA3.exp && + test_cmp queryA3.exp queryA3.out +' +test_expect_success 'powerman --diag -1 t[0-7] works' ' + $powerman -h $testaddr --diag -1 t[0-7] >onA2.out && + echo Command completed successfully >onA2.exp && + test_cmp onA2.exp onA2.out +' +test_expect_success 'powerman -q shows t[0-7] on' ' + $powerman -h $testaddr --diag -q >queryA4.out && + makeoutput "t[0-7]" "t[8-15]" "" >queryA4.exp && + test_cmp queryA4.exp queryA4.out +' +test_expect_success 'powerman -0 t[0-7] works' ' + $powerman -h $testaddr --diag -0 t[0-7] >offA2.out && + echo Command completed successfully >offA2.exp && + test_cmp offA2.exp offA2.out +' +test_expect_success 'powerman -q shows all off' ' + $powerman -h $testaddr --diag -q >queryA5.out && + makeoutput "" "t[0-15]" "" >queryA5.exp && + test_cmp queryA5.exp queryA5.out +' +test_expect_success 'stop powerman daemon' ' + kill -15 $(cat powermand.pid) && + wait +' + +# +# t7,t15 set to bad, so will not work with anything below +# + +test_expect_success 'create powerman.conf for 16 cray redfish nodes and bad host' ' + cat >powerman_all_bad_host.conf <<-EOT + listen "$testaddr" + include "$testdevicesdir/redfishpower-setresult-range.dev" + device "test0" "redfishpower-setresult" "$redfishdir/redfishpower -h t[0-15] --test-mode --test-fail-power-cmd-hosts=t7,t15 |&" + node "t[0-15]" "test0" + EOT +' +test_expect_success 'start powerman daemon and wait for it to start' ' + $powermand -Y -c powerman_all_bad_host.conf & + echo $! >powermand.pid && + $powerman --retry-connect=100 --server-host=$testaddr -d +' +test_expect_success 'powerman -q shows t[0-6,8-14] off, t[7,15] unknown' ' + $powerman -h $testaddr --diag -q >queryAF1.out && + echo t[7,15]: error >queryAF1.exp && + makeoutput "" "t[0-6,8-14]" "t[7,15]" >>queryAF1.exp && + test_cmp queryAF1.exp queryAF1.out +' +test_expect_success 'powerman --diag -1 t[0-15] fails' ' + test_must_fail $powerman -h $testaddr --diag -1 t[0-15] >onAF1.out && + echo t[7,15]: error >onAF1.exp && + echo Command completed with errors >>onAF1.exp && + test_cmp onAF1.exp onAF1.out +' +test_expect_success 'powerman -q shows t[0-6,8-14] on, t[7,15] unknown' ' + $powerman -h $testaddr --diag -q >queryAF2.out && + echo t[7,15]: error >queryAF2.exp && + makeoutput "t[0-6,8-14]" "" "t[7,15]" >>queryAF2.exp && + test_cmp queryAF2.exp queryAF2.out +' +test_expect_success 'powerman -0 t[0-15] fails' ' + test_must_fail $powerman -h $testaddr --diag -0 t[0-15] >offAF1.out && + echo t[7,15]: error >offAF1.exp && + echo Command completed with errors >>offAF1.exp && + test_cmp offAF1.exp offAF1.out +' +test_expect_success 'powerman -q shows t[0-6,8-14] off, t[7,15] unknown' ' + $powerman -h $testaddr --diag -q >queryAF3.out && + echo t[7,15]: error >queryAF3.exp && + makeoutput "" "t[0-6,8-14]" "t[7,15]" >>queryAF3.exp && + test_cmp queryAF3.exp queryAF3.out +' +test_expect_success 'powerman --diag -1 t[0-7] fails' ' + test_must_fail $powerman -h $testaddr --diag -1 t[0-7] >onAF2.out && + echo t7: error >onAF2.exp && + echo Command completed with errors >>onAF2.exp && + test_cmp onAF2.exp onAF2.out +' +test_expect_success 'powerman -q shows t[0-6] on' ' + $powerman -h $testaddr --diag -q >queryAF4.out && + echo t[7,15]: error >queryAF4.exp && + makeoutput "t[0-6]" "t[8-14]" "t[7,15]" >>queryAF4.exp && + test_cmp queryAF4.exp queryAF4.out +' +test_expect_success 'powerman -0 t[0-7] fails' ' + test_must_fail $powerman -h $testaddr --diag -0 t[0-7] >offAF2.out && + echo t7: error >offAF2.exp && + echo Command completed with errors >>offAF2.exp && + test_cmp offAF2.exp offAF2.out +' +test_expect_success 'powerman -q shows t[0-6,8-14] off, t[7,15] unknown' ' + $powerman -h $testaddr --diag -q >queryAF5.out && + echo t[7,15]: error >queryAF5.exp && + makeoutput "" "t[0-6,8-14]" "t[7,15]" >>queryAF5.exp && + test_cmp queryAF5.exp queryAF5.out +' +test_expect_success 'stop powerman daemon' ' + kill -15 $(cat powermand.pid) && + wait +' + +test_done + +# vi: set ft=sh