removed NodeInfo and using NodeConf instead
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>
This commit is contained in:
committed by
Jonathon Anderson
parent
dc8fb2d6f2
commit
28a7b9fe84
@@ -1,12 +1,11 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
@@ -15,45 +14,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ConfigFile string
|
||||
DefaultConfig string
|
||||
ConfigFile string
|
||||
)
|
||||
|
||||
// used as fallback if DefaultConfig can't be read
|
||||
var FallBackConf = `---
|
||||
defaultnode:
|
||||
runtime overlay:
|
||||
- hosts
|
||||
- ssh.authorized_keys
|
||||
- syncuser
|
||||
system overlay:
|
||||
- wwinit
|
||||
- wwclient
|
||||
- fstab
|
||||
- hostname
|
||||
- ssh.host_keys
|
||||
- issue
|
||||
- resolv
|
||||
- udev.netname
|
||||
- systemd.netname
|
||||
- ifcfg
|
||||
- NetworkManager
|
||||
- debian.interfaces
|
||||
- wicked
|
||||
- ignition
|
||||
kernel:
|
||||
args: quiet crashkernel=no vga=791 net.naming-scheme=v238
|
||||
init: /sbin/init
|
||||
root: initramfs
|
||||
ipxe template: default
|
||||
profiles:
|
||||
- default
|
||||
network devices:
|
||||
dummy:
|
||||
type: ethernet
|
||||
netmask: 255.255.255.0
|
||||
onboot: true`
|
||||
|
||||
/*
|
||||
Creates a new nodeDb object from the on-disk configuration
|
||||
*/
|
||||
@@ -62,9 +25,6 @@ func New() (NodeYaml, error) {
|
||||
if ConfigFile == "" {
|
||||
ConfigFile = path.Join(conf.Paths.Sysconfdir, "warewulf/nodes.conf")
|
||||
}
|
||||
if DefaultConfig == "" {
|
||||
DefaultConfig = path.Join(conf.Warewulf.DataStore, "warewulf/defaults.conf")
|
||||
}
|
||||
wwlog.Verbose("Opening node configuration file: %s", ConfigFile)
|
||||
data, err := os.ReadFile(ConfigFile)
|
||||
if err != nil {
|
||||
@@ -78,276 +38,204 @@ func New() (NodeYaml, error) {
|
||||
// error if any parsed value is not of a valid type for the given
|
||||
// parameter.
|
||||
func Parse(data []byte) (NodeYaml, error) {
|
||||
var ret NodeYaml
|
||||
var nodeList NodeYaml
|
||||
var err error
|
||||
wwlog.Debug("Unmarshaling the node configuration")
|
||||
err = yaml.Unmarshal(data, &ret)
|
||||
err = yaml.Unmarshal(data, &nodeList)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
return nodeList, err
|
||||
}
|
||||
wwlog.Debug("Checking nodes for types")
|
||||
if ret.Nodes == nil {
|
||||
ret.Nodes = map[string]*NodeConf{}
|
||||
wwlog.Debug("Checking profiles for types")
|
||||
if nodeList.Nodes == nil {
|
||||
nodeList.Nodes = map[string]*NodeConf{}
|
||||
}
|
||||
for nodeName, node := range ret.Nodes {
|
||||
err = node.Check()
|
||||
if err != nil {
|
||||
wwlog.Warn("node: %s parsing error: %s", nodeName, err)
|
||||
return ret, err
|
||||
/*
|
||||
for nodeName, node := range nodeList.Nodes {
|
||||
err = node.Check()
|
||||
if err != nil {
|
||||
wwlog.Warn("node: %s parsing error: %s", nodeName, err)
|
||||
return nodeList, err
|
||||
}
|
||||
}
|
||||
*/
|
||||
if nodeList.NodeProfiles == nil {
|
||||
nodeList.NodeProfiles = map[string]*ProfileConf{}
|
||||
}
|
||||
if ret.NodeProfiles == nil {
|
||||
ret.NodeProfiles = map[string]*NodeConf{}
|
||||
}
|
||||
for profileName, profile := range ret.NodeProfiles {
|
||||
err = profile.Check()
|
||||
if err != nil {
|
||||
wwlog.Warn("node: %s parsing error: %s", profileName, err)
|
||||
return ret, err
|
||||
/*
|
||||
for profileName, profile := range nodeList.NodeProfiles {
|
||||
err = profile.Check()
|
||||
if err != nil {
|
||||
wwlog.Warn("node: %s parsing error: %s", profileName, err)
|
||||
return nodeList, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
wwlog.Debug("Returning node object")
|
||||
return ret, nil
|
||||
return nodeList, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Get all the nodes of a configuration. This function also merges
|
||||
the nodes with the given profiles and set the default values
|
||||
for every node
|
||||
Get a node with its merged in profiles
|
||||
*/
|
||||
func (config *NodeYaml) FindAllNodes() ([]NodeInfo, error) {
|
||||
var ret []NodeInfo
|
||||
var defConf map[string]*NodeConf
|
||||
wwlog.Verbose("Opening defaults from file failed %s\n", DefaultConfig)
|
||||
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)
|
||||
func (config *NodeYaml) GetNode(id string) (node NodeConf, err error) {
|
||||
if _, ok := config.Nodes[id]; !ok {
|
||||
return node, ErrNotFound
|
||||
}
|
||||
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)
|
||||
err = yaml.Unmarshal([]byte(FallBackConf), &defConf)
|
||||
wwlog.Debug("constructing node: %s", id)
|
||||
for _, p := range config.Nodes[id].Profiles {
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
dec := gob.NewDecoder(&buf)
|
||||
includedProfile, err := config.GetProfile(p)
|
||||
if err != nil {
|
||||
wwlog.Warn("Could not get any defaults")
|
||||
return node, err
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
wwlog.Debug("In node loop: %s", nodename)
|
||||
n.NetDevs = make(map[string]*NetDevEntry)
|
||||
n.Tags = make(map[string]*Entry)
|
||||
n.Kernel = new(KernelEntry)
|
||||
n.Ipmi = new(IpmiEntry)
|
||||
n.SetDefFrom(defConf["defaultnode"])
|
||||
fullname := strings.SplitN(nodename, ".", 2)
|
||||
if len(fullname) > 1 {
|
||||
n.ClusterName.SetDefault(fullname[1])
|
||||
}
|
||||
// special handling for profile to get the default one
|
||||
if len(node.Profiles) == 0 {
|
||||
n.Profiles.SetSlice([]string{"default"})
|
||||
} else {
|
||||
n.Profiles.SetSlice(node.Profiles)
|
||||
}
|
||||
// node explicitly nodename field in NodeConf
|
||||
n.Id.Set(nodename)
|
||||
// backward compatibility
|
||||
for keyname, key := range node.Keys {
|
||||
node.Tags[keyname] = key
|
||||
delete(node.Keys, keyname)
|
||||
}
|
||||
err = node.Check()
|
||||
err = enc.Encode(includedProfile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("node: %s check error: %s", nodename, err)
|
||||
return node, err
|
||||
}
|
||||
n.SetFrom(node)
|
||||
// only now the netdevs start to exist so that default values can be set
|
||||
for _, netdev := range n.NetDevs {
|
||||
SetDefFrom(defConfNet, netdev)
|
||||
err = dec.Decode(&node)
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
// backward compatibility
|
||||
n.Ipmi.Ipaddr.Set(node.IpmiIpaddr)
|
||||
n.Ipmi.Netmask.Set(node.IpmiNetmask)
|
||||
n.Ipmi.Port.Set(node.IpmiPort)
|
||||
n.Ipmi.Gateway.Set(node.IpmiGateway)
|
||||
n.Ipmi.UserName.Set(node.IpmiUserName)
|
||||
n.Ipmi.Password.Set(node.IpmiPassword)
|
||||
n.Ipmi.Interface.Set(node.IpmiInterface)
|
||||
n.Ipmi.Write.Set(node.IpmiWrite)
|
||||
n.Kernel.Args.Set(node.KernelArgs)
|
||||
n.Kernel.Override.Set(node.KernelOverride)
|
||||
n.Kernel.Override.Set(node.KernelVersion)
|
||||
// delete deprecated structures so that they do not get unmarshalled
|
||||
node.IpmiIpaddr = ""
|
||||
node.IpmiNetmask = ""
|
||||
node.IpmiGateway = ""
|
||||
node.IpmiUserName = ""
|
||||
node.IpmiPassword = ""
|
||||
node.IpmiInterface = ""
|
||||
node.IpmiWrite = ""
|
||||
node.KernelArgs = ""
|
||||
node.KernelOverride = ""
|
||||
node.KernelVersion = ""
|
||||
// Merge Keys into Tags for backwards compatibility
|
||||
if len(node.Tags) == 0 {
|
||||
node.Tags = make(map[string]string)
|
||||
}
|
||||
|
||||
for _, profileName := range n.Profiles.GetSlice() {
|
||||
if _, ok := config.NodeProfiles[profileName]; !ok {
|
||||
wwlog.Warn("Profile not found for node '%s': %s", nodename, profileName)
|
||||
continue
|
||||
}
|
||||
// can't call setFrom() as we have to use SetAlt instead of Set for an Entry
|
||||
wwlog.Verbose("Merging profile into node: %s <- %s", nodename, profileName)
|
||||
n.SetAltFrom(config.NodeProfiles[profileName], profileName)
|
||||
}
|
||||
// set default/primary network is just one network exist
|
||||
if len(n.NetDevs) >= 1 && !n.PrimaryNetDev.Defined() {
|
||||
tmpNets := make([]string, 0, len(n.NetDevs))
|
||||
for key := range n.NetDevs {
|
||||
tmpNets = append(tmpNets, key)
|
||||
}
|
||||
sort.Strings(tmpNets)
|
||||
// if a value is present in profile or node, default is not visible
|
||||
wwlog.Debug("%s setting primary network device: %s", n.Id.Get(), tmpNets[0])
|
||||
n.PrimaryNetDev.SetDefault(tmpNets[0])
|
||||
}
|
||||
if dev, ok := n.NetDevs[n.PrimaryNetDev.Get()]; ok {
|
||||
dev.Primary.SetDefaultB(true)
|
||||
}
|
||||
ret = append(ret, n)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
dec := gob.NewDecoder(&buf)
|
||||
err = enc.Encode(config.Nodes[id])
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
err = dec.Decode(&node)
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
// finally set no exported values
|
||||
node.id = id
|
||||
node.valid = true
|
||||
if netdev, ok := node.NetDevs[node.PrimaryNetDev]; ok {
|
||||
netdev.primary = true
|
||||
} else {
|
||||
keys := make([]string, 0)
|
||||
for k := range node.NetDevs {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
if len(keys) > 0 {
|
||||
wwlog.Debug("%s: no primary defined, sanitizing to: %s", id, keys[0])
|
||||
node.NetDevs[keys[0]].primary = true
|
||||
node.PrimaryNetDev = keys[0]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
sort.Slice(ret, func(i, j int) bool {
|
||||
if ret[i].ClusterName.Get() < ret[j].ClusterName.Get() {
|
||||
/*
|
||||
Get the profile with all the profiles merged in
|
||||
*/
|
||||
func (config *NodeYaml) GetProfile(id string) (profile NodeConf, err error) {
|
||||
if _, ok := config.NodeProfiles[id]; !ok {
|
||||
return profile, ErrNotFound
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
dec := gob.NewDecoder(&buf)
|
||||
// finally merge in the real profile
|
||||
err = enc.Encode(config.NodeProfiles[id])
|
||||
if err != nil {
|
||||
return profile, err
|
||||
}
|
||||
err = dec.Decode(&profile)
|
||||
if err != nil {
|
||||
return profile, err
|
||||
}
|
||||
// finally set no exported values
|
||||
profile.id = id
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Get the profiles from the loaded configuration. This function also merges
|
||||
the profiles with the given profiles.
|
||||
*/
|
||||
func (config *NodeYaml) FindAllNodes(profiles ...string) (nodeList []NodeConf, err error) {
|
||||
if len(profiles) == 0 {
|
||||
for n := range config.Nodes {
|
||||
profiles = append(profiles, n)
|
||||
}
|
||||
}
|
||||
wwlog.Debug("Finding profiles: %s", profiles)
|
||||
for _, profileId := range profiles {
|
||||
node, err := config.GetNode(profileId)
|
||||
if err != nil {
|
||||
return nodeList, err
|
||||
}
|
||||
nodeList = append(nodeList, node)
|
||||
}
|
||||
sort.Slice(nodeList, func(i, j int) bool {
|
||||
if nodeList[i].ClusterName < nodeList[j].ClusterName {
|
||||
return true
|
||||
} else if ret[i].ClusterName.Get() == ret[j].ClusterName.Get() {
|
||||
if ret[i].Id.Get() < ret[j].Id.Get() {
|
||||
} else if nodeList[i].ClusterName == nodeList[j].ClusterName {
|
||||
if nodeList[i].id < nodeList[j].id {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
return ret, nil
|
||||
return nodeList, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Return all profiles as NodeInfo
|
||||
*/
|
||||
func (config *NodeYaml) FindAllProfiles() ([]NodeInfo, error) {
|
||||
var ret []NodeInfo
|
||||
|
||||
for name, profile := range config.NodeProfiles {
|
||||
var p NodeInfo
|
||||
p.NetDevs = make(map[string]*NetDevEntry)
|
||||
p.Tags = make(map[string]*Entry)
|
||||
p.Kernel = new(KernelEntry)
|
||||
p.Ipmi = new(IpmiEntry)
|
||||
p.Id.Set(name)
|
||||
for keyname, key := range profile.Keys {
|
||||
profile.Tags[keyname] = key
|
||||
delete(profile.Keys, keyname)
|
||||
func (config *NodeYaml) FindAllProfiles(profiles ...string) (profileList []NodeConf, err error) {
|
||||
if len(profiles) == 0 {
|
||||
for n := range config.NodeProfiles {
|
||||
profiles = append(profiles, n)
|
||||
}
|
||||
|
||||
p.SetFrom(profile)
|
||||
p.Ipmi.Ipaddr.Set(profile.IpmiIpaddr)
|
||||
p.Ipmi.Netmask.Set(profile.IpmiNetmask)
|
||||
p.Ipmi.Port.Set(profile.IpmiPort)
|
||||
p.Ipmi.Gateway.Set(profile.IpmiGateway)
|
||||
p.Ipmi.UserName.Set(profile.IpmiUserName)
|
||||
p.Ipmi.Password.Set(profile.IpmiPassword)
|
||||
p.Ipmi.Interface.Set(profile.IpmiInterface)
|
||||
p.Ipmi.Write.Set(profile.IpmiWrite)
|
||||
p.Kernel.Args.Set(profile.KernelArgs)
|
||||
p.Kernel.Override.Set(profile.KernelOverride)
|
||||
p.Kernel.Override.Set(profile.KernelVersion)
|
||||
// delete deprecated stuff
|
||||
profile.IpmiIpaddr = ""
|
||||
profile.IpmiNetmask = ""
|
||||
profile.IpmiGateway = ""
|
||||
profile.IpmiUserName = ""
|
||||
profile.IpmiPassword = ""
|
||||
profile.IpmiInterface = ""
|
||||
profile.IpmiWrite = ""
|
||||
profile.KernelArgs = ""
|
||||
profile.KernelOverride = ""
|
||||
profile.KernelVersion = ""
|
||||
// Merge Keys into Tags for backwards compatibility
|
||||
if len(profile.Tags) == 0 {
|
||||
profile.Tags = make(map[string]string)
|
||||
}
|
||||
|
||||
ret = append(ret, p)
|
||||
}
|
||||
sort.Slice(ret, func(i, j int) bool {
|
||||
if ret[i].ClusterName.Get() < ret[j].ClusterName.Get() {
|
||||
wwlog.Debug("Finding profiles: %s", profiles)
|
||||
for _, profileId := range profiles {
|
||||
node, err := config.GetProfile(profileId)
|
||||
if err != nil {
|
||||
return profileList, err
|
||||
}
|
||||
profileList = append(profileList, node)
|
||||
}
|
||||
sort.Slice(profileList, func(i, j int) bool {
|
||||
if profileList[i].ClusterName < profileList[j].ClusterName {
|
||||
return true
|
||||
} else if ret[i].ClusterName.Get() == ret[j].ClusterName.Get() {
|
||||
if ret[i].Id.Get() < ret[j].Id.Get() {
|
||||
} else if profileList[i].ClusterName == profileList[j].ClusterName {
|
||||
if profileList[i].id < profileList[j].id {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
return ret, nil
|
||||
return profileList, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Return the names of all available nodes
|
||||
*/
|
||||
func (config *NodeYaml) ListAllNodes() []string {
|
||||
nodeList := make([]string, len(config.Nodes))
|
||||
for name := range config.Nodes {
|
||||
nodeList = append(nodeList, name)
|
||||
}
|
||||
return nodeList
|
||||
}
|
||||
|
||||
/*
|
||||
Return the names of all available profiles
|
||||
*/
|
||||
func (config *NodeYaml) ListAllProfiles() []string {
|
||||
var ret []string
|
||||
var nodeList []string
|
||||
for name := range config.NodeProfiles {
|
||||
ret = append(ret, name)
|
||||
nodeList = append(nodeList, name)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
/*
|
||||
return a map where the key is the profile id
|
||||
*/
|
||||
func (config *NodeYaml) MapAllProfiles() (retMap map[string]*NodeInfo, err error) {
|
||||
retMap = make(map[string]*NodeInfo)
|
||||
profileList, err := config.FindAllProfiles()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, pr := range profileList {
|
||||
retMap[pr.Id.Get()] = &pr
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
return a map where the key is the node id
|
||||
*/
|
||||
func (config *NodeYaml) MapAllNodes() (retMap map[string]*NodeInfo, err error) {
|
||||
retMap = make(map[string]*NodeInfo)
|
||||
nodeList, err := config.FindAllNodes()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, nd := range nodeList {
|
||||
retMap[nd.Id.Get()] = &nd
|
||||
}
|
||||
return
|
||||
return nodeList
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -356,26 +244,25 @@ interface to associate with the discovered interface. If the node has
|
||||
a primary interface, it is returned; otherwise, the first interface
|
||||
without a hardware address is returned.
|
||||
|
||||
If no unconfigured nodes are found, an error is returned.
|
||||
If no unconfigured node is found, an error is returned.
|
||||
*/
|
||||
func (config *NodeYaml) FindDiscoverableNode() (NodeInfo, string, error) {
|
||||
var ret NodeInfo
|
||||
func (config *NodeYaml) FindDiscoverableNode() (string, string, error) {
|
||||
|
||||
nodes, _ := config.FindAllNodes()
|
||||
|
||||
for _, node := range nodes {
|
||||
if !node.Discoverable.GetB() {
|
||||
if !node.Discoverable {
|
||||
continue
|
||||
}
|
||||
if _, ok := node.NetDevs[node.PrimaryNetDev.Get()]; ok {
|
||||
return node, node.PrimaryNetDev.Get(), nil
|
||||
if _, ok := node.NetDevs[node.PrimaryNetDev]; ok {
|
||||
return node.Id(), node.PrimaryNetDev, nil
|
||||
}
|
||||
for netdev, dev := range node.NetDevs {
|
||||
if !dev.Hwaddr.Defined() {
|
||||
return node, netdev, nil
|
||||
if dev.Hwaddr != "" {
|
||||
return node.Id(), netdev, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret, "", errors.New("no unconfigured nodes found")
|
||||
return "", "", ErrNoUnconfigured
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user