diff --git a/cmd/blade/add_blade.go b/cmd/blade/add_blade.go index 59efa44c..8eb47fc8 100644 --- a/cmd/blade/add_blade.go +++ b/cmd/blade/add_blade.go @@ -42,11 +42,12 @@ import ( // AddBladeCmd represents the blade add command var AddBladeCmd = &cobra.Command{ - Use: "blade", + Use: "blade PROVIDER", Short: "Add blades to the inventory.", Long: `Add blades to the inventory.`, PreRunE: validHardware, // Hardware can only be valid if defined in the hardware library - RunE: addBlade, // Add a blade when this sub-command is called + Args: cobra.ExactArgs(1), + RunE: addBlade, // Add a blade when this sub-command is called } // addBlade adds a blade to the inventory diff --git a/cmd/blade/init.go b/cmd/blade/init.go index 7452c7df..636e1489 100644 --- a/cmd/blade/init.go +++ b/cmd/blade/init.go @@ -26,7 +26,12 @@ package blade import ( + "os" + root "github.com/Cray-HPE/cani/cmd" + "github.com/Cray-HPE/cani/internal/domain" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" ) var ( @@ -40,7 +45,8 @@ var ( sortBy string ) -func init() { +func Init() { + log.Trace().Msgf("%+v", "github.com/Cray-HPE/cani/cmd/blade.init") // Add blade variants to root commands root.AddCmd.AddCommand(AddBladeCmd) root.ListCmd.AddCommand(ListBladeCmd) @@ -63,4 +69,15 @@ func init() { RemoveBladeCmd.Flags().BoolVarP(&recursion, "recursive", "R", false, "Recursively delete child hardware") ListBladeCmd.Flags().StringVarP(&format, "format", "f", "pretty", "Format output") ListBladeCmd.Flags().StringVarP(&sortBy, "sort", "s", "location", "Sort by a specific key") + + // Register all provider commands during init() + for _, p := range domain.GetProviders() { + for _, c := range []*cobra.Command{AddBladeCmd, ListBladeCmd} { + err := root.RegisterProviderCommand(p, c) + if err != nil { + log.Error().Msgf("Unable to get command '%s %s' from provider %s ", c.Parent().Name(), c.Name(), p.Slug()) + os.Exit(1) + } + } + } } diff --git a/cmd/blade/list_blade.go b/cmd/blade/list_blade.go index 640da1a1..45880aa9 100644 --- a/cmd/blade/list_blade.go +++ b/cmd/blade/list_blade.go @@ -35,10 +35,10 @@ import ( // ListBladeCmd represents the blade list command var ListBladeCmd = &cobra.Command{ - Use: "blade", + Use: "blade PROVIDER", Short: "List blades in the inventory.", Long: `List blades in the inventory.`, - Args: cobra.ArbitraryArgs, + Args: cobra.ExactArgs(1), RunE: listBlade, } diff --git a/cmd/blade/validate.go b/cmd/blade/validate.go index 13c4a460..0a1f4e78 100644 --- a/cmd/blade/validate.go +++ b/cmd/blade/validate.go @@ -31,13 +31,11 @@ import ( "os" root "github.com/Cray-HPE/cani/cmd" - "github.com/rs/zerolog/log" "github.com/spf13/cobra" ) // validHardware checks that the hardware type is valid by comparing it against the list of hardware types func validHardware(cmd *cobra.Command, args []string) (err error) { - log.Debug().Msgf("Validating hardware %+v", root.D) if cmd.Flags().Changed("list-supported-types") { cmd.SetOut(os.Stdout) for _, hw := range root.BladeTypes { diff --git a/cmd/cabinet/add_cabinet.go b/cmd/cabinet/add_cabinet.go index cac81516..cf9be270 100644 --- a/cmd/cabinet/add_cabinet.go +++ b/cmd/cabinet/add_cabinet.go @@ -42,11 +42,12 @@ import ( // AddCabinetCmd represents the cabinet add command var AddCabinetCmd = &cobra.Command{ - Use: "cabinet", + Use: "cabinet PROVIDER", Short: "Add cabinets to the inventory.", Long: `Add cabinets to the inventory.`, PreRunE: validHardware, // Hardware can only be valid if defined in the hardware library - RunE: addCabinet, // Add a cabinet when this sub-command is called + Args: cobra.ExactArgs(1), + RunE: addCabinet, } // addCabinet adds a cabinet to the inventory @@ -75,7 +76,7 @@ func addCabinet(cmd *cobra.Command, args []string) (err error) { } // log the provider recommendations to the screen - recommendations.Print() + root.D.PrintRecommendations(cmd, args, recommendations) } // Add the cabinet to the inventory using domain methods @@ -98,8 +99,6 @@ func addCabinet(cmd *cobra.Command, args []string) (err error) { var filtered = make(map[uuid.UUID]inventory.Hardware, 0) for _, result := range result.AddedHardware { if result.Hardware.Type == hardwaretypes.Cabinet { - log.Debug().Msgf("%s added at %s with parent %s (%s)", result.Hardware.Type, result.Location.String(), hardwaretypes.System, result.Hardware.Parent) - log.Info().Str("status", "SUCCESS").Msgf("%s %d was successfully staged to be added to the system", hardwaretypes.Cabinet, recommendations.CabinetOrdinal) filtered[result.Hardware.ID] = result.Hardware } } diff --git a/cmd/cabinet/init.go b/cmd/cabinet/init.go index 9f900cce..533e3707 100644 --- a/cmd/cabinet/init.go +++ b/cmd/cabinet/init.go @@ -29,22 +29,20 @@ import ( "os" root "github.com/Cray-HPE/cani/cmd" + "github.com/Cray-HPE/cani/internal/domain" "github.com/rs/zerolog/log" "github.com/spf13/cobra" ) var ( - cabinetNumber int - auto bool - accept bool - format string - sortBy string - ProviderAddCabinetCmd = &cobra.Command{} - ProviderListCabinetCmd = &cobra.Command{} + auto bool + accept bool + format string + sortBy string ) -func init() { - var err error +func Init() { + log.Trace().Msgf("%+v", "github.com/Cray-HPE/cani/cmd/cabinet.init") // Add subcommands to root commands root.AddCmd.AddCommand(AddCabinetCmd) @@ -53,22 +51,22 @@ func init() { // Common 'add cabinet' flags and then merge with provider-specified command AddCabinetCmd.Flags().BoolP("list-supported-types", "L", false, "List supported hardware types.") - 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) - 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") - err = root.MergeProviderCommand(ListCabinetCmd) - if err != nil { - log.Error().Msgf("%+v", err) - os.Exit(1) + + // Register all provider commands during init() + for _, p := range domain.GetProviders() { + for _, c := range []*cobra.Command{AddCabinetCmd, ListCabinetCmd} { + err := root.RegisterProviderCommand(p, c) + if err != nil { + log.Error().Msgf("Unable to get command '%s %s' from provider %s ", c.Parent().Name(), c.Name(), p.Slug()) + os.Exit(1) + } + } } } diff --git a/cmd/cabinet/list_cabinet.go b/cmd/cabinet/list_cabinet.go index b4d3837a..8e97cf1b 100644 --- a/cmd/cabinet/list_cabinet.go +++ b/cmd/cabinet/list_cabinet.go @@ -35,10 +35,10 @@ import ( // ListCabinetCmd represents the cabinet list command var ListCabinetCmd = &cobra.Command{ - Use: "cabinet", + Use: "cabinet PROVIDER", Short: "List cabinets in the inventory.", Long: `List cabinets in the inventory.`, - Args: cobra.ArbitraryArgs, + Args: cobra.ExactArgs(1), RunE: listCabinet, } diff --git a/cmd/cabinet/validate.go b/cmd/cabinet/validate.go index dc27acab..19fa8bb1 100644 --- a/cmd/cabinet/validate.go +++ b/cmd/cabinet/validate.go @@ -72,33 +72,5 @@ func validHardware(cmd *cobra.Command, args []string) (err error) { } } - err = validFlagCombos(cmd, args) - if err != nil { - return err - } - - return nil -} - -// validFlagCombos has additional flag logic to account for overiding required flags with the --auto flag -func validFlagCombos(cmd *cobra.Command, args []string) error { - cabinetSet := cmd.Flags().Changed("cabinet") - vlanIdSet := cmd.Flags().Changed("vlan-id") - autoSet := cmd.Flags().Changed("auto") - // if auto is set, the values are recommended and the required flags are bypassed - if autoSet { - return nil - } else { - if !cabinetSet && !vlanIdSet { - return errors.New("required flag(s) \"cabinet\", \"vlan-id\" not set") - } - if cabinetSet && !vlanIdSet { - return errors.New("required flag(s) \"vlan-id\" not set") - } - if !cabinetSet && vlanIdSet { - return errors.New("required flag(s) \"cabinet\" not set") - } - } - return nil } diff --git a/cmd/cdu/init.go b/cmd/cdu/init.go index c8c2403a..3b2e3bd8 100644 --- a/cmd/cdu/init.go +++ b/cmd/cdu/init.go @@ -27,6 +27,7 @@ package cdu import ( root "github.com/Cray-HPE/cani/cmd" + "github.com/rs/zerolog/log" ) var ( @@ -34,7 +35,8 @@ var ( supportedHw []string ) -func init() { +func Init() { + log.Trace().Msgf("%+v", "github.com/Cray-HPE/cani/cmd/cdu.init") // Add variants to root commands root.AddCmd.AddCommand(AddCduCmd) root.ListCmd.AddCommand(ListCduCmd) diff --git a/cmd/chassis/init.go b/cmd/chassis/init.go index e2a2d637..cec3654e 100644 --- a/cmd/chassis/init.go +++ b/cmd/chassis/init.go @@ -27,6 +27,7 @@ package chassis import ( root "github.com/Cray-HPE/cani/cmd" + "github.com/rs/zerolog/log" ) var ( @@ -36,7 +37,8 @@ var ( sortBy string ) -func init() { +func Init() { + log.Trace().Msgf("%+v", "github.com/Cray-HPE/cani/cmd/chassis.init") // Add variants to root commands root.AddCmd.AddCommand(AddChassisCmd) root.ListCmd.AddCommand(ListChassisCmd) diff --git a/cmd/config/config.go b/cmd/config/config.go index 9f132e43..b895c7d1 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -36,6 +36,10 @@ import ( "gopkg.in/yaml.v3" ) +func Init() { + log.Trace().Msgf("%+v", "github.com/Cray-HPE/cani/cmd/config.init") +} + // Config is the top-level config struct that is written/read to/from a file type Config struct { Session *Session `yaml:"session"` @@ -145,16 +149,34 @@ func WriteConfig(path string, cfg *Config) error { return nil } -// AvailableDomains returns a slice of available domains read from the config -func (c *Config) AvailableDomains() (map[string]*domain.Domain, error) { - domains := make(map[string]*domain.Domain, 0) - +func (c *Config) ActiveProvider() (activeDomain *domain.Domain, err error) { + // Find an active session + activeDomains := []*domain.Domain{} + activeProviders := []string{} for p, d := range c.Session.Domains { - log.Info().Msgf("Found an available domain: %+v", p) - _, exists := domains[p] - if !exists { - domains[p] = d + if d.Active { + log.Debug().Msgf("Provider '%s' is ACTIVE", p) + activeDomains = append(activeDomains, d) + activeProviders = append(activeProviders, p) + } else { + log.Debug().Msgf("Provider '%s' is inactive", p) } } - return domains, nil + + // Check that only one session is active + if len(activeProviders) > 1 { + for _, p := range activeProviders { + err := fmt.Errorf("currently active: %v", p) + log.Error().Msgf("%v", err) + } + + return nil, fmt.Errorf("only one session may be active at a time") + } + if len(activeDomains) == 0 { + log.Info().Msgf("No active domains") + return nil, nil + } + activeDomain = activeDomains[0] + + return activeDomain, nil } diff --git a/cmd/export.go b/cmd/export.go index 8d0049d7..7240a620 100644 --- a/cmd/export.go +++ b/cmd/export.go @@ -35,9 +35,10 @@ var ( // ExportCmd represents the export command var ExportCmd = &cobra.Command{ - Use: "export", + Use: "export PROVIDER", Short: "Export assets from the inventory.", Long: `Export assets from the inventory.`, + Args: cobra.ExactArgs(1), RunE: export, } diff --git a/cmd/import.go b/cmd/import.go index 0adb0729..ad7b4daa 100644 --- a/cmd/import.go +++ b/cmd/import.go @@ -35,9 +35,10 @@ var ( // ImportCmd represents the import command var ImportCmd = &cobra.Command{ - Use: "import [FILE]", + Use: "import PROVIDER [FILE]", Short: "Import assets into the inventory.", Long: `Import assets into the inventory.`, + Args: cobra.ExactArgs(1), RunE: importCmd, } diff --git a/cmd/init.go b/cmd/init.go index 718b33f3..eab68d33 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -40,7 +40,14 @@ import ( "golang.org/x/term" ) -func init() { +var ( + ActiveDomain = &domain.Domain{} +) + +// Init() is called during init() in internal/initmanager for multi-provider support +func Init() { + setupLogging() + log.Trace().Msgf("%+v", "github.com/Cray-HPE/cani/cmd.init") // Create or load a yaml config and the database cobra.OnInitialize(setupLogging, initConfig) @@ -55,23 +62,43 @@ func init() { AlphaCmd.AddCommand(ValidateCmd) AlphaCmd.AddCommand(ExportCmd) - err := MergeProviderCommand(ExportCmd) - if err != nil { - log.Error().Msgf("%+v", err) - os.Exit(1) - } - AlphaCmd.AddCommand(ImportCmd) - err = MergeProviderCommand(ImportCmd) - if err != nil { - log.Error().Msgf("%+v", err) - os.Exit(1) - } // Global root command flags RootCmd.PersistentFlags().StringVar(&cfgFile, "config", cfgFile, "Path to the configuration file") RootCmd.PersistentFlags().BoolVarP(&Debug, "debug", "D", false, "additional debug output") RootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "additional verbose output") + + // Register all provider commands during init() + for _, p := range domain.GetProviders() { + for _, c := range []*cobra.Command{ImportCmd, ExportCmd} { + err := RegisterProviderCommand(p, c) + if err != nil { + log.Error().Msgf("Unable to get command '%s %s' from provider %s ", c.Parent().Name(), c.Name(), p.Slug()) + os.Exit(1) + } + } + } +} + +func GetActiveDomain() (err error) { + log.Trace().Msgf("%+v", "github.com/Cray-HPE/cani/cmd.InitGetActiveDomain") + // Get the active domain, needed for selecting provider commands early during init + ActiveDomain, err = Conf.ActiveProvider() + if err != nil { + return err + } + + // once loaded, we can determine early on if there is an active provider + // and set the appropriate commands to use + if ActiveDomain != nil { + if ActiveDomain.Active { + log.Debug().Msgf("Active domain provider: %+v", ActiveDomain.Provider) + } else { + log.Debug().Msgf("No active domain") + } + } + return nil } // setupLogging sets up the global logger @@ -95,6 +122,10 @@ func setupLogging() { log.Logger = log.With().Caller().Logger() } } + if Verbose || os.Getenv("CANI_DEBUG") != "" { + zerolog.SetGlobalLevel(zerolog.TraceLevel) + log.Logger = log.With().Caller().Logger() + } } func initConfig() { @@ -145,6 +176,7 @@ func setupDomain(cmd *cobra.Command, args []string) (err error) { } } + // FIXME: Duplicated by config.ActiveProvider if cmd.Parent().Name() != "init" { // Error if no sessions are active if len(activeProviders) == 0 { @@ -152,7 +184,7 @@ func setupDomain(cmd *cobra.Command, args []string) (err error) { // so SetupDomain is called manually // The timing of events works out such that simply returning the error // will exit without the message - if cmd.Name() == "status" { + if cmd.Parent().Name() == "status" { log.Info().Msgf("No active session.") return nil } else { diff --git a/cmd/makeprovider.go b/cmd/makeprovider.go index d06e09af..68e9fa6e 100644 --- a/cmd/makeprovider.go +++ b/cmd/makeprovider.go @@ -34,7 +34,6 @@ import ( "reflect" "regexp" "strings" - "unicode/utf8" "github.com/Cray-HPE/cani/internal/provider" "github.com/rs/zerolog/log" @@ -130,15 +129,13 @@ func generateStub(method reflect.Method, pkg string) string { returns = append(returns, returnType.String()) } - recieverVar, _ := utf8.DecodeRuneInString(pkg) - firstLetter := strings.ToLower(string((recieverVar))) + // recieverVar, _ := utf8.DecodeRuneInString(pkg) + // firstLetter := strings.ToLower(string((recieverVar))) // Construct the function signature - signature := fmt.Sprintf("// %s implements the %s method of the InventoryProvider interface\nfunc (%s %s) %s(%s)", + signature := fmt.Sprintf("// %s implements the %s method of the InventoryProvider interface\nfunc %s(%s)", method.Name, method.Name, - firstLetter, - strings.Title(pkg), method.Name, strings.Join(params, ", ")) @@ -146,14 +143,40 @@ func generateStub(method reflect.Method, pkg string) string { signature += " (" + strings.Join(returns, ", ") + ")" } + returnsModified := []string{} + // stupid, but works for immediate need + for _, r := range returns { + switch r { + case "error": + returnsModified = append(returnsModified, `nil`) + case "string": + returnsModified = append(returnsModified, `""`) + case "[]string": + returnsModified = append(returnsModified, `[]string{}`) + case "*cobra.Command": + returnsModified = append(returnsModified, `&cobra.Command{}`) + case "interface": + returnsModified = append(returnsModified, `map[string]interface{}{}`) + case "interface{}": + returnsModified = append(returnsModified, `interface{}{}sdf`) + case "map[uuid.UUID]provider.HardwareValidationResult": + returnsModified = append(returnsModified, `map[uuid.UUID]provider.HardwareValidationResult{}`) + case "[]provider.FieldMetadata": + returnsModified = append(returnsModified, `[]provider.FieldMetadata{}`) + case "provider.HardwareRecommendations": + returnsModified = append(returnsModified, `provider.HardwareRecommendations{}`) + case "provider.SetFieldsResult": + returnsModified = append(returnsModified, `provider.SetFieldsResult{}`) + default: + returnsModified = append(returnsModified, r) + } + } // Add a basic function body body := fmt.Sprintf(`{ -// TODO: Implement -log.Info().Msgf("not yet implemented") +log.Info().Msgf("%s not yet implemented") return %s -}`, - strings.Join(returns, ", ")) +}`, method.Name, strings.Join(returnsModified, ", ")) return signature + " " + body } @@ -194,6 +217,56 @@ func formatGoFile(filePath string) error { // initStub creates a file with the init command func initStub(dir, pkg string) error { + newProviderCmd := ` + // NewProviderCmd returns the appropriate command to the cmd layer + func NewProviderCmd(caniCmd *cobra.Command) (providerCmd *cobra.Command, err error) { + // first, choose the right command + switch caniCmd.Name() { + case "init": + providerCmd, err = NewSessionInitCommand(caniCmd) + case "cabinet": + switch caniCmd.Parent().Name() { + case "add": + providerCmd, err = NewAddCabinetCommand(caniCmd) + case "update": + providerCmd, err = NewUpdateCabinetCommand(caniCmd) + case "list": + providerCmd, err = NewListCabinetCommand(caniCmd) + } + case "blade": + switch caniCmd.Parent().Name() { + case "add": + providerCmd, err = NewAddBladeCommand(caniCmd) + case "update": + providerCmd, err = NewUpdateBladeCommand(caniCmd) + case "list": + providerCmd, err = NewListBladeCommand(caniCmd) + } + case "node": + // check for add/update variants + switch caniCmd.Parent().Name() { + case "add": + providerCmd, err = NewAddNodeCommand(caniCmd) + case "update": + providerCmd, err = NewUpdateNodeCommand(caniCmd) + case "list": + providerCmd, err = NewListNodeCommand(caniCmd) + } + case "export": + providerCmd, err = NewExportCommand(caniCmd) + case "import": + providerCmd, err = NewImportCommand(caniCmd) + default: + err = fmt.Errorf("Command not implemented by provider: %s %s", caniCmd.Parent().Name(), caniCmd.Name()) + } + if err != nil { + return providerCmd, err + } + + return providerCmd, nil + } +` + iStub := filepath.Join(dir, "init.go") log.Info().Msgf("Generating %+v", iStub) f, err := os.Create(iStub) @@ -206,8 +279,13 @@ func initStub(dir, pkg string) error { providerCommands := reflect.TypeOf((*provider.ProviderCommands)(nil)).Elem() content := []string{} for i := 0; i < providerCommands.NumMethod(); i++ { + fileContent := "" method := providerCommands.Method(i) - fileContent := generateStub(method, pkg) + if method.Name != "NewProviderCmd" { + fileContent = generateStub(method, pkg) + } else { + fileContent = newProviderCmd + } if err != nil { return err } @@ -215,7 +293,9 @@ func initStub(dir, pkg string) error { } // this is what gets written - payload := fmt.Sprintf("package %s\n\n%s", + payload := fmt.Sprintf(`package %s + +%s`, strings.ToLower(pkg), strings.Join(content, "\n")) diff --git a/cmd/node/add_node.go b/cmd/node/add_node.go index aa55c8eb..0e81f612 100644 --- a/cmd/node/add_node.go +++ b/cmd/node/add_node.go @@ -32,9 +32,10 @@ import ( // AddNodeCmd represents the node add command var AddNodeCmd = &cobra.Command{ - Use: "node", + Use: "node PROVIDER", Short: "Add nodes to the inventory.", Long: `Add nodes to the inventory.`, + Args: cobra.ExactArgs(1), PreRunE: validHardware, // Hardware can only be valid if defined in the hardware library RunE: addNode, // Add a node when this sub-command is called } diff --git a/cmd/node/init.go b/cmd/node/init.go index 81ea53eb..f4198e09 100644 --- a/cmd/node/init.go +++ b/cmd/node/init.go @@ -29,10 +29,9 @@ import ( "os" root "github.com/Cray-HPE/cani/cmd" - "github.com/rs/zerolog" + "github.com/Cray-HPE/cani/internal/domain" "github.com/rs/zerolog/log" "github.com/spf13/cobra" - "golang.org/x/term" ) var ( @@ -49,15 +48,9 @@ var ( ProviderUpdateNodeCmd = &cobra.Command{} ) -func init() { - log.Logger = log.Output( - zerolog.ConsoleWriter{ - Out: os.Stderr, - // When not in a terminal disable color - NoColor: !term.IsTerminal(int(os.Stderr.Fd())), - }, - ) - var err error +func Init() { + log.Trace().Msgf("%+v", "github.com/Cray-HPE/cani/cmd/node.init") + // Add variants to root commands root.AddCmd.AddCommand(AddNodeCmd) root.ListCmd.AddCommand(ListNodeCmd) @@ -67,14 +60,6 @@ func init() { // Add a flag to show supported types AddNodeCmd.Flags().BoolP("list-supported-types", "L", false, "List supported hardware types.") - // 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(AddNodeCmd) - if err != nil { - log.Error().Msgf("%+v", err) - os.Exit(1) - } - // Blades have several parents, so we need to add flags for each UpdateNodeCmd.Flags().IntVar(&cabinet, "cabinet", 1001, "Parent cabinet") UpdateNodeCmd.Flags().IntVar(&chassis, "chassis", 7, "Parent chassis") @@ -88,16 +73,14 @@ func init() { ListNodeCmd.Flags().StringVarP(&format, "format", "f", "pretty", "Format output") ListNodeCmd.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(UpdateNodeCmd) - if err != nil { - log.Error().Msgf("%+v", err) - os.Exit(1) - } - err = root.MergeProviderCommand(ListNodeCmd) - if err != nil { - log.Error().Msgf("%+v", err) - os.Exit(1) + // Register all provider commands during init() + for _, p := range domain.GetProviders() { + for _, c := range []*cobra.Command{AddNodeCmd, ListNodeCmd, UpdateNodeCmd} { + err := root.RegisterProviderCommand(p, c) + if err != nil { + log.Error().Msgf("Unable to get command '%s %s' from provider %s ", c.Parent().Name(), c.Name(), p.Slug()) + os.Exit(1) + } + } } } diff --git a/cmd/node/list_node.go b/cmd/node/list_node.go index fa1caa7c..19461773 100644 --- a/cmd/node/list_node.go +++ b/cmd/node/list_node.go @@ -35,10 +35,10 @@ import ( // ListNodeCmd represents the node list command var ListNodeCmd = &cobra.Command{ - Use: "node", + Use: "node PROVIDER", Short: "List nodes in the inventory.", Long: `List nodes in the inventory.`, - Args: cobra.ArbitraryArgs, + Args: cobra.ExactArgs(1), RunE: listNode, } diff --git a/cmd/node/update_node.go b/cmd/node/update_node.go index b5ed07f2..5cc3065b 100644 --- a/cmd/node/update_node.go +++ b/cmd/node/update_node.go @@ -38,9 +38,10 @@ import ( // UpdateNodeCmd represents the node update command var UpdateNodeCmd = &cobra.Command{ - Use: "node", + Use: "node PROVIDER", Short: "Update nodes in the inventory.", Long: `Update nodes in the inventory.`, + Args: cobra.ExactArgs(1), RunE: updateNode, // Update a node when this sub-command is called } diff --git a/cmd/pdu/init.go b/cmd/pdu/init.go index b68346e0..4846ce00 100644 --- a/cmd/pdu/init.go +++ b/cmd/pdu/init.go @@ -27,6 +27,7 @@ package pdu import ( root "github.com/Cray-HPE/cani/cmd" + "github.com/rs/zerolog/log" ) var ( @@ -34,7 +35,8 @@ var ( supportedHw []string ) -func init() { +func Init() { + log.Trace().Msgf("%+v", "github.com/Cray-HPE/cani/cmd/pdu.init") // Add variants to root commands root.AddCmd.AddCommand(AddPduCmd) root.ListCmd.AddCommand(ListPduCmd) diff --git a/cmd/session/init.go b/cmd/session/init.go index 3fdb25ef..9474cc30 100644 --- a/cmd/session/init.go +++ b/cmd/session/init.go @@ -42,7 +42,6 @@ var ( ignoreValidationMessage = "Ignore validation failures. Use this to allow unconventional configurations." forceInit bool - ProviderInitCmds = map[string]*cobra.Command{} // BootstapCmd is used to start a session with a specific provider and allows the provider to define // how the real init command is defined using their custom business logic SessionInitCmd = &cobra.Command{ @@ -55,38 +54,14 @@ var ( } ) -func init() { +func Init() { + log.Trace().Msgf("%+v", "github.com/Cray-HPE/cani/cmd/session.init") // Define the bare minimum needed to determine who the provider for the session will be SessionInitCmd.Flags().BoolVar(&ignoreExternalValidation, "ignore-validation", false, ignoreValidationMessage) SessionInitCmd.Flags().BoolVarP(&forceInit, "force", "f", false, "Overwrite the existing session with a new session") SessionInitCmd.Flags().BoolP("insecure", "k", false, "Allow insecure connections when using HTTPS") SessionInitCmd.Flags().BoolP("use-simulator", "S", false, "Use simulation environtment settings") - for _, p := range domain.GetProviders() { - // Create a provider "init" command - providerCmd, err := domain.NewSessionInitCommand(p.Slug()) - if err != nil { - log.Error().Msgf("unable to get provider init command: %v", err) - os.Exit(1) - } - - // Merge cani's default flags into the provider command - err = root.MergeProviderFlags(providerCmd, SessionInitCmd) - if err != nil { - log.Error().Msgf("unable to get flags from provider: %v", err) - os.Exit(1) - } - - // set its use to the provider name (to be used as an arg to the "init" command) - providerCmd.Use = p.Slug() - // run cani's initialization function - providerCmd.RunE = initSessionWithProviderCmd - - // add it as a sub-command to "init" so when an arg is passed, it will call the appropriate provider command - SessionInitCmd.AddCommand(providerCmd) - - } - // Add session commands to root commands root.SessionCmd.AddCommand(SessionInitCmd) root.SessionCmd.AddCommand(SessionApplyCmd) @@ -97,4 +72,14 @@ func init() { SessionApplyCmd.Flags().BoolVarP(&commit, "commit", "c", false, "Commit changes to session") SessionApplyCmd.Flags().BoolVarP(&dryrun, "dryrun", "d", false, "Perform dryrun, and do not make changes to the system") SessionApplyCmd.Flags().BoolVar(&ignoreExternalValidation, "ignore-validation", false, ignoreValidationMessage) + + for _, p := range domain.GetProviders() { + for _, c := range []*cobra.Command{SessionInitCmd} { + err := root.RegisterProviderCommand(p, c) + if err != nil { + log.Error().Msgf("Unable to get command '%s %s' from provider %s ", c.Parent().Name(), c.Name(), p.Slug()) + os.Exit(1) + } + } + } } diff --git a/cmd/session/session_init.go b/cmd/session/session_init.go index aa9109c0..2e3b4e94 100644 --- a/cmd/session/session_init.go +++ b/cmd/session/session_init.go @@ -49,7 +49,7 @@ func initSessionWithProviderCmd(cmd *cobra.Command, args []string) (err error) { return err } - root.D.Provider = cmd.Use + root.D.Provider = cmd.Name() // Set the datastore log.Debug().Msgf("checking provider %s", root.D.Provider) @@ -57,7 +57,7 @@ func initSessionWithProviderCmd(cmd *cobra.Command, args []string) (err error) { case taxonomy.CSM: root.D.DatastorePath = filepath.Join(config.ConfigDir, taxonomy.DsFileCSM) default: - root.D.DatastorePath = filepath.Join(config.ConfigDir, taxonomy.DsFile) + err = fmt.Errorf("not a valid provider: %s", root.D.Provider) } // Set the paths needed for starting a session root.D.CustomHardwareTypesDir = config.CustomDir diff --git a/cmd/switch/init.go b/cmd/switch/init.go index 688e2634..5489c4f4 100644 --- a/cmd/switch/init.go +++ b/cmd/switch/init.go @@ -27,6 +27,7 @@ package sw import ( root "github.com/Cray-HPE/cani/cmd" + "github.com/rs/zerolog/log" ) var ( @@ -34,7 +35,8 @@ var ( supportedHw []string ) -func init() { +func Init() { + log.Trace().Msgf("%+v", "github.com/Cray-HPE/cani/cmd/switch.init") // Add variants to root commands root.AddCmd.AddCommand(AddSwitchCmd) root.ListCmd.AddCommand(ListSwitchCmd) diff --git a/cmd/taxonomy/taxonomy.go b/cmd/taxonomy/taxonomy.go index 68701190..20d9c9be 100644 --- a/cmd/taxonomy/taxonomy.go +++ b/cmd/taxonomy/taxonomy.go @@ -28,6 +28,8 @@ package taxonomy import ( "path/filepath" "sort" + + "github.com/rs/zerolog/log" ) const ( @@ -50,6 +52,7 @@ var ( SupportedProviders = []string{CSM} ) -func init() { +func Init() { + log.Trace().Msgf("%+v", "github.com/Cray-HPE/cani/cmd/taxonomy.init") sort.Strings(SupportedProviders) } diff --git a/cmd/util.go b/cmd/util.go index 1760702f..db5d3c5e 100644 --- a/cmd/util.go +++ b/cmd/util.go @@ -26,9 +26,10 @@ package cmd import ( - "os" + "fmt" "github.com/Cray-HPE/cani/internal/domain" + "github.com/Cray-HPE/cani/internal/provider" "github.com/rs/zerolog/log" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -38,51 +39,44 @@ import ( // Initilizing a session is where all the information needed to interact with the inventory system(s) is gathered // Plugin authors can call this to create their own flags based on their custom business logic // A few common flags are set here, but the rest is up to the plugin author -func MergeProviderFlags(bootstrapCmd *cobra.Command, providerCmd *cobra.Command) (err error) { - providerFlagset := &pflag.FlagSet{} +func MergeProviderFlags(providerCmd *cobra.Command, caniCmd *cobra.Command) (err error) { + caniFlagset := &pflag.FlagSet{} // get the appropriate flagset from the provider's crafted command - providerFlagset = providerCmd.Flags() - - if err != nil { - return err - } + caniFlagset = caniCmd.Flags() // add the provider flags to the command - bootstrapCmd.Flags().AddFlagSet(providerFlagset) + if providerCmd != nil { + providerCmd.Flags().AddFlagSet(caniFlagset) + } return nil } -func MergeProviderCommand(bootstrapCmd *cobra.Command) (err error) { - // each provider can craft their own commands - // since this runs during init(), the domain object is not yet set up, switch statements are used to call the necessary functions - log.Debug().Msgf("Merging '%s %s' command with provider command", bootstrapCmd.Parent().Name(), bootstrapCmd.Name()) - providerCmd := &cobra.Command{} - providerCmd, err = domain.NewProviderCmd(bootstrapCmd) +// RegisterProviderCommand adds a provider-defined command as a subcommand to a specific cani command +// this is meant to be run during init() so each provider has their commands available for use +// TODO: execute subcommand automatically so it is seamless to the user +// at present, the user must run commands + provider name, like: +// +// cani add cabinet csm +// cani list blade hpengi +// +// this requires hiding the provider sub command and dynamically executing it, as opposed to making the user type it in +func RegisterProviderCommand(p provider.InventoryProvider, caniCmd *cobra.Command) (err error) { + log.Debug().Msgf("Registering '%s %s' command from %s", caniCmd.Parent().Name(), caniCmd.Name(), p.Slug()) + // Get the provider-defined command + providerCmd, err := domain.NewProviderCmd(caniCmd, p.Slug()) if err != nil { - log.Error().Msgf("unable to get cmd from provider: %v", err) - os.Exit(1) + return fmt.Errorf("unable to get provider init() command: %v", err) } - // the provider command should be the same as the bootstrap command, allowing it to override the bootstrap cmd - providerCmd.Use = bootstrapCmd.Name() - // all flags should be set in init(). - // You can set flags after the fact, but it is much easier to work with everything up front - // this will set existing variables for each provider - err = MergeProviderFlags(bootstrapCmd, providerCmd) - if err != nil { - log.Error().Msgf("unable to get flags from provider: %v", err) - os.Exit(1) - } + providerCmd.Hidden = false + // set the provider command's use to that of the cani command + providerCmd.Use = p.Slug() + // but add the provider as an alias so it can be keyed off of during command execution + providerCmd.Aliases = append(providerCmd.Aliases, caniCmd.Name()) + // add it as a sub-command the cani command so it can be called during runtime + caniCmd.AddCommand(providerCmd) - // Now the provider command has CANI's settings and those set by the provider - // It may seem redundant to run this again, but in order to do things like MarkFlagsRequiredTogether(), - // it is necessary to have all of the flags available during init, which is what the MergeProviderFlags will do - err = domain.UpdateProviderCmd(bootstrapCmd) - if err != nil { - log.Error().Msgf("unable to get cmd from provider: %v", err) - os.Exit(1) - } return nil } diff --git a/internal/domain/blade.go b/internal/domain/blade.go index 8e06985d..2723da7e 100644 --- a/internal/domain/blade.go +++ b/internal/domain/blade.go @@ -176,7 +176,7 @@ func (d *Domain) AddBlade(cmd *cobra.Command, args []string, cabinetOrdinal, cha // Generate the CANI hardware inventory version of the hardware build out data hardware = inventory.NewHardwareFromBuildOut(hardwareBuildOut, inventory.HardwareStatusStaged) - log.Debug().Str("path", hardwareBuildOut.LocationPath.String()).Msg("Hardware Build out") + log.Trace().Str("path", hardwareBuildOut.LocationPath.String()).Msg("Hardware Build out") // TODO need a check to see if all the needed information exists, // Things like role/subrole/nid/alias could be injected at a later time. @@ -223,7 +223,7 @@ func (d *Domain) AddBlade(cmd *cobra.Command, args []string, cabinetOrdinal, cha } pair.Hardware.LocationPath = hardwareLocation result.AddedHardware = append(result.AddedHardware, pair) - log.Debug().Str("path", hardwareLocation.String()).Msg("Datastore") + log.Trace().Str("path", hardwareLocation.String()).Msg("Datastore") } diff --git a/internal/domain/cabinet.go b/internal/domain/cabinet.go index 536ab6ae..d3c27b2b 100644 --- a/internal/domain/cabinet.go +++ b/internal/domain/cabinet.go @@ -29,6 +29,7 @@ import ( "errors" "fmt" + "github.com/Cray-HPE/cani/cmd/taxonomy" "github.com/Cray-HPE/cani/internal/inventory" "github.com/Cray-HPE/cani/internal/provider" "github.com/Cray-HPE/cani/pkg/hardwaretypes" @@ -112,17 +113,20 @@ func (d *Domain) AddCabinet(cmd *cobra.Command, args []string, recommendations p ) } - hardwareLocation, err := d.datastore.GetLocation(hardware) - if err != nil { - panic(err) + hlp := HardwareLocationPair{ + Hardware: hardware, } - result.AddedHardware = append(result.AddedHardware, HardwareLocationPair{ - Hardware: hardware, - Location: hardwareLocation, - }) - log.Debug().Str("path", hardwareLocation.String()).Msg("Datastore") + result.AddedHardware = append(result.AddedHardware, hlp) + if d.Provider == taxonomy.CSM { + hardwareLocation, err := d.datastore.GetLocation(hardware) + if err != nil { + panic(err) + } + hlp.Location = hardwareLocation + log.Debug().Str("path", hardwareLocation.String()).Msg("Datastore") + } } // Validate the current state of CANI's inventory data against the provider plugin diff --git a/internal/domain/domain.go b/internal/domain/domain.go index de78c10d..eff2e052 100644 --- a/internal/domain/domain.go +++ b/internal/domain/domain.go @@ -60,6 +60,7 @@ var SessionInitCmd *cobra.Command // New returns a new Domain func New(cmd *cobra.Command, args []string) (d *Domain, err error) { d = &Domain{} + d.Provider = cmd.Name() return d, nil } @@ -73,7 +74,7 @@ func (d *Domain) SetupDomain(cmd *cobra.Command, args []string, configDomains ma for _, sessionDomain := range configDomains { if sessionDomain.Active { d.Active = true - log.Debug().Msgf("Setting top level Domain to Active=true") + log.Trace().Msgf("Setting top level Domain to Active=true") } } @@ -84,14 +85,13 @@ func (d *Domain) SetupDomain(cmd *cobra.Command, args []string, configDomains ma } } - log.Debug().Msgf("Setting up datastore interface: %s", d.DatastorePath) + log.Trace().Msgf("Setting up datastore interface: %s", d.DatastorePath) // Load the datastore. Different providers have different storage needs switch d.Provider { case taxonomy.CSM: d.datastore, err = inventory.NewDatastoreJSONCSM(d.DatastorePath, d.LogFilePath, inventory.Provider(d.Provider)) default: - log.Warn().Msgf("using default provider datastore") - d.datastore, err = inventory.NewDatastoreJSON(d.DatastorePath, d.LogFilePath, inventory.Provider(d.Provider)) + return fmt.Errorf("invalid provider: %s", d.Provider) } if err != nil { return errors.Join( @@ -100,7 +100,7 @@ func (d *Domain) SetupDomain(cmd *cobra.Command, args []string, configDomains ma ) } - log.Debug().Msgf("loading embedded and custom hardwaretypes: %s", d.CustomHardwareTypesDir) + log.Trace().Msgf("loading embedded and custom hardwaretypes: %s", d.CustomHardwareTypesDir) // Load the hardware type library. Supported hardware is embedded // this also loads any custom-deinfed hardware at the given path d.hardwareTypeLibrary, err = hardwaretypes.NewEmbeddedLibrary(d.CustomHardwareTypesDir) @@ -111,12 +111,11 @@ func (d *Domain) SetupDomain(cmd *cobra.Command, args []string, configDomains ma ) } - log.Debug().Msgf("getting plugin settings for provider %s", d.Provider) + log.Trace().Msgf("getting plugin settings for provider %s", d.Provider) // Instantiate the provider interface object switch d.Provider { case taxonomy.CSM: d.externalInventoryProvider, err = csm.New(cmd, args, d.hardwareTypeLibrary, d.Options) - default: return fmt.Errorf("unknown external inventory provider provided (%s)", d.Provider) } @@ -128,7 +127,7 @@ func (d *Domain) SetupDomain(cmd *cobra.Command, args []string, configDomains ma ) } - if cmd.Name() == "init" { + if cmd.Parent().Name() == "init" { err := d.externalInventoryProvider.SetProviderOptions(cmd, args) if err != nil { return err @@ -170,3 +169,7 @@ func GetProviders() []provider.InventoryProvider { } return supportedProviders } + +func (d *Domain) Slug() string { + return d.externalInventoryProvider.Slug() +} diff --git a/internal/domain/init.go b/internal/domain/init.go index 67be864b..a6b7c045 100644 --- a/internal/domain/init.go +++ b/internal/domain/init.go @@ -26,48 +26,28 @@ package domain import ( - "github.com/Cray-HPE/cani/cmd/taxonomy" + "fmt" + "github.com/Cray-HPE/cani/internal/provider/csm" + "github.com/rs/zerolog/log" "github.com/spf13/cobra" ) -func NewSessionInitCommand(p string) (providerCmd *cobra.Command, err error) { - switch p { - case "csm": - providerCmd, err = csm.NewSessionInitCommand() - } - if err != nil { - return providerCmd, err - } - return providerCmd, nil +func init() { + log.Trace().Msgf("%+v", "github.com/Cray-HPE/cani/internal/domain.init") } // NewProviderCmd returns the appropriate command to the cmd layer -func NewProviderCmd(bootstrapCmd *cobra.Command) (providerCmd *cobra.Command, err error) { +func NewProviderCmd(caniCmd *cobra.Command, p string) (providerCmd *cobra.Command, err error) { providerCmd = &cobra.Command{} - for _, p := range GetProviders() { - switch p.Slug() { - case taxonomy.CSM: - providerCmd, err = csm.NewProviderCmd(bootstrapCmd) - } - if err != nil { - return providerCmd, nil - } + switch p { + case "csm": + providerCmd, err = csm.NewProviderCmd(caniCmd) + default: + err = fmt.Errorf("no command matched for provider %s", p) } - - return providerCmd, nil -} - -// UpdateProviderCmd allows the provider to make updates to the command after cani defines its settings -func UpdateProviderCmd(bootstrapCmd *cobra.Command) (err error) { - for _, p := range GetProviders() { - switch p.Slug() { - case taxonomy.CSM: - err = csm.UpdateProviderCmd(bootstrapCmd) - } - if err != nil { - return err - } + if err != nil { + return providerCmd, nil } - return nil + return providerCmd, nil } diff --git a/internal/domain/print.go b/internal/domain/print.go index 51175bb8..c8529f25 100644 --- a/internal/domain/print.go +++ b/internal/domain/print.go @@ -27,10 +27,21 @@ package domain import ( "github.com/Cray-HPE/cani/internal/inventory" + "github.com/Cray-HPE/cani/internal/provider" "github.com/google/uuid" "github.com/spf13/cobra" ) +// PrintRecommendations implements the InventoryProvider interface +// it prints the hardware to stdout based on the command +func (d *Domain) PrintRecommendations(cmd *cobra.Command, args []string, recommendations provider.HardwareRecommendations) error { + err := d.externalInventoryProvider.PrintRecommendations(cmd, args, recommendations) + if err != nil { + return err + } + return nil +} + func (d *Domain) PrintHardware(cmd *cobra.Command, args []string, filtered map[uuid.UUID]inventory.Hardware) error { err := d.externalInventoryProvider.PrintHardware(cmd, args, filtered) if err != nil { diff --git a/internal/inventory/buildout.go b/internal/inventory/buildout.go index 001636f7..8b266578 100644 --- a/internal/inventory/buildout.go +++ b/internal/inventory/buildout.go @@ -103,7 +103,7 @@ func GenerateHardwareBuildOut(l *hardwaretypes.Library, opts GenerateHardwareBui current := queue[0] queue = queue[1:] - log.Debug().Msgf("Visiting: %s", current.DeviceTypeSlug) + log.Trace().Msgf("Visiting: %s", current.DeviceTypeSlug) currentDeviceType, ok := l.DeviceTypes[current.DeviceTypeSlug] if !ok { return nil, fmt.Errorf("device type (%v) does not exist", current.DeviceTypeSlug) @@ -125,9 +125,9 @@ func GenerateHardwareBuildOut(l *hardwaretypes.Library, opts GenerateHardwareBui } for _, deviceBay := range currentDeviceType.DeviceBays { - log.Debug().Msgf(" Device bay: %s", deviceBay.Name) + log.Trace().Msgf(" Device bay: %s", deviceBay.Name) if deviceBay.Default != nil { - log.Debug().Msgf(" Default: %s", deviceBay.Default.Slug) + log.Trace().Msgf(" Default: %s", deviceBay.Default.Slug) queue = append(queue, HardwareBuildOut{ // Hardware type is deferred until when it is processed diff --git a/internal/inventory/init.go b/internal/inventory/init.go index c200d138..d1cce8c9 100644 --- a/internal/inventory/init.go +++ b/internal/inventory/init.go @@ -29,6 +29,7 @@ import ( "os" "github.com/rs/zerolog" + "github.com/rs/zerolog/log" ) var ( @@ -37,7 +38,5 @@ var ( ) func init() { - // Default level for this example is info, unless debug flag is present - zerolog.SetGlobalLevel(zerolog.InfoLevel) - zerolog.TimeFieldFormat = zerolog.TimeFormatUnix + log.Trace().Msgf("%+v", "github.com/Cray-HPE/cani/internal/inventory.init") } diff --git a/internal/provider/csm/init.go b/internal/provider/csm/init.go index 746bdff2..b120e16c 100644 --- a/internal/provider/csm/init.go +++ b/internal/provider/csm/init.go @@ -26,8 +26,10 @@ package csm import ( + "errors" "fmt" + "github.com/Cray-HPE/cani/pkg/utils" "github.com/rs/zerolog/log" "github.com/spf13/cobra" ) @@ -64,46 +66,50 @@ var ( alias string ) +func Init() { + log.Trace().Msgf("%+v", "github.com/Cray-HPE/cani/internal/provider/csm.init") +} + // NewProviderCmd returns the appropriate command to the cmd layer -func NewProviderCmd(bootstrapCmd *cobra.Command) (providerCmd *cobra.Command, err error) { +func NewProviderCmd(caniCmd *cobra.Command) (providerCmd *cobra.Command, err error) { // first, choose the right command - switch bootstrapCmd.Name() { + switch caniCmd.Name() { case "init": - providerCmd, err = NewSessionInitCommand() + providerCmd, err = NewSessionInitCommand(caniCmd) case "cabinet": - switch bootstrapCmd.Parent().Name() { + switch caniCmd.Parent().Name() { case "add": - providerCmd, err = NewAddCabinetCommand() + providerCmd, err = NewAddCabinetCommand(caniCmd) case "update": - providerCmd, err = NewUpdateCabinetCommand() + providerCmd, err = NewUpdateCabinetCommand(caniCmd) case "list": - providerCmd, err = NewListCabinetCommand() + providerCmd, err = NewListCabinetCommand(caniCmd) } case "blade": - switch bootstrapCmd.Parent().Name() { + switch caniCmd.Parent().Name() { case "add": - providerCmd, err = NewAddBladeCommand() + providerCmd, err = NewAddBladeCommand(caniCmd) case "update": - providerCmd, err = NewUpdateBladeCommand() + providerCmd, err = NewUpdateBladeCommand(caniCmd) case "list": - providerCmd, err = NewListBladeCommand() + providerCmd, err = NewListBladeCommand(caniCmd) } case "node": // check for add/update variants - switch bootstrapCmd.Parent().Name() { + switch caniCmd.Parent().Name() { case "add": - providerCmd, err = NewAddNodeCommand() + providerCmd, err = NewAddNodeCommand(caniCmd) case "update": - providerCmd, err = NewUpdateNodeCommand() + providerCmd, err = NewUpdateNodeCommand(caniCmd) case "list": - providerCmd, err = NewListNodeCommand() + providerCmd, err = NewListNodeCommand(caniCmd) } case "export": - providerCmd, err = NewExportCommand() + providerCmd, err = NewExportCommand(caniCmd) case "import": - providerCmd, err = NewImportCommand() + providerCmd, err = NewImportCommand(caniCmd) default: - log.Debug().Msgf("Command not implemented by provider: %s %s", bootstrapCmd.Parent().Name(), bootstrapCmd.Name()) + err = fmt.Errorf("Command not implemented by provider: %s %s", caniCmd.Parent().Name(), caniCmd.Name()) } if err != nil { return providerCmd, err @@ -112,55 +118,9 @@ func NewProviderCmd(bootstrapCmd *cobra.Command) (providerCmd *cobra.Command, er return providerCmd, nil } -func UpdateProviderCmd(bootstrapCmd *cobra.Command) (err error) { - // first, choose the right command - switch bootstrapCmd.Name() { - case "init": - err = UpdateSessionInitCommand(bootstrapCmd) - case "cabinet": - // check for add/update variants - switch bootstrapCmd.Parent().Name() { - case "add": - err = UpdateAddCabinetCommand(bootstrapCmd) - case "update": - err = UpdateUpdateCabinetCommand(bootstrapCmd) - case "list": - err = UpdateListCabinetCommand(bootstrapCmd) - } - case "blade": - // check for add/update variants - switch bootstrapCmd.Parent().Name() { - case "add": - err = UpdateAddBladeCommand(bootstrapCmd) - case "update": - err = UpdateUpdateBladeCommand(bootstrapCmd) - case "list": - err = UpdateListBladeCommand(bootstrapCmd) - } - case "node": - // check for add/update variants - switch bootstrapCmd.Parent().Name() { - case "add": - err = UpdateAddNodeCommand(bootstrapCmd) - case "update": - err = UpdateUpdateNodeCommand(bootstrapCmd) - case "list": - err = UpdateUpdateNodeCommand(bootstrapCmd) - } - } - if err != nil { - return fmt.Errorf("unable to update cmd from provider: %v", err) - } - return nil -} - -func NewSessionInitCommand() (cmd *cobra.Command, err error) { - // cmd represents the session init command - cmd = &cobra.Command{} +func NewSessionInitCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) { + cmd = utils.CloneCommand(caniCmd) cmd.Long = `Query SLS and HSM. Validate the data against a schema before allowing an import into CANI.` - // ValidArgs: DO NOT CONFIGURE. This is set by cani's cmd pkg - // Args: DO NOT CONFIGURE. This is set by cani's cmd pkg - // RunE: DO NOT CONFIGURE. This is set by cani's cmd pkg // Session init flags cmd.Flags().String("csm-url-sls", "", "(CSM Provider) Base URL for the System Layout Service (SLS)") cmd.Flags().String("csm-url-hsm", "", "(CSM Provider) Base URL for the Hardware State Manager (HSM)") @@ -192,47 +152,70 @@ func NewSessionInitCommand() (cmd *cobra.Command, err error) { return cmd, nil } -func UpdateSessionInitCommand(caniCmd *cobra.Command) error { - return nil -} - -func NewAddCabinetCommand() (cmd *cobra.Command, err error) { - // cmd represents the session init command - cmd = &cobra.Command{} +func NewAddCabinetCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) { + cmd = utils.CloneCommand(caniCmd) + cmd.Flags().Int("cabinet", 1001, "Cabinet number.") cmd.Flags().Int("vlan-id", -1, "Vlan ID for the cabinet.") + cmd.MarkFlagsRequiredTogether("cabinet", "vlan-id") + cmd.MarkFlagsMutuallyExclusive("auto") + + // make a wrapper script if PreRunE is already set + if caniCmd.PreRunE != nil { + fn := func(c *cobra.Command, a []string) error { + err = caniCmd.PreRunE(c, a) + if err != nil { + return err + } + + err = validCAddCabinetFlags(c, a) + if err != nil { + return err + } + return nil + } + // assign the wrapper to the PreRunE field + cmd.PreRunE = fn + } return cmd, nil } -// UpdateAddCabinetCommand is run during init and allows the provider to set additional options for CANI flags -// such as marking certain options mutually exclusive with the auto flag -func UpdateAddCabinetCommand(caniCmd *cobra.Command) error { - caniCmd.MarkFlagsRequiredTogether("cabinet", "vlan-id") - caniCmd.MarkFlagsMutuallyExclusive("auto") - return nil -} - -func NewUpdateCabinetCommand() (cmd *cobra.Command, err error) { - cmd = &cobra.Command{} - return cmd, nil -} +// validCAddCabinetFlags has additional flag logic to account for overiding required flags with the --auto flag +func validCAddCabinetFlags(cmd *cobra.Command, args []string) error { + cabinetSet := cmd.Flags().Changed("cabinet") + vlanIdSet := cmd.Flags().Changed("vlan-id") + autoSet := cmd.Flags().Changed("auto") + // if auto is set, the values are recommended and the required flags are bypassed + if autoSet { + return nil + } else { + if !cabinetSet && !vlanIdSet { + return errors.New("required flag(s) \"cabinet\", \"vlan-id\" not set") + } + if cabinetSet && !vlanIdSet { + return errors.New("required flag(s) \"vlan-id\" not set") + } + if !cabinetSet && vlanIdSet { + return errors.New("required flag(s) \"cabinet\" not set") + } + } -func UpdateUpdateCabinetCommand(caniCmd *cobra.Command) error { return nil } -func NewListCabinetCommand() (cmd *cobra.Command, err error) { - cmd = &cobra.Command{} +func NewUpdateCabinetCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) { + cmd = utils.CloneCommand(caniCmd) return cmd, nil } -func UpdateListCabinetCommand(caniCmd *cobra.Command) error { - return nil +func NewListCabinetCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) { + cmd = utils.CloneCommand(caniCmd) + return cmd, nil } -func NewAddNodeCommand() (cmd *cobra.Command, err error) { +func NewAddNodeCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) { // cmd represents for cani alpha add node - cmd = &cobra.Command{} + cmd = utils.CloneCommand(caniCmd) cmd.Flags().StringVar(&role, "role", "", "Role of the node") cmd.Flags().StringVar(&subrole, "subrole", "", "Subrole of the node") cmd.Flags().IntVar(&nid, "nid", 0, "NID of the node") @@ -241,13 +224,9 @@ func NewAddNodeCommand() (cmd *cobra.Command, err error) { return cmd, nil } -func UpdateAddNodeCommand(caniCmd *cobra.Command) error { - return nil -} - -func NewUpdateNodeCommand() (cmd *cobra.Command, err error) { +func NewUpdateNodeCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) { // cmd represents for cani alpha update node - cmd = &cobra.Command{} + cmd = utils.CloneCommand(caniCmd) cmd.Flags().String("role", "", "Role of the node") cmd.Flags().String("subrole", "", "Subrole of the node") cmd.Flags().Int("nid", 0, "NID of the node") @@ -257,28 +236,14 @@ func NewUpdateNodeCommand() (cmd *cobra.Command, err error) { } // NewListNodeCommand implements the NewListNodeCommand method of the InventoryProvider interface -func NewListNodeCommand() (cmd *cobra.Command, err error) { - // TODO: Implement - cmd = &cobra.Command{} - +func NewListNodeCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) { + cmd = utils.CloneCommand(caniCmd) return cmd, nil } -// UpdateListNodeCommand implements the UpdateListNodeCommand method of the InventoryProvider interface -func UpdateListNodeCommand(caniCmd *cobra.Command) error { - // TODO: Implement - return nil -} - -// UpdateUpdateNodeCommand -func UpdateUpdateNodeCommand(caniCmd *cobra.Command) error { - - return nil -} - -func NewExportCommand() (cmd *cobra.Command, err error) { +func NewExportCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) { // cmd represents cani alpha export - cmd = &cobra.Command{} + cmd = utils.CloneCommand(caniCmd) cmd.Flags().StringVar( &csvHeaders, "headers", "Type,Vlan,Role,SubRole,Status,Nid,Alias,Name,ID,Location", "Comma separated list of fields to get") cmd.Flags().StringVarP( @@ -291,42 +256,23 @@ func NewExportCommand() (cmd *cobra.Command, err error) { return cmd, nil } -func NewImportCommand() (cmd *cobra.Command, err error) { - // cmd represents cani alpha import - cmd = &cobra.Command{} - +func NewImportCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) { + cmd = utils.CloneCommand(caniCmd) return cmd, nil } -func NewAddBladeCommand() (cmd *cobra.Command, err error) { - // cmd represents cani alpha import - cmd = &cobra.Command{} - +func NewAddBladeCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) { + cmd = utils.CloneCommand(caniCmd) + cmd.MarkFlagsMutuallyExclusive("auto") return cmd, nil } -func UpdateAddBladeCommand(caniCmd *cobra.Command) error { - return nil -} - -func NewUpdateBladeCommand() (cmd *cobra.Command, err error) { - // cmd represents cani alpha import - cmd = &cobra.Command{} - +func NewUpdateBladeCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) { + cmd = utils.CloneCommand(caniCmd) return cmd, nil } -func UpdateUpdateBladeCommand(caniCmd *cobra.Command) error { - return nil -} - -func NewListBladeCommand() (cmd *cobra.Command, err error) { - // cmd represents cani alpha import - cmd = &cobra.Command{} - +func NewListBladeCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) { + cmd = utils.CloneCommand(caniCmd) return cmd, nil } - -func UpdateListBladeCommand(caniCmd *cobra.Command) error { - return nil -} diff --git a/internal/provider/csm/print.go b/internal/provider/csm/print.go index 9eec1237..45eeb1dc 100644 --- a/internal/provider/csm/print.go +++ b/internal/provider/csm/print.go @@ -35,16 +35,26 @@ import ( "text/tabwriter" "github.com/Cray-HPE/cani/internal/inventory" + "github.com/Cray-HPE/cani/internal/provider" "github.com/Cray-HPE/cani/pkg/hardwaretypes" "github.com/google/uuid" "github.com/rs/zerolog/log" "github.com/spf13/cobra" ) +// PrintRecommendations implements the InventoryProvider interface +// it prints the hardware to stdout based on the command +func (csm *CSM) PrintRecommendations(cmd *cobra.Command, args []string, recommendations provider.HardwareRecommendations) error { + log.Info().Msgf("Suggested cabinet number: %d", recommendations.CabinetOrdinal) + log.Info().Msgf("Suggested VLAN ID: %d", recommendations.ProviderMetadata["HMNVlan"]) + + return nil +} + // PrintHardware implements the InventoryProvider interface // it prints the hardware to stdout based on the command func (csm *CSM) PrintHardware(cmd *cobra.Command, args []string, filtered map[uuid.UUID]inventory.Hardware) (err error) { - switch cmd.Parent().Name() { + switch cmd.Parent().Parent().Name() { case "add": err = csm.printHardwareForAddCommand(cmd, args, filtered) case "list": @@ -54,7 +64,7 @@ func (csm *CSM) PrintHardware(cmd *cobra.Command, args []string, filtered map[uu case "remove": err = csm.printHardwareForRemoveCommand(cmd, args, filtered) default: - log.Warn().Msgf("No print function for command %+v", cmd.Name()) + log.Warn().Msgf("No print function for command '%s %s %s'", cmd.Name(), cmd.Parent().Name(), cmd.Parent().Parent().Name()) } if err != nil { return err @@ -107,6 +117,7 @@ func (csm *CSM) printHardwareForRemoveCommand(cmd *cobra.Command, args []string, // printForAddCabinetCommand prints the hardware for the add cabinet command func printForAddCabinetCommand(cmd *cobra.Command, args []string, hw inventory.Hardware) error { + log.Info().Str("status", "SUCCESS").Msgf("%s was successfully %s to be added to the system", hardwaretypes.Cabinet, hw.Status) log.Info().Msgf("UUID: %s", hw.ID) log.Info().Msgf("Cabinet Number: %d", *hw.LocationOrdinal) log.Info().Msgf("VLAN ID: %d", hw.ProviderMetadata["csm"]["Cabinet"].(map[string]interface{})["HMNVlan"]) @@ -177,7 +188,7 @@ func printForListCommand(cmd *cobra.Command, args []string, filtered map[uuid.UU // prettyPrintForListCommand prints the hardware for the list command // in a pretty, human-readable format and based on the type of hardware func prettyPrintForListCommand(cmd *cobra.Command, args []string, filtered map[uuid.UUID]inventory.Hardware) (err error) { - switch cmd.Name() { + switch cmd.Parent().Name() { case "cabinet": err = prettyPrintForListCabinet(cmd, args, filtered) case "chassis": diff --git a/internal/provider/csm/recommend.go b/internal/provider/csm/recommend.go index b12d64b2..783d6ce7 100644 --- a/internal/provider/csm/recommend.go +++ b/internal/provider/csm/recommend.go @@ -40,7 +40,7 @@ import ( func (csm *CSM) RecommendHardware(inv inventory.Inventory, cmd *cobra.Command, args []string, auto bool) (recommended provider.HardwareRecommendations, err error) { var deviceTypeSlug string - if cmd.Parent().Name() == "add" { + if cmd.Parent().Parent().Name() == "add" { deviceTypeSlug = args[0] } // loop through the existing inventory to check for vlans diff --git a/internal/provider/csm/validation.go b/internal/provider/csm/validation.go index fc8e6f77..7c55200a 100644 --- a/internal/provider/csm/validation.go +++ b/internal/provider/csm/validation.go @@ -120,7 +120,7 @@ func (csm *CSM) validateInternalNode(allHardware map[uuid.UUID]inventory.Hardwar if cHardware.Type != hardwaretypes.Node { continue } - log.Debug().Msgf("Validating %s: %v", cHardware.ID, cHardware) + log.Trace().Msgf("Validating %s: %v", cHardware.ID, cHardware) metadata, err := DecodeProviderMetadata(cHardware) if err != nil { @@ -260,7 +260,7 @@ func (csm *CSM) validateInternalCabinet(allHardware map[uuid.UUID]inventory.Hard continue } - log.Debug().Msgf("Validating %s: %v", cHardware.ID, cHardware) + log.Trace().Msgf("Validating %s: %v", cHardware.ID, cHardware) metadata, err := DecodeProviderMetadata(cHardware) if err != nil { diff --git a/internal/provider/interface.go b/internal/provider/interface.go index c1cda7f7..aa39852e 100644 --- a/internal/provider/interface.go +++ b/internal/provider/interface.go @@ -34,6 +34,10 @@ import ( "github.com/spf13/cobra" ) +func Init() { + log.Info().Msgf("%+v", "github.com/Cray-HPE/cani/internal/provider.init") +} + var ErrDataValidationFailure = fmt.Errorf("data validation failure") // TODO Need to think about how internal data structures should be supplied to the Inventory Provider @@ -90,6 +94,7 @@ type InventoryProvider interface { // Print PrintHardware(cmd *cobra.Command, args []string, filtered map[uuid.UUID]inventory.Hardware) error + PrintRecommendations(cmd *cobra.Command, args []string, recommendations HardwareRecommendations) error // Provider's name Slug() string @@ -132,12 +137,17 @@ func (r HardwareRecommendations) Print() { // ProviderCommands is an interface for the commands that are specific to a provider // this isn't usually used directly, but is used to generate the commands with 'makeprovdier' type ProviderCommands interface { - NewSessionInitCommand() (cmd *cobra.Command, err error) - NewAddCabinetCommand() (cmd *cobra.Command, err error) - UpdateAddCabinetCommand(caniCmd *cobra.Command) error - NewAddNodeCommand() (cmd *cobra.Command, err error) - NewUpdateNodeCommand() (cmd *cobra.Command, err error) - UpdateUpdateNodeCommand(caniCmd *cobra.Command) error - NewExportCommand() (cmd *cobra.Command, err error) - NewImportCommand() (cmd *cobra.Command, err error) + NewProviderCmd(caniCmd *cobra.Command) (providerCmd *cobra.Command, err error) + NewSessionInitCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) + NewAddCabinetCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) + NewUpdateCabinetCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) + NewListCabinetCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) + NewAddBladeCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) + NewUpdateBladeCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) + NewListBladeCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) + NewAddNodeCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) + NewUpdateNodeCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) + NewListNodeCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) + NewExportCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) + NewImportCommand(caniCmd *cobra.Command) (cmd *cobra.Command, err error) } diff --git a/main.go b/main.go index 28d15c42..e772b4b6 100644 --- a/main.go +++ b/main.go @@ -25,18 +25,39 @@ package main import ( "github.com/Cray-HPE/cani/cmd" - _ "github.com/Cray-HPE/cani/cmd/blade" - _ "github.com/Cray-HPE/cani/cmd/cabinet" - _ "github.com/Cray-HPE/cani/cmd/cdu" - _ "github.com/Cray-HPE/cani/cmd/chassis" - _ "github.com/Cray-HPE/cani/cmd/config" - _ "github.com/Cray-HPE/cani/cmd/node" - _ "github.com/Cray-HPE/cani/cmd/pdu" - _ "github.com/Cray-HPE/cani/cmd/session" - _ "github.com/Cray-HPE/cani/cmd/switch" - _ "github.com/Cray-HPE/cani/cmd/taxonomy" + "github.com/Cray-HPE/cani/cmd/blade" + "github.com/Cray-HPE/cani/cmd/cabinet" + "github.com/Cray-HPE/cani/cmd/cdu" + "github.com/Cray-HPE/cani/cmd/chassis" + "github.com/Cray-HPE/cani/cmd/node" + "github.com/Cray-HPE/cani/cmd/pdu" + "github.com/Cray-HPE/cani/cmd/session" + sw "github.com/Cray-HPE/cani/cmd/switch" + _ "github.com/Cray-HPE/cani/internal/provider" + _ "github.com/Cray-HPE/cani/pkg/hardwaretypes" + "github.com/rs/zerolog/log" ) +// Run all package init functions in a specific order +// this is mainly due to all cobra commands needing flags and other settings +// to be set during init +func init() { + // configure logging first for friendly console-logs + log.Trace().Msgf("%+v", "github.com/Cray-HPE/cani/main.init") + // load the config and generate root commands + cmd.Init() + // initialize cobra commands for each package + session.Init() + cabinet.Init() + cdu.Init() + chassis.Init() + blade.Init() + node.Init() + pdu.Init() + sw.Init() +} + func main() { + log.Trace().Msgf("%+v", "github.com/Cray-HPE/cani/main.main") cmd.Execute() } diff --git a/pkg/hardwaretypes/library.go b/pkg/hardwaretypes/library.go index 68032f7d..b6eb80c2 100644 --- a/pkg/hardwaretypes/library.go +++ b/pkg/hardwaretypes/library.go @@ -88,24 +88,24 @@ func NewEmbeddedLibrary(customDir string) (*Library, error) { // Load the embedded hardware type embedded files basePath := "hardware-types" - log.Debug().Msgf("Looking for built-in hardware-types") + log.Trace().Msgf("Looking for built-in hardware-types") defaultFiles, err := defaultHardwareTypesFS.ReadDir(basePath) if err != nil { return nil, err } - log.Debug().Msgf("Looking for custom hardware-types in %s", customDir) + log.Trace().Msgf("Looking for custom hardware-types in %s", customDir) // append user-defined hardware-type files to the default embedded ones customFiles, err := os.ReadDir(customDir) if err != nil { // it is ok if no custom files exist - log.Debug().Msgf("No custom hardware-types defined in %s", customDir) + log.Trace().Msgf("No custom hardware-types defined in %s", customDir) } // Parse hardware type files for _, file := range defaultFiles { filePath := path.Join(basePath, file.Name()) - log.Debug().Msgf("Parsing built-in hardware-type: %s", filePath) + log.Trace().Msgf("Parsing built-in hardware-type: %s", filePath) fileRaw, err := defaultHardwareTypesFS.ReadFile(filePath) if err != nil { @@ -118,7 +118,7 @@ func NewEmbeddedLibrary(customDir string) (*Library, error) { } for _, deviceType := range fileDeviceTypes { - log.Debug().Msgf("Registering device type: %s", deviceType.Slug) + log.Trace().Msgf("Registering device type: %s", deviceType.Slug) if err := library.RegisterDeviceType(deviceType); err != nil { return nil, errors.Join( fmt.Errorf("failed to register device type '%s'", deviceType.Slug), @@ -132,7 +132,7 @@ func NewEmbeddedLibrary(customDir string) (*Library, error) { if len(customFiles) != 0 { for _, file := range customFiles { filePath := filepath.Join(customDir, file.Name()) - log.Debug().Msgf("Parsing custom hardware-type: %v", filePath) + log.Trace().Msgf("Parsing custom hardware-type: %v", filePath) fileRaw, err := os.ReadFile(filePath) if err != nil { @@ -145,7 +145,7 @@ func NewEmbeddedLibrary(customDir string) (*Library, error) { } for _, deviceType := range fileDeviceTypes { - log.Debug().Msgf("Registering device type: %s", deviceType.Slug) + log.Trace().Msgf("Registering device type: %s", deviceType.Slug) if err := library.RegisterDeviceType(deviceType); err != nil { return nil, errors.Join( fmt.Errorf("failed to register device type '%s'", deviceType.Slug), diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go new file mode 100644 index 00000000..c60c590b --- /dev/null +++ b/pkg/utils/utils.go @@ -0,0 +1,54 @@ +/* + * + * MIT License + * + * (C) Copyright 2023 Hewlett Packard Enterprise Development LP + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +package utils + +import "github.com/spf13/cobra" + +func CloneCommand(cmd *cobra.Command) (newCmd *cobra.Command) { + if cmd == nil { + return nil + } + + // copy certain fields, omitting some currently not in use by cani + newCmd = &cobra.Command{ + Use: cmd.Use, + Short: cmd.Short, + Long: cmd.Long, + RunE: cmd.RunE, + PreRunE: cmd.PreRunE, + ValidArgs: cmd.ValidArgs, + } + + // copy flags to the new command + newCmd.Flags().AddFlagSet(cmd.Flags()) + + // copy any subcommands recursively to the new command + for _, subCmd := range cmd.Commands() { + newCmd.AddCommand(CloneCommand(subCmd)) + } + + return newCmd +} diff --git a/spec/functional/cani_add_cabinet_spec.sh b/spec/functional/cani_add_cabinet_spec.sh index df4a3349..f1b4ec61 100755 --- a/spec/functional/cani_add_cabinet_spec.sh +++ b/spec/functional/cani_add_cabinet_spec.sh @@ -148,7 +148,7 @@ It "--config $CANI_CONF hpe-ex2000 --cabinet 1234 --vlan-id 1234" BeforeCall use_valid_datastore_system_only # deploy a valid datastore When call bin/cani alpha add cabinet csm --config "$CANI_CONF" hpe-ex2000 --cabinet 1234 --vlan-id 1234 The status should equal 0 - The line 1 of stderr should include "Cabinet 1234 was successfully staged to be added to the system" + The line 1 of stderr should include "Cabinet was successfully staged to be added to the system" The line 2 of stderr should include "UUID: " The line 3 of stderr should include "Cabinet Number: 1234" The line 4 of stderr should include "VLAN ID: 1234" diff --git a/spec/integration/add_cabinet_ex2000_spec.sh b/spec/integration/add_cabinet_ex2000_spec.sh index d39ea1c6..c5ba4e95 100644 --- a/spec/integration/add_cabinet_ex2000_spec.sh +++ b/spec/integration/add_cabinet_ex2000_spec.sh @@ -55,7 +55,7 @@ It 'add ex2000 cabinet' The line 1 of stderr should include 'Querying inventory to suggest Cabinet' The line 2 of stderr should include 'Suggested cabinet number: 9001' The line 3 of stderr should include 'Suggested VLAN ID: 3001' - The line 4 of stderr should include 'Cabinet 9001 was successfully staged to be added to the system' + The line 4 of stderr should include 'Cabinet was successfully staged to be added to the system' End It 'commit and reconcile' diff --git a/spec/support/bin/cani_integrate.sh b/spec/support/bin/cani_integrate.sh index 3f0360c6..40056d94 100755 --- a/spec/support/bin/cani_integrate.sh +++ b/spec/support/bin/cani_integrate.sh @@ -22,7 +22,7 @@ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # -# set -e +set -e set -u # SIM_REPO="${1:-../hms-simulation-environment}" diff --git a/testdata/fixtures/cani/add/blade/help b/testdata/fixtures/cani/add/blade/help index 9625ae47..b1a5171a 100644 --- a/testdata/fixtures/cani/add/blade/help +++ b/testdata/fixtures/cani/add/blade/help @@ -6,8 +6,6 @@ Usage: Available Commands: csm Add blades to the inventory. - hpcm Add blades to the inventory. - hpengi Add blades to the inventory. Flags: -y, --accept Automatically accept recommended values. diff --git a/testdata/fixtures/cani/add/cabinet/help b/testdata/fixtures/cani/add/cabinet/help index 05707835..ffc9fd2c 100644 --- a/testdata/fixtures/cani/add/cabinet/help +++ b/testdata/fixtures/cani/add/cabinet/help @@ -6,8 +6,6 @@ Usage: Available Commands: csm Add cabinets to the inventory. - hpcm Add cabinets to the inventory. - hpengi Add cabinets to the inventory. Flags: -y, --accept Automatically accept recommended values. diff --git a/testdata/fixtures/cani/add/node/help b/testdata/fixtures/cani/add/node/help index 3bdc1e8c..9c532f70 100644 --- a/testdata/fixtures/cani/add/node/help +++ b/testdata/fixtures/cani/add/node/help @@ -6,8 +6,6 @@ Usage: Available Commands: csm Add nodes to the inventory. - hpcm Add nodes to the inventory. - hpengi Add nodes to the inventory. Flags: -h, --help help for node diff --git a/testdata/fixtures/cani/export/help b/testdata/fixtures/cani/export/help index 9208304f..d25526c5 100644 --- a/testdata/fixtures/cani/export/help +++ b/testdata/fixtures/cani/export/help @@ -6,8 +6,6 @@ Usage: Available Commands: csm Export assets from the inventory. - hpcm Export assets from the inventory. - hpengi Export assets from the inventory. Flags: -h, --help help for export diff --git a/testdata/fixtures/cani/import/help b/testdata/fixtures/cani/import/help index 15361b81..e117fd65 100644 --- a/testdata/fixtures/cani/import/help +++ b/testdata/fixtures/cani/import/help @@ -6,8 +6,6 @@ Usage: Available Commands: csm Import assets into the inventory. - hpcm Import assets into the inventory. - hpengi Import assets into the inventory. Flags: -h, --help help for import