From 84d3b72f63d8c515d5a497e167e31d48f2e2aa74 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Fri, 1 Jul 2022 13:57:36 +0200 Subject: [PATCH] added more abstrac setter for nested structs --- internal/app/wwctl/node/set/root.go | 48 ++-------- internal/pkg/node/datastructure.go | 12 +-- internal/pkg/node/methods.go | 144 ++++++++++++++++++++++++++-- 3 files changed, 152 insertions(+), 52 deletions(-) diff --git a/internal/app/wwctl/node/set/root.go b/internal/app/wwctl/node/set/root.go index 976f28d9..e63048ef 100644 --- a/internal/app/wwctl/node/set/root.go +++ b/internal/app/wwctl/node/set/root.go @@ -1,8 +1,8 @@ package set import ( + "fmt" "log" - "reflect" "github.com/hpcng/warewulf/internal/pkg/container" "github.com/hpcng/warewulf/internal/pkg/kernel" @@ -33,10 +33,7 @@ var ( return node_names, cobra.ShellCompDirectiveNoFileComp }, } - SetComment string - SetContainer string SetKernelOverride string - SetKernelArgs string SetNetName string SetNetDev string SetIpaddr string @@ -66,48 +63,27 @@ var ( SetAddProfile []string SetDelProfile []string SetForce bool - SetInit string SetDiscoverable bool SetUndiscoverable bool - SetRoot string SetTags []string SetDelTags []string - SetAssetKey string SetNetTags []string SetNetDelTags []string OptionStrMap map[string]*string + KernelStrMapmap map[string]*string ) func init() { - OptionStrMap = make(map[string]*string) + //var emptyNodeConf node.NodeConf + //var emptyKernelE node.KernelEntry + myBase := node.CobraCommand{baseCmd} var emptyNodeConf node.NodeConf - nodeStruct := reflect.ValueOf(emptyNodeConf) - nodeType := nodeStruct.Type() - for i := 0; i < nodeStruct.NumField(); i++ { - field := nodeType.Field(i) - if field.Tag.Get("comment") != "" { - kind := field.Type.Kind() - if kind == reflect.String { - var optionVal string - OptionStrMap[field.Name] = &optionVal - if field.Tag.Get("sopt") != "" { - baseCmd.PersistentFlags().StringVarP(&optionVal, - field.Tag.Get("lopt"), - field.Tag.Get("sopt"), - field.Tag.Get("default"), - field.Tag.Get("comment")) - } else { - baseCmd.PersistentFlags().StringVar(&optionVal, - field.Tag.Get("lopt"), - field.Tag.Get("default"), - field.Tag.Get("comment")) + emptyNodeConf.Kernel = new(node.KernelConf) + emptyNodeConf.Ipmi = new(node.IpmiConf) + //emptyNodeConf.NetDevs = make(map[string]*node.NetDevs) + OptionStrMap = myBase.CreateFlags(emptyNodeConf) - } - } - } - } - //baseCmd.PersistentFlags().StringVar(&SetComment, "comment", "", "Set a comment for this node") - baseCmd.PersistentFlags().StringVarP(&SetContainer, "container", "C", "", "Set the container (VNFS) for this node") + fmt.Printf("OptionStrMap: %v\n", OptionStrMap) if err := baseCmd.RegisterFlagCompletionFunc("container", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { list, _ := container.ListSources() return list, cobra.ShellCompDirectiveNoFileComp @@ -121,12 +97,8 @@ func init() { }); err != nil { log.Println(err) } - baseCmd.PersistentFlags().StringVarP(&SetKernelArgs, "kernelargs", "A", "", "Set Kernel argument for nodes") baseCmd.PersistentFlags().StringVarP(&SetClusterName, "cluster", "c", "", "Set the node's cluster group") baseCmd.PersistentFlags().StringVar(&SetIpxe, "ipxe", "", "Set the node's iPXE template name") - baseCmd.PersistentFlags().StringVarP(&SetInit, "init", "i", "", "Define the init process to boot the container") - baseCmd.PersistentFlags().StringVar(&SetRoot, "root", "", "Define the rootfs") - baseCmd.PersistentFlags().StringVar(&SetAssetKey, "assetkey", "", "Set the node's Asset tag (key)") baseCmd.PersistentFlags().StringVarP(&SetInitOverlay, "wwinit", "O", "", "Set the node's initialization overlay") baseCmd.PersistentFlags().StringSliceVarP(&SetRuntimeOverlay, "runtime", "R", []string{}, "Set the node's runtime overlay") if err := baseCmd.RegisterFlagCompletionFunc("runtime", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { diff --git a/internal/pkg/node/datastructure.go b/internal/pkg/node/datastructure.go index 91818c1e..f5745a6a 100644 --- a/internal/pkg/node/datastructure.go +++ b/internal/pkg/node/datastructure.go @@ -19,9 +19,9 @@ type NodeYaml struct { NodeConf is the datastructure which is stored on disk. */ type NodeConf struct { - Comment string `yaml:"comment,omitempty" lopt:"comment" comment:"Set Comment"` + Comment string `yaml:"comment,omitempty" lopt:"comment" comment:"Set arbitrary string comment"` ClusterName string `yaml:"cluster name,omitempty"` - ContainerName string `yaml:"container name,omitempty"` + ContainerName string `yaml:"container name,omitempty" lopt:"container" sopt:"C" comment:"Set container name"` Ipxe string `yaml:"ipxe template,omitempty"` KernelVersion string `yaml:"kernel version,omitempty"` KernelOverride string `yaml:"kernel override,omitempty"` @@ -38,9 +38,9 @@ type NodeConf struct { SystemOverlay []string `yaml:"system overlay,omitempty"` Kernel *KernelConf `yaml:"kernel,omitempty"` Ipmi *IpmiConf `yaml:"ipmi,omitempty"` - Init string `yaml:"init,omitempty"` - Root string `yaml:"root,omitempty"` - AssetKey string `yaml:"asset key,omitempty"` + Init string `yaml:"init,omitempty" lopt:"init" sopt:"i" comment:"Define the init process to boot the container"` + Root string `yaml:"root,omitempty" lopt:"root" comment:"Define the rootfs" ` + AssetKey string `yaml:"asset key,omitempty" lopt:"asset" comment:"Set the node's Asset tag (key)"` Discoverable string `yaml:"discoverable,omitempty"` Profiles []string `yaml:"profiles,omitempty"` NetDevs map[string]*NetDevs `yaml:"network devices,omitempty"` @@ -61,7 +61,7 @@ type IpmiConf struct { type KernelConf struct { Version string `yaml:"version,omitempty"` Override string `yaml:"override,omitempty"` - Args string `yaml:"args,omitempty"` + Args string `yaml:"args,omitempty" lopt:"kernelargs" sopt:"A" comment:"Set Kernel argument"` } type NetDevs struct { diff --git a/internal/pkg/node/methods.go b/internal/pkg/node/methods.go index b4e05a42..d6d62262 100644 --- a/internal/pkg/node/methods.go +++ b/internal/pkg/node/methods.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/spf13/cobra" ) /********** @@ -304,25 +305,152 @@ func (ent *Entry) Defined() bool { /* Set the Entry trough an interface by trying to cast the interface */ -func SetEntry(entryPrt interface{}, val string) { - fmt.Printf("entryPtr: %v\n", reflect.TypeOf(entryPrt)) - if entry, ok := entryPrt.(*Entry); ok { - entry.Set(val) +func SetEntry(entryPtr interface{}, val interface{}) { + if reflect.TypeOf(entryPtr) == reflect.TypeOf((*Entry)(nil)) { + entry := entryPtr.(*Entry) + valKind := reflect.TypeOf(val) + if valKind.Kind() == reflect.String { + entry.Set(val.(string)) + } else if valKind.Kind() == reflect.Slice { + if valKind.Elem().Kind() == reflect.String { + entry.SetSlice(val.([]string)) + } else { + panic("Got unknown slice type") + } + } } else { - panic(fmt.Sprintf("Can't convert %s to *node.Entry\n", reflect.TypeOf(entryPrt))) + panic(fmt.Sprintf("Can't convert %s to *node.Entry\n", reflect.TypeOf(entryPtr))) } } /* -Call SetEntry for given field +Call SetEntry for given field (NodeInfo) */ func (node *NodeInfo) SetField(fieldName string, val interface{}) { field := reflect.ValueOf(node).Elem().FieldByName(fieldName) if field.IsValid() { - SetEntry(field.Addr().Interface(), val.(string)) + //fmt.Println(reflect.TypeOf(field.Addr().Interface())) + SetEntry(field.Addr().Interface(), val) } else { - panic(fmt.Sprintf("field %s does not exists in node.NodeInfo\n", fieldName)) + fieldNames := strings.Split(fieldName, ".") + if len(fieldNames) == 2 { + nestedField := reflect.ValueOf(node).Elem().FieldByName(fieldNames[0]) + if nestedField.IsValid() { + switch nestedField.Addr().Type() { + case reflect.TypeOf((**KernelEntry)(nil)): + entry := nestedField.Addr().Interface().(**KernelEntry) + (*entry).SetField(fieldNames[1], val) + case reflect.TypeOf((**IpmiEntry)(nil)): + entry := nestedField.Addr().Interface().(**IpmiEntry) + (*entry).SetField(fieldNames[1], val) + default: + panic(fmt.Sprintf("not implemented type %v\n", nestedField.Addr().Type())) + } + } else { + panic(fmt.Sprintf("field %s is not a nested type of %s", fieldNames[0], fieldName)) + } + } else { + panic(fmt.Sprintf("field %s does not exists in node.NodeInfo\n", fieldName)) + } } } + +/* +Call SetEntry for given field (KernelEntry) +*/ +func (node *KernelEntry) SetField(fieldName string, val interface{}) { + field := reflect.ValueOf(node).Elem().FieldByName(fieldName) + if field.IsValid() { + SetEntry(field.Addr().Interface(), val) + } else { + panic(fmt.Sprintf("field %s does not exists in node.KernEntry\n", fieldName)) + } +} + +/* +Call SetEntry for given field (ImpiEntry) +*/ +func (node *IpmiEntry) SetField(fieldName string, val interface{}) { + field := reflect.ValueOf(node).Elem().FieldByName(fieldName) + if field.IsValid() { + SetEntry(field.Addr().Interface(), val) + } else { + panic(fmt.Sprintf("field %s does not exists in node.KernEntry\n", fieldName)) + } +} + +/* +Get all names of the fields in the given struct (recursive) +and create a map[name of struct field]*string if the the field +of the struct bears the comment tag. +*/ +func GetOptionsMap(theStruct interface{}) map[string]*string { + optionsMap := make(map[string]*string) + structVal := reflect.ValueOf(theStruct) + structTyp := structVal.Type() + for i := 0; i < structVal.NumField(); i++ { + field := structTyp.Field(i) + if field.Type.Kind() == reflect.Struct { + subStruct := GetOptionsMap(field) + for key, val := range subStruct { + optionsMap[key] = val + } + } else if field.Tag.Get("comment") != "" { + var newStr string + optionsMap[field.Name] = &newStr + } + + } + return optionsMap +} + +type CobraCommand struct { + *cobra.Command +} + +/* +Get all names of the fields in the given struct (recursive) +and create a map[name of struct field]*string if the the field +of the struct bears the comment tag. +*/ +func (baseCmd *CobraCommand) CreateFlags(theStruct interface{}) map[string]*string { + optionsMap := make(map[string]*string) + structVal := reflect.ValueOf(theStruct) + structTyp := structVal.Type() + for i := 0; i < structVal.NumField(); i++ { + field := structTyp.Field(i) + fmt.Printf("%s: field.Kind() == %s\n", field.Name, field.Type.Kind()) + if field.Type.Kind() == reflect.Ptr { + a := structVal.Field(i).Elem().Interface() + subStruct := baseCmd.CreateFlags(a) + for key, val := range subStruct { + optionsMap[field.Name+"."+key] = val + } + + } else if field.Type.Kind() == reflect.Map { + // Just check for network map + fmt.Println(reflect.TypeOf(structVal.Field(i).Elem())) + + } else if field.Tag.Get("comment") != "" { + var newStr string + optionsMap[field.Name] = &newStr + if field.Tag.Get("sopt") != "" { + baseCmd.PersistentFlags().StringVarP(&newStr, + field.Tag.Get("lopt"), + field.Tag.Get("sopt"), + field.Tag.Get("default"), + field.Tag.Get("comment")) + } else { + baseCmd.PersistentFlags().StringVar(&newStr, + field.Tag.Get("lopt"), + field.Tag.Get("default"), + field.Tag.Get("comment")) + + } + } + + } + return optionsMap +}