Skip to content

Commit cace0dc

Browse files
committed
feat: add support for displaying power status
Signed-off-by: Chris Harris <cjh@lbl.gov>
1 parent 1219bee commit cace0dc

File tree

6 files changed

+359
-4
lines changed

6 files changed

+359
-4
lines changed

cmd/pcs-status-list.go

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// This source code is licensed under the license found in the LICENSE file at
2+
// the root directory of this source tree.
3+
package cmd
4+
5+
import (
6+
"encoding/json"
7+
"errors"
8+
"fmt"
9+
"os"
10+
"strings"
11+
12+
"github.com/spf13/cobra"
13+
14+
"github.com/OpenCHAMI/ochami/internal/log"
15+
"github.com/OpenCHAMI/ochami/pkg/client"
16+
"github.com/OpenCHAMI/ochami/pkg/format"
17+
)
18+
19+
var statusXnames []string
20+
21+
type PowerFilter string
22+
23+
var powerFilter PowerFilter = ""
24+
25+
const (
26+
powerOn PowerFilter = "on"
27+
powerOff PowerFilter = "off"
28+
powerUndefined PowerFilter = "undefined"
29+
)
30+
31+
func (l *PowerFilter) String() string {
32+
return string(*l)
33+
}
34+
35+
func (l *PowerFilter) Set(value string) error {
36+
switch strings.ToLower(value) {
37+
case "on", "off", "undefined":
38+
*l = PowerFilter(strings.ToLower(value))
39+
return nil
40+
default:
41+
return fmt.Errorf("invalid power filter: %s (must be on, off, or undefined)", value)
42+
}
43+
}
44+
45+
func (l PowerFilter) Type() string {
46+
return "PowerFilter"
47+
}
48+
49+
var (
50+
powerFilterHelp = map[string]string{
51+
string(powerOn): "Include components that are powered on",
52+
string(powerOff): "Include components that are powered off",
53+
string(powerUndefined): "Include components with undefined power state",
54+
}
55+
)
56+
57+
func pcsStatusListPowerFilterCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
58+
var helpSlice []string
59+
for k, v := range powerFilterHelp {
60+
helpSlice = append(helpSlice, fmt.Sprintf("%s\t%s", k, v))
61+
}
62+
return helpSlice, cobra.ShellCompDirectiveNoFileComp
63+
}
64+
65+
type MgmtFilter string
66+
67+
var mgmtFilter MgmtFilter = ""
68+
69+
func (l *MgmtFilter) String() string {
70+
return string(*l)
71+
}
72+
73+
func (l *MgmtFilter) Set(value string) error {
74+
switch strings.ToLower(value) {
75+
case "available", "unavailable":
76+
*l = MgmtFilter(strings.ToLower(value))
77+
return nil
78+
default:
79+
return fmt.Errorf("invalid management filter: %s (must be available or unavailable)", value)
80+
}
81+
}
82+
83+
func (l MgmtFilter) Type() string {
84+
return "MgmtFilter"
85+
}
86+
87+
var (
88+
mgmtFilterHelp = map[string]string{
89+
"available": "Include components that are available",
90+
"unavailable": "Include components that are unavailable",
91+
}
92+
)
93+
94+
func pcsStatusListMgmtFilterCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
95+
var helpSlice []string
96+
for k, v := range mgmtFilterHelp {
97+
helpSlice = append(helpSlice, fmt.Sprintf("%s\t%s", k, v))
98+
}
99+
return helpSlice, cobra.ShellCompDirectiveNoFileComp
100+
}
101+
102+
// pcsStatusListCmd represents the "pcs status list" command
103+
var pcsStatusListCmd = &cobra.Command{
104+
Use: "list",
105+
Args: cobra.NoArgs,
106+
Short: "List active PCS transitions",
107+
Long: `List active PCS transitions.
108+
109+
See ochami-pcs(1) for more details.`,
110+
Example: ` # List status
111+
ochami pcs status list`,
112+
Run: func(cmd *cobra.Command, args []string) {
113+
// Create client to use for requests
114+
pcsClient := pcsGetClient(cmd)
115+
116+
// Handle token for this command
117+
handleToken(cmd)
118+
119+
// Get status
120+
statusHttpEnv, err := pcsClient.GetStatus(statusXnames, string(powerFilter), string(mgmtFilter), token)
121+
if err != nil {
122+
if errors.Is(err, client.UnsuccessfulHTTPError) {
123+
log.Logger.Error().Err(err).Msg("PCS status request yielded unsuccessful HTTP response")
124+
} else {
125+
log.Logger.Error().Err(err).Msg("failed to list PCS transitions")
126+
}
127+
logHelpError(cmd)
128+
os.Exit(1)
129+
}
130+
131+
var output interface{}
132+
err = json.Unmarshal(statusHttpEnv.Body, &output)
133+
if err != nil {
134+
log.Logger.Error().Err(err).Msg("failed to unmarshal status response")
135+
logHelpError(cmd)
136+
os.Exit(1)
137+
}
138+
139+
// Print output
140+
if outBytes, err := format.MarshalData(output, formatOutput); err != nil {
141+
log.Logger.Error().Err(err).Msg("failed to format output")
142+
logHelpError(cmd)
143+
os.Exit(1)
144+
} else {
145+
fmt.Println(string(outBytes))
146+
}
147+
},
148+
}
149+
150+
func init() {
151+
pcsStatusListCmd.Flags().StringSliceVarP(&statusXnames, "xname", "x", []string{}, "one or more xnames to get the status for")
152+
pcsStatusListCmd.Flags().VarP(&powerFilter, "power-filter", "p", "filter results by power state (on, off, undefined)")
153+
pcsStatusListCmd.RegisterFlagCompletionFunc("power-filter", pcsStatusListPowerFilterCompletion)
154+
pcsStatusListCmd.Flags().VarP(&mgmtFilter, "mgmt-filter", "m", "filter results by management state (available, unavailable)")
155+
pcsStatusListCmd.RegisterFlagCompletionFunc("mgmt-filter", pcsStatusListMgmtFilterCompletion)
156+
157+
pcsStatusListCmd.Flags().VarP(&formatOutput, "format-output", "F", "format of output printed to standard output (json,json-pretty,yaml)")
158+
pcsStatusListCmd.RegisterFlagCompletionFunc("format-output", completionFormatData)
159+
160+
pcsStatusCmd.AddCommand(pcsStatusListCmd)
161+
}

