465 lines
14 KiB
Go
465 lines
14 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.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")
|
|
|
|
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.ClusterName.Set(node.ClusterName)
|
|
n.Ipxe.Set(node.Ipxe)
|
|
n.Init.Set(node.Init)
|
|
// backward compatibility for old Ipmi config
|
|
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)
|
|
// delete deprectated structures so that they do not get unmarshalled
|
|
node.IpmiIpaddr = ""
|
|
node.IpmiNetmask = ""
|
|
node.IpmiGateway = ""
|
|
node.IpmiUserName = ""
|
|
node.IpmiPassword = ""
|
|
node.IpmiInterface = ""
|
|
node.IpmiWrite = ""
|
|
|
|
if node.Ipmi != nil {
|
|
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)
|
|
// backward compatibility
|
|
n.Kernel.Args.Set(node.KernelArgs)
|
|
n.Kernel.Override.Set(node.KernelOverride)
|
|
n.Kernel.Override.Set(node.KernelVersion)
|
|
node.KernelArgs = ""
|
|
node.KernelOverride = ""
|
|
node.KernelVersion = ""
|
|
|
|
if node.Kernel != nil {
|
|
n.Kernel.Args.Set(node.Kernel.Args)
|
|
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(strings.ToLower(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].Primary.Set(netdev.Primary)
|
|
n.NetDevs[devname].Primary.Set(netdev.Default) // backwards compatibility
|
|
// for just one netdev, it is always the primary
|
|
if len(node.NetDevs) == 1 {
|
|
n.NetDevs[devname].Primary.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)
|
|
}
|
|
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)
|
|
if config.NodeProfiles[p].Kernel != nil {
|
|
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)
|
|
if config.NodeProfiles[p].Ipmi != nil {
|
|
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.SetAltB(config.NodeProfiles[p].Ipmi.Write, p)
|
|
}
|
|
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 != nil {
|
|
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(strings.ToLower(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].Primary.SetAlt(netdev.Primary, p)
|
|
if len(netdev.Tags) != 0 {
|
|
if len(n.NetDevs[devname].Tags) == 0 {
|
|
n.NetDevs[devname].Tags = make(map[string]*Entry)
|
|
}
|
|
for keyname, key := range netdev.Tags {
|
|
if _, ok := n.NetDevs[devname].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.Kernel = new(KernelEntry)
|
|
p.Ipmi = new(IpmiEntry)
|
|
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)
|
|
// backward compatibility
|
|
p.Kernel.Args.Set(profile.KernelArgs)
|
|
p.Kernel.Override.Set(profile.KernelOverride)
|
|
p.Kernel.Override.Set(profile.KernelVersion)
|
|
profile.KernelArgs = ""
|
|
profile.KernelOverride = ""
|
|
profile.KernelVersion = ""
|
|
if profile.Kernel != nil {
|
|
p.Kernel.Args.Set(profile.Kernel.Args)
|
|
if profile.Kernel.Override != "" {
|
|
p.Kernel.Override.Set(profile.Kernel.Override)
|
|
} else if profile.Kernel.Version != "" {
|
|
p.Kernel.Override.Set(profile.Kernel.Version)
|
|
}
|
|
}
|
|
// backward compatibility for old Ipmi config
|
|
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)
|
|
// delete deprectated structures so that they do not get unmarshalled
|
|
profile.IpmiIpaddr = ""
|
|
profile.IpmiNetmask = ""
|
|
profile.IpmiGateway = ""
|
|
profile.IpmiUserName = ""
|
|
profile.IpmiPassword = ""
|
|
profile.IpmiInterface = ""
|
|
profile.IpmiWrite = ""
|
|
if profile.Ipmi != nil {
|
|
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)
|
|
|
|
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].Primary.Set(netdev.Primary)
|
|
p.NetDevs[devname].Primary.Set(netdev.Default) // backwards compatibility
|
|
|
|
// 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")
|
|
}
|