Skip to content

Commit

Permalink
feat: add smd-group-member-set command
Browse files Browse the repository at this point in the history
synackd committed Jan 28, 2025
1 parent 28b06d5 commit a43c657
Showing 3 changed files with 141 additions and 0 deletions.
63 changes: 63 additions & 0 deletions cmd/smd-group-member-set.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// This source code is licensed under the license found in the LICENSE file at
// the root directory of this source tree.
package cmd

import (
"errors"
"os"

"github.com/OpenCHAMI/ochami/internal/client"
"github.com/OpenCHAMI/ochami/internal/log"
"github.com/spf13/cobra"
)

// groupMemberSetCmd represents the smd-group-member-set command
var groupMemberSetCmd = &cobra.Command{
Use: "set <group_label> <component>...",
Args: cobra.MinimumNArgs(2),
Short: "Set group membership list to a list of components",
Long: `Set group membership list to a list of components. The components specified
in the list are set as the only members of the group. If a component
specified is already in the group, it remains in the group. If a
component specified is not already in te group, it is added to the
group. If a component is in the group but not specified, it is
removed from the group.`,
Example: ` ochami smd group member set compute x1000c1s7b1n0 x1000c1s7b2n0`,
Run: func(cmd *cobra.Command, args []string) {
// Without a base URI, we cannot do anything
smdBaseURI, err := getBaseURI(cmd)
if err != nil {
log.Logger.Error().Err(err).Msg("failed to get base URI for SMD")
os.Exit(1)
}

// This endpoint requires authentication, so a token is needed
setTokenFromEnvVar(cmd)
checkToken(cmd)

// Create client to make request to SMD
smdClient, err := client.NewSMDClient(smdBaseURI, insecure)
if err != nil {
log.Logger.Error().Err(err).Msg("error creating new SMD client")
os.Exit(1)
}

// Check if a CA certificate was passed and load it into client if valid
useCACert(smdClient.OchamiClient)

// Send off request
_, err = smdClient.PutGroupMembers(token, args[0], args[1:]...)
if err != nil {
if errors.Is(err, client.UnsuccessfulHTTPError) {
log.Logger.Error().Err(err).Msgf("SMD group member request for group %s yielded unsuccessful HTTP response", args[0])
} else {
log.Logger.Error().Err(err).Msgf("failed to set group membership for group %s in SMD", args[0])
}
os.Exit(1)
}
},
}

func init() {
groupMemberCmd.AddCommand(groupMemberSetCmd)
}
57 changes: 57 additions & 0 deletions internal/client/smd.go
Original file line number Diff line number Diff line change
@@ -118,6 +118,13 @@ type Group struct {
} `json:"members,omitempty"`
}

// GroupMembers represents the payload structure for SMD group membership for
// PUT requests. It consists of only the group label and list of group IDs.
type GroupMembers struct {
Label string `json:"label"`
IDs []string `json:"ids"`
}

// NewSMDClient takes a baseURI and basePath and returns a pointer to a new
// SMDClient. If an error occurred creating the embedded OchamiClient, it is
// returned. If insecure is true, TLS certificates will not be verified.
@@ -775,6 +782,56 @@ func (sc *SMDClient) PutRedfishEndpointsV2(rfes RedfishEndpointSliceV2, token st
return henvs, errors, nil
}

// PutGroupMembers is a wrapper function around OchamiClient.PutData that takes
// a token, group name, and a list of one or more component IDs. It puts the
// token in the request headers as an authorization bearer and calls
// OchamiClient.PostData on the SMD group members API endpoint with the group
// and member list.
func (sc *SMDClient) PutGroupMembers(token, group string, members ...string) (HTTPEnvelope, error) {
var (
henv HTTPEnvelope
headers *HTTPHeaders
body HTTPBody
)

// Check that group and member list are non-empty
if group == "" {
return henv, fmt.Errorf("PutGroupMembers(): no group label specified to set members of")
}
if len(members) == 0 {
return henv, fmt.Errorf("PutGroupMembers(): no members specified")
}

// Add token to headers
headers = NewHTTPHeaders()
if token != "" {
if err := headers.SetAuthorization(token); err != nil {
return henv, fmt.Errorf("PutGroupMembers(): error setting token in HTTP headers: %w", err)
}
}

// Calculate endpoint path for group
groupPath, err := url.JoinPath(SMDRelpathGroups, group, "members")
if err != nil {
return henv, fmt.Errorf("PutGroupMembers(): failed to join group path (%s) with group label (%s): %w", SMDRelpathGroups, group)
}

// Send request and return response
g := GroupMembers{
Label: group,
IDs: members,
}
if body, err = json.Marshal(g); err != nil {
return henv, fmt.Errorf("PutGroupMembers(): failed to marshal group data: %w", err)
}
henv, err = sc.PutData(groupPath, "", headers, body)
if err != nil {
err = fmt.Errorf("PutGroupMembers(): failed to PUT members to group %s: %w", group, err)
}

return henv, err
}

// PatchComponentsNID is a wrapper function around OchamiClient.PatchData that
// takes a slice of Components and a token. It doesn't read any data fields
// within each Component except ID (xname) and NID, and for each Component, all
21 changes: 21 additions & 0 deletions man/ochami-smd.1.sc
Original file line number Diff line number Diff line change
@@ -132,6 +132,21 @@ Below is an example of a single *Group* in JSON form.
]
```

If performing a PUT on group membership, e.g. with *ochami smd group member
set*, then the form uses _label_ and _ids_ as:

```
{
"label": "blue",
"ids": [
"x1c0s1b0n0",
"x1c0s1b0n1",
"x1c0s2b0n0",
"x1c0s2b0n1"
]
}
```

## EthernetInterface

The *EthernetInterface* contains information on a network interface for a
@@ -569,6 +584,12 @@ Subcommands for this command are as follows:
- _json_ (default)
- _yaml_

*set* _group_name_ _xname_...
Set the membership list of _group_name_ to _xname_.... Xnames specified that
are not already in the group are added to it, xnames specified that are
already in the group remain in the group, and xnames not specified that are
already in the group are removed from the group.

# AUTHOR

Written by Devon T. Bautista and maintained by the OpenCHAMI developers.

0 comments on commit a43c657

Please sign in to comment.