cmd/pcs-status-show.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// This source code is licensed under the license found in the LICENSE file at
2+
// the root directory of this source tree.
3+
package cmd
4+
5+
import (
6+
"encoding/json"
7+
"errors"
8+
"fmt"
9+
"os"
10+
11+
"github.com/spf13/cobra"
12+
13+
"github.com/OpenCHAMI/ochami/internal/log"
14+
"github.com/OpenCHAMI/ochami/pkg/client"
15+
"github.com/OpenCHAMI/ochami/pkg/format"
16+
)
17+
18+
type statusResponse struct {
19+
Status []map[string]interface{} `json:"status"`
20+
}
21+
22+
// pcsStatusShowCmd represents the "pcs status show" command
23+
var pcsStatusShowCmd = &cobra.Command{
24+
Use: "show <xname>",
25+
Args: cobra.ExactArgs(1),
26+
Short: "Show power status of target component",
27+
Long: `Show power status of target component .
28+
29+
See ochami-pcs(1) for more details.`,
30+
Example: ` # show power status of component
31+
ochami pcs status show x3000c0s15b0`,
32+
Run: func(cmd *cobra.Command, args []string) {
33+
xname := args[0]
34+
35+
// Create client to use for requests
36+
pcsClient := pcsGetClient(cmd)
37+
38+
// Handle token for this command
39+
handleToken(cmd)
40+
41+
// Get status
42+
statusHttpEnv, err := pcsClient.GetStatus([]string{xname}, "", "", token)
43+
if err != nil {
44+
if errors.Is(err, client.UnsuccessfulHTTPError) {
45+
log.Logger.Error().Err(err).Msg("PCS status request yielded unsuccessful HTTP response")
46+
} else {
47+
log.Logger.Error().Err(err).Msg("failed to get power status")
48+
}
49+
logHelpError(cmd)
50+
os.Exit(1)
51+
}
52+
53+
var output statusResponse
54+
55+
err = json.Unmarshal(statusHttpEnv.Body, &output)
56+
if err != nil {
57+
log.Logger.Error().Err(err).Msg("failed to unmarshal status")
58+
logHelpError(cmd)
59+
os.Exit(1)
60+
}
61+
62+
// Check if status array is empty
63+
if len(output.Status) == 0 {
64+
log.Logger.Error().Msg("no status found for the specified component")
65+
logHelpError(cmd)
66+
os.Exit(1)
67+
}
68+
69+
// Print output just for first element in status array
70+
if outBytes, err := format.MarshalData(output.Status[0], formatOutput); err != nil {
71+
log.Logger.Error().Err(err).Msg("failed to format output")
72+
logHelpError(cmd)
73+
os.Exit(1)
74+
} else {
75+
fmt.Println(string(outBytes))
76+
}
77+
},
78+
}
79+
80+
func init() {
81+
pcsStatusShowCmd.Flags().VarP(&formatOutput, "format-output", "F", "format of output printed to standard output (json,json-pretty,yaml)")
82+
83+
pcsStatusShowCmd.RegisterFlagCompletionFunc("format-output", completionFormatData)
84+
85+
pcsStatusCmd.AddCommand(pcsStatusShowCmd)
86+
}

cmd/pcs-status.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// This source code is licensed under the license found in the LICENSE file at
2+
// the root directory of this source tree.
3+
package cmd
4+
5+
import (
6+
"github.com/spf13/cobra"
7+
)
8+
9+
// pcsStatusCmd represents the "pcs status" command
10+
var pcsStatusCmd = &cobra.Command{
11+
Use: "status",
12+
Args: cobra.NoArgs,
13+
Short: "Manage PCS status",
14+
Run: func(cmd *cobra.Command, args []string) {
15+
if len(args) == 0 {
16+
printUsageHandleError(cmd)
17+
}
18+
},
19+
}
20+
21+
func init() {
22+
pcsCmd.AddCommand(pcsStatusCmd)
23+
}

cmd/pcs-transition-start.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import (
1515
"github.com/OpenCHAMI/ochami/pkg/format"
1616
)
1717

18-
var xnames []string
1918
var operation string
19+
var transitionsXnames []string
2020

2121
// validOperations returns a list of valid PCS operations
2222
func validOperations() []string {
@@ -68,15 +68,15 @@ See ochami-pcs(1) for more details.`,
6868

6969
// Get the list of target components
7070
var err error
71-
xnames, err = cmd.Flags().GetStringSlice("xname")
71+
transitionsXnames, err = cmd.Flags().GetStringSlice("xname")
7272
if err != nil {
7373
log.Logger.Error().Err(err).Msg("failed to get value for --xname")
7474
logHelpError(cmd)
7575
os.Exit(1)
7676
}
7777

7878
// Create transition
79-
transitionHttpEnv, err := pcsClient.CreateTransition(operation, nil, xnames, token)
79+
transitionHttpEnv, err := pcsClient.CreateTransition(operation, nil, transitionsXnames, token)
8080
if err != nil {
8181
if errors.Is(err, client.UnsuccessfulHTTPError) {
8282
log.Logger.Error().Err(err).Msg("PCS transition create request yielded unsuccessful HTTP response")

man/ochami-pcs.1.sc

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,53 @@ Subcommands for this command are as follows:
7373

7474
## status
7575

76-
Manage power status.
76+
Display power status.
77+
78+
Subcommands for this command are as follows:
79+
80+
*show* [-F _format_] _xname_
81+
Show the power status of a component.
82+
83+
This command accepts the following options:
84+
85+
*-F, --format-output* _format_
86+
Output response data in specified _format_. Supported values are:
87+
88+
- _json_ (default)
89+
- _json-pretty_
90+
- _yaml_
91+
92+
*xname*
93+
XName of the component to show the power status for.
94+
95+
*list* [-F _format_]
96+
List the power status of all components.
97+
98+
This command accepts the following options:
99+
100+
*-F, --format-output* _format_
101+
Output response data in specified _format_. Supported values are:
102+
103+
- _json_ (default)
104+
- _json-pretty_
105+
- _yaml_
106+
107+
*-x, --xname* _xname_,...
108+
Comma-separated list of xnames to show power status for. If not
109+
specified, status for all components will be shown.
110+
111+
*-p, --power-filter* _state_
112+
power state filter. Supported values are:
113+
114+
- _on_
115+
- _off_
116+
- _undefined_
117+
118+
*-m, --mgmt-filter* _state_
119+
management state filter. Supported values are:
120+
121+
- _available_
122+
- _unavailable_
77123

78124
## transitions
79125

0 commit comments

Comments
 (0)