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 -}}
```
388 lines
12 KiB
Go
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")
|
|
}
|