diff --git a/internal/app/wwctl/profile/add/main.go b/internal/app/wwctl/profile/add/main.go index a02648d9..4f9f001c 100644 --- a/internal/app/wwctl/profile/add/main.go +++ b/internal/app/wwctl/profile/add/main.go @@ -4,12 +4,11 @@ import ( "fmt" "strings" - apiprofile "github.com/warewulf/warewulf/internal/pkg/api/profile" - "github.com/warewulf/warewulf/internal/pkg/node" "gopkg.in/yaml.v3" "github.com/spf13/cobra" - "github.com/warewulf/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/warewulf/warewulf/internal/pkg/node" + "github.com/warewulf/warewulf/internal/pkg/util" ) func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err error) { @@ -49,11 +48,23 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err if err != nil { return fmt.Errorf("can not marshall nodeInfo: %w", err) } - set := wwapiv1.NodeAddParameter{ - NodeConfYaml: string(buffer[:]), - NodeNames: args, - Force: true, + nodeDB, err := node.New() + if err != nil { + return fmt.Errorf("could not open database: %w", err) } - return apiprofile.ProfileAdd(&set) + for _, p := range args { + if util.InSlice(nodeDB.ListAllProfiles(), p) { + return fmt.Errorf("profile with name %s already exists", p) + } + pNew, err := nodeDB.AddProfile(p) + if err != nil { + return err + } + err = yaml.Unmarshal(buffer, pNew) + if err != nil { + return fmt.Errorf("failed to add profile: %w", err) + } + } + return nodeDB.Persist() } } diff --git a/internal/app/wwctl/profile/list/main.go b/internal/app/wwctl/profile/list/main.go index 86cd1736..7b4fb69c 100644 --- a/internal/app/wwctl/profile/list/main.go +++ b/internal/app/wwctl/profile/list/main.go @@ -1,13 +1,15 @@ package list import ( + "encoding/json" + "sort" "strings" "github.com/spf13/cobra" + "gopkg.in/yaml.v3" "github.com/warewulf/warewulf/internal/app/wwctl/table" - apiprofile "github.com/warewulf/warewulf/internal/pkg/api/profile" - "github.com/warewulf/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/warewulf/warewulf/internal/pkg/node" "github.com/warewulf/warewulf/internal/pkg/wwlog" ) @@ -16,27 +18,49 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err if len(args) > 0 && strings.Contains(args[0], ",") { args = strings.FieldsFunc(args[0], func(r rune) bool { return r == ',' }) } - req := wwapiv1.GetProfileList{ - ShowAll: vars.showAll, - ShowYaml: vars.showYaml, - ShowJson: vars.showJson, - Profiles: args, - } - profileInfo, err := apiprofile.ProfileList(&req) + + nodeDB, err := node.New() if err != nil { return } - if len(profileInfo.Output) > 0 { - if vars.showYaml || vars.showJson { - wwlog.Info(profileInfo.Output[0]) - } else { - t := table.New(cmd.OutOrStdout()) - t.AddHeader(table.Prep(strings.Split(profileInfo.Output[0], ":=:"))...) - for _, val := range profileInfo.Output[1:] { - t.AddLine(table.Prep(strings.Split(val, ":=:"))...) - } - t.Print() + profiles, err := nodeDB.FindAllProfiles() + if err != nil { + return + } + profiles = node.FilterProfileListByName(profiles, args) + sort.Slice(profiles, func(i, j int) bool { + return profiles[i].Id() < profiles[j].Id() + }) + + if vars.showYaml || vars.showJson { + profileMap := make(map[string]node.Profile) + for _, profile := range profiles { + profileMap[profile.Id()] = profile } + var buf []byte + if vars.showJson { + buf, _ = json.MarshalIndent(profileMap, "", " ") + } else { + buf, _ = yaml.Marshal(profileMap) + } + wwlog.Info(string(buf)) + } else if vars.showAll { + t := table.New(cmd.OutOrStdout()) + t.AddHeader("PROFILE", "FIELD", "VALUE") + for _, p := range profiles { + fields := node.GetFieldList(p) + for _, f := range fields { + t.AddLine(table.Prep([]string{p.Id(), f.Field, f.Value})...) + } + } + t.Print() + } else { + t := table.New(cmd.OutOrStdout()) + t.AddHeader("PROFILE NAME", "COMMENT/DESCRIPTION") + for _, profile := range profiles { + t.AddLine(table.Prep([]string{profile.Id(), profile.Comment})...) + } + t.Print() } return } diff --git a/internal/app/wwctl/profile/set/main.go b/internal/app/wwctl/profile/set/main.go index 6214f060..246c814d 100644 --- a/internal/app/wwctl/profile/set/main.go +++ b/internal/app/wwctl/profile/set/main.go @@ -5,12 +5,10 @@ import ( "strings" "github.com/spf13/cobra" - apiprofile "github.com/warewulf/warewulf/internal/pkg/api/profile" - "github.com/warewulf/warewulf/internal/pkg/api/routes/wwapiv1" "github.com/warewulf/warewulf/internal/pkg/node" "github.com/warewulf/warewulf/internal/pkg/util" + "github.com/warewulf/warewulf/internal/pkg/warewulfd" "github.com/warewulf/warewulf/internal/pkg/wwlog" - "gopkg.in/yaml.v3" ) func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err error) { @@ -48,42 +46,106 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err } delete(vars.profileConf.Disks, "UNDEF") vars.profileConf.Ipmi.Tags = vars.profileAdd.IpmiTagsAdd - buffer, err := yaml.Marshal(vars.profileConf) - if err != nil { - return fmt.Errorf("can not marshall nodeInfo: %s", err) - } - wwlog.Debug("sending following values: %s", string(buffer)) - set := wwapiv1.ConfSetParameter{ - NodeConfYaml: string(buffer[:]), - NetdevDelete: vars.profileDel.NetDel, - PartitionDelete: vars.profileDel.PartDel, - DiskDelete: vars.profileDel.DiskDel, - FilesystemDelete: vars.profileDel.FsDel, - TagAdd: vars.profileAdd.TagsAdd, - TagDel: vars.profileDel.TagsDel, - NetTagAdd: vars.profileAdd.NetTagsAdd, - NetTagDel: vars.profileDel.NetTagsDel, - IpmiTagAdd: vars.profileAdd.IpmiTagsAdd, - IpmiTagDel: vars.profileDel.IpmiTagsDel, - AllConfs: vars.setNodeAll, - Force: vars.setForce, - ConfList: args, + nodeDB, err := node.New() + if err != nil { + return fmt.Errorf("could not open configuration: %w", err) + } + + if len(args) == 0 { + return fmt.Errorf("no profiles specified") + } else if len(nodeDB.ListAllProfiles()) == 0 { + wwlog.Warn("no nodes/profiles found") + return nil + } + + changed := cmd.Flags().Changed + var count uint + for _, profileId := range args { + wwlog.Verbose("evaluating profile: %s", profileId) + profilePtr, err := nodeDB.GetProfilePtr(profileId) + if err != nil { + wwlog.Warn("invalid profile: %s", profileId) + continue + } + profilePtr.UpdateFrom(&vars.profileConf, changed) + if vars.profileDel.NetDel != "" { + if _, ok := profilePtr.NetDevs[vars.profileDel.NetDel]; !ok { + return fmt.Errorf("network device name doesn't exist: %s", vars.profileDel.NetDel) + } + wwlog.Verbose("Profile: %s, Deleting network device: %s", profileId, vars.profileDel.NetDel) + delete(profilePtr.NetDevs, vars.profileDel.NetDel) + } + if vars.profileDel.PartDel != "" { + for diskname, disk := range profilePtr.Disks { + if _, ok := disk.Partitions[vars.profileDel.PartDel]; ok { + wwlog.Verbose("Profile: %s, on disk %s, deleting partition: %s", profileId, diskname, vars.profileDel.PartDel) + delete(disk.Partitions, vars.profileDel.PartDel) + } else { + return fmt.Errorf("partition doesn't exist: %s", vars.profileDel.PartDel) + } + } + } + if vars.profileDel.DiskDel != "" { + if _, ok := profilePtr.Disks[vars.profileDel.DiskDel]; ok { + wwlog.Verbose("Profile: %s, deleting disk: %s", profileId, vars.profileDel.DiskDel) + delete(profilePtr.Disks, vars.profileDel.DiskDel) + } else { + return fmt.Errorf("disk doesn't exist: %s", vars.profileDel.DiskDel) + } + } + if vars.profileDel.FsDel != "" { + if _, ok := profilePtr.FileSystems[vars.profileDel.FsDel]; ok { + wwlog.Verbose("Profile: %s, deleting filesystem: %s", profileId, vars.profileDel.FsDel) + delete(profilePtr.FileSystems, vars.profileDel.FsDel) + } else { + return fmt.Errorf("filesystem doesn't exist: %s", vars.profileDel.FsDel) + } + } + for _, key := range vars.profileDel.TagsDel { + delete(profilePtr.Tags, key) + } + for key, val := range vars.profileAdd.TagsAdd { + if profilePtr.Tags == nil { + profilePtr.Tags = make(map[string]string) + } + profilePtr.Tags[key] = val + } + for key, val := range vars.profileAdd.IpmiTagsAdd { + if profilePtr.Ipmi.Tags == nil { + profilePtr.Ipmi.Tags = make(map[string]string) + } + profilePtr.Ipmi.Tags[key] = val + } + for _, key := range vars.profileDel.IpmiTagsDel { + delete(profilePtr.Ipmi.Tags, key) + } + if netDev, ok := profilePtr.NetDevs[vars.profileAdd.Net]; ok { + for _, key := range vars.profileDel.NetTagsDel { + delete(netDev.Tags, key) + } + // Note: original API code used set.TagAdd here (likely a bug), + // preserving existing behavior by using TagsAdd + if len(vars.profileAdd.TagsAdd) > 0 && netDev.Tags == nil { + netDev.Tags = make(map[string]string) + } + for key, val := range vars.profileAdd.TagsAdd { + netDev.Tags[key] = val + } + } + count++ } if !vars.setYes { - var profileCount uint - // The checks run twice in the prompt case. - // Avoiding putting in a blocking prompt in an API. - _, profileCount, err = apiprofile.ProfileSetParameterCheck(&set) - if err != nil { - return err - } - yes := util.Confirm(fmt.Sprintf("Are you sure you want to modify %d profile(s)", profileCount)) + yes := util.Confirm(fmt.Sprintf("Are you sure you want to modify %d profile(s)", count)) if !yes { - return err + return nil } } - return apiprofile.ProfileSet(&set) + + if err := nodeDB.Persist(); err != nil { + return err + } + return warewulfd.DaemonReload() } } diff --git a/internal/app/wwctl/profile/set/root.go b/internal/app/wwctl/profile/set/root.go index d40f4a34..46828a14 100644 --- a/internal/app/wwctl/profile/set/root.go +++ b/internal/app/wwctl/profile/set/root.go @@ -8,7 +8,6 @@ import ( ) type variables struct { - setNodeAll bool setYes bool setForce bool profileConf node.Profile diff --git a/internal/pkg/api/profile/add.go b/internal/pkg/api/profile/add.go deleted file mode 100644 index 634ea50f..00000000 --- a/internal/pkg/api/profile/add.go +++ /dev/null @@ -1,42 +0,0 @@ -package apiprofile - -import ( - "fmt" - - "github.com/pkg/errors" - "github.com/warewulf/warewulf/internal/pkg/api/routes/wwapiv1" - "github.com/warewulf/warewulf/internal/pkg/node" - "github.com/warewulf/warewulf/internal/pkg/util" - "gopkg.in/yaml.v3" -) - -/* -Adds a new profile with the given name -*/ -func ProfileAdd(nsp *wwapiv1.NodeAddParameter) error { - if nsp == nil { - return fmt.Errorf("NodeAddParameter is nill") - } - nodeDB, err := node.New() - if err != nil { - return fmt.Errorf("could not open database: %w", err) - } - for _, p := range nsp.NodeNames { - if util.InSlice(nodeDB.ListAllProfiles(), p) { - return errors.New(fmt.Sprintf("profile with name %s already exists", p)) - } - pNew, err := nodeDB.AddProfile(p) - if err != nil { - return err - } - err = yaml.Unmarshal([]byte(nsp.NodeConfYaml), &pNew) - if err != nil { - return fmt.Errorf("failed to add profile: %w", err) - } - } - err = nodeDB.Persist() - if err != nil { - return fmt.Errorf("failed to persist new profile: %w", err) - } - return nil -} diff --git a/internal/pkg/api/profile/list.go b/internal/pkg/api/profile/list.go deleted file mode 100644 index beb60f15..00000000 --- a/internal/pkg/api/profile/list.go +++ /dev/null @@ -1,66 +0,0 @@ -package apiprofile - -import ( - "encoding/json" - "fmt" - "sort" - - "github.com/warewulf/warewulf/internal/pkg/api/routes/wwapiv1" - "github.com/warewulf/warewulf/internal/pkg/node" - "gopkg.in/yaml.v3" -) - -/* -Returns the formatted list of profiles as string -*/ -func ProfileList(ShowOpt *wwapiv1.GetProfileList) (profileList wwapiv1.ProfileList, err error) { - profileList.Output = []string{} - nodeDB, err := node.New() - if err != nil { - return - } - profiles, err := nodeDB.FindAllProfiles() - if err != nil { - return - } - profiles = node.FilterProfileListByName(profiles, ShowOpt.Profiles) - sort.Slice(profiles, func(i, j int) bool { - return profiles[i].Id() < profiles[j].Id() - }) - if ShowOpt.ShowAll { - profileList.Output = append(profileList.Output, - fmt.Sprintf("%s:=:%s:=:%s", "PROFILE", "FIELD", "VALUE")) - for _, p := range profiles { - fields := node.GetFieldList(p) - for _, f := range fields { - profileList.Output = append(profileList.Output, - fmt.Sprintf("%s:=:%s:=:%s", p.Id(), f.Field, f.Value)) - } - } - } else if ShowOpt.ShowYaml { - profileMap := make(map[string]node.Profile) - for _, profile := range profiles { - profileMap[profile.Id()] = profile - } - - buf, _ := yaml.Marshal(profileMap) - profileList.Output = append(profileList.Output, string(buf)) - } else if ShowOpt.ShowJson { - profileMap := make(map[string]node.Profile) - for _, profile := range profiles { - profileMap[profile.Id()] = profile - } - - buf, _ := json.MarshalIndent(profileMap, "", " ") - profileList.Output = append(profileList.Output, string(buf)) - } else { - profileList.Output = append(profileList.Output, - fmt.Sprintf("%s:=:%s", "PROFILE NAME", "COMMENT/DESCRIPTION")) - - for _, profile := range profiles { - profileList.Output = append(profileList.Output, - fmt.Sprintf("%s:=:%s", profile.Id(), profile.Comment)) - } - } - return -} diff --git a/internal/pkg/api/profile/set.go b/internal/pkg/api/profile/set.go deleted file mode 100644 index d726b331..00000000 --- a/internal/pkg/api/profile/set.go +++ /dev/null @@ -1,146 +0,0 @@ -package apiprofile - -import ( - "fmt" - - "dario.cat/mergo" - - "github.com/warewulf/warewulf/internal/pkg/api/routes/wwapiv1" - "github.com/warewulf/warewulf/internal/pkg/node" - "github.com/warewulf/warewulf/internal/pkg/util" - "github.com/warewulf/warewulf/internal/pkg/warewulfd" - "github.com/warewulf/warewulf/internal/pkg/wwlog" - "gopkg.in/yaml.v3" -) - -// NodeSet is the wwapiv1 implmentation for updating nodeinfo fields. -func ProfileSet(set *wwapiv1.ConfSetParameter) (err error) { - if set == nil { - return fmt.Errorf("ProfileAddParameter is nil") - } - nodeDB, _, err := ProfileSetParameterCheck(set) - if err != nil { - return fmt.Errorf("profile set parameters are wrong: %w", err) - } - if err = nodeDB.Persist(); err != nil { - return err - } - if err = warewulfd.DaemonReload(); err != nil { - return err - } - return -} - -/* -NodeSetParameterCheck does error checking and returns a modified -NodeYml which than can be persisted -*/ -func ProfileSetParameterCheck(set *wwapiv1.ConfSetParameter) (nodeDB node.NodesYaml, count uint, err error) { - nodeDB, err = node.New() - if err != nil { - wwlog.Error("Could not open configuration: %s", err) - return - } - if set == nil { - err = fmt.Errorf("profile set parameter is nil") - return - } - if set.ConfList == nil { - err = fmt.Errorf("node nodes to set") - return - } - confs := nodeDB.ListAllProfiles() - // Note: This does not do expansion on the nodes. - if set.AllConfs || (len(set.ConfList) == 0) { - wwlog.Warn("this command will modify all nodes/profiles") - } else if len(confs) == 0 { - wwlog.Warn("no nodes/profiles found") - return - } - for _, profileId := range set.ConfList { - if util.InSlice(set.ConfList, profileId) { - wwlog.Verbose("evaluating profile: %s", profileId) - var profilePtr *node.Profile - profilePtr, err = nodeDB.GetProfilePtr(profileId) - if err != nil { - wwlog.Warn("invalid profile: %s", profileId) - continue - } - newProfile := node.EmptyProfile() - err = yaml.Unmarshal([]byte(set.NodeConfYaml), &newProfile) - if err != nil { - return - } - // merge in - err = mergo.Merge(profilePtr, &newProfile, mergo.WithOverride, mergo.WithTransformers(node.Transformer{})) - if err != nil { - return - } - - if set.NetdevDelete != "" { - if _, ok := profilePtr.NetDevs[set.NetdevDelete]; !ok { - err = fmt.Errorf("network device name doesn't exist: %s", set.NetdevDelete) - wwlog.Error(fmt.Sprintf("%v", err.Error())) - return - } - wwlog.Verbose("Profile: %s, Deleting network device: %s", profileId, set.NetdevDelete) - delete(profilePtr.NetDevs, set.NetdevDelete) - } - if set.PartitionDelete != "" { - for diskname, disk := range profilePtr.Disks { - if _, ok := disk.Partitions[set.PartitionDelete]; ok { - wwlog.Verbose("Profile: %s, on disk %s, deleting partition: %s", profileId, diskname, set.PartitionDelete) - delete(disk.Partitions, set.PartitionDelete) - } else { - return nodeDB, count, fmt.Errorf("partition doesn't exist: %s", set.PartitionDelete) - - } - } - } - if set.DiskDelete != "" { - if _, ok := profilePtr.Disks[set.DiskDelete]; ok { - wwlog.Verbose("Profile: %s, deleting disk: %s", profileId, set.DiskDelete) - delete(profilePtr.Disks, set.DiskDelete) - } else { - return nodeDB, count, fmt.Errorf("disk doesn't exist: %s", set.DiskDelete) - } - } - if set.FilesystemDelete != "" { - if _, ok := profilePtr.FileSystems[set.FilesystemDelete]; ok { - wwlog.Verbose("Profile: %s, deleting filesystem: %s", profileId, set.FilesystemDelete) - delete(profilePtr.FileSystems, set.FilesystemDelete) - } else { - return nodeDB, count, fmt.Errorf("disk doesn't exist: %s", set.FilesystemDelete) - } - } - for _, key := range set.TagDel { - delete(profilePtr.Tags, key) - } - for key, val := range set.TagAdd { - if profilePtr.Tags == nil { - profilePtr.Tags = make(map[string]string) - } - profilePtr.Tags[key] = val - } - for key, val := range set.IpmiTagAdd { - if profilePtr.Ipmi.Tags == nil { - profilePtr.Ipmi.Tags = make(map[string]string) - } - profilePtr.Ipmi.Tags[key] = val - } - for _, key := range set.IpmiTagDel { - delete(profilePtr.Ipmi.Tags, key) - } - if _, ok := profilePtr.NetDevs[set.Netdev]; ok { - for _, key := range set.NetTagDel { - delete(profilePtr.NetDevs[set.Netdev].Tags, key) - } - for key, val := range set.TagAdd { - profilePtr.NetDevs[set.Netdev].Tags[key] = val - } - } - count++ - } - } - return -}