diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index 612eacbd..d3d3bc1c 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -166,7 +166,7 @@ func (config *NodeYaml) FindAllNodes() ([]NodeInfo, error) { n.SetFrom(node) // only now the netdevs start to exist so that default values can be set for _, netdev := range n.NetDevs { - netdev.SetDefFrom(defConfNet) + SetDefFrom(defConfNet, netdev) } // backward compatibility n.Ipmi.Ipaddr.Set(node.IpmiIpaddr) diff --git a/internal/pkg/node/methods.go b/internal/pkg/node/methods.go index 0d60254f..b92bb361 100644 --- a/internal/pkg/node/methods.go +++ b/internal/pkg/node/methods.go @@ -380,11 +380,6 @@ func NewInfo() (nodeInfo NodeInfo) { return nodeInfo } -func NewNetDevEntry() (netdev NetDevEntry) { - netdev.Tags = make(map[string]*Entry) - return -} - /* Get a entry by its name */ diff --git a/internal/pkg/node/modifiers.go b/internal/pkg/node/modifiers.go index 361e2365..455ea514 100644 --- a/internal/pkg/node/modifiers.go +++ b/internal/pkg/node/modifiers.go @@ -48,12 +48,19 @@ func (config *NodeYaml) DelNode(nodeID string) error { return nil } +/* +update the node in the database +*/ func (config *NodeYaml) NodeUpdate(node NodeInfo) error { nodeID := node.Id.Get() - + wwlog.Debug("updating node %s: %v", nodeID, node) if _, ok := config.Nodes[nodeID]; !ok { return errors.New("Nodename does not exist: " + nodeID) } + // maps may have deleted elements which will not be updated + // so we delete the node and call GetRealFrom on an empty NodeConv + delete(config.Nodes, nodeID) + config.Nodes[nodeID] = new(NodeConf) config.Nodes[nodeID].GetRealFrom(node) return nil } @@ -102,6 +109,10 @@ func (config *NodeYaml) ProfileUpdate(profile NodeInfo) error { if _, ok := config.NodeProfiles[profileID]; !ok { return errors.New("Profile name does not exist: " + profileID) } + // maps may have deleted elements which will not be updated + // so we delete the profile and call GetRealFrom on an empty NodeConv + delete(config.NodeProfiles, profileID) + config.NodeProfiles[profileID] = new(NodeConf) config.NodeProfiles[profileID].GetRealFrom(profile) return nil } @@ -133,7 +144,6 @@ func (config *NodeYaml) Persist() error { return nil } - // Dump returns a YAML document representing the nodeDb // instance. Passes through any errors generated by yaml.Marshal. func (config *NodeYaml) Dump() ([]byte, error) { diff --git a/internal/pkg/node/transformers.go b/internal/pkg/node/transformers.go index 66d24328..2dff47ea 100644 --- a/internal/pkg/node/transformers.go +++ b/internal/pkg/node/transformers.go @@ -104,17 +104,17 @@ func (node *NodeInfo) SetFrom(n *NodeConf) { setSliceWrap := func(entr *Entry, val []string, nameArg string) { entr.SetSlice(val) } - node.setterFrom(n, "", setWrap, setSliceWrap) + recursiveSetter(n, node, "", setWrap, setSliceWrap) } /* Populates all fields of NodeInfo with SetAlt from the values of NodeConf. The string profileName is used to -destermine from which source/NodeInfo the entry came +determine from which source/NodeInfo the entry came from. */ func (node *NodeInfo) SetAltFrom(n *NodeConf, profileName string) { - node.setterFrom(n, profileName, (*Entry).SetAlt, (*Entry).SetAltSlice) + recursiveSetter(n, node, profileName, (*Entry).SetAlt, (*Entry).SetAltSlice) } /* @@ -128,120 +128,95 @@ func (node *NodeInfo) SetDefFrom(n *NodeConf) { setSliceWrap := func(entr *Entry, val []string, nameArg string) { entr.SetDefaultSlice(val) } - node.setterFrom(n, "", setWrap, setSliceWrap) + recursiveSetter(n, node, "", setWrap, setSliceWrap) +} + +func SetDefFrom(source, target interface{}) { + setWrap := func(entr *Entry, val string, nameArg string) { + entr.SetDefault(val) + } + setSliceWrap := func(entr *Entry, val []string, nameArg string) { + entr.SetDefaultSlice(val) + } + recursiveSetter(source, target, "", setWrap, setSliceWrap) + } /* Abstract function which populates a NodeInfo from a NodeConf via -setter functionns. +setter functions. Panics if other type than string, []string *ptr is used in NodeConf. */ -func (node *NodeInfo) setterFrom(n *NodeConf, nameArg string, - setter func(*Entry, string, string), +func recursiveSetter(source, target interface{}, nameArg string, setter func(*Entry, string, string), setterSlice func(*Entry, []string, string)) { - // get the full memory, taking the shortcut and init Ipmi and Kernel directly - if node.Kernel == nil { - node.Kernel = new(KernelEntry) - } - if node.Ipmi == nil { - node.Ipmi = new(IpmiEntry) - } - // also n could be nil - if n == nil { - myn := NewConf() - n = &myn - } - nodeInfoVal := reflect.ValueOf(node) - nodeInfoType := reflect.TypeOf(node) - nodeConfVal := reflect.ValueOf(n) - // now iterate of every field - for i := 0; i < nodeInfoType.Elem().NumField(); i++ { - valField := nodeConfVal.Elem().FieldByName(nodeInfoType.Elem().Field(i).Name) - if valField.IsValid() { - // found field with same name for Conf and Info - if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf(Entry{}) { - if valField.Type().Kind() == reflect.String { - setter(nodeInfoVal.Elem().Field(i).Addr().Interface().(*Entry), valField.String(), nameArg) - } else if valField.Type() == reflect.TypeOf([]string{}) { - setterSlice(nodeInfoVal.Elem().Field(i).Addr().Interface().(*Entry), valField.Interface().([]string), nameArg) - } - } else if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.Ptr && !valField.IsZero() { - nestedInfoType := reflect.TypeOf(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 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 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 { - if entr, ok := tagMap[key]; ok { - setter(entr, val, nameArg) - } else { - entr := new(Entry) - tagMap[key] = entr - setter(entr, val, nameArg) + sourceValue := reflect.ValueOf(source) + targetType := reflect.TypeOf(target) + targetValue := reflect.ValueOf(target) + if targetValue.Elem().Kind() == reflect.Struct && sourceValue.Elem().Kind() == reflect.Struct { + for i := 0; i < targetType.Elem().NumField(); i++ { + sourceValueMatched := sourceValue.Elem().FieldByName(targetType.Elem().Field(i).Name) + if sourceValueMatched.IsValid() { + if targetValue.Elem().Field(i).Type() == reflect.TypeOf(Entry{}) { + // get the fields which are part of the struct + switch sourceValueMatched.Type() { + case reflect.TypeOf(""): + setter(targetValue.Elem().Field(i).Addr().Interface().(*Entry), sourceValueMatched.String(), nameArg) + case reflect.TypeOf([]string{}): + setterSlice(targetValue.Elem().Field(i).Addr().Interface().(*Entry), sourceValueMatched.Interface().([]string), nameArg) + default: + panic(fmt.Errorf("can't convert an Entry to %s", targetValue.Elem().Field(i).Type())) + } + } else if sourceValueMatched.Kind() == reflect.Ptr { + // if we get a pointer, initialize if empty and then have a recursive call + if targetValue.Elem().Field(i).IsZero() { + targetValue.Elem().Field(i).Set(reflect.New(targetType.Elem().Field(i).Type.Elem())) + } + recursiveSetter(sourceValueMatched.Interface(), targetValue.Elem().Field(i).Interface(), nameArg, setter, setterSlice) + } else if sourceValueMatched.Type().Kind() == reflect.Map { + if targetValue.Elem().Field(i).IsZero() { + targetValue.Elem().Field(i).Set(reflect.MakeMap(targetType.Elem().Field(i).Type)) + } + // delete a ap element which is only in the target + if targetValue.Elem().Field(i).Len() > 0 && targetValue.Elem().Field(i).Len() < 0 { + sourceIter := sourceValueMatched.MapRange() + targetIter := targetValue.Elem().Field(i).MapRange() + for targetIter.Next() { + sameKey := false + for sourceIter.Next() { + if sourceIter.Key() == targetIter.Key() { + sameKey = true } } + if !sameKey { + targetValue.Elem().Field(i).SetMapIndex(targetIter.Key(), reflect.Value{}) + } } } - } - } else if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf(map[string](*Entry)(nil)) { - confMap := valField.Interface().(map[string]string) - for key, val := range confMap { - 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)) - for netName, netVals := range netValMap { - netValsType := reflect.ValueOf(netVals) - netMap := nodeInfoVal.Elem().Field(i).Interface().(map[string](*NetDevEntry)) - if nodeInfoVal.Elem().Field(i).IsNil() { - netMap = make(map[string]*NetDevEntry) - } - if _, ok := netMap[netName]; !ok { - var newNet NetDevEntry - newNet.Tags = make(map[string]*Entry) - netMap[netName] = &newNet - } - netInfoType := reflect.TypeOf(*netMap[netName]) - netInfoVal := reflect.ValueOf(netMap[netName]) - for j := 0; j < netInfoType.NumField(); j++ { - netVal := netValsType.Elem().FieldByName(netInfoType.Field(j).Name) - if netVal.IsValid() { - if netVal.Type().Kind() == reflect.String { - setter(netInfoVal.Elem().Field(j).Addr().Interface().((*Entry)), netVal.String(), nameArg) - } else if netVal.Type() == reflect.TypeOf(map[string]string{}) { - for key, val := range (netVal.Interface()).(map[string]string) { - //netTagMap := netInfoVal.Elem().Field(j).Interface().((map[string](*Entry))) - if _, ok := netInfoVal.Elem().Field(j).Interface().((map[string](*Entry)))[key]; !ok { - netInfoVal.Elem().Field(j).Interface().((map[string](*Entry)))[key] = new(Entry) - } - setter(netInfoVal.Elem().Field(j).Interface().((map[string](*Entry)))[key], val, nameArg) - } + sourceIter := sourceValueMatched.MapRange() + if sourceValueMatched.Type().Elem() == reflect.TypeOf("") { + // go over a simple map with strings + for sourceIter.Next() { + if !targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).IsValid() { + newEntr := new(Entry) + setter(newEntr, sourceIter.Value().String(), nameArg) + targetValue.Elem().Field(i).SetMapIndex(sourceIter.Key(), reflect.ValueOf(newEntr)) } } + } else { + // now the complicated map which contains pointers to objects + for sourceIter.Next() { + if !targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).IsValid() { + newPtr := reflect.New(targetType.Elem().Field(i).Type.Elem().Elem()) + targetValue.Elem().Field(i).SetMapIndex(sourceIter.Key(), newPtr) + } + recursiveSetter(sourceIter.Value().Interface(), targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).Interface(), nameArg, setter, setterSlice) + + } } } } } } + } /* @@ -280,78 +255,6 @@ func recursiveFlatten(strct interface{}) { } } -/* -Populates all fields of NetDevEntry with Set from the -values of NetDevs. -Actually not used, just for completeness. -*/ -func (netDev *NetDevEntry) SetFrom(netYaml *NetDevs) { - setWrap := func(entr *Entry, val string, nameArg string) { - entr.Set(val) - } - setSliceWrap := func(entr *Entry, val []string, nameArg string) { - entr.SetSlice(val) - } - netDev.setterFrom(netYaml, "", setWrap, setSliceWrap) -} - -/* -Populates all fields of NetDevEntry with SetAlt from the -values of NetDevs. The string profileName is used to -destermine from which source/NodeInfo the entry came -from. -Actually not used, just for completeness. -*/ -func (netDev *NetDevEntry) SetAltFrom(netYaml *NetDevs, profileName string) { - netDev.setterFrom(netYaml, profileName, (*Entry).SetAlt, (*Entry).SetAltSlice) -} - -/* -Populates all fields of NodeInfo with SetDefault from the -values of NodeConf. -*/ -func (netDev *NetDevEntry) SetDefFrom(netYaml *NetDevs) { - setWrap := func(entr *Entry, val string, nameArg string) { - entr.SetDefault(val) - } - setSliceWrap := func(entr *Entry, val []string, nameArg string) { - entr.SetDefaultSlice(val) - } - netDev.setterFrom(netYaml, "", setWrap, setSliceWrap) -} - -/* -Abstract function for setting a NetDevEntry from a NetDevs -*/ -func (netDev *NetDevEntry) setterFrom(netYaml *NetDevs, nameArg string, - setter func(*Entry, string, string), - setterSlice func(*Entry, []string, string)) { - // check if netYaml is empty - if netYaml == nil { - netYaml = new(NetDevs) - } - netValues := reflect.ValueOf(netDev) - netInfoType := reflect.TypeOf(*netYaml) - netInfoVal := reflect.ValueOf(*netYaml) - for j := 0; j < netInfoType.NumField(); j++ { - netVal := netValues.Elem().FieldByName(netInfoType.Field(j).Name) - if netVal.IsValid() { - if netInfoVal.Field(j).Type().Kind() == reflect.String { - setter(netVal.Addr().Interface().((*Entry)), netInfoVal.Field(j).String(), nameArg) - } else if netVal.Type() == reflect.TypeOf(map[string]string{}) { - // danger zone following code is not tested - for key, val := range (netVal.Interface()).(map[string]string) { - //netTagMap := netInfoVal.Elem().Field(j).Interface().((map[string](*Entry))) - if _, ok := netInfoVal.Elem().Field(j).Interface().((map[string](*Entry)))[key]; !ok { - netInfoVal.Elem().Field(j).Interface().((map[string](*Entry)))[key] = new(Entry) - } - setter(netInfoVal.Elem().Field(j).Interface().((map[string](*Entry)))[key], val, nameArg) - } - } - } - } -} - /* Create a string slice, where every element represents a yaml entry */