Files
warewulf/internal/pkg/node/constructors.go
Christian Goll 95050a44ce add user defined keys for networks/netdev
Every network in the profile and for the nodes can have induvidual keys values
pairs, called Tags, which are propergated to the templates. So they can be used
for things like MTU size like
  `wwct node set node01 --nettag mtu=9000`
and in in ifcfg.xml.ww
```
{{ if $netdev.Tags.mtu -}}
<mtu> {{ $netdev.Tags.mtu }} </mtu>
{{ end -}}
```
2022-04-06 21:36:17 +02:00

388 lines
12 KiB
Go

package node
import (
"errors"
"fmt"
"io/ioutil"
"net"
"path"
"sort"
"strings"
"github.com/hpcng/warewulf/internal/pkg/buildconfig"
"github.com/hpcng/warewulf/internal/pkg/warewulfconf"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"gopkg.in/yaml.v2"
)
var ConfigFile string
func init() {
if ConfigFile == "" {
ConfigFile = path.Join(buildconfig.SYSCONFDIR(), "warewulf/nodes.conf")
}
}
func New() (nodeYaml, error) {
var ret nodeYaml
wwlog.Printf(wwlog.VERBOSE, "Opening node configuration file: %s\n", ConfigFile)
data, err := ioutil.ReadFile(ConfigFile)
if err != nil {
return ret, err
}
wwlog.Printf(wwlog.DEBUG, "Unmarshaling the node configuration\n")
err = yaml.Unmarshal(data, &ret)
if err != nil {
return ret, err
}
wwlog.Printf(wwlog.DEBUG, "Returning node object\n")
return ret, 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
*/
func (config *nodeYaml) FindAllNodes() ([]NodeInfo, error) {
var ret []NodeInfo
wwconfig, err := warewulfconf.New()
if err != nil {
return ret, err
}
wwlog.Printf(wwlog.DEBUG, "Finding all nodes...\n")
for nodename, node := range config.Nodes {
var n NodeInfo
wwlog.Printf(wwlog.DEBUG, "In node loop: %s\n", nodename)
n.NetDevs = make(map[string]*NetDevEntry)
n.Tags = make(map[string]*Entry)
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")
fullname := strings.SplitN(nodename, ".", 2)
if len(fullname) > 1 {
n.ClusterName.SetDefault(fullname[1])
}
if len(node.Profiles) == 0 {
n.Profiles = []string{"default"}
} else {
n.Profiles = node.Profiles
}
n.Id.Set(nodename)
n.Comment.Set(node.Comment)
n.ContainerName.Set(node.ContainerName)
n.Kernel.Args.Set(node.Kernel.Args)
n.ClusterName.Set(node.ClusterName)
n.Ipxe.Set(node.Ipxe)
n.Init.Set(node.Init)
n.Ipmi.Ipaddr.Set(node.Ipmi.Ipaddr)
n.Ipmi.Netmask.Set(node.Ipmi.Netmask)
n.Ipmi.Port.Set(node.Ipmi.Port)
n.Ipmi.Gateway.Set(node.Ipmi.Gateway)
n.Ipmi.UserName.Set(node.Ipmi.UserName)
n.Ipmi.Password.Set(node.Ipmi.Password)
n.Ipmi.Interface.Set(node.Ipmi.Interface)
n.Ipmi.Write.SetB(node.Ipmi.Write)
n.SystemOverlay.SetSlice(node.SystemOverlay)
n.RuntimeOverlay.SetSlice(node.RuntimeOverlay)
n.Root.Set(node.Root)
n.AssetKey.Set(node.AssetKey)
n.Discoverable.Set(node.Discoverable)
if node.Kernel.Override != "" {
n.Kernel.Override.Set(node.Kernel.Override)
} else if node.Kernel.Version != "" {
n.Kernel.Override.Set(node.Kernel.Version)
}
for devname, netdev := range node.NetDevs {
if _, ok := n.NetDevs[devname]; !ok {
var netdev NetDevEntry
n.NetDevs[devname] = &netdev
}
n.NetDevs[devname].Device.Set(netdev.Device)
n.NetDevs[devname].Ipaddr.Set(netdev.Ipaddr)
n.NetDevs[devname].Ipaddr6.Set(netdev.Ipaddr6)
// Derive value of ipv6 address from ipv4 if not explicitly set
if wwconfig.Ipaddr6 != "" && netdev.Ipaddr != "" {
ipv4Arr := strings.Split(netdev.Ipaddr, ".")
// error can be ignored as check was done at init
_, ipv6Net, _ := net.ParseCIDR(wwconfig.Ipaddr6)
mSize, _ := ipv6Net.Mask.Size()
ipv6str := fmt.Sprintf("%s%s:%s:%s:%s/%v",
ipv6Net.IP.String(), ipv4Arr[0], ipv4Arr[1], ipv4Arr[2], ipv4Arr[3], mSize)
if strings.Count(ipv6Net.IP.String(), ":") == 5 {
ipv6str = strings.Replace(ipv6str, "::", ":", -1)
}
n.NetDevs[devname].Ipaddr6.SetDefault(ipv6str)
}
n.NetDevs[devname].Netmask.Set(netdev.Netmask)
n.NetDevs[devname].Netmask.SetDefault("255.255.255.0")
n.NetDevs[devname].Hwaddr.Set(netdev.Hwaddr)
n.NetDevs[devname].Gateway.Set(netdev.Gateway)
n.NetDevs[devname].Type.Set(netdev.Type)
n.NetDevs[devname].OnBoot.Set(netdev.OnBoot)
n.NetDevs[devname].Default.Set(netdev.Default)
// for just one netdev, it is always the default
if len(node.NetDevs) == 1 {
n.NetDevs[devname].Default.Set("true")
}
n.NetDevs[devname].Tags = make(map[string]*Entry)
for keyname, key := range netdev.Tags {
if _, ok := n.Tags[keyname]; !ok {
var keyVar Entry
n.NetDevs[devname].Tags[keyname] = &keyVar
}
n.NetDevs[devname].Tags[keyname].Set(key)
}
}
// Merge Keys into Tags for backwards compatibility
if len(node.Tags) == 0 {
node.Tags = make(map[string]string)
}
for keyname, key := range node.Keys {
node.Tags[keyname] = key
delete(node.Keys, keyname)
}
for keyname, key := range node.Tags {
if _, ok := n.Tags[keyname]; !ok {
var key Entry
n.Tags[keyname] = &key
}
n.Tags[keyname].Set(key)
}
for _, p := range n.Profiles {
if _, ok := config.NodeProfiles[p]; !ok {
wwlog.Printf(wwlog.WARN, "Profile not found for node '%s': %s\n", nodename, p)
continue
}
wwlog.Printf(wwlog.VERBOSE, "Merging profile into node: %s <- %s\n", nodename, p)
n.Comment.SetAlt(config.NodeProfiles[p].Comment, p)
n.ClusterName.SetAlt(config.NodeProfiles[p].ClusterName, p)
n.ContainerName.SetAlt(config.NodeProfiles[p].ContainerName, p)
n.Kernel.Args.SetAlt(config.NodeProfiles[p].Kernel.Args, p)
n.Ipxe.SetAlt(config.NodeProfiles[p].Ipxe, p)
n.Init.SetAlt(config.NodeProfiles[p].Init, p)
n.Ipmi.Ipaddr.SetAlt(config.NodeProfiles[p].Ipmi.Ipaddr, p)
n.Ipmi.Netmask.SetAlt(config.NodeProfiles[p].Ipmi.Netmask, p)
n.Ipmi.Port.SetAlt(config.NodeProfiles[p].Ipmi.Port, p)
n.Ipmi.Gateway.SetAlt(config.NodeProfiles[p].Ipmi.Gateway, p)
n.Ipmi.UserName.SetAlt(config.NodeProfiles[p].Ipmi.UserName, p)
n.Ipmi.Password.SetAlt(config.NodeProfiles[p].Ipmi.Password, p)
n.Ipmi.Interface.SetAlt(config.NodeProfiles[p].Ipmi.Interface, p)
n.Ipmi.Write.SetB(config.NodeProfiles[p].Ipmi.Write)
n.SystemOverlay.SetAltSlice(config.NodeProfiles[p].SystemOverlay, p)
n.RuntimeOverlay.SetAltSlice(config.NodeProfiles[p].RuntimeOverlay, p)
n.Root.SetAlt(config.NodeProfiles[p].Root, p)
n.AssetKey.SetAlt(config.NodeProfiles[p].AssetKey, p)
n.Discoverable.SetAlt(config.NodeProfiles[p].Discoverable, p)
if config.NodeProfiles[p].Kernel.Override != "" {
n.Kernel.Override.SetAlt(config.NodeProfiles[p].Kernel.Override, p)
} else if config.NodeProfiles[p].Kernel.Version != "" {
n.Kernel.Override.SetAlt(config.NodeProfiles[p].Kernel.Version, p)
}
for devname, netdev := range config.NodeProfiles[p].NetDevs {
if _, ok := n.NetDevs[devname]; !ok {
var netdev NetDevEntry
n.NetDevs[devname] = &netdev
}
wwlog.Printf(wwlog.DEBUG, "Updating profile (%s) netdev: %s\n", p, devname)
n.NetDevs[devname].Device.SetAlt(netdev.Device, p)
n.NetDevs[devname].Ipaddr.SetAlt(netdev.Ipaddr, p) //FIXME? <- Ipaddr must be uniq
n.NetDevs[devname].Netmask.SetAlt(netdev.Netmask, p)
n.NetDevs[devname].Hwaddr.SetAlt(netdev.Hwaddr, p)
n.NetDevs[devname].Gateway.SetAlt(netdev.Gateway, p)
n.NetDevs[devname].Type.SetAlt(netdev.Type, p)
n.NetDevs[devname].OnBoot.SetAlt(netdev.OnBoot, p)
n.NetDevs[devname].Default.SetAlt(netdev.Default, p)
if len(netdev.Tags) != 0 {
for keyname, key := range netdev.Tags {
if _, ok := n.Tags[keyname]; !ok {
var keyVar Entry
n.NetDevs[devname].Tags[keyname] = &keyVar
}
n.NetDevs[devname].Tags[keyname].SetAlt(key, p)
}
}
}
// Merge Keys into Tags for backwards compatibility
if len(config.NodeProfiles[p].Tags) == 0 {
config.NodeProfiles[p].Tags = make(map[string]string)
}
for keyname, key := range config.NodeProfiles[p].Keys {
config.NodeProfiles[p].Tags[keyname] = key
delete(config.NodeProfiles[p].Keys, keyname)
}
for keyname, key := range config.NodeProfiles[p].Tags {
if _, ok := n.Tags[keyname]; !ok {
var key Entry
n.Tags[keyname] = &key
}
n.Tags[keyname].SetAlt(key, p)
}
}
ret = append(ret, n)
}
sort.Slice(ret, func(i, j int) bool {
if ret[i].ClusterName.Get() < ret[j].ClusterName.Get() {
return true
} else if ret[i].ClusterName.Get() == ret[j].ClusterName.Get() {
if ret[i].Id.Get() < ret[j].Id.Get() {
return true
}
}
return false
})
return ret, nil
}
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.Id.Set(name)
p.Comment.Set(profile.Comment)
p.ClusterName.Set(profile.ClusterName)
p.ContainerName.Set(profile.ContainerName)
p.Ipxe.Set(profile.Ipxe)
p.Init.Set(profile.Init)
p.Kernel.Args.Set(profile.Kernel.Args)
p.Ipmi.Netmask.Set(profile.Ipmi.Netmask)
p.Ipmi.Port.Set(profile.Ipmi.Port)
p.Ipmi.Gateway.Set(profile.Ipmi.Gateway)
p.Ipmi.UserName.Set(profile.Ipmi.UserName)
p.Ipmi.Password.Set(profile.Ipmi.Password)
p.Ipmi.Interface.Set(profile.Ipmi.Interface)
p.Ipmi.Write.SetB(profile.Ipmi.Write)
p.RuntimeOverlay.SetSlice(profile.RuntimeOverlay)
p.SystemOverlay.SetSlice(profile.SystemOverlay)
p.Root.Set(profile.Root)
p.AssetKey.Set(profile.AssetKey)
p.Discoverable.Set(profile.Discoverable)
if profile.Kernel.Override != "" {
p.Kernel.Override.Set(profile.Kernel.Override)
} else if profile.Kernel.Version != "" {
p.Kernel.Override.Set(profile.Kernel.Version)
}
for devname, netdev := range profile.NetDevs {
if _, ok := p.NetDevs[devname]; !ok {
var netdev NetDevEntry
p.NetDevs[devname] = &netdev
}
wwlog.Printf(wwlog.DEBUG, "Updating profile netdev: %s\n", devname)
p.NetDevs[devname].Device.Set(netdev.Device)
p.NetDevs[devname].Netmask.Set(netdev.Netmask)
p.NetDevs[devname].Gateway.Set(netdev.Gateway)
p.NetDevs[devname].Type.Set(netdev.Type)
p.NetDevs[devname].OnBoot.Set(netdev.OnBoot)
p.NetDevs[devname].Default.Set(netdev.Default)
// The following should not be set in a profile.
if netdev.Ipaddr != "" {
wwlog.Printf(wwlog.WARN, "Ignoring ip address %v in profile %v\n", netdev.Ipaddr, name)
}
if netdev.Hwaddr != "" {
wwlog.Printf(wwlog.WARN, "Ignoring hardware address %v in profile %v\n", netdev.Hwaddr, name)
}
p.NetDevs[devname].Tags = make(map[string]*Entry)
for keyname, key := range netdev.Tags {
if _, ok := p.Tags[keyname]; !ok {
var keyVar Entry
p.NetDevs[devname].Tags[keyname] = &keyVar
}
p.NetDevs[devname].Tags[keyname].Set(key)
}
}
// Merge Keys into Tags for backwards compatibility
if len(profile.Tags) == 0 {
profile.Tags = make(map[string]string)
}
for keyname, key := range profile.Keys {
profile.Tags[keyname] = key
delete(profile.Keys, keyname)
}
for keyname, key := range profile.Tags {
if _, ok := p.Tags[keyname]; !ok {
var key Entry
p.Tags[keyname] = &key
}
p.Tags[keyname].Set(key)
}
// TODO: Validate or die on all inputs
ret = append(ret, p)
}
sort.Slice(ret, func(i, j int) bool {
if ret[i].ClusterName.Get() < ret[j].ClusterName.Get() {
return true
} else if ret[i].ClusterName.Get() == ret[j].ClusterName.Get() {
if ret[i].Id.Get() < ret[j].Id.Get() {
return true
}
}
return false
})
return ret, nil
}
func (config *nodeYaml) FindDiscoverableNode() (NodeInfo, string, error) {
var ret NodeInfo
nodes, _ := config.FindAllNodes()
for _, node := range nodes {
if !node.Discoverable.GetB() {
continue
}
for netdev, dev := range node.NetDevs {
if !dev.Hwaddr.Defined() {
return node, netdev, nil
}
}
}
return ret, "", errors.New("no unconfigured nodes found")
}