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

generalize list cabinet workflow for multiple providers #161

Merged
merged 1 commit into from
Nov 28, 2023
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
30 changes: 16 additions & 14 deletions cmd/cabinet/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,37 +34,39 @@ import (
)

var (
cabinetNumber int
auto bool
accept bool
format string
sortBy string
ProviderAddCabinetCmd = &cobra.Command{}
cabinetNumber int
auto bool
accept bool
format string
sortBy string
ProviderAddCabinetCmd = &cobra.Command{}
ProviderListCabinetCmd = &cobra.Command{}
)

func init() {
var err error

// Add variants to root commands
// Add subcommands to root commands
root.AddCmd.AddCommand(AddCabinetCmd)
root.ListCmd.AddCommand(ListCabinetCmd)
root.RemoveCmd.AddCommand(RemoveCabinetCmd)

// Add a flag to show supported types
// Common 'add cabinet' flags and then merge with provider-specified command
AddCabinetCmd.Flags().BoolP("list-supported-types", "L", false, "List supported hardware types.")

// Cabinets
AddCabinetCmd.Flags().IntVar(&cabinetNumber, "cabinet", 1001, "Cabinet number.")
AddCabinetCmd.Flags().BoolVar(&auto, "auto", false, "Automatically recommend and assign required flags.")
AddCabinetCmd.MarkFlagsMutuallyExclusive("auto")
AddCabinetCmd.Flags().BoolVarP(&accept, "accept", "y", false, "Automatically accept recommended values.")
err = root.MergeProviderCommand(AddCabinetCmd, ProviderAddCabinetCmd)
if err != nil {
log.Error().Msgf("%+v", err)
os.Exit(1)
}

// Common 'list cabinet' flags and then merge with provider-specified command
ListCabinetCmd.Flags().StringVarP(&format, "format", "f", "pretty", "Format out output")
ListCabinetCmd.Flags().StringVarP(&sortBy, "sort", "s", "location", "Sort by a specific key")

// Merge CANI's command with the provider-specified command
// this allows for CANI's operations to remain consistent, while adding provider config on top
err = root.MergeProviderCommand(AddCabinetCmd, ProviderAddCabinetCmd)
err = root.MergeProviderCommand(ListCabinetCmd, ProviderListCabinetCmd)
if err != nil {
log.Error().Msgf("%+v", err)
os.Exit(1)
Expand Down
72 changes: 47 additions & 25 deletions cmd/cabinet/list_cabinet.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ import (
"fmt"
"os"
"sort"
"strings"
"text/tabwriter"

root "github.com/Cray-HPE/cani/cmd"
"github.com/Cray-HPE/cani/internal/inventory"
"github.com/Cray-HPE/cani/internal/provider/csm"
"github.com/Cray-HPE/cani/pkg/hardwaretypes"
"github.com/Cray-HPE/cani/pkg/pointers"
"github.com/google/uuid"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -90,12 +90,26 @@ func listCabinet(cmd *cobra.Command, args []string) error {
w := tabwriter.NewWriter(os.Stdout, minwidth, tabwidth, padding, padchar, tabwriter.AlignRight)
defer w.Flush()

fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%s\n",
// set the CANI columns
caniColumns := []string{
"UUID",
"Status",
"Type",
"HMN VLAN",
"Location")
"Location",
}
// Get columns set by the provider
providerColumns := root.D.ListCabinetMetadataColumns()

// combine CANI and provider columns
columns := []string{}
for _, col := range [][]string{caniColumns, providerColumns} {
columns = append(columns, col...)
}

fmt.Fprint(
w,
fmt.Sprintf("%v%s", strings.Join(columns, "\t"), "\n"),
)

// make keys slice to sort by values in the map
keys := make([]uuid.UUID, 0, len(filtered))
Expand All @@ -120,28 +134,36 @@ func listCabinet(cmd *cobra.Command, args []string) error {
return filtered[keys[i]].LocationPath.String() < filtered[keys[j]].LocationPath.String()
})

for _, hw := range keys {
// Start with an empty cabinet metadata struct, just in case if this cabinet doesn't have any
// metadata set
cabinetMetadata := csm.CabinetMetadata{}

if _, exists := filtered[hw].ProviderMetadata[inventory.CSMProvider]; exists {
csmMetadata, err := csm.DecodeProviderMetadata(filtered[hw])
if err != nil {
return err
}

if csmMetadata.Cabinet != nil {
cabinetMetadata = *csmMetadata.Cabinet
}
for _, u := range keys {
hw, exists := filtered[u]
if !exists {
return err
}
// get the provider-specific fields
providerValues, err := root.D.ListCabinetMetadataRow(hw)
if err != nil {
return err
}

fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%s\n",
filtered[hw].ID.String(),
filtered[hw].Status,
filtered[hw].DeviceTypeSlug,
pointers.IntPtrToStr(cabinetMetadata.HMNVlan),
filtered[hw].LocationPath.String())
// Set the fields CANI uses
fields := []string{"%s", "%s", "%s"}
// append any provider-specified ones, using a %+v to display them to avoid any typing issues at the cost of something ugly printing
for _, n := range providerColumns {
log.Debug().Msgf("Using provider-defined column: %+v", n)
fields = append(fields, "%+v")
}
// print the table with CANI and provider columns/rows
fmt.Fprint(
w,
fmt.Sprintf(strings.Join(fields, "\t"),
filtered[u].ID.String(),
filtered[u].Status,
filtered[u].DeviceTypeSlug,
filtered[u].LocationPath.String()),
"\t",
fmt.Sprintf(strings.Join(providerValues, "\t")),
"\n",
)
}

}
Expand Down
13 changes: 13 additions & 0 deletions internal/domain/cabinet.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,16 @@ func (d *Domain) Recommend(cmd *cobra.Command, args []string, auto bool) (recomm
}
return recommendations, nil
}

func (d *Domain) ListCabinetMetadataColumns() (columns []string) {
columns = d.externalInventoryProvider.ListCabinetMetadataColumns()
return columns
}

func (d *Domain) ListCabinetMetadataRow(hw inventory.Hardware) (row []string, err error) {
row, err = d.externalInventoryProvider.ListCabinetMetadataRow(hw)
if err != nil {
return row, err
}
return row, nil
}
18 changes: 18 additions & 0 deletions internal/provider/csm/csm.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ package csm

import (
"fmt"
"strconv"
"strings"

"github.com/Cray-HPE/cani/cmd/taxonomy"
"github.com/Cray-HPE/cani/internal/inventory"
"github.com/Cray-HPE/cani/internal/provider/csm/validate"
"github.com/Cray-HPE/cani/pkg/hardwaretypes"
"github.com/rs/zerolog/log"
Expand Down Expand Up @@ -198,3 +200,19 @@ func (csm *CSM) setupClients() (err error) {

return nil
}

// ListCabinetMetadataColumns returns a slice of strings, which are columns names of CSM-specific metadata to be shown when listing cabinets
func (csm *CSM) ListCabinetMetadataColumns() (columns []string) {
return []string{"HMN VLAN"}
}

// ListCabinetMetadataRow returns a slice of strings, which are values from the hardware that correlate to the columns they will be shown in
func (csm *CSM) ListCabinetMetadataRow(hw inventory.Hardware) (values []string, err error) {
md, err := DecodeProviderMetadata(hw)
if err != nil {
return values, err
}
vlan := strconv.Itoa(*md.Cabinet.HMNVlan)
values = append(values, vlan)
return values, nil
}
4 changes: 4 additions & 0 deletions internal/provider/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ type InventoryProvider interface {

// Return metadata about each field
GetFieldMetadata() ([]FieldMetadata, error)

// Workflows
ListCabinetMetadataColumns() (columns []string)
ListCabinetMetadataRow(inventory.Hardware) (values []string, err error)
}

type HardwareValidationResult struct {
Expand Down
Loading