diff --git a/internal/app/wwctl/profile/add/main.go b/internal/app/wwctl/profile/add/main.go index 6e5ef57a..8a219a95 100644 --- a/internal/app/wwctl/profile/add/main.go +++ b/internal/app/wwctl/profile/add/main.go @@ -1,28 +1,52 @@ package add import ( - "os" + "fmt" - "github.com/hpcng/warewulf/internal/pkg/node" - "github.com/hpcng/warewulf/internal/pkg/wwlog" + apinode "github.com/hpcng/warewulf/internal/pkg/api/node" + apiprofile "github.com/hpcng/warewulf/internal/pkg/api/profile" + + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/hpcng/warewulf/internal/pkg/api/util" "github.com/pkg/errors" "github.com/spf13/cobra" ) -func CobraRunE(cmd *cobra.Command, args []string) error { - nodeDB, err := node.New() - if err != nil { - wwlog.Printf(wwlog.ERROR, "Failed opening node database: %s\n", err) - os.Exit(1) +func CobraRunE(cmd *cobra.Command, args []string) (err error) { + OptionStrMap, haveNetname := apinode.AddNetname(OptionStrMap) + if !haveNetname { + return errors.New("a netname must be given for any network related configuration") + } + realMap := make(map[string]string) + + for key, val := range OptionStrMap { + realMap[key] = *val } - for _, p := range args { - _, err := nodeDB.AddProfile(p) + set := wwapiv1.NodeSetParameter{ + OptionsStrMap: realMap, + NetdevDelete: SetNetDevDel, + AllNodes: SetNodeAll, + Force: SetForce, + NodeNames: args, + } + + if !SetYes { + // The checks run twice in the prompt case. + // Avoiding putting in a blocking prompt in an API. + apiprofile.AddProfile(&set, false) if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - os.Exit(1) + return + } + _, _, err = apiprofile.ProfileSetParameterCheck(&set, false) + if err != nil { + return + } + + yes := util.ConfirmationPrompt(fmt.Sprintf("Are you sure you add the profile %s", args)) + if !yes { + return } } - - return errors.Wrap(nodeDB.Persist(), "failed to persist nodedb") + return apiprofile.ProfileSet(&set) } diff --git a/internal/app/wwctl/profile/add/root.go b/internal/app/wwctl/profile/add/root.go index 9e95d8d2..7380dec4 100644 --- a/internal/app/wwctl/profile/add/root.go +++ b/internal/app/wwctl/profile/add/root.go @@ -1,19 +1,65 @@ package add -import "github.com/spf13/cobra" +import ( + "log" + + "github.com/hpcng/warewulf/internal/pkg/container" + "github.com/hpcng/warewulf/internal/pkg/kernel" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/overlay" + "github.com/spf13/cobra" +) var ( baseCmd = &cobra.Command{ DisableFlagsInUseLine: true, - Use: "add PROFILE", - Short: "Add a new node profile", - Long: "This command adds a new named PROFILE.", - RunE: CobraRunE, - Args: cobra.MinimumNArgs(1), + Use: "add PROFILE", + Short: "Add a new node profile", + Long: "This command adds a new named PROFILE.", + RunE: CobraRunE, + Args: cobra.ExactArgs(1), } + SetNetDevDel string + SetNodeAll bool + SetYes bool + SetForce bool + OptionStrMap map[string]*string ) // GetRootCommand returns the root cobra.Command for the application. func GetCommand() *cobra.Command { + // init empty helper structs, so that we know what's inside + myBase := node.CobraCommand{Command: baseCmd} + var emptyNodeConf node.NodeConf + emptyNodeConf.Kernel = new(node.KernelConf) + emptyNodeConf.Ipmi = new(node.IpmiConf) + OptionStrMap = myBase.CreateFlags(emptyNodeConf, + []string{"ipaddr", "ipaddr6", "ipmiaddr", "profile"}) + // register the command line completions + if err := baseCmd.RegisterFlagCompletionFunc("container", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + list, _ := container.ListSources() + return list, cobra.ShellCompDirectiveNoFileComp + }); err != nil { + log.Println(err) + } + if err := baseCmd.RegisterFlagCompletionFunc("kerneloverride", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + list, _ := kernel.ListKernels() + return list, cobra.ShellCompDirectiveNoFileComp + }); err != nil { + log.Println(err) + } + if err := baseCmd.RegisterFlagCompletionFunc("runtime", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + list, _ := overlay.FindOverlays() + return list, cobra.ShellCompDirectiveNoFileComp + }); err != nil { + log.Println(err) + } + if err := baseCmd.RegisterFlagCompletionFunc("wwinit", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + list, _ := overlay.FindOverlays() + return list, cobra.ShellCompDirectiveNoFileComp + }); err != nil { + log.Println(err) + } + return baseCmd } diff --git a/internal/pkg/api/profile/profile.go b/internal/pkg/api/profile/profile.go index c3a04188..24ebff8f 100644 --- a/internal/pkg/api/profile/profile.go +++ b/internal/pkg/api/profile/profile.go @@ -7,7 +7,9 @@ import ( apinode "github.com/hpcng/warewulf/internal/pkg/api/node" "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/util" "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/pkg/errors" ) // NodeSet is the wwapiv1 implmentation for updating nodeinfo fields. @@ -20,7 +22,7 @@ func ProfileSet(set *wwapiv1.NodeSetParameter) (err error) { var nodeDB node.NodeYaml nodeDB, _, err = ProfileSetParameterCheck(set, false) if err != nil { - return + return errors.Wrap(err, "Could not open database") } return apinode.DbSave(&nodeDB) } @@ -61,7 +63,7 @@ func ProfileSetParameterCheck(set *wwapiv1.NodeSetParameter, console bool) (node // Note: This does not do expansion on the nodes. - if set.AllNodes || (len(set.NodeNames) == 0 && len(profiles) > 0) { + if set.AllNodes || (len(set.NodeNames) == 0) { if console { fmt.Printf("\n*** WARNING: This command will modify all profiles! ***\n\n") } @@ -75,33 +77,58 @@ func ProfileSetParameterCheck(set *wwapiv1.NodeSetParameter, console bool) (node } for _, p := range profiles { - wwlog.Printf(wwlog.VERBOSE, "Evaluating profile: %s\n", p.Id.Get()) - for key, val := range set.OptionsStrMap { - if val != "" { - wwlog.Verbose("profile:%s setting %s to %s\n", p.Id.Get(), key, val) - p.SetField(key, val) - } - } - - if set.NetdevDelete != "" { - - if _, ok := p.NetDevs[set.NetdevDelete]; !ok { - err = fmt.Errorf("Network device name doesn't exist: %s", set.NetdevDelete) - wwlog.Error(fmt.Sprintf("%v\n", err.Error())) - return + if util.InSlice(set.NodeNames, p.Id.Get()) { + wwlog.Printf(wwlog.VERBOSE, "Evaluating profile: %s\n", p.Id.Get()) + for key, val := range set.OptionsStrMap { + if val != "" { + wwlog.Verbose("profile:%s setting %s to %s\n", p.Id.Get(), key, val) + p.SetField(key, val) + } } - wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Deleting network device: %s\n", p.Id.Get(), set.NetdevDelete) - delete(p.NetDevs, set.NetdevDelete) - } + if set.NetdevDelete != "" { - err := nodeDB.ProfileUpdate(p) - if err != nil { - wwlog.Error("%s\n", err) - os.Exit(1) - } + if _, ok := p.NetDevs[set.NetdevDelete]; !ok { + err = fmt.Errorf("Network device name doesn't exist: %s", set.NetdevDelete) + wwlog.Error(fmt.Sprintf("%v\n", err.Error())) + return + } - profileCount++ + wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Deleting network device: %s\n", p.Id.Get(), set.NetdevDelete) + delete(p.NetDevs, set.NetdevDelete) + } + + err := nodeDB.ProfileUpdate(p) + if err != nil { + wwlog.Error("%s\n", err) + os.Exit(1) + } + + profileCount++ + } } return } + +/* +Adds a new profile with the given name +*/ +func AddProfile(set *wwapiv1.NodeSetParameter, console bool) error { + nodeDB, err := node.New() + if err != nil { + return errors.Wrap(err, "Could not open database") + } + + if util.InSlice(nodeDB.ListAllProfiles(), set.NodeNames[0]) { + return errors.New(fmt.Sprintf("profile with name %s allready exists", set.NodeNames[0])) + } + _, err = nodeDB.AddProfile(set.NodeNames[0]) + if err != nil { + return errors.Wrap(err, "Could not create new profile") + } + err = apinode.DbSave(&nodeDB) + if err != nil { + return errors.Wrap(err, "Could not persist new profile") + } + return nil +} diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index 958cc0de..3e56fb98 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -312,6 +312,9 @@ func (config *NodeYaml) FindAllNodes() ([]NodeInfo, error) { return ret, nil } +/* +Return all profiles as NodeInfo +*/ func (config *NodeYaml) FindAllProfiles() ([]NodeInfo, error) { var ret []NodeInfo @@ -429,7 +432,6 @@ func (config *NodeYaml) FindAllProfiles() ([]NodeInfo, error) { ret = append(ret, p) } - sort.Slice(ret, func(i, j int) bool { if ret[i].ClusterName.Get() < ret[j].ClusterName.Get() { return true @@ -444,6 +446,17 @@ func (config *NodeYaml) FindAllProfiles() ([]NodeInfo, error) { return ret, nil } +/* +Return the names of all available profiles +*/ +func (config *NodeYaml) ListAllProfiles() []string { + var ret []string + for name, _ := range config.NodeProfiles { + ret = append(ret, name) + } + return ret +} + func (config *NodeYaml) FindDiscoverableNode() (NodeInfo, string, error) { var ret NodeInfo