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

redfishpower: support plug substitution #159

Merged
merged 3 commits into from
Mar 26, 2024
Merged
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
8 changes: 6 additions & 2 deletions man/redfishpower.8.in
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,15 @@ Set default path and optional post data to turn off plug.
.TP
.I "setplugs plugnames hostindices"
Associate a plug name with one of the hostnames specified on the
command line, referred to by its zero origin index. Can be called
multiple times to configure all possible plugs.
command line, referred to by its zero origin index. Can be called
multiple times to configure all possible plugs. In most
cases the number of plugs should equal the number of indices.
Multiple plugs can be mapped to a single host index, which is typically
used along with plug substitution (see "setpath" command below).
.TP
.I "setpath <plugnames> <cmd> <path> [postdata]"
Set path for specific plug power command ("stat", "on", "off") and optional post data.
The plug name can be substituted into the URI path by specifying "{{plug}}" in the path.
.TP
.I "settimeout <seconds>"
Set command timeout in seconds.
Expand Down
102 changes: 74 additions & 28 deletions src/redfishpower/redfishpower.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,39 +164,63 @@ void help(void)
printf(" off [plugs]\n");
}

static char *calc_path(const char *lpath, const char *plugname)
{
char *ptr;

/* assumption, at most one {{plug}} in path */
if ((ptr = strstr(lpath, "{{plug}}"))) {
/* +1 for NUL, -8 for {{plug}}, + strlen(plugname) */
char *tmp = xmalloc(strlen(lpath) + 1 - 8 + strlen(plugname));
int prefixlen = ptr - lpath;
strncpy(tmp, lpath, prefixlen);
strcpy(tmp + prefixlen, plugname);
strcpy(tmp + prefixlen + strlen(plugname), ptr + 8);
return tmp;
}
return xstrdup(lpath);
}

static void get_path(const char *cmd,
const char *plugname,
char **path,
char **postdata)
{
struct plug_data *pd = plugs_get_data(plugs, plugname);
char *lpath = NULL;
char *lpostdata = NULL;

if (strcmp(cmd, CMD_STAT) == 0) {
if (pd && pd->stat)
(*path) = pd->stat;
lpath = pd->stat;
else
(*path) = statpath;
lpath = statpath;
}
else if (strcmp(cmd, CMD_ON) == 0) {
if (pd && pd->on) {
(*path) = pd->on;
(*postdata) = pd->onpostdata;
lpath = pd->on;
lpostdata = pd->onpostdata;
}
else {
(*path) = onpath;
(*postdata) = onpostdata;
lpath = onpath;
lpostdata = onpostdata;
}
}
else if (strcmp(cmd, CMD_OFF) == 0) {
if (pd && pd->off) {
(*path) = pd->off;
(*postdata) = pd->offpostdata;
lpath = pd->off;
lpostdata = pd->offpostdata;
}
else {
(*path) = offpath;
(*postdata) = offpostdata;
lpath = offpath;
lpostdata = offpostdata;
}
}

if (lpath)
(*path) = calc_path(lpath, plugname);
if (lpostdata)
(*postdata) = xstrdup(lpostdata);
}

static size_t output_cb(void *contents, size_t size, size_t nmemb, void *userp)
Expand Down Expand Up @@ -379,6 +403,7 @@ static struct powermsg *stat_cmd_plug(CURLM * mh, char *plugname)
if (verbose > 1)
printf("DEBUG: %s hostname=%s plugname=%s path=%s\n",
CMD_STAT, pd->hostname, plugname, path);
free(path);
return pm;
}

Expand Down Expand Up @@ -547,6 +572,8 @@ struct powermsg *power_cmd_plug(CURLM * mh,
if (verbose > 1)
printf("DEBUG: %s hostname=%s plugname=%s path=%s\n",
cmd, pd->hostname, plugname, path);
free(path);
free(postdata);
return pm;
}

Expand Down Expand Up @@ -631,6 +658,7 @@ static void send_status_poll(struct powermsg *pm)
STATE_WAIT_UNTIL_ON_OFF);
if (!(nextpm->handle = zlistx_add_end(delayedcmds, nextpm)))
err_exit(true, "zlistx_add_end");
free(path);
}

static void on_off_process(struct powermsg *pm)
Expand Down Expand Up @@ -847,10 +875,9 @@ static void setplugs(char **av)
hostlist_t lplugs = NULL;
hostlist_t hostindices = NULL;
int plugcount, hostindexcount;
hostlist_iterator_t lplugsitr = NULL;
hostlist_iterator_t hostindicesitr = NULL;
char *plug;
char *hostindexstr;
int i;

if (!av[0] || !av[1]) {
printf("Usage: setplugs <plugnames> <hostindices>\n");
Expand All @@ -868,32 +895,51 @@ static void setplugs(char **av)

plugcount = hostlist_count(lplugs);
hostindexcount = hostlist_count(hostindices);
if (plugcount != hostindexcount) {
printf("setplugs: plugs count not equal to host index count");
goto cleanup;
}

if (!(lplugsitr = hostlist_iterator_create(lplugs)))
err_exit(true, "hostlist_iterator_create");
if (!(hostindicesitr = hostlist_iterator_create(hostindices)))
err_exit(true, "hostlist_iterator_create");

/* if user is electing to configure their own plugs, we must remove
* all of the initial ones configured in setup_hosts()
*/
remove_initial_plugs();

while ((plug = hostlist_next(lplugsitr))
&& (hostindexstr = hostlist_next(hostindicesitr))) {
if (setup_plug(plug, hostindexstr) < 0)
if (plugcount != hostindexcount) {
/* special case, user will use plug substitution */
if (plugcount > 1 && hostindexcount == 1) {
if (!(hostindexstr = hostlist_nth(hostindices, 0)))
err_exit(false, "setplugs: hostlist_nth contains no indices");
for (i = 0; i < plugcount; i++) {
if (!(plug = hostlist_nth(lplugs, i)))
err_exit(false, "setplugs: hostlist_nth plugs");
if (setup_plug(plug, hostindexstr) < 0) {
free(plug);
free(hostindexstr);
goto cleanup;
}
free(plug);
}
free(hostindexstr);
}
else {
printf("setplugs: plugs count not equal to host index count\n");
goto cleanup;
free(plug);
free(hostindexstr);
}
}
else {
for (i = 0; i < plugcount; i++) {
if (!(plug = hostlist_nth(lplugs, i)))
err_exit(false, "setplugs: hostlist_nth plugs");
if (!(hostindexstr = hostlist_nth(hostindices, i)))
err_exit(false, "setplugs: hostlist_nth indices");
if (setup_plug(plug, hostindexstr) < 0) {
free(plug);
free(hostindexstr);
goto cleanup;
}
free(plug);
free(hostindexstr);
}
}

cleanup:
hostlist_iterator_destroy(lplugsitr);
hostlist_iterator_destroy(hostindicesitr);
hostlist_destroy(lplugs);
hostlist_destroy(hostindices);
}
Expand Down
4 changes: 3 additions & 1 deletion t/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ EXTRA_DIST= \
etc/mcr_plugs.conf \
etc/vpc.dev \
etc/redfishpower-setplugs.dev \
etc/redfishpower-setpath.dev
etc/redfishpower-setpath.dev \
etc/redfishpower-plugsub.dev \
etc/redfishpower-plugsub-blades.dev


AM_CFLAGS = @WARNING_CFLAGS@
Expand Down
54 changes: 54 additions & 0 deletions t/etc/redfishpower-plugsub-blades.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Variant of redfishpower-cray-r272z30.dev that covers use of plug
# substitution and the assumption of blades.
#
# Notes:
# - hypothetical blades go through Node0
# - for easier grepping in tests, simplify paths
specification "redfishpower-plugsub-blades" {
timeout 60

script login {
expect "redfishpower> "
send "auth USER:PASS\n"
expect "redfishpower> "
send "setheader Content-Type:application/json\n"
expect "redfishpower> "
send "setplugs Node[0-15] [0-15]\n"
expect "redfishpower> "
send "setplugs Blade[0-3] 0\n"
expect "redfishpower> "
send "setstatpath redfish/Default-{{plug}}/Stat\n"
expect "redfishpower> "
send "setonpath redfish/Default-{{plug}}/Reset {\"ResetType\":\"On\"}\n"
expect "redfishpower> "
send "setoffpath redfish/Default-{{plug}}/Reset {\"ResetType\":\"ForceOff\"}\n"
expect "redfishpower> "
send "setcyclepath redfish/Default-{{plug}}/Reset {\"ResetType\":\"ForceRestart\"}\n"
Comment on lines +20 to +26
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If setpath with a <cmd> argument is the new way, maybe new dev scripts should start using that instead of the old set<cmd>path form?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setpath requires you to specify plugnames, so not as convenient for setting "default" paths for everything (which is all we need in this test case).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I thought it was just there for backwards compatibility.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's there for that too :-)

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"
expect "redfishpower> "
}
script off_ranged {
send "off %s\n"
expect "redfishpower> "
}
script cycle_ranged {
send "cycle %s\n"
expect "redfishpower> "
}
}
62 changes: 62 additions & 0 deletions t/etc/redfishpower-plugsub.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Variant of redfishpower-cray-r272z30.dev that covers use of plug
# substitution.
#
# Notes:
# - for easier grepping in tests, simplify paths
# - different paths for different chunks of nodes/plugs
# - no plug specific paths set for Node[8-15], will use defaults
# set via setstatpath, setonpath, etc.
specification "redfishpower-plugsub" {
timeout 60

script login {
expect "redfishpower> "
send "auth USER:PASS\n"
expect "redfishpower> "
send "setheader Content-Type:application/json\n"
expect "redfishpower> "
send "setplugs Node[0-15] [0-15]\n"
expect "redfishpower> "
send "setpath Node[0-7] stat redfish/Group0\n"
expect "redfishpower> "
send "setpath Node[0-7] on redfish/Group0-{{plug}}/Reset {\"ResetType\":\"On\"}\n"
expect "redfishpower> "
send "setpath Node[0-7] off redfish/Group0-{{plug}}/Reset {\"ResetType\":\"ForceOff\"}\n"
expect "redfishpower> "
send "setpath Node[0-7] cycle redfish/Group0-{{plug}}/Reset {\"ResetType\":\"ForceRestart\"}\n"
expect "redfishpower> "
send "setstatpath redfish/Default\n"
expect "redfishpower> "
send "setonpath redfish/Default-{{plug}}/Reset {\"ResetType\":\"On\"}\n"
expect "redfishpower> "
send "setoffpath redfish/Default-{{plug}}/Reset {\"ResetType\":\"ForceOff\"}\n"
expect "redfishpower> "
send "setcyclepath redfish/Default-{{plug}}/Reset {\"ResetType\":\"ForceRestart\"}\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"
expect "redfishpower> "
}
script off_ranged {
send "off %s\n"
expect "redfishpower> "
}
script cycle_ranged {
send "cycle %s\n"
expect "redfishpower> "
}
}
Loading
Loading