Merge branch 'development' into wwlog-newlines

This commit is contained in:
Christian Goll
2022-10-13 14:46:03 +02:00
committed by GitHub
36 changed files with 1804 additions and 391 deletions

View File

@@ -14,11 +14,34 @@ import (
)
var ConfigFile string
var DefaultConfig string
// used as fallback if DefaultConfig can't be read
var FallBackConf = `
defaultnode:
runtime overlay:
- generic
system overlay:
- wwinit
kernel:
args: quiet crashkernel=no vga=791 net.naming-scheme=v238
init: /sbin/init
root: initramfs
profiles:
- default
network devices:
dummy:
device: eth0
type: ethernet
netmask: 255.255.255.0`
func init() {
if ConfigFile == "" {
ConfigFile = path.Join(buildconfig.SYSCONFDIR(), "warewulf/nodes.conf")
}
if DefaultConfig == "" {
DefaultConfig = path.Join(buildconfig.SYSCONFDIR(), "warewulf/defaults.conf")
}
}
func New() (NodeYaml, error) {
@@ -54,6 +77,30 @@ func (config *NodeYaml) FindAllNodes() ([]NodeInfo, error) {
return ret, err
}
*/
var defConf map[string]*NodeConf
wwlog.Verbose("Opening defaults failed %s\n", DefaultConfig)
defData, err := ioutil.ReadFile(DefaultConfig)
if err != nil {
wwlog.Verbose("Couldn't read DefaultConfig :%s\n", err)
}
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")
}
}
var defConfNet *NetDevs
if _, ok := defConf["defaultnode"]; ok {
if _, ok := defConf["defaultnode"].NetDevs["dummy"]; ok {
defConfNet = defConf["defaultnode"].NetDevs["dummy"]
}
defConf["defaultnode"].NetDevs = nil
}
wwlog.Debug("Finding all nodes...")
for nodename, node := range config.Nodes {
var n NodeInfo
@@ -63,13 +110,7 @@ func (config *NodeYaml) FindAllNodes() ([]NodeInfo, error) {
n.Tags = make(map[string]*Entry)
n.Kernel = new(KernelEntry)
n.Ipmi = new(IpmiEntry)
n.SystemOverlay.SetDefault("wwinit")
n.RuntimeOverlay.SetDefault("generic")
n.Ipxe.SetDefault("default")
n.Init.SetDefault("/sbin/init")
n.Root.SetDefault("initramfs")
n.Kernel.Args.SetDefault("quiet crashkernel=no vga=791")
n.SetDefFrom(defConf["defaultnode"])
fullname := strings.SplitN(nodename, ".", 2)
if len(fullname) > 1 {
n.ClusterName.SetDefault(fullname[1])
@@ -80,14 +121,18 @@ func (config *NodeYaml) FindAllNodes() ([]NodeInfo, error) {
} else {
n.Profiles.SetSlice(node.Profiles)
}
// node explciti nodename field in NodeConf
// node explicitly nodename field in NodeConf
n.Id.Set(nodename)
// backward compatibilty
// backward compatibility
for keyname, key := range node.Keys {
node.Tags[keyname] = key
delete(node.Keys, keyname)
}
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)
}
// set default/primary network is just one network exist
if len(n.NetDevs) == 1 {
// only way to get the key

View File

@@ -1,14 +1,11 @@
package node
import (
"github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
)
/******
* YAML data representations
******/
/*
Structure of which goes to disk
*/
type NodeYaml struct {
WWInternal int `yaml:"WW_INTERNAL"`
NodeProfiles map[string]*NodeConf
@@ -16,7 +13,7 @@ type NodeYaml struct {
}
/*
NodeConf is the datastructure which is stored on disk.
NodeConf is the datastructure describing a node and a profile which in disk format.
*/
type NodeConf struct {
Comment string `yaml:"comment,omitempty" lopt:"comment" comment:"Set arbitrary string comment"`
@@ -24,11 +21,11 @@ type NodeConf struct {
ContainerName string `yaml:"container name,omitempty" lopt:"container" sopt:"C" comment:"Set container name"`
Ipxe string `yaml:"ipxe template,omitempty" lopt:"ipxe" comment:"Set the iPXE template name"`
// Deprecated start
// Kernel settings here are deprecated and here for backward comptability
// Kernel settings here are deprecated and here for backward compatibility
KernelVersion string `yaml:"kernel version,omitempty"`
KernelOverride string `yaml:"kernel override,omitempty"`
KernelArgs string `yaml:"kernel args,omitempty"`
// Ipmi settings herer are deprecated and here for backward comptability
// Ipmi settings herer are deprecated and here for backward compatibility
IpmiUserName string `yaml:"ipmi username,omitempty"`
IpmiPassword string `yaml:"ipmi password,omitempty"`
IpmiIpaddr string `yaml:"ipmi ipaddr,omitempty"`
@@ -82,6 +79,7 @@ type NetDevs struct {
Prefix string `yaml:"prefix,omitempty"`
Netmask string `yaml:"netmask,omitempty" lopt:"netmask" sopt:"M" comment:"Set the networks netmask"`
Gateway string `yaml:"gateway,omitempty" lopt:"gateway" sopt:"G" comment:"Set the node's network device gateway"`
MTU string `yaml:"mtu,omitempty" lopt:"mtu" comment:"Set the mtu"`
Primary string `yaml:"primary,omitempty" lopt:"primary" comment:"Enable/disable network device as primary (yes/no)"`
Default string `yaml:"default,omitempty"` /* backward compatibility */
Tags map[string]string `yaml:"tags,omitempty" lopt:"nettagadd" comment:"network tags"`
@@ -156,6 +154,7 @@ type NetDevEntry struct {
Prefix Entry
Netmask Entry
Gateway Entry
MTU Entry
Primary Entry
Tags map[string]*Entry
}
@@ -163,6 +162,8 @@ type NetDevEntry struct {
// string which is printed if no value is set
const NoValue = "--"
/*
Has no real purpose as only New() needs it
func init() {
// Check that nodes.conf is found
if !util.IsFile(ConfigFile) {
@@ -171,3 +172,4 @@ func init() {
return
}
}
*/

View File

@@ -26,8 +26,7 @@ func FilterByName(set []NodeInfo, searchList []string) []NodeInfo {
if len(searchList) > 0 {
for _, search := range searchList {
for _, entry := range set {
b, _ := regexp.MatchString("^"+search+"$", entry.Id.Get())
if b {
if match, _ := regexp.MatchString("^"+search+"$", entry.Id.Get()); match {
unique[entry.Id.Get()] = entry
}
}
@@ -42,6 +41,24 @@ func FilterByName(set []NodeInfo, searchList []string) []NodeInfo {
return ret
}
/*
Filter a given map of NodeConf against given regular expression.
*/
func FilterMapByName(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
}
/**********
*
* Sets

View File

@@ -2,8 +2,10 @@ package node
import (
"reflect"
"strings"
"github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"github.com/spf13/cobra"
)
@@ -186,6 +188,10 @@ func (nodeConf *NodeConf) getterFrom(nodeInfo NodeInfo,
}
}
}
/*
Create cmd line flags from the NodeConf fields
*/
func (nodeConf *NodeConf) CreateFlags(baseCmd *cobra.Command, excludeList []string) {
nodeInfoType := reflect.TypeOf(nodeConf)
nodeInfoVal := reflect.ValueOf(nodeConf)
@@ -303,6 +309,20 @@ func (node *NodeInfo) SetAltFrom(n *NodeConf, profileName string) {
node.setterFrom(n, profileName, (*Entry).SetAlt, (*Entry).SetAltSlice)
}
/*
Populates all fields of NodeInfo with SetDefault from the
values of NodeConf.
*/
func (node *NodeInfo) SetDefFrom(n *NodeConf) {
setWrap := func(entr *Entry, val string, nameArg string) {
entr.SetDefault(val)
}
setSliceWrap := func(entr *Entry, val []string, nameArg string) {
entr.SetDefaultSlice(val)
}
node.setterFrom(n, "", setWrap, setSliceWrap)
}
/*
Abstract function which populates a NodeInfo from a NodeConf via
setter functionns.
@@ -438,3 +458,213 @@ func (info *NodeConf) Flatten() {
}
}
}
/*
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)) {
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
*/
func (nodeConf *NodeConf) UnmarshalConf(excludeList []string) (lines []string) {
nodeInfoType := reflect.TypeOf(nodeConf)
nodeInfoVal := reflect.ValueOf(nodeConf)
// now iterate of every field
for i := 0; i < nodeInfoVal.Elem().NumField(); i++ {
if nodeInfoType.Elem().Field(i).Tag.Get("lopt") != "" {
if ymlStr, ok := getYamlString(nodeInfoType.Elem().Field(i), excludeList); ok {
lines = append(lines, ymlStr...)
}
} else if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.Ptr {
nestType := reflect.TypeOf(nodeInfoVal.Elem().Field(i).Interface())
if ymlStr, ok := getYamlString(nodeInfoType.Elem().Field(i), excludeList); ok {
lines = append(lines, ymlStr...)
}
for j := 0; j < nestType.Elem().NumField(); j++ {
if nestType.Elem().Field(j).Tag.Get("lopt") != "" &&
!util.InSlice(excludeList, nestType.Elem().Field(j).Tag.Get("lopt")) {
if ymlStr, ok := getYamlString(nestType.Elem().Field(j), excludeList); ok {
for _, str := range ymlStr {
lines = append(lines, " "+str)
}
}
}
}
} else if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf(map[string]*NetDevs(nil)) {
netMap := nodeInfoVal.Elem().Field(i).Interface().(map[string]*NetDevs)
// add a default network so that it can hold values
key := "default"
if len(netMap) == 0 {
netMap[key] = new(NetDevs)
} else {
for keyIt := range netMap {
key = keyIt
break
}
}
if ymlStr, ok := getYamlString(nodeInfoType.Elem().Field(i), excludeList); ok {
lines = append(lines, ymlStr[0]+":", " "+key+":")
netType := reflect.TypeOf(netMap[key])
for j := 0; j < netType.Elem().NumField(); j++ {
if ymlStr, ok := getYamlString(netType.Elem().Field(j), excludeList); ok {
for _, str := range ymlStr {
lines = append(lines, " "+str)
}
}
} // lines
} // this
} //not
} //do
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("lopt") == "" && myType.Type.Kind() == reflect.String {
return []string{""}, false
}
if myType.Type.Kind() == reflect.String {
ymlStr += ": string"
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
}
/*
Set the field of the NodeConf with the given lopt name, returns true if the
field was found. String slices must be comma separated. Network must have the form
net.$NETNAME.lopt or netname.$NETNAME.lopt
*/
func (nodeConf *NodeConf) SetLopt(lopt string, value string) (found bool) {
found = false
nodeInfoType := reflect.TypeOf(nodeConf)
nodeInfoVal := reflect.ValueOf(nodeConf)
// try to find the normal fields, networks come later
for i := 0; i < nodeInfoVal.Elem().NumField(); i++ {
//fmt.Println(nodeInfoType.Elem().Field(i).Tag.Get("lopt"), lopt)
if nodeInfoType.Elem().Field(i).Tag.Get("lopt") == lopt {
if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.String {
wwlog.Verbose("Found lopt %s mapping to %s, setting to %s\n",
lopt, nodeInfoType.Elem().Field(i).Name, value)
confVal := nodeInfoVal.Elem().Field(i).Addr().Interface().(*string)
*confVal = value
found = true
} else if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf([]string{}) {
wwlog.Verbose("Found lopt %s mapping to %s, setting to %s\n",
lopt, nodeInfoType.Elem().Field(i).Name, value)
confVal := nodeInfoVal.Elem().Field(i).Addr().Interface().(*[]string)
*confVal = strings.Split(value, ",")
found = true
}
}
}
// check network
loptSlice := strings.Split(lopt, ".")
wwlog.Debug("Trying to get network out of %s\n", loptSlice)
if !found && len(loptSlice) == 3 && (loptSlice[0] == "net" || loptSlice[0] == "network" || loptSlice[0] == "netname") {
if nodeConf.NetDevs == nil {
nodeConf.NetDevs = make(map[string]*NetDevs)
}
if nodeConf.NetDevs[loptSlice[1]] == nil {
nodeConf.NetDevs[loptSlice[1]] = new(NetDevs)
}
netInfoType := reflect.TypeOf(nodeConf.NetDevs[loptSlice[1]])
netInfoVal := reflect.ValueOf(nodeConf.NetDevs[loptSlice[1]])
for i := 0; i < netInfoVal.Elem().NumField(); i++ {
if netInfoType.Elem().Field(i).Tag.Get("lopt") == loptSlice[2] {
if netInfoType.Elem().Field(i).Type.Kind() == reflect.String {
wwlog.Verbose("Found lopt %s for network %s mapping to %s, setting to %s\n",
lopt, loptSlice[1], netInfoType.Elem().Field(i).Name, value)
confVal := netInfoVal.Elem().Field(i).Addr().Interface().(*string)
*confVal = value
found = true
} else if netInfoType.Elem().Field(i).Type == reflect.TypeOf([]string{}) {
wwlog.Verbose("Found lopt %s for network %s mapping to %s, setting to %s\n",
lopt, loptSlice[1], netInfoType.Elem().Field(i).Name, value)
confVal := netInfoVal.Elem().Field(i).Addr().Interface().(*[]string)
*confVal = strings.Split(value, ",")
found = true
}
}
}
}
return found
}