diff --git a/cmd/cabinet/init.go b/cmd/cabinet/init.go index a62080e4..6913e3bc 100644 --- a/cmd/cabinet/init.go +++ b/cmd/cabinet/init.go @@ -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) diff --git a/cmd/cabinet/list_cabinet.go b/cmd/cabinet/list_cabinet.go index 52e0d047..8d869422 100644 --- a/cmd/cabinet/list_cabinet.go +++ b/cmd/cabinet/list_cabinet.go @@ -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" ) @@ -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)) @@ -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", + ) } } diff --git a/internal/domain/cabinet.go b/internal/domain/cabinet.go index 238c3b5e..73a1e03a 100644 --- a/internal/domain/cabinet.go +++ b/internal/domain/cabinet.go @@ -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 +} diff --git a/internal/provider/csm/csm.go b/internal/provider/csm/csm.go index 5796900c..efc8a114 100644 --- a/internal/provider/csm/csm.go +++ b/internal/provider/csm/csm.go @@ -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" @@ -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 +} diff --git a/internal/provider/interface.go b/internal/provider/interface.go index 126a763b..7682fdeb 100644 --- a/internal/provider/interface.go +++ b/internal/provider/interface.go @@ -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 {