This is a significant change in the undelying data model! nodeDb, err := node.New() will result in a structure which contains the on disk values. Only nodeDb.FindAllNodes() or nodeDb.GetNode(id) will give the nodes with its merged in profiles. Signed-off-by: Christian Goll <cgoll@suse.com>
261 lines
7.4 KiB
Go
261 lines
7.4 KiB
Go
package node
|
|
|
|
import (
|
|
"net"
|
|
"reflect"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/warewulf/warewulf/internal/pkg/util"
|
|
)
|
|
|
|
type sortByName []NodeConf
|
|
|
|
func (a sortByName) Len() int { return len(a) }
|
|
func (a sortByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
func (a sortByName) Less(i, j int) bool { return a[i].id < a[j].id }
|
|
func GetUnsetVerbs() []string {
|
|
return []string{"UNSET", "DELETE", "UNDEF", "undef", "--", "nil", "0.0.0.0"}
|
|
}
|
|
|
|
/**********
|
|
*
|
|
* Filters
|
|
*
|
|
*********/
|
|
|
|
/*
|
|
Filter a given slice of NodeConf against a given
|
|
regular expression
|
|
*/
|
|
func FilterByName(set []NodeConf, searchList []string) []NodeConf {
|
|
var ret []NodeConf
|
|
unique := make(map[string]NodeConf)
|
|
|
|
if len(searchList) > 0 {
|
|
for _, search := range searchList {
|
|
for _, entry := range set {
|
|
if match, _ := regexp.MatchString("^"+search+"$", entry.id); match {
|
|
unique[entry.id] = entry
|
|
}
|
|
}
|
|
}
|
|
for _, n := range unique {
|
|
ret = append(ret, n)
|
|
}
|
|
} else {
|
|
ret = set
|
|
}
|
|
|
|
sort.Sort(sortByName(ret))
|
|
return ret
|
|
}
|
|
|
|
/*
|
|
Filter a given map of NodeConf against given regular expression.
|
|
*/
|
|
func FilterNodesByName(inputMap map[string]*NodeConf, searchList []string) (retMap map[string]*NodeConf) {
|
|
retMap = map[string]*NodeConf{}
|
|
if len(searchList) > 0 {
|
|
for _, search := range searchList {
|
|
for name, nConf := range inputMap {
|
|
if match, _ := regexp.MatchString("^"+search+"$", name); match {
|
|
retMap[name] = nConf
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return retMap
|
|
}
|
|
|
|
/*
|
|
Filter a given map of NodeConf against given regular expression.
|
|
*/
|
|
func FilterProfilesByName(inputMap map[string]*ProfileConf, searchList []string) (retMap map[string]*ProfileConf) {
|
|
retMap = map[string]*ProfileConf{}
|
|
if len(searchList) > 0 {
|
|
for _, search := range searchList {
|
|
for name, nConf := range inputMap {
|
|
if match, _ := regexp.MatchString("^"+search+"$", name); match {
|
|
retMap[name] = nConf
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return retMap
|
|
}
|
|
|
|
/*
|
|
Creates an NodeConf with the given id. Doesn't add it to the database
|
|
*/
|
|
func NewNode(id string) (nodeconf NodeConf) {
|
|
nodeconf = EmptyNode()
|
|
nodeconf.id = id
|
|
return nodeconf
|
|
}
|
|
|
|
/*
|
|
Creates a ProfileConf but doesn't add it to the database.
|
|
*/
|
|
func EmptyProfile() (profileconf ProfileConf) {
|
|
profileconf.Ipmi = new(IpmiConf)
|
|
profileconf.Ipmi.Tags = map[string]string{}
|
|
profileconf.Kernel = new(KernelConf)
|
|
profileconf.NetDevs = make(map[string]*NetDevs)
|
|
profileconf.Tags = map[string]string{}
|
|
return profileconf
|
|
}
|
|
|
|
/*
|
|
Flattens out a NodeConf, which means if there are no explicit values in *IpmiConf
|
|
or *KernelConf, these pointer will set to nil. This will remove something like
|
|
ipmi: {} from nodes.conf
|
|
*/
|
|
func (info *NodeConf) Flatten() {
|
|
recursiveFlatten(info)
|
|
}
|
|
|
|
/*
|
|
Flattens out a ProfileConf, which means if there are no explicit values in *IpmiConf
|
|
or *KernelConf, these pointer will set to nil. This will remove something like
|
|
ipmi: {} from nodes.conf
|
|
*/
|
|
func (info *ProfileConf) Flatten() {
|
|
recursiveFlatten(info)
|
|
}
|
|
|
|
// abstract flatten
|
|
func recursiveFlatten(strct interface{}) {
|
|
confType := reflect.TypeOf(strct)
|
|
confVal := reflect.ValueOf(strct)
|
|
for j := 0; j < confType.Elem().NumField(); j++ {
|
|
if confVal.Elem().Field(j).Type().Kind() == reflect.Ptr && !confVal.Elem().Field(j).IsNil() {
|
|
// iterate now over the ptr fields
|
|
setToNil := true
|
|
nestedType := reflect.TypeOf(confVal.Elem().Field(j).Interface())
|
|
nestedVal := reflect.ValueOf(confVal.Elem().Field(j).Interface())
|
|
for i := 0; i < nestedType.Elem().NumField(); i++ {
|
|
if nestedType.Elem().Field(i).Type.Kind() == reflect.String &&
|
|
nestedVal.Elem().Field(i).Interface().(string) != "" &&
|
|
nestedVal.Elem().Field(i).Interface().(string) != undef {
|
|
setToNil = false
|
|
} else if nestedType.Elem().Field(i).Type == reflect.TypeOf([]string{}) &&
|
|
len(nestedVal.Elem().Field(i).Interface().([]string)) != 0 {
|
|
setToNil = false
|
|
} else if nestedType.Elem().Field(i).Type == reflect.TypeOf(map[string]string{}) &&
|
|
len(nestedVal.Elem().Field(i).Interface().(map[string]string)) != 0 {
|
|
setToNil = false
|
|
} else if nestedType.Elem().Field(i).Type == reflect.TypeOf(net.IP{}) {
|
|
val := nestedVal.Elem().Field(i).Interface().(net.IP)
|
|
if len(val) != 0 && !val.IsUnspecified() {
|
|
setToNil = false
|
|
}
|
|
}
|
|
}
|
|
if setToNil {
|
|
confVal.Elem().Field(j).Set(reflect.Zero(confVal.Elem().Field(j).Type()))
|
|
}
|
|
} else if confType.Elem().Field(j).Anonymous {
|
|
recursiveFlatten(confVal.Elem().Field(j).Addr().Interface())
|
|
} else if confType.Elem().Field(j).IsExported() && confType.Elem().Field(j).Type.Kind() == reflect.String {
|
|
if confVal.Elem().Field(j).Interface().(string) == undef {
|
|
confVal.Elem().Field(j).SetString("")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Create a string slice, where every element represents a yaml entry, used for node/profile edit
|
|
in order to get a summary of all available elements
|
|
*/
|
|
func UnmarshalConf(obj interface{}, excludeList []string) (lines []string) {
|
|
objType := reflect.TypeOf(obj)
|
|
// now iterate of every field
|
|
for i := 0; i < objType.NumField(); i++ {
|
|
if objType.Field(i).Tag.Get("comment") != "" {
|
|
if ymlStr, ok := getYamlString(objType.Field(i), excludeList); ok {
|
|
lines = append(lines, ymlStr...)
|
|
}
|
|
}
|
|
if objType.Field(i).Type.Kind() == reflect.Ptr && objType.Field(i).Tag.Get("yaml") != "" {
|
|
typeLine := objType.Field(i).Tag.Get("yaml")
|
|
if len(strings.Split(typeLine, ",")) > 1 {
|
|
typeLine = strings.Split(typeLine, ",")[0] + ":"
|
|
}
|
|
lines = append(lines, typeLine)
|
|
nestedLine := UnmarshalConf(reflect.New(objType.Field(i).Type.Elem()).Elem().Interface(), excludeList)
|
|
for _, ln := range nestedLine {
|
|
lines = append(lines, " "+ln)
|
|
}
|
|
} else if objType.Field(i).Type.Kind() == reflect.Map && objType.Field(i).Type.Elem().Kind() == reflect.Ptr {
|
|
typeLine := objType.Field(i).Tag.Get("yaml")
|
|
if len(strings.Split(typeLine, ",")) > 1 {
|
|
typeLine = strings.Split(typeLine, ",")[0] + ":"
|
|
}
|
|
lines = append(lines, typeLine, " element:")
|
|
nestedLine := UnmarshalConf(reflect.New(objType.Field(i).Type.Elem().Elem()).Elem().Interface(), excludeList)
|
|
for _, ln := range nestedLine {
|
|
lines = append(lines, " "+ln)
|
|
}
|
|
}
|
|
}
|
|
return lines
|
|
}
|
|
|
|
/*
|
|
Get the string of the yaml tag
|
|
*/
|
|
func getYamlString(myType reflect.StructField, excludeList []string) ([]string, bool) {
|
|
ymlStr := myType.Tag.Get("yaml")
|
|
if len(strings.Split(ymlStr, ",")) > 1 {
|
|
ymlStr = strings.Split(ymlStr, ",")[0]
|
|
}
|
|
if util.InSlice(excludeList, ymlStr) {
|
|
return []string{""}, false
|
|
} else if myType.Tag.Get("comment") == "" && myType.Type.Kind() == reflect.String {
|
|
return []string{""}, false
|
|
}
|
|
if myType.Type.Kind() == reflect.String {
|
|
fieldType := myType.Tag.Get("type")
|
|
if fieldType == "" {
|
|
fieldType = "string"
|
|
}
|
|
ymlStr += ": " + fieldType
|
|
return []string{ymlStr}, true
|
|
} else if myType.Type == reflect.TypeOf([]string{}) {
|
|
return []string{ymlStr + ":", " - string"}, true
|
|
} else if myType.Type == reflect.TypeOf(map[string]string{}) {
|
|
return []string{ymlStr + ":", " key: value"}, true
|
|
} else if myType.Type.Kind() == reflect.Ptr {
|
|
return []string{ymlStr + ":"}, true
|
|
}
|
|
return []string{ymlStr}, true
|
|
}
|
|
|
|
/*
|
|
Getters for unexported fields
|
|
*/
|
|
|
|
/*
|
|
Returns the id of the node/profile
|
|
*/
|
|
func (node *NodeConf) Id() string {
|
|
return node.id
|
|
}
|
|
|
|
/*
|
|
Returns if the node is a valid in the database
|
|
*/
|
|
func (node *NodeConf) Valid() bool {
|
|
return node.valid
|
|
}
|
|
|
|
/*
|
|
Check if the netdev is the primary one
|
|
*/
|
|
func (dev *NetDevs) Primary() bool {
|
|
return dev.primary
|
|
}
|