diff --git a/internal/app/wwctl/node/add/main.go b/internal/app/wwctl/node/add/main.go index a3dec5aa..266e6690 100644 --- a/internal/app/wwctl/node/add/main.go +++ b/internal/app/wwctl/node/add/main.go @@ -9,7 +9,6 @@ import ( "github.com/spf13/cobra" ) -<<<<<<< HEAD /* RunE needs a function of type func(*cobraCommand,[]string) err, but in order to avoid global variables which mess up testing a function of @@ -17,10 +16,12 @@ the required type is returned */ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { - // run converters for different types - for _, c := range Converters { - c() - } + // run converters for different types + for _, c := range vars.converters { + if err := c(); err != nil { + return err + } + } // remove the default network as all network values are assigned // to this network if _, ok := vars.nodeConf.NetDevs["default"]; ok && vars.netName != "" { diff --git a/internal/app/wwctl/node/add/root.go b/internal/app/wwctl/node/add/root.go index f36241ac..83a0b172 100644 --- a/internal/app/wwctl/node/add/root.go +++ b/internal/app/wwctl/node/add/root.go @@ -14,7 +14,7 @@ import ( type variables struct { netName string nodeConf node.NodeConf - converters []func() + converters []func() error } // Returns the newly created command diff --git a/internal/app/wwctl/node/set/main.go b/internal/app/wwctl/node/set/main.go index e95e77a2..4bc44f21 100644 --- a/internal/app/wwctl/node/set/main.go +++ b/internal/app/wwctl/node/set/main.go @@ -15,7 +15,9 @@ import ( func CobraRunE(cmd *cobra.Command, args []string) (err error) { // run converters for different types for _, c := range Converters { - c() + if err := c(); err != nil { + return err + } } // remove the default network as the all network values are assigned // to this network diff --git a/internal/app/wwctl/node/set/root.go b/internal/app/wwctl/node/set/root.go index e9cc48e9..a5ab4df4 100644 --- a/internal/app/wwctl/node/set/root.go +++ b/internal/app/wwctl/node/set/root.go @@ -38,7 +38,7 @@ var ( SetYes bool SetForce bool NodeConf node.NodeConf - Converters []func() + Converters []func() error ) func init() { diff --git a/internal/app/wwctl/profile/add/main.go b/internal/app/wwctl/profile/add/main.go index e509e9d9..dc23d8fc 100644 --- a/internal/app/wwctl/profile/add/main.go +++ b/internal/app/wwctl/profile/add/main.go @@ -16,7 +16,9 @@ import ( func CobraRunE(cmd *cobra.Command, args []string) (err error) { // run converters for different types for _, c := range Converters { - c() + if err := c(); err != nil { + return err + } } // remove the default network as the all network values are assigned // to this network diff --git a/internal/app/wwctl/profile/add/root.go b/internal/app/wwctl/profile/add/root.go index 2cfec736..6edf403d 100644 --- a/internal/app/wwctl/profile/add/root.go +++ b/internal/app/wwctl/profile/add/root.go @@ -25,7 +25,7 @@ var ( SetForce bool NetName string ProfileConf node.NodeConf - Converters []func() + Converters []func() error ) // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/app/wwctl/profile/set/root.go b/internal/app/wwctl/profile/set/root.go index c9af0599..9fc70f86 100644 --- a/internal/app/wwctl/profile/set/root.go +++ b/internal/app/wwctl/profile/set/root.go @@ -38,7 +38,7 @@ var ( SetForce bool NetName string ProfileConf node.NodeConf - Converters []func() + Converters []func() error ) func init() { diff --git a/internal/pkg/node/checkconf.go b/internal/pkg/node/checkconf.go index 1fa052b5..a595a1c5 100644 --- a/internal/pkg/node/checkconf.go +++ b/internal/pkg/node/checkconf.go @@ -2,7 +2,7 @@ package node import ( "fmt" - "net/netip" + "net" "reflect" "strconv" "strings" @@ -18,9 +18,11 @@ func (nodeConf *NodeConf) Check() (err error) { for i := 0; i < nodeInfoVal.Elem().NumField(); i++ { //wwlog.Debug("checking field: %s type: %s", nodeInfoType.Elem().Field(i).Name, nodeInfoVal.Elem().Field(i).Type()) if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.String { - err = checker(nodeInfoVal.Elem().Field(i).Interface().(string), nodeInfoType.Elem().Field(i).Tag.Get("type")) + newFmt, err := checker(nodeInfoVal.Elem().Field(i).Interface().(string), nodeInfoType.Elem().Field(i).Tag.Get("type")) if err != nil { return fmt.Errorf("field: %s value:%s err: %s", nodeInfoType.Elem().Field(i).Name, nodeInfoVal.Elem().Field(i).String(), err) + } else if newFmt != "" { + nodeInfoVal.Elem().Field(i).SetString(newFmt) } } else if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.Ptr && !nodeInfoVal.Elem().Field(i).IsNil() { nestType := reflect.TypeOf(nodeInfoVal.Elem().Field(i).Interface()) @@ -28,9 +30,11 @@ func (nodeConf *NodeConf) Check() (err error) { for j := 0; j < nestType.Elem().NumField(); j++ { if nestType.Elem().Field(j).Type.Kind() == reflect.String { //wwlog.Debug("checking field: %s type: %s", nestType.Elem().Field(j).Name, nestType.Elem().Field(j).Tag.Get("type")) - err = checker(nestVal.Elem().Field(j).Interface().(string), nestType.Elem().Field(j).Tag.Get("type")) + newFmt, err := checker(nestVal.Elem().Field(j).Interface().(string), nestType.Elem().Field(j).Tag.Get("type")) if err != nil { return fmt.Errorf("field: %s value:%s err: %s", nestType.Elem().Field(j).Name, nestVal.Elem().Field(j).String(), err) + } else if newFmt != "" { + nestVal.Elem().Field(j).SetString(newFmt) } } } @@ -40,9 +44,11 @@ func (nodeConf *NodeConf) Check() (err error) { netType := reflect.TypeOf(val) netVal := reflect.ValueOf(val) for j := 0; j < netType.Elem().NumField(); j++ { - err = checker(netVal.Elem().Field(j).String(), netType.Elem().Field(j).Tag.Get("type")) + newFmt, err := checker(netVal.Elem().Field(j).String(), netType.Elem().Field(j).Tag.Get("type")) if err != nil { return fmt.Errorf("field: %s value:%s err: %s", netType.Elem().Field(j).Name, netVal.Elem().Field(j).String(), err) + } else if newFmt != "" { + netVal.Elem().Field(j).SetString(newFmt) } } } @@ -51,26 +57,39 @@ func (nodeConf *NodeConf) Check() (err error) { return nil } -func checker(value string, valType string) (err error) { +func checker(value string, valType string) (niceValue string, err error) { if valType == "" || value == "" { - return nil + return "", nil } //wwlog.Debug("checker: %s is %s", value, valType) switch valType { case "": - return nil + return "", nil case "bool": if strings.ToLower(value) == "yes" { - return nil + return "true", nil } if strings.ToLower(value) == "no" { - return nil + return "false", nil } - _, err = strconv.ParseBool(value) - return err + myBool, err := strconv.ParseBool(value) + return strconv.FormatBool(myBool), err case "IP": - _, err = netip.ParseAddr(value) - return err + if addr := net.ParseIP(value); addr == nil { + return "", fmt.Errorf("%s can't be parsed to ip address", value) + } else { + return addr.String(), nil + } + case "MAC": + if mac, err := net.ParseMAC(value); err != nil { + return "", fmt.Errorf("%s can't be parsed to MAC address: %s", value, err) + } else { + return mac.String(), nil + } + case "uint": + if _, err := strconv.ParseUint(value, 10, 64); err != nil { + return "", fmt.Errorf("%s is not a uint: %s", value, err) + } } - return nil + return "", nil } diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index 06fc4c05..578cb094 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -135,12 +135,13 @@ func (config *NodeYaml) FindAllNodes() ([]NodeInfo, error) { defData, err := os.ReadFile(DefaultConfig) if err != nil { wwlog.Verbose("Couldn't read DefaultConfig :%s\n", err) + wwlog.Verbose("Using building defaults") + defData = []byte(FallBackConf) } wwlog.Debug("Unmarshalling default config\n") err = yaml.Unmarshal(defData, &defConf) if err != nil { wwlog.Verbose("Couldn't unmarshall defaults from file :%s\n", err) - wwlog.Verbose("Using building defaults") err = yaml.Unmarshal([]byte(FallBackConf), &defConf) if err != nil { wwlog.Warn("Could not get any defaults") diff --git a/internal/pkg/node/datastructure.go b/internal/pkg/node/datastructure.go index a4dab9c2..2c27c289 100644 --- a/internal/pkg/node/datastructure.go +++ b/internal/pkg/node/datastructure.go @@ -75,14 +75,14 @@ type NetDevs struct { Type string `yaml:"type,omitempty" lopt:"type" sopt:"T" comment:"Set device type of given network"` OnBoot string `yaml:"onboot,omitempty" lopt:"onboot" comment:"Enable/disable network device (true/false)" type:"bool"` Device string `yaml:"device,omitempty" lopt:"netdev" sopt:"N" comment:"Set the device for given network"` - Hwaddr string `yaml:"hwaddr,omitempty" lopt:"hwaddr" sopt:"H" comment:"Set the device's HW address for given network"` + Hwaddr string `yaml:"hwaddr,omitempty" lopt:"hwaddr" sopt:"H" comment:"Set the device's HW address for given network" type:"MAC"` Ipaddr string `yaml:"ipaddr,omitempty" comment:"IPv4 address in given network" sopt:"I" lopt:"ipaddr" type:"IP"` IpCIDR string `yaml:"ipcidr,omitempty"` Ipaddr6 string `yaml:"ip6addr,omitempty" lopt:"ipaddr6" comment:"IPv6 address" type:"IP"` Prefix string `yaml:"prefix,omitempty"` Netmask string `yaml:"netmask,omitempty" lopt:"netmask" sopt:"M" comment:"Set the networks netmask" type:"IP"` Gateway string `yaml:"gateway,omitempty" lopt:"gateway" sopt:"G" comment:"Set the node's network device gateway" type:"IP"` - MTU string `yaml:"mtu,omitempty" lopt:"mtu" comment:"Set the mtu"` + MTU string `yaml:"mtu,omitempty" lopt:"mtu" comment:"Set the mtu" type:"uint"` Primary string `yaml:"primary,omitempty" lopt:"primary" comment:"Enable/disable network device as primary (true/false)" type:"bool"` Default string `yaml:"default,omitempty"` /* backward compatibility */ Tags map[string]string `yaml:"tags,omitempty" lopt:"nettagadd" comment:"network tags"` diff --git a/internal/pkg/node/flags.go b/internal/pkg/node/flags.go index 6a12b914..4804f7c9 100644 --- a/internal/pkg/node/flags.go +++ b/internal/pkg/node/flags.go @@ -1,14 +1,13 @@ package node import ( + "fmt" "net" - "os" "reflect" "strconv" "strings" "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/spf13/cobra" ) @@ -17,7 +16,7 @@ Create cmd line flags from the NodeConf fields. Returns a []func() where every f must be called, as the commandline parser returns e.g. netip.IP objects which must be parsedf back to strings. */ -func (nodeConf *NodeConf) CreateFlags(baseCmd *cobra.Command, excludeList []string) (converters []func()) { +func (nodeConf *NodeConf) CreateFlags(baseCmd *cobra.Command, excludeList []string) (converters []func() error) { nodeInfoType := reflect.TypeOf(nodeConf) nodeInfoVal := reflect.ValueOf(nodeConf) // now iterate of every field @@ -60,48 +59,54 @@ func (nodeConf *NodeConf) CreateFlags(baseCmd *cobra.Command, excludeList []stri Helper function to create the different PerisitantFlags() for different types. */ func createFlags(baseCmd *cobra.Command, excludeList []string, - myType reflect.StructField, myVal *reflect.Value) (converters []func()) { + myType reflect.StructField, myVal *reflect.Value) (converters []func() error) { if myType.Tag.Get("lopt") != "" { if myType.Type.Kind() == reflect.String { ptr := myVal.Addr().Interface().(*string) switch myType.Tag.Get("type") { case "uint": - defaultConv, _ := strconv.ParseUint(myType.Tag.Get("default"), 10, 32) - var valueRaw uint - converters = append(converters, func() { *ptr = strconv.FormatUint(uint64(valueRaw), 10) }) + converters = append(converters, func() error { + if !util.InSlice(GetUnsetVerbs(), *ptr) && *ptr != "" { + _, err := strconv.ParseUint(myType.Tag.Get(*ptr), 10, 32) + if err != nil { + return err + } + } + return nil + }) if myType.Tag.Get("sopt") != "" { - baseCmd.PersistentFlags().UintVarP(&valueRaw, + baseCmd.PersistentFlags().StringVarP(ptr, myType.Tag.Get("lopt"), myType.Tag.Get("sopt"), - uint(defaultConv), + myType.Tag.Get("default"), myType.Tag.Get("comment")) } else { - baseCmd.PersistentFlags().UintVar(&valueRaw, + baseCmd.PersistentFlags().StringVar(ptr, myType.Tag.Get("lopt"), - uint(defaultConv), + myType.Tag.Get("default"), myType.Tag.Get("comment")) } case "bool": /* Can't use the bool var from pflag as we need the UNSET verbs to be passwd correctly */ - converters = append(converters, func() { + converters = append(converters, func() error { if !util.InSlice(GetUnsetVerbs(), *ptr) && *ptr != "" { if strings.ToLower(*ptr) != "yes" { *ptr = "true" - return + return nil } if strings.ToLower(*ptr) != "no" { *ptr = "false" - return + return nil } val, err := strconv.ParseBool(*ptr) if err != nil { - wwlog.Error("commandline option %s needs to be bool", myType.Tag.Get("lopt")) - os.Exit(1) + return fmt.Errorf("commandline option %s needs to be bool", myType.Tag.Get("lopt")) } *ptr = strconv.FormatBool(val) } + return nil }) if myType.Tag.Get("sopt") != "" { baseCmd.PersistentFlags().StringVarP(ptr, @@ -119,10 +124,12 @@ func createFlags(baseCmd *cobra.Command, excludeList []string, case "IP": defaultConv := net.ParseIP(myType.Tag.Get("default")) var valueRaw net.IP - converters = append(converters, func() { + converters = append(converters, func() error { if valueRaw != nil { + // will always get a IP, not a string *ptr = valueRaw.String() } + return nil }) if myType.Tag.Get("sopt") != "" { baseCmd.PersistentFlags().IPVarP(&valueRaw, @@ -139,7 +146,14 @@ func createFlags(baseCmd *cobra.Command, excludeList []string, case "IPMask": defaultConv := net.ParseIP(myType.Tag.Get("default")).DefaultMask() var valueRaw net.IPMask - converters = append(converters, func() { *ptr = valueRaw.String() }) + converters = append(converters, func() error { + if valueRaw != nil { + *ptr = valueRaw.String() + return nil + } else { + return fmt.Errorf("could not parse %s to IP", valueRaw.String()) + } + }) if myType.Tag.Get("sopt") != "" { baseCmd.PersistentFlags().IPMaskVarP(&valueRaw, myType.Tag.Get("lopt"), @@ -152,6 +166,27 @@ func createFlags(baseCmd *cobra.Command, excludeList []string, defaultConv, myType.Tag.Get("comment")) } + case "MAC": + converters = append(converters, func() error { + myMac, err := net.ParseMAC(*ptr) + if err != nil { + return err + } + *ptr = myMac.String() + return nil + }) + if myType.Tag.Get("sopt") != "" { + baseCmd.PersistentFlags().StringVarP(ptr, + myType.Tag.Get("lopt"), + myType.Tag.Get("sopt"), + "", + myType.Tag.Get("comment")) + } else { + baseCmd.PersistentFlags().StringVar(ptr, + myType.Tag.Get("lopt"), + "", + myType.Tag.Get("comment")) + } default: if myType.Tag.Get("sopt") != "" { baseCmd.PersistentFlags().StringVarP(ptr, diff --git a/internal/pkg/node/transformer_test.go b/internal/pkg/node/transformer_test.go index 8742f2a3..c09c2843 100644 --- a/internal/pkg/node/transformer_test.go +++ b/internal/pkg/node/transformer_test.go @@ -1,37 +1,223 @@ package node import ( + "fmt" "reflect" + "strconv" "testing" + + "gopkg.in/yaml.v2" ) +func NewTransformerTestNode() NodeYaml { + var data = ` +nodeprofiles: + default: + comment: This profile is automatically included for each node + ipmi: + username: greg + profile2: + tags: + foo: foo profile2 + comment: Comment profile2 + ipmi: + tags: + foo: foo ipmi profile +nodes: + test_node1: + comment: Node Comment + profiles: + - default + network devices: + net0: + device: eth1 + discoverable: true + ipmi: + username: chris + tags: + baar: baar node1 + test_node2: + primary: net0 + profiles: + - default + - profile2 + network devices: + net0: + netmask: 1.1.1.1 + net1: + ipaddr: 1.2.3.4 + tags: + baar: baar node2 + test_node3: + profiles: + - profile2 + tags: + foo: foo node3 + foobaar: foobaar node3 + ipmi: + ipaddr: 1.1.1.1 + tags: + foo: foo ipmi node3 + ` + var ret NodeYaml + _ = yaml.Unmarshal([]byte(data), &ret) + return ret +} func Test_nodeYaml_SetFrom(t *testing.T) { - c, _ := NewTestNode() - singleNodeConf := c.Nodes["test_node"] - singleNodeInfo := NewInfo() - singleNodeInfo.SetFrom(singleNodeConf) - tests := []struct { + c := NewTransformerTestNode() + nodes, _ := c.FindAllNodes() + test_node1 := NewInfo() + test_node2 := NewInfo() + test_node3 := NewInfo() + for _, n := range nodes { + if n.Id.Get() == "test_node1" { + test_node1 = n + } + if n.Id.Get() == "test_node2" { + test_node2 = n + } + if n.Id.Get() == "test_node3" { + test_node3 = n + } + } + getByNametests := []struct { name string arg string want string wantErr bool }{ - {"Right comment", "Comment", "Node Comment", false}, - {"FieldName", "comment", "NodeComment", true}, + {"GetByName: FieldValue", "Comment", "Node Comment", false}, + {"GetByName: FieldName", "comment", "NodeComment", true}, } - for _, tt := range tests { + for _, tt := range getByNametests { t.Run(tt.name, func(t *testing.T) { - got, err := GetByName(&singleNodeInfo, tt.arg) + got, err := GetByName(&test_node1, tt.arg) if (err != nil) != tt.wantErr { t.Errorf("GetByName(%s,%s) error = %v, wantErr %v", - reflect.TypeOf(singleNodeConf), tt.arg, err, tt.wantErr) + reflect.TypeOf(test_node1), tt.arg, err, tt.wantErr) return } if (got != tt.want) != tt.wantErr { t.Errorf("GetByName(%s,%s) got = %v, want = %v", - reflect.TypeOf(singleNodeConf), tt.arg, got, tt.want) + reflect.TypeOf(test_node1), tt.arg, got, tt.want) return } }) } + t.Run("Get() comment", func(t *testing.T) { + comment := test_node1.Comment.Get() + if comment != "Node Comment" { + t.Errorf("Get() returned wrong comment: %s", comment) + } + }) + t.Run("Get() profile comment", func(t *testing.T) { + comment := test_node2.Comment.Get() + if comment != "Comment profile2" { + t.Errorf("Get() returned wrong comment: %s", comment) + } + }) + t.Run("Get() default ipxe", func(t *testing.T) { + value := test_node1.Ipxe.Get() + if value != "default" { + t.Errorf("Get() returned wrong ipxe template: %s", value) + } + }) + t.Run("GetSlice() default profile", func(t *testing.T) { + value := test_node1.Profiles.GetSlice()[0] + if value != "default" { + t.Errorf("GetSlice() returned wrong profile: %s", value) + } + }) + t.Run("Get() default kernel args", func(t *testing.T) { + value := test_node1.Kernel.Args.Get() + if value != "quiet crashkernel=no vga=791 net.naming-scheme=v238" { + t.Errorf("Get() returned wrong kernel args: %s", value) + } + }) + t.Run("Get() default network mask", func(t *testing.T) { + value := test_node1.NetDevs["net0"].Netmask.Get() + if value != "255.255.255.0" { + t.Errorf("Get() returned wrong default netmask: %s", value) + } + }) + t.Run("Get() default network mask", func(t *testing.T) { + value := test_node2.NetDevs["net0"].Netmask.Get() + if value != "1.1.1.1" { + t.Errorf("Get() returned wrong default netmask: %s", value) + } + }) + t.Run("GetB() primary for single network", func(t *testing.T) { + value := test_node1.NetDevs["net0"].Primary.GetB() + if !value { + t.Errorf("GetB() returned wrong: %s", strconv.FormatBool(value)) + } + }) + t.Run("GetB() for primary with two networks", func(t *testing.T) { + value := test_node2.NetDevs["net0"].Primary.GetB() + if !value { + t.Errorf("GetB() returned wrong: %s", strconv.FormatBool(value)) + } + }) + t.Run("GetB() for primary with two networks, get secondary network", func(t *testing.T) { + value := test_node2.NetDevs["net1"].Primary.GetB() + if value { + t.Errorf("GetB() returned wrong: %s", strconv.FormatBool(value)) + } + }) + t.Run("GetB() default discoverable", func(t *testing.T) { + value := test_node1.Discoverable.GetB() + if !value { + t.Errorf("GetB() returned wrong: %s", strconv.FormatBool(value)) + } + }) + t.Run("GetB() default discoverable", func(t *testing.T) { + value := test_node2.Discoverable.GetB() + if value { + t.Errorf("GetB() returned wrong: %s", strconv.FormatBool(value)) + } + }) + t.Run("Get() ipmi user from profile", func(t *testing.T) { + value := test_node2.Ipmi.UserName.Get() + if value != "greg" { + t.Errorf("Get() returned wrong ipmi username: %s", value) + } + }) + t.Run("Get() ipmi user from node", func(t *testing.T) { + value := test_node1.Ipmi.UserName.Get() + if value != "chris" { + t.Errorf("Get() returned wrong ipmi username: %s", value) + } + }) + t.Run("Get() tag foo from profile, node does not have this tag", func(t *testing.T) { + value := test_node2.Tags["foo"].Get() + if value != "foo profile2" { + t.Errorf("Get() returned wrong tag for foo: %s", value) + } + }) + t.Run("Get() tag baar from node, node tag map is not overwritten", func(t *testing.T) { + value := test_node2.Tags["baar"].Get() + if value != "baar node2" { + t.Errorf("Get() returned wrong tag for foo: %s", value) + } + }) + t.Run("Get() tag foo from node, tag present in profile", func(t *testing.T) { + value := test_node3.Tags["foo"].Get() + if value != "foo node3" { + t.Errorf("Get() returned wrong tag for foo: %s", value) + } + }) + t.Run("Get() tag foobaar from node", func(t *testing.T) { + value := test_node3.Tags["foobaar"].Get() + if value != "foobaar node3" { + t.Errorf("Get() returned wrong tag for foo: %s", value) + } + }) + t.Run("Get() ipmitag foo from profile, node does not have this tag", func(t *testing.T) { + fmt.Println("ipmi tags", test_node3.Ipmi.Tags) + fmt.Println(c.Nodes["test_node3"].Ipmi) + value := test_node3.Ipmi.Tags["foo"].Get() + if value != "foo ipmi node3" { + t.Errorf("Get() returned wrong tag for foo: %s", value) + } + }) } diff --git a/internal/pkg/node/transformers.go b/internal/pkg/node/transformers.go index 4acaa859..9eceac5d 100644 --- a/internal/pkg/node/transformers.go +++ b/internal/pkg/node/transformers.go @@ -266,24 +266,28 @@ func (node *NodeInfo) setterFrom(n *NodeConf, nameArg string, } } else if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.Ptr && !valField.IsZero() { nestedInfoType := reflect.TypeOf(nodeInfoVal.Elem().Field(i).Interface()) - netstedInfoVal := reflect.ValueOf(nodeInfoVal.Elem().Field(i).Interface()) + nestedInfoVal := reflect.ValueOf(nodeInfoVal.Elem().Field(i).Interface()) nestedConfVal := reflect.ValueOf(valField.Interface()) for j := 0; j < nestedInfoType.Elem().NumField(); j++ { nestedVal := nestedConfVal.Elem().FieldByName(nestedInfoType.Elem().Field(j).Name) if nestedVal.IsValid() { - if netstedInfoVal.Elem().Field(j).Type() == reflect.TypeOf(Entry{}) { - setter(netstedInfoVal.Elem().Field(j).Addr().Interface().(*Entry), nestedVal.String(), nameArg) - } else { + if nestedInfoVal.Elem().Field(j).Type() == reflect.TypeOf(Entry{}) { + setter(nestedInfoVal.Elem().Field(j).Addr().Interface().(*Entry), nestedVal.String(), nameArg) + } else if nestedInfoVal.Elem().Field(j).Type() == reflect.TypeOf(map[string](*Entry){}) { confMap := nestedVal.Interface().(map[string]string) - if netstedInfoVal.Elem().Field(j).IsNil() { - newMap := make(map[string]*Entry) - mapPtr := (netstedInfoVal.Elem().Field(j).Addr().Interface()).(*map[string](*Entry)) - *mapPtr = newMap + if nestedInfoVal.Elem().Field(j).IsNil() { + ptr := nestedInfoVal.Elem().Field(j).Addr().Interface().(*map[string](*Entry)) + *ptr = make(map[string]*Entry) } + tagMap := nestedInfoVal.Elem().Field(j).Interface().(map[string](*Entry)) for key, val := range confMap { - entr := new(Entry) - setter(entr, val, nameArg) - (netstedInfoVal.Elem().Field(j).Interface()).(map[string](*Entry))[key] = entr + if entr, ok := tagMap[key]; ok { + setter(entr, val, nameArg) + } else { + entr := new(Entry) + tagMap[key] = entr + setter(entr, val, nameArg) + } } } } @@ -291,9 +295,17 @@ func (node *NodeInfo) setterFrom(n *NodeConf, nameArg string, } else if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf(map[string](*Entry)(nil)) { confMap := valField.Interface().(map[string]string) for key, val := range confMap { - entr := new(Entry) - setter(entr, val, nameArg) - (nodeInfoVal.Elem().Field(i).Interface()).(map[string](*Entry))[key] = entr + tagMap := nodeInfoVal.Elem().Field(i).Interface().(map[string](*Entry)) + if nodeInfoVal.Elem().Field(i).IsNil() { + tagMap = make(map[string]*Entry) + } + if entr, ok := tagMap[key]; ok { + setter(entr, val, nameArg) + } else { + entr := new(Entry) + tagMap[key] = entr + setter(entr, val, nameArg) + } } } else if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf(map[string](*NetDevEntry)(nil)) { netValMap := valField.Interface().(map[string](*NetDevs)) diff --git a/internal/pkg/node/util.go b/internal/pkg/node/util.go index c641376c..8470a82b 100644 --- a/internal/pkg/node/util.go +++ b/internal/pkg/node/util.go @@ -27,10 +27,11 @@ func (config *NodeYaml) FindByHwaddr(hwa string) (NodeInfo, error) { } func (config *NodeYaml) FindByIpaddr(ipaddr string) (NodeInfo, error) { - if net.ParseIP(ipaddr) == nil { + if addr := net.ParseIP(ipaddr); addr == nil { return NodeInfo{}, errors.New("invalid IP:" + ipaddr) + } else { + ipaddr = addr.String() } - var ret NodeInfo n, _ := config.FindAllNodes() diff --git a/internal/pkg/node/util_test.go b/internal/pkg/node/util_test.go index bdce3bf7..2519105e 100644 --- a/internal/pkg/node/util_test.go +++ b/internal/pkg/node/util_test.go @@ -6,7 +6,7 @@ import ( "gopkg.in/yaml.v2" ) -func NewTestNode() (NodeYaml, error) { +func NewUtilTestNode() (NodeYaml, error) { var data = ` nodeprofiles: default: @@ -49,7 +49,7 @@ nodes: } func Test_nodeYaml_FindByHwaddr(t *testing.T) { - c, _ := NewTestNode() + c, _ := NewUtilTestNode() //type fields struct { // NodeProfiles map[string]*NodeConf // Nodes map[string]*NodeConf @@ -90,7 +90,7 @@ func Test_nodeYaml_FindByHwaddr(t *testing.T) { } func Test_nodeYaml_FindByIpaddr(t *testing.T) { - c, _ := NewTestNode() + c, _ := NewUtilTestNode() type args struct { ipaddr string }