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
@@ -262,6 +262,14 @@ Official v4.5.0 release.
|
||||
|
||||
## 4.5.0rc1, 2024-02-08
|
||||
|
||||
### Refactoring
|
||||
|
||||
- removed NodeInfo completely. node.FindAllNodes() will now give back
|
||||
the list of nodes with its merged in profiles. Also there is now a
|
||||
node.GetNode(id) command which will give back a node with its merged
|
||||
in profiles. Changes to this structures will not be reflected to the
|
||||
database, but must nodeYaml.Nodes or nodeYaml.NodeProfiles
|
||||
|
||||
### Added
|
||||
|
||||
- Start building packages for Rocky Linux 9. #951
|
||||
|
||||
1
go.mod
1
go.mod
@@ -33,6 +33,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/AdamKorcz/go-fuzz-headers v0.0.0-20210312213058-32f4d319f0d2 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
|
||||
1
go.sum
1
go.sum
@@ -1,5 +1,6 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/AdamKorcz/go-fuzz-headers v0.0.0-20210312213058-32f4d319f0d2 h1:dIxAd7URQa+ovSiQURY3UJu8Q7A2dG7QKTlxOlvDZHI=
|
||||
github.com/AdamKorcz/go-fuzz-headers v0.0.0-20210312213058-32f4d319f0d2/go.mod h1:VPevheIvXETHZT/ddjwarP3POR5p/cnH9Hy5yoFnQjc=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
|
||||
94
internal/pkg/api/node/status.go
Normal file
94
internal/pkg/api/node/status.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package apinode
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
|
||||
|
||||
"github.com/warewulf/warewulf/internal/pkg/api/routes/wwapiv1"
|
||||
"github.com/warewulf/warewulf/internal/pkg/hostlist"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
// NodeStatus returns the imaging state for nodes.
|
||||
// This requires warewulfd.
|
||||
func NodeStatus(nodeNames []string) (nodeStatusResponse *wwapiv1.NodeStatusResponse, err error) {
|
||||
|
||||
// Local structs for translating json from warewulfd.
|
||||
type nodeStatusInternal struct {
|
||||
NodeName string `json:"node name"`
|
||||
Stage string `json:"stage"`
|
||||
Sent string `json:"sent"`
|
||||
Ipaddr string `json:"ipaddr"`
|
||||
Lastseen int64 `json:"last seen"`
|
||||
}
|
||||
|
||||
// all status is a map with one key (nodes)
|
||||
// and maps of [nodeName]NodeStatus underneath.
|
||||
type allStatus struct {
|
||||
Nodes map[string]*nodeStatusInternal `json:"nodes"`
|
||||
}
|
||||
|
||||
controller := warewulfconf.Get()
|
||||
|
||||
if controller.Ipaddr == "" {
|
||||
err = fmt.Errorf("the Warewulf Server IP Address is not properly configured")
|
||||
wwlog.Error(fmt.Sprintf("%v", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
statusURL := fmt.Sprintf("http://%s:%d/status", controller.Ipaddr, controller.Warewulf.Port)
|
||||
wwlog.Verbose("Connecting to: %s", statusURL)
|
||||
|
||||
resp, err := http.Get(statusURL)
|
||||
if err != nil {
|
||||
wwlog.Error("Could not connect to Warewulf server: %s", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
var wwNodeStatus allStatus
|
||||
|
||||
err = decoder.Decode(&wwNodeStatus)
|
||||
if err != nil {
|
||||
wwlog.Error("Could not decode JSON: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Translate struct and filter.
|
||||
nodeStatusResponse = &wwapiv1.NodeStatusResponse{}
|
||||
|
||||
if len(nodeNames) == 0 {
|
||||
for _, v := range wwNodeStatus.Nodes {
|
||||
nodeStatusResponse.NodeStatus = append(nodeStatusResponse.NodeStatus,
|
||||
&wwapiv1.NodeStatus{
|
||||
NodeName: v.NodeName,
|
||||
Stage: v.Stage,
|
||||
Sent: v.Sent,
|
||||
Ipaddr: v.Ipaddr,
|
||||
Lastseen: v.Lastseen,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
nodeList := hostlist.Expand(nodeNames)
|
||||
for _, v := range wwNodeStatus.Nodes {
|
||||
for j := 0; j < len(nodeList); j++ {
|
||||
if v.NodeName == nodeList[j] {
|
||||
nodeStatusResponse.NodeStatus = append(nodeStatusResponse.NodeStatus,
|
||||
&wwapiv1.NodeStatus{
|
||||
NodeName: v.NodeName,
|
||||
Stage: v.Stage,
|
||||
Sent: v.Sent,
|
||||
Ipaddr: v.Ipaddr,
|
||||
Lastseen: v.Lastseen,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -20,10 +20,11 @@ func Hostfile() (err error) {
|
||||
return fmt.Errorf("'the overlay template '/etc/hosts.ww' does not exists in 'host' overlay")
|
||||
}
|
||||
|
||||
nodeInfo := node.NewInfo()
|
||||
hostname, _ := os.Hostname()
|
||||
nodeInfo.Id.Set(hostname)
|
||||
tstruct := overlay.InitStruct(&nodeInfo)
|
||||
tstruct, err := overlay.InitStruct(node.NewNode(hostname))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buffer, backupFile, writeFile, err := overlay.RenderTemplateFile(
|
||||
hostTemplate,
|
||||
tstruct)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package node
|
||||
|
||||
import "net"
|
||||
|
||||
const undef string = "UNDEF"
|
||||
|
||||
/******
|
||||
* YAML data representations
|
||||
******/
|
||||
@@ -8,7 +12,7 @@ Structure of which goes to disk
|
||||
*/
|
||||
type NodeYaml struct {
|
||||
WWInternal int `yaml:"WW_INTERNAL,omitempty" json:"WW_INTERNAL,omitempty"`
|
||||
NodeProfiles map[string]*NodeConf
|
||||
NodeProfiles map[string]*ProfileConf
|
||||
Nodes map[string]*NodeConf
|
||||
}
|
||||
|
||||
@@ -16,56 +20,47 @@ type NodeYaml struct {
|
||||
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" json:"comment,omitempty"`
|
||||
ClusterName string `yaml:"cluster name,omitempty" lopt:"cluster" sopt:"c" comment:"Set cluster group" json:"cluster name,omitempty"`
|
||||
ContainerName string `yaml:"container name,omitempty" lopt:"container" sopt:"C" comment:"Set container name" json:"container name,omitempty"`
|
||||
Ipxe string `yaml:"ipxe template,omitempty" lopt:"ipxe" comment:"Set the iPXE template name" json:"ipxe template,omitempty"`
|
||||
// Deprecated start
|
||||
// Kernel settings here are deprecated and here for backward compatibility
|
||||
KernelVersion string `yaml:"kernel version,omitempty" json:"kernel version,omitempty"`
|
||||
KernelOverride string `yaml:"kernel override,omitempty" json:"kernel override,omitempty"`
|
||||
KernelArgs string `yaml:"kernel args,omitempty" json:"kernel args,omitempty"`
|
||||
// Ipmi settings herer are deprecated and here for backward compatibility
|
||||
IpmiUserName string `yaml:"ipmi username,omitempty" json:"ipmi username,omitempty"`
|
||||
IpmiPassword string `yaml:"ipmi password,omitempty" json:"ipmi password,omitempty"`
|
||||
IpmiIpaddr string `yaml:"ipmi ipaddr,omitempty" json:"ipmi ipaddr,omitempty"`
|
||||
IpmiNetmask string `yaml:"ipmi netmask,omitempty" json:"ipmi netmask,omitempty"`
|
||||
IpmiPort string `yaml:"ipmi port,omitempty" json:"ipmi port,omitempty"`
|
||||
IpmiGateway string `yaml:"ipmi gateway,omitempty" json:"ipmi gateway,omitempty"`
|
||||
IpmiInterface string `yaml:"ipmi interface,omitempty" json:"ipmi interface,omitempty"`
|
||||
IpmiEscapeChar string `yaml:"ipmi escapechar,omitempty" json:"ipmi escapechar,omitempty"`
|
||||
IpmiWrite string `yaml:"ipmi write,omitempty" json:"ipmi write,omitempty"`
|
||||
// Deprecated end
|
||||
RuntimeOverlay []string `yaml:"runtime overlay,omitempty" lopt:"runtime" sopt:"R" comment:"Set the runtime overlay" json:"runtime overlay,omitempty"`
|
||||
SystemOverlay []string `yaml:"system overlay,omitempty" lopt:"wwinit" sopt:"O" comment:"Set the system overlay" json:"system overlay,omitempty"`
|
||||
Kernel *KernelConf `yaml:"kernel,omitempty" json:"kernel,omitempty"`
|
||||
Ipmi *IpmiConf `yaml:"ipmi,omitempty" json:"ipmi,omitempty"`
|
||||
Init string `yaml:"init,omitempty" lopt:"init" sopt:"i" comment:"Define the init process to boot the container" json:"init,omitempty"`
|
||||
Root string `yaml:"root,omitempty" lopt:"root" comment:"Define the rootfs" json:"root,omitempty"`
|
||||
AssetKey string `yaml:"asset key,omitempty" lopt:"asset" comment:"Set the node's Asset tag (key)" json:"asset key,omitempty"`
|
||||
Discoverable string `yaml:"discoverable,omitempty" lopt:"discoverable" sopt:"e" comment:"Make discoverable in given network (true/false)" type:"bool" json:"discoverable,omitempty"`
|
||||
Profiles []string `yaml:"profiles,omitempty" lopt:"profile" sopt:"P" comment:"Set the node's profile members (comma separated)" json:"profiles,omitempty"`
|
||||
NetDevs map[string]*NetDevs `yaml:"network devices,omitempty" json:"network devices,omitempty"`
|
||||
Tags map[string]string `yaml:"tags,omitempty" lopt:"tagadd" comment:"base key" json:"tags,omitempty"`
|
||||
TagsDel []string `yaml:"tagsdel,omitempty" lopt:"tagdel" comment:"remove this tags" json:"tagsdel,omitempty"` // should not go to disk only to wire
|
||||
Keys map[string]string `yaml:"keys,omitempty" json:"keys,omitempty"` // Reverse compatibility
|
||||
PrimaryNetDev string `yaml:"primary network,omitempty" lopt:"primarynet" sopt:"p" comment:"Set the primary network interface" json:"primary network,omitempty"`
|
||||
Disks map[string]*Disk `yaml:"disks,omitempty" json:"disks,omitempty"`
|
||||
FileSystems map[string]*FileSystem `yaml:"filesystems,omitempty" json:"filesystems,omitempty"`
|
||||
id string
|
||||
valid bool // Is set true, if called by the constructor
|
||||
Discoverable bool `yaml:"discoverable,omitempty" lopt:"discoverable" sopt:"e" comment:"Make discoverable in given network (true/false)"`
|
||||
AssetKey string `yaml:"asset key,omitempty" lopt:"asset" comment:"Set the node's Asset tag (key)"`
|
||||
Profiles []string `yaml:"profiles,omitempty" lopt:"profile" sopt:"P" comment:"Set the node's profile members (comma separated)"`
|
||||
ProfileConf `yaml:"-,inline"` // include all values set in the profile, but inline them in yaml output if these are part of NodeConf
|
||||
}
|
||||
|
||||
/*
|
||||
Holds the data which can be set for profiles and nodes.
|
||||
*/
|
||||
type ProfileConf struct {
|
||||
// exported values
|
||||
Comment string `yaml:"comment,omitempty" lopt:"comment" comment:"Set arbitrary string comment"`
|
||||
ClusterName string `yaml:"cluster name,omitempty" lopt:"cluster" sopt:"c" comment:"Set cluster group"`
|
||||
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"`
|
||||
RuntimeOverlay []string `yaml:"runtime overlay,omitempty" lopt:"runtime" sopt:"R" comment:"Set the runtime overlay"`
|
||||
SystemOverlay []string `yaml:"system overlay,omitempty" lopt:"wwinit" sopt:"O" comment:"Set the system overlay"`
|
||||
Kernel *KernelConf `yaml:"kernel,omitempty"`
|
||||
Ipmi *IpmiConf `yaml:"ipmi,omitempty"`
|
||||
Init string `yaml:"init,omitempty" lopt:"init" sopt:"i" comment:"Define the init process to boot the container"`
|
||||
Root string `yaml:"root,omitempty" lopt:"root" comment:"Define the rootfs" `
|
||||
NetDevs map[string]*NetDevs `yaml:"network devices,omitempty"`
|
||||
Tags map[string]string `yaml:"tags,omitempty"`
|
||||
PrimaryNetDev string `yaml:"primary network,omitempty" lopt:"primarynet" sopt:"p" comment:"Set the primary network interface"`
|
||||
Disks map[string]*Disk `yaml:"disks,omitempty"`
|
||||
FileSystems map[string]*FileSystem `yaml:"filesystems,omitempty"`
|
||||
}
|
||||
|
||||
type IpmiConf struct {
|
||||
UserName string `yaml:"username,omitempty" lopt:"ipmiuser" comment:"Set the IPMI username" json:"username,omitempty"`
|
||||
Password string `yaml:"password,omitempty" lopt:"ipmipass" comment:"Set the IPMI password" json:"password,omitempty"`
|
||||
Ipaddr string `yaml:"ipaddr,omitempty" lopt:"ipmiaddr" comment:"Set the IPMI IP address" type:"IP" json:"ipaddr,omitempty"`
|
||||
Netmask string `yaml:"netmask,omitempty" lopt:"ipminetmask" comment:"Set the IPMI netmask" type:"IP" json:"netmask,omitempty"`
|
||||
Port string `yaml:"port,omitempty" lopt:"ipmiport" comment:"Set the IPMI port" json:"port,omitempty"`
|
||||
Gateway string `yaml:"gateway,omitempty" lopt:"ipmigateway" comment:"Set the IPMI gateway" type:"IP" json:"gateway,omitempty"`
|
||||
Interface string `yaml:"interface,omitempty" lopt:"ipmiinterface" comment:"Set the node's IPMI interface (defaults: 'lan')" json:"interface,omitempty"`
|
||||
EscapeChar string `yaml:"escapechar,omitempty" lopt:"ipmiescapechar" comment:"Set the IPMI escape character (defaults: '~')" json:"escapechar,omitempty"`
|
||||
Write string `yaml:"write,omitempty" lopt:"ipmiwrite" comment:"Enable the write of impi configuration (true/false)" type:"bool" json:"write,omitempty"`
|
||||
Tags map[string]string `yaml:"tags,omitempty" lopt:"ipmitagadd" comment:"add ipmitags" json:"tags,omitempty"`
|
||||
TagsDel []string `yaml:"tagsdel,omitempty" lopt:"ipmitagdel" comment:"remove ipmitags" json:"tagsdel,omitempty"` // should not go to disk only to wire
|
||||
UserName string `yaml:"username,omitempty" lopt:"ipmiuser" comment:"Set the IPMI username"`
|
||||
Password string `yaml:"password,omitempty" lopt:"ipmipass" comment:"Set the IPMI password"`
|
||||
Ipaddr net.IP `yaml:"ipaddr,omitempty" lopt:"ipmiaddr" comment:"Set the IPMI IP address" type:"IP"`
|
||||
Gateway net.IP `yaml:"gateway,omitempty" lopt:"ipmigateway" comment:"Set the IPMI gateway" type:"IP"`
|
||||
Netmask net.IPMask `yaml:"netmask,omitempty" lopt:"ipminetmask" comment:"Set the IPMI netmask" type:"IP"`
|
||||
Port string `yaml:"port,omitempty" lopt:"ipmiport" comment:"Set the IPMI port"`
|
||||
Interface string `yaml:"interface,omitempty" lopt:"ipmiinterface" comment:"Set the node's IPMI interface (defaults: 'lan')"`
|
||||
EscapeChar string `yaml:"escapechar,omitempty" lopt:"ipmiescapechar" comment:"Set the IPMI escape character (defaults: '~')"`
|
||||
Write bool `yaml:"write,omitempty" lopt:"ipmiwrite" comment:"Enable the write of impi configuration (true/false)"`
|
||||
Tags map[string]string `yaml:"tags,omitempty"`
|
||||
}
|
||||
type KernelConf struct {
|
||||
Version string `yaml:"version,omitempty" json:"version,omitempty"`
|
||||
@@ -75,27 +70,24 @@ type KernelConf struct {
|
||||
|
||||
type NetDevs struct {
|
||||
Type string `yaml:"type,omitempty" lopt:"type" sopt:"T" comment:"Set device type of given network"`
|
||||
OnBoot string `yaml:"onboot,omitempty" lopt:"onboot" comment:"Enable/disable network device (true/false)" type:"bool"`
|
||||
OnBoot bool `yaml:"onboot,omitempty" lopt:"onboot" comment:"Enable/disable network device (true/false)"`
|
||||
Device string `yaml:"device,omitempty" lopt:"netdev" sopt:"N" comment:"Set the device for given network"`
|
||||
Hwaddr string `yaml:"hwaddr,omitempty" lopt:"hwaddr" sopt:"H" comment:"Set the device's HW address for given network" type:"MAC"`
|
||||
Ipaddr string `yaml:"ipaddr,omitempty" comment:"IPv4 address in given network" sopt:"I" lopt:"ipaddr" type:"IP"`
|
||||
IpCIDR string `yaml:"ipcidr,omitempty"`
|
||||
Ipaddr6 string `yaml:"ip6addr,omitempty" lopt:"ipaddr6" comment:"IPv6 address" type:"IP"`
|
||||
Prefix string `yaml:"prefix,omitempty"`
|
||||
Netmask string `yaml:"netmask,omitempty" lopt:"netmask" sopt:"M" comment:"Set the networks netmask" type:"IP"`
|
||||
Gateway string `yaml:"gateway,omitempty" lopt:"gateway" sopt:"G" comment:"Set the node's network device gateway" type:"IP"`
|
||||
Ipaddr net.IP `yaml:"ipaddr,omitempty" comment:"IPv4 address in given network" sopt:"I" lopt:"ipaddr" type:"IP"`
|
||||
Ipaddr6 net.IP `yaml:"ip6addr,omitempty" lopt:"ipaddr6" comment:"IPv6 address" type:"IP"`
|
||||
Prefix net.IP `yaml:"prefix,omitempty"`
|
||||
Netmask net.IPMask `yaml:"netmask,omitempty" lopt:"netmask" sopt:"M" comment:"Set the networks netmask" type:"IP"`
|
||||
Gateway net.IP `yaml:"gateway,omitempty" lopt:"gateway" sopt:"G" comment:"Set the node's network device gateway" type:"IP"`
|
||||
MTU string `yaml:"mtu,omitempty" lopt:"mtu" comment:"Set the mtu" type:"uint"`
|
||||
Primary string `yaml:"primary,omitempty" type:"bool"`
|
||||
Default string `yaml:"default,omitempty"` /* backward compatibility */
|
||||
Tags map[string]string `yaml:"tags,omitempty" lopt:"nettagadd" comment:"network tags"`
|
||||
TagsDel []string `yaml:"tagsdel,omitempty" lopt:"nettagdel" comment:"delete network tags"` // should not go to disk only to wire
|
||||
primary bool `yaml:"primary,omitempty"`
|
||||
Tags map[string]string `yaml:"tags,omitempty"`
|
||||
}
|
||||
|
||||
/*
|
||||
Holds the disks of a node
|
||||
*/
|
||||
type Disk struct {
|
||||
WipeTable string `yaml:"wipe_table,omitempty" type:"bool" lopt:"diskwipe" comment:"whether or not the partition tables shall be wiped"`
|
||||
WipeTable bool `yaml:"wipe_table,omitempty" lopt:"diskwipe" comment:"whether or not the partition tables shall be wiped"`
|
||||
Partitions map[string]*Partition `yaml:"partitions,omitempty"`
|
||||
}
|
||||
|
||||
@@ -104,14 +96,14 @@ partition definition, the label must be uniq so its used as the key in the
|
||||
Partitions map
|
||||
*/
|
||||
type Partition struct {
|
||||
Number string `yaml:"number,omitempty" lopt:"partnumber" comment:"set the partition number, if not set next free slot is used"`
|
||||
SizeMiB string `yaml:"size_mib,omitempty" lopt:"partsize" comment:"set the size of the partition, if not set maximal possible size is used"`
|
||||
StartMiB string `yaml:"start_mib,omitempty" comment:"the start of the partition"`
|
||||
Number string `yaml:"number,omitempty" lopt:"partnumber" comment:"set the partition number, if not set next free slot is used" type:"uint"`
|
||||
SizeMiB string `yaml:"size_mib,omitempty" lopt:"partsize " comment:"set the size of the partition, if not set maximal possible size is used" type:"uint"`
|
||||
StartMiB string `yaml:"start_mib,omitempty" comment:"the start of the partition" type:"uint"`
|
||||
TypeGuid string `yaml:"type_guid,omitempty" comment:"Linux filesystem data will be used if empty"`
|
||||
Guid string `yaml:"guid,omitempty" comment:"the GPT unique partition GUID"`
|
||||
WipePartitionEntry string `yaml:"wipe_partition_entry,omitempty" comment:"if true, Ignition will clobber an existing partition if it does not match the config" type:"bool"`
|
||||
ShouldExist string `yaml:"should_exist,omitempty" lopt:"partcreate" comment:"create partition if not exist" type:"bool"`
|
||||
Resize string `yaml:"resize,omitempty" comment:" whether or not the existing partition should be resize" type:"bool"`
|
||||
WipePartitionEntry bool `yaml:"wipe_partition_entry,omitempty" comment:"if true, Ignition will clobber an existing partition if it does not match the config"`
|
||||
ShouldExist bool `yaml:"should_exist,omitempty" lopt:"partcreate" comment:"create partition if not exist"`
|
||||
Resize bool `yaml:"resize,omitempty" comment:" whether or not the existing partition should be resize"`
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -120,122 +112,9 @@ Definition of a filesystem. The device is uniq so its used as key
|
||||
type FileSystem struct {
|
||||
Format string `yaml:"format,omitempty" lopt:"fsformat" comment:"format of the file system"`
|
||||
Path string `yaml:"path,omitempty" lopt:"fspath" comment:"the mount point of the file system"`
|
||||
WipeFileSystem string `yaml:"wipe_filesystem,omitempty" lopt:"fswipe" comment:"wipe file system at boot" type:"bool"`
|
||||
WipeFileSystem bool `yaml:"wipe_filesystem,omitempty" lopt:"fswipe" comment:"wipe file system at boot"`
|
||||
Label string `yaml:"label,omitempty" comment:"the label of the filesystem"`
|
||||
Uuid string `yaml:"uuid,omitempty" comment:"the uuid of the filesystem"`
|
||||
Options []string `yaml:"options,omitempty" comment:"any additional options to be passed to the format-specific mkfs utility"`
|
||||
MountOptions []string `yaml:"mount_options,omitempty" comment:"any special options to be passed to the mount command"`
|
||||
}
|
||||
|
||||
/******
|
||||
* Internal code data representations
|
||||
******/
|
||||
/*
|
||||
Holds string values, when accessed via Get, its value
|
||||
is returned which is the default or if set the value
|
||||
from the profile or if set the value of the node itself
|
||||
*/
|
||||
type Entry struct {
|
||||
value []string
|
||||
altvalue []string
|
||||
from string
|
||||
def []string
|
||||
isSlice bool
|
||||
}
|
||||
|
||||
/*
|
||||
NodeInfo is the in memory datastructure, which can containe
|
||||
a default value, which is overwritten by the overlay from the
|
||||
overlay (altvalue) which is overwitten by the value of the
|
||||
node itself, for all values of type Entry.
|
||||
*/
|
||||
type NodeInfo struct {
|
||||
Id Entry
|
||||
Comment Entry
|
||||
ClusterName Entry
|
||||
ContainerName Entry
|
||||
Ipxe Entry
|
||||
Grub Entry
|
||||
RuntimeOverlay Entry
|
||||
SystemOverlay Entry
|
||||
Root Entry
|
||||
Discoverable Entry
|
||||
Init Entry //TODO: Finish adding this...
|
||||
AssetKey Entry
|
||||
Kernel *KernelEntry
|
||||
Ipmi *IpmiEntry
|
||||
Profiles Entry
|
||||
PrimaryNetDev Entry
|
||||
NetDevs map[string]*NetDevEntry
|
||||
Tags map[string]*Entry
|
||||
Disks map[string]*DiskEntry
|
||||
FileSystems map[string]*FileSystemEntry
|
||||
}
|
||||
|
||||
type IpmiEntry struct {
|
||||
Ipaddr Entry
|
||||
Netmask Entry
|
||||
Port Entry
|
||||
Gateway Entry
|
||||
UserName Entry
|
||||
Password Entry
|
||||
Interface Entry
|
||||
EscapeChar Entry
|
||||
Write Entry
|
||||
Tags map[string]*Entry
|
||||
}
|
||||
|
||||
type KernelEntry struct {
|
||||
Override Entry
|
||||
Args Entry
|
||||
}
|
||||
|
||||
type NetDevEntry struct {
|
||||
Type Entry
|
||||
OnBoot Entry
|
||||
Device Entry
|
||||
Hwaddr Entry
|
||||
Ipaddr Entry
|
||||
Ipaddr6 Entry
|
||||
IpCIDR Entry
|
||||
Prefix Entry
|
||||
Netmask Entry
|
||||
Gateway Entry
|
||||
MTU Entry
|
||||
Primary Entry
|
||||
Tags map[string]*Entry
|
||||
}
|
||||
|
||||
type DiskEntry struct {
|
||||
WipeTable Entry
|
||||
Partitions map[string]*PartitionEntry
|
||||
}
|
||||
|
||||
type PartitionEntry struct {
|
||||
Number Entry
|
||||
SizeMiB Entry
|
||||
StartMiB Entry
|
||||
TypeGuid Entry
|
||||
Guid Entry
|
||||
WipePartitionEntry Entry
|
||||
ShouldExist Entry
|
||||
Resize Entry
|
||||
}
|
||||
|
||||
type FileSystemEntry struct {
|
||||
Format Entry
|
||||
Path Entry
|
||||
WipeFileSystem Entry
|
||||
Label Entry
|
||||
Uuid Entry
|
||||
Options Entry
|
||||
MountOptions Entry
|
||||
}
|
||||
|
||||
// string which is printed if no value is set
|
||||
const NoValue = "--"
|
||||
|
||||
func (e Entry) MarshalText() (buf []byte, err error) {
|
||||
buf = append(buf, []byte(e.Get())...)
|
||||
return buf, nil
|
||||
MountOptions string `yaml:"mount_options,omitempty" comment:"any special options to be passed to the mount command"`
|
||||
}
|
||||
|
||||
6
internal/pkg/node/error.go
Normal file
6
internal/pkg/node/error.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package node
|
||||
|
||||
import "errors"
|
||||
|
||||
var ErrNotFound = errors.New("node/profile not found")
|
||||
var ErrNoUnconfigured = errors.New("no unconfigured node")
|
||||
@@ -1,37 +1,59 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/warewulf/warewulf/internal/pkg/util"
|
||||
)
|
||||
|
||||
/*
|
||||
Create cmd line flags from the NodeConf fields. Returns a []func() where every function
|
||||
must be called, as the commandline parser returns e.g. netip.IP objects which must be parsedf
|
||||
back to strings.
|
||||
*/
|
||||
func (nodeConf *NodeConf) CreateFlags(baseCmd *cobra.Command, excludeList []string) (converters []func() error) {
|
||||
return RecursiveCreateFlags(nodeConf, baseCmd, excludeList)
|
||||
type NodeConfDel struct {
|
||||
TagsDel []string `lopt:"tagdel" comment:"add tags"`
|
||||
IpmiTagsDel []string `lopt:"ipmitagdel" comment:"delete ipmi tags"`
|
||||
NetTagsDel []string `lopt:"nettagdel" comment:"delete network tags"`
|
||||
NetDel string `lopt:"netdel" comment:"network to delete"`
|
||||
DiskDel string `lopt:"diskdel" comment:"delete the disk from the configuration"`
|
||||
PartDel string `lopt:"partdel" comment:"delete the partition from the configuration"`
|
||||
FsDel string `lopt:"fsdel" comment:"delete the fs from the configuration"`
|
||||
}
|
||||
type NodeConfAdd struct {
|
||||
TagsAdd map[string]string `lopt:"tagadd" comment:"add tags"`
|
||||
IpmiTagsAdd map[string]string `lopt:"ipmitagadd" comment:"add ipmi tags"`
|
||||
NetTagsAdd map[string]string `lopt:"nettagadd" comment:"add network tags"`
|
||||
Net string `lopt:"netname" comment:"network which is modified"`
|
||||
DiskName string `lopt:"diskname" comment:"set diskdevice name"`
|
||||
PartName string `lopt:"partname" comment:"set the partition name so it can be used by a file system"`
|
||||
FsName string `lopt:"fsname" comment:"set the file system name which must match a partition name"`
|
||||
}
|
||||
|
||||
func RecursiveCreateFlags(obj interface{}, baseCmd *cobra.Command, excludeList []string) (converters []func() error) {
|
||||
/*
|
||||
Create cmd line flags from the NodeConf fields. Returns a []func() where every function must be called, as the command line parser returns e.g. netip.IP objects which must be parsed
|
||||
back to strings.
|
||||
*/
|
||||
func (nodeConf *NodeConf) CreateFlags(baseCmd *cobra.Command) {
|
||||
recursiveCreateFlags(nodeConf, baseCmd)
|
||||
}
|
||||
|
||||
func (del *NodeConfDel) CreateDelFlags(baseCmd *cobra.Command) {
|
||||
recursiveCreateFlags(del, baseCmd)
|
||||
|
||||
}
|
||||
func (add *NodeConfAdd) CreateAddFlags(baseCmd *cobra.Command) {
|
||||
recursiveCreateFlags(add, baseCmd)
|
||||
|
||||
}
|
||||
|
||||
func recursiveCreateFlags(obj interface{}, baseCmd *cobra.Command) {
|
||||
// now iterate of every field
|
||||
nodeInfoType := reflect.TypeOf(obj)
|
||||
nodeInfoVal := reflect.ValueOf(obj)
|
||||
for i := 0; i < nodeInfoVal.Elem().NumField(); i++ {
|
||||
if nodeInfoType.Elem().Field(i).Tag.Get("comment") != "" &&
|
||||
!util.InSlice(excludeList, nodeInfoType.Elem().Field(i).Tag.Get("lopt")) {
|
||||
if nodeInfoType.Elem().Field(i).Tag.Get("comment") != "" {
|
||||
field := nodeInfoVal.Elem().Field(i)
|
||||
converters = append(converters, createFlags(baseCmd, excludeList, nodeInfoType.Elem().Field(i), &field)...)
|
||||
createFlags(baseCmd, nodeInfoType.Elem().Field(i), &field)
|
||||
|
||||
} else if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.Ptr {
|
||||
newConv := RecursiveCreateFlags(nodeInfoVal.Elem().Field(i).Interface(), baseCmd, excludeList)
|
||||
converters = append(converters, newConv...)
|
||||
recursiveCreateFlags(nodeInfoVal.Elem().Field(i).Interface(), baseCmd)
|
||||
|
||||
} else if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.Map &&
|
||||
nodeInfoType.Elem().Field(i).Type != reflect.TypeOf(map[string]string{}) {
|
||||
@@ -46,174 +68,46 @@ func RecursiveCreateFlags(obj interface{}, baseCmd *cobra.Command, excludeList [
|
||||
} else {
|
||||
key = nodeInfoVal.Elem().Field(i).MapKeys()[0]
|
||||
}
|
||||
newConv := RecursiveCreateFlags(nodeInfoVal.Elem().Field(i).MapIndex(key).Interface(), baseCmd, excludeList)
|
||||
converters = append(converters, newConv...)
|
||||
recursiveCreateFlags(nodeInfoVal.Elem().Field(i).MapIndex(key).Interface(), baseCmd)
|
||||
} else if nodeInfoType.Elem().Field(i).Anonymous {
|
||||
recursiveCreateFlags(nodeInfoVal.Elem().Field(i).Addr().Interface(), baseCmd)
|
||||
}
|
||||
}
|
||||
return converters
|
||||
}
|
||||
|
||||
/*
|
||||
Helper function to create the different PersistentFlags() for different types.
|
||||
*/
|
||||
func createFlags(baseCmd *cobra.Command, excludeList []string,
|
||||
myType reflect.StructField, myVal *reflect.Value) (converters []func() error) {
|
||||
func createFlags(baseCmd *cobra.Command,
|
||||
myType reflect.StructField, myVal *reflect.Value) {
|
||||
if myType.Tag.Get("lopt") != "" {
|
||||
if myType.Type.Kind() == reflect.String {
|
||||
ptr := myVal.Addr().Interface().(*string)
|
||||
switch myType.Tag.Get("type") {
|
||||
case "uint":
|
||||
converters = append(converters, func() error {
|
||||
if !util.InSlice(GetUnsetVerbs(), *ptr) && *ptr != "" {
|
||||
_, err := strconv.ParseUint(*ptr, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if myType.Tag.Get("sopt") != "" {
|
||||
baseCmd.PersistentFlags().StringVarP(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("sopt"),
|
||||
myType.Tag.Get("default"),
|
||||
myType.Tag.Get("comment"))
|
||||
} else {
|
||||
baseCmd.PersistentFlags().StringVar(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("default"),
|
||||
myType.Tag.Get("comment"))
|
||||
}
|
||||
case "bool":
|
||||
/*
|
||||
Can't use the bool var from pflag as we need the UNSET verbs to be passwd correctly
|
||||
*/
|
||||
converters = append(converters, func() error {
|
||||
if !util.InSlice(GetUnsetVerbs(), *ptr) && *ptr != "" {
|
||||
if strings.ToLower(*ptr) == "yes" {
|
||||
*ptr = "true"
|
||||
return nil
|
||||
}
|
||||
if strings.ToLower(*ptr) == "no" {
|
||||
*ptr = "false"
|
||||
return nil
|
||||
}
|
||||
val, err := strconv.ParseBool(*ptr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("commandline option %s needs to be bool", myType.Tag.Get("lopt"))
|
||||
}
|
||||
*ptr = strconv.FormatBool(val)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if myType.Tag.Get("sopt") != "" {
|
||||
baseCmd.PersistentFlags().StringVarP(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("sopt"),
|
||||
"",
|
||||
myType.Tag.Get("comment"))
|
||||
} else {
|
||||
baseCmd.PersistentFlags().StringVar(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
"",
|
||||
myType.Tag.Get("comment"))
|
||||
}
|
||||
baseCmd.PersistentFlags().Lookup(myType.Tag.Get("lopt")).NoOptDefVal = "true"
|
||||
case "IP":
|
||||
converters = append(converters, func() error {
|
||||
if !util.InSlice(GetUnsetVerbs(), *ptr) && *ptr != "" {
|
||||
ipval := net.ParseIP(*ptr)
|
||||
if ipval == nil {
|
||||
return fmt.Errorf("commandline option %s needs to be an IP address", myType.Tag.Get("lopt"))
|
||||
}
|
||||
*ptr = ipval.String()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if myType.Tag.Get("sopt") != "" {
|
||||
baseCmd.PersistentFlags().StringVarP(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("sopt"),
|
||||
myType.Tag.Get("default"),
|
||||
myType.Tag.Get("comment"))
|
||||
} else {
|
||||
baseCmd.PersistentFlags().StringVar(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("default"),
|
||||
myType.Tag.Get("comment"))
|
||||
}
|
||||
case "IPMask":
|
||||
defaultConv := net.ParseIP(myType.Tag.Get("default")).DefaultMask()
|
||||
var valueRaw net.IPMask
|
||||
converters = append(converters, func() error {
|
||||
if valueRaw != nil {
|
||||
*ptr = valueRaw.String()
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("could not parse %s to IP", valueRaw.String())
|
||||
}
|
||||
})
|
||||
if myType.Tag.Get("sopt") != "" {
|
||||
baseCmd.PersistentFlags().IPMaskVarP(&valueRaw,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("sopt"),
|
||||
defaultConv,
|
||||
myType.Tag.Get("comment"))
|
||||
} else {
|
||||
baseCmd.PersistentFlags().IPMaskVar(&valueRaw,
|
||||
myType.Tag.Get("lopt"),
|
||||
defaultConv,
|
||||
myType.Tag.Get("comment"))
|
||||
}
|
||||
case "MAC":
|
||||
converters = append(converters, func() error {
|
||||
if !util.InSlice(GetUnsetVerbs(), *ptr) && *ptr != "" {
|
||||
myMac, err := net.ParseMAC(*ptr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*ptr = myMac.String()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if myType.Tag.Get("sopt") != "" {
|
||||
baseCmd.PersistentFlags().StringVarP(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("sopt"),
|
||||
"",
|
||||
myType.Tag.Get("comment"))
|
||||
} else {
|
||||
baseCmd.PersistentFlags().StringVar(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
"",
|
||||
myType.Tag.Get("comment"))
|
||||
}
|
||||
default:
|
||||
if myType.Tag.Get("sopt") != "" {
|
||||
baseCmd.PersistentFlags().StringVarP(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("sopt"),
|
||||
myType.Tag.Get("default"),
|
||||
myType.Tag.Get("comment"))
|
||||
} else {
|
||||
baseCmd.PersistentFlags().StringVar(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("default"),
|
||||
myType.Tag.Get("comment"))
|
||||
}
|
||||
if myType.Tag.Get("sopt") != "" {
|
||||
baseCmd.PersistentFlags().StringVarP(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("sopt"),
|
||||
myType.Tag.Get("default"),
|
||||
myType.Tag.Get("comment"))
|
||||
} else {
|
||||
baseCmd.PersistentFlags().StringVar(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("default"),
|
||||
myType.Tag.Get("comment"))
|
||||
}
|
||||
|
||||
} else if myType.Type == reflect.TypeOf([]string{}) {
|
||||
ptr := myVal.Addr().Interface().(*[]string)
|
||||
if myType.Tag.Get("sopt") != "" {
|
||||
baseCmd.PersistentFlags().StringSliceVarP(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("sopt"),
|
||||
[]string{myType.Tag.Get("default")},
|
||||
[]string{},
|
||||
myType.Tag.Get("comment"))
|
||||
} else if !util.InSlice(excludeList, myType.Tag.Get("lopt")) {
|
||||
} else {
|
||||
baseCmd.PersistentFlags().StringSliceVar(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
[]string{myType.Tag.Get("default")},
|
||||
[]string{},
|
||||
myType.Tag.Get("comment"))
|
||||
|
||||
}
|
||||
@@ -225,14 +119,69 @@ func createFlags(baseCmd *cobra.Command, excludeList []string,
|
||||
myType.Tag.Get("sopt"),
|
||||
map[string]string{}, // empty default!
|
||||
myType.Tag.Get("comment"))
|
||||
} else if !util.InSlice(excludeList, myType.Tag.Get("lopt")) {
|
||||
} else {
|
||||
baseCmd.PersistentFlags().StringToStringVar(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
map[string]string{}, // empty default!
|
||||
myType.Tag.Get("comment"))
|
||||
}
|
||||
} else if myType.Type == reflect.TypeOf(true) {
|
||||
ptr := myVal.Addr().Interface().(*bool)
|
||||
if myType.Tag.Get("sopt") != "" {
|
||||
baseCmd.PersistentFlags().BoolVarP(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("sopt"),
|
||||
false, // empty default!
|
||||
myType.Tag.Get("comment"))
|
||||
} else {
|
||||
baseCmd.PersistentFlags().BoolVar(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
false, // empty default!
|
||||
myType.Tag.Get("comment"))
|
||||
}
|
||||
|
||||
} else if myType.Type == reflect.TypeOf(true) {
|
||||
ptr := myVal.Addr().Interface().(*bool)
|
||||
if myType.Tag.Get("sopt") != "" {
|
||||
baseCmd.PersistentFlags().BoolVarP(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("sopt"),
|
||||
false, // empty default!
|
||||
myType.Tag.Get("comment"))
|
||||
} else {
|
||||
baseCmd.PersistentFlags().BoolVar(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
false, // empty default!
|
||||
myType.Tag.Get("comment"))
|
||||
}
|
||||
} else if myType.Type == reflect.TypeOf(net.IP{}) {
|
||||
ptr := myVal.Addr().Interface().(*net.IP)
|
||||
if myType.Tag.Get("sopt") != "" {
|
||||
baseCmd.PersistentFlags().IPVarP(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("sopt"),
|
||||
net.IP{}, // empty default!
|
||||
myType.Tag.Get("comment"))
|
||||
} else {
|
||||
baseCmd.PersistentFlags().IPVar(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
net.IP{}, // empty default!
|
||||
myType.Tag.Get("comment"))
|
||||
}
|
||||
} else if myType.Type == reflect.TypeOf(net.IPMask{}) {
|
||||
ptr := myVal.Addr().Interface().(*net.IPMask)
|
||||
if myType.Tag.Get("sopt") != "" {
|
||||
baseCmd.PersistentFlags().IPMaskVarP(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("sopt"),
|
||||
net.IPMask{}, // empty default!
|
||||
myType.Tag.Get("comment"))
|
||||
} else {
|
||||
baseCmd.PersistentFlags().IPMaskVar(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
net.IPMask{}, // empty default!
|
||||
myType.Tag.Get("comment"))
|
||||
}
|
||||
}
|
||||
}
|
||||
return converters
|
||||
}
|
||||
|
||||
16
internal/pkg/node/helper.go
Normal file
16
internal/pkg/node/helper.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package node
|
||||
|
||||
/*
|
||||
helper functions
|
||||
*/
|
||||
|
||||
// string which is printed if no value is set
|
||||
const NoValue = "--"
|
||||
|
||||
// print NoValue if string is empty
|
||||
func PrintVal(in string) (out string) {
|
||||
if in != "" {
|
||||
return in
|
||||
}
|
||||
return NoValue
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package node
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
types_3_4 "github.com/coreos/ignition/v2/config/v3_2/types"
|
||||
"github.com/coreos/vcontext/path"
|
||||
@@ -12,37 +13,37 @@ import (
|
||||
/*
|
||||
Create a ignition struct class for ignition
|
||||
*/
|
||||
func (node *NodeInfo) GetStorage() (stor types_3_4.Storage, err error, rep string) {
|
||||
func (node *NodeConf) GetStorage() (stor types_3_4.Storage, err error, rep string) {
|
||||
var fileSystems []types_3_4.Filesystem
|
||||
for fsdevice, fs := range node.FileSystems {
|
||||
var mountOptions []types_3_4.MountOption
|
||||
for _, opt := range fs.MountOptions.GetSlice() {
|
||||
for _, opt := range fs.MountOptions {
|
||||
mountOptions = append(mountOptions, types_3_4.MountOption(opt))
|
||||
}
|
||||
var fsOption []types_3_4.FilesystemOption
|
||||
for _, opt := range fs.Options.GetSlice() {
|
||||
for _, opt := range fs.Options {
|
||||
fsOption = append(fsOption, types_3_4.FilesystemOption(opt))
|
||||
}
|
||||
wipe := fs.WipeFileSystem.GetB()
|
||||
wipe := fs.WipeFileSystem
|
||||
myFs := types_3_4.Filesystem{
|
||||
Device: fsdevice,
|
||||
Path: fs.Path.GetPointer(),
|
||||
Path: &fs.Path,
|
||||
WipeFilesystem: &wipe,
|
||||
}
|
||||
if fs.Format.Get() != "" {
|
||||
myFs.Format = fs.Format.GetPointer()
|
||||
if fs.Format != "" {
|
||||
myFs.Format = &fs.Format
|
||||
}
|
||||
if fs.Label.Get() != "" {
|
||||
myFs.Label = fs.Label.GetPointer()
|
||||
if fs.Label != "" {
|
||||
myFs.Label = &fs.Label
|
||||
}
|
||||
if fs.MountOptions.Get() != "" {
|
||||
if fs.MountOptions != "" {
|
||||
myFs.MountOptions = mountOptions
|
||||
}
|
||||
if fs.Options.Get() != "" {
|
||||
if len(fs.Options) != 0 {
|
||||
myFs.Options = fsOption
|
||||
}
|
||||
if fs.Options.Get() != "" {
|
||||
myFs.UUID = fs.Uuid.GetPointer()
|
||||
if fs.Uuid != "" {
|
||||
myFs.UUID = &fs.Uuid
|
||||
}
|
||||
wwlog.Debug("created file system struct: %v", myFs)
|
||||
fileSystems = append(fileSystems, myFs)
|
||||
@@ -54,30 +55,47 @@ func (node *NodeInfo) GetStorage() (stor types_3_4.Storage, err error, rep strin
|
||||
for diskDev, disk := range node.Disks {
|
||||
var partitions []types_3_4.Partition
|
||||
for partlabel, part := range disk.Partitions {
|
||||
resize := part.Resize.GetB()
|
||||
shouldExist := part.ShouldExist.GetB()
|
||||
wipe := part.WipePartitionEntry.GetB()
|
||||
resize := part.Resize
|
||||
shouldExist := part.ShouldExist
|
||||
wipe := part.WipePartitionEntry
|
||||
label := partlabel
|
||||
var number int
|
||||
if part.Number != "" {
|
||||
number, err = strconv.Atoi(part.Number)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
myPart := types_3_4.Partition{
|
||||
Label: &label,
|
||||
Number: part.Number.GetInt(),
|
||||
Number: number,
|
||||
ShouldExist: &shouldExist,
|
||||
WipePartitionEntry: &wipe,
|
||||
}
|
||||
if part.Guid.Get() != "" {
|
||||
myPart.GUID = part.Guid.GetPointer()
|
||||
if part.Guid != "" {
|
||||
myPart.GUID = &part.Guid
|
||||
}
|
||||
if part.Resize.Get() != "" {
|
||||
if part.Resize {
|
||||
myPart.Resize = &resize
|
||||
}
|
||||
if part.SizeMiB.Get() != "" {
|
||||
myPart.SizeMiB = part.SizeMiB.GetIntPtr()
|
||||
if part.SizeMiB != "" {
|
||||
var size int
|
||||
size, err = strconv.Atoi(part.SizeMiB)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
myPart.SizeMiB = &size
|
||||
}
|
||||
if part.StartMiB.Get() != "" {
|
||||
myPart.StartMiB = part.StartMiB.GetIntPtr()
|
||||
if part.StartMiB != "" {
|
||||
var start int
|
||||
start, err = strconv.Atoi(part.SizeMiB)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
myPart.StartMiB = &start
|
||||
}
|
||||
if part.TypeGuid.Get() != "" {
|
||||
myPart.TypeGUID = part.TypeGuid.GetPointer()
|
||||
if part.TypeGuid != "" {
|
||||
myPart.TypeGUID = &part.TypeGuid
|
||||
}
|
||||
partitions = append(partitions, myPart)
|
||||
}
|
||||
@@ -93,7 +111,7 @@ func (node *NodeInfo) GetStorage() (stor types_3_4.Storage, err error, rep strin
|
||||
}
|
||||
return partitions[i].Number < partitions[j].Number
|
||||
})
|
||||
wipe := disk.WipeTable.GetB()
|
||||
wipe := disk.WipeTable
|
||||
disks = append(disks, types_3_4.Disk{
|
||||
Device: diskDev,
|
||||
Partitions: partitions,
|
||||
@@ -123,7 +141,7 @@ type SimpleIgnitionConfig struct {
|
||||
/*
|
||||
Get a simple config which can be marshalled to json
|
||||
*/
|
||||
func (node *NodeInfo) GetConfig() (conf SimpleIgnitionConfig, rep string, err error) {
|
||||
func (node *NodeConf) GetConfig() (conf SimpleIgnitionConfig, rep string, err error) {
|
||||
conf.Storage, err, rep = node.GetStorage()
|
||||
conf.Ignition.Version = "3.1.0"
|
||||
return
|
||||
|
||||
@@ -14,66 +14,54 @@ type NodeFields struct {
|
||||
}
|
||||
|
||||
/*
|
||||
Get all the info out of NodeInfo. If emptyFields is set true, all fields
|
||||
are shown not only the ones with effective values
|
||||
Get all the info out of NodeConf. If emptyFields is set true, all fields are shown not only the ones with effective values
|
||||
*/
|
||||
func (node *NodeInfo) GetFields(emptyFields bool) (output []NodeFields) {
|
||||
return recursiveFields(node, emptyFields, "")
|
||||
func (nodeYml *NodeYaml) GetFields(node NodeConf, emptyFields bool) (output []NodeFields) {
|
||||
fieldMap := make(map[string]NodeFields)
|
||||
for _, p := range node.Profiles {
|
||||
if profile, ok := nodeYml.NodeProfiles[p]; ok {
|
||||
recursiveFields(profile, emptyFields, "", fieldMap, p)
|
||||
}
|
||||
}
|
||||
recursiveFields(node, emptyFields, "", fieldMap, "")
|
||||
return output
|
||||
}
|
||||
|
||||
/*
|
||||
Internal function which travels through all fields of a NodeInfo and for this
|
||||
Internal function which travels through all fields of a NodeConf and for this
|
||||
reason needs tb called via interface{}
|
||||
*/
|
||||
func recursiveFields(obj interface{}, emptyFields bool, prefix string) (output []NodeFields) {
|
||||
func recursiveFields(obj interface{}, emptyFields bool, prefix string,
|
||||
fieldMap map[string]NodeFields, source string) {
|
||||
valObj := reflect.ValueOf(obj)
|
||||
typeObj := reflect.TypeOf(obj)
|
||||
for i := 0; i < typeObj.Elem().NumField(); i++ {
|
||||
if typeObj.Elem().Field(i).Type == reflect.TypeOf(Entry{}) {
|
||||
myField := valObj.Elem().Field(i).Interface().(Entry)
|
||||
if emptyFields || myField.Get() != "" {
|
||||
output = append(output, NodeFields{
|
||||
if valObj.Elem().Field(i).IsValid() {
|
||||
if valObj.Elem().Field(i).String() != "" {
|
||||
fieldMap[prefix+typeObj.Elem().Field(i).Name] = NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name,
|
||||
Source: myField.Source(),
|
||||
Value: myField.Print(),
|
||||
})
|
||||
}
|
||||
} else if typeObj.Elem().Field(i).Type == reflect.TypeOf(map[string]*Entry{}) {
|
||||
for key, val := range valObj.Elem().Field(i).Interface().(map[string]*Entry) {
|
||||
if emptyFields || val.Get() != "" {
|
||||
output = append(output, NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name + "[" + key + "]",
|
||||
Source: val.Source(),
|
||||
Value: val.Print(),
|
||||
})
|
||||
Source: source,
|
||||
Value: valObj.Elem().Field(i).String(),
|
||||
}
|
||||
} else if emptyFields {
|
||||
fieldMap[prefix+typeObj.Elem().Field(i).Name] = NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name + "[]",
|
||||
Source: source,
|
||||
}
|
||||
}
|
||||
if valObj.Elem().Field(i).Len() == 0 && emptyFields {
|
||||
output = append(output, NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name + "[]",
|
||||
})
|
||||
}
|
||||
} else if typeObj.Elem().Field(i).Type.Kind() == reflect.Map {
|
||||
mapIter := valObj.Elem().Field(i).MapRange()
|
||||
for mapIter.Next() {
|
||||
nestedOut := recursiveFields(mapIter.Value().Interface(), emptyFields, prefix+typeObj.Elem().Field(i).Name+"["+mapIter.Key().String()+"].")
|
||||
if len(nestedOut) == 0 {
|
||||
output = append(output, NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name + "[" + mapIter.Key().String() + "]",
|
||||
})
|
||||
} else {
|
||||
output = append(output, nestedOut...)
|
||||
}
|
||||
recursiveFields(mapIter.Value().Interface(),
|
||||
emptyFields, prefix+typeObj.Elem().Field(i).Name+"["+mapIter.Key().String()+"].", fieldMap, source)
|
||||
}
|
||||
if valObj.Elem().Field(i).Len() == 0 && emptyFields {
|
||||
output = append(output, NodeFields{
|
||||
fieldMap[prefix+typeObj.Elem().Field(i).Name] = NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name + "[]",
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if typeObj.Elem().Field(i).Type.Kind() == reflect.Ptr {
|
||||
nestedOut := recursiveFields(valObj.Elem().Field(i).Interface(), emptyFields, prefix+typeObj.Elem().Field(i).Name+".")
|
||||
output = append(output, nestedOut...)
|
||||
recursiveFields(valObj.Elem().Field(i).Interface(), emptyFields, prefix+typeObj.Elem().Field(i).Name+".", fieldMap, source)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/warewulf/warewulf/internal/pkg/util"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
type sortByName []NodeInfo
|
||||
type sortByName []NodeConf
|
||||
|
||||
func (a sortByName) Len() int { return len(a) }
|
||||
func (a sortByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a sortByName) Less(i, j int) bool { return a[i].Id.Print() < a[j].Id.Print() }
|
||||
|
||||
func (a sortByName) Less(i, j int) bool { return a[i].id < a[j].id }
|
||||
func GetUnsetVerbs() []string {
|
||||
return []string{"UNSET", "DELETE", "UNDEF", "undef", "--", "nil", "0.0.0.0"}
|
||||
}
|
||||
@@ -29,18 +26,18 @@ func GetUnsetVerbs() []string {
|
||||
*********/
|
||||
|
||||
/*
|
||||
Filter a given slice of NodeInfo against a given
|
||||
Filter a given slice of NodeConf against a given
|
||||
regular expression
|
||||
*/
|
||||
func FilterByName(set []NodeInfo, searchList []string) []NodeInfo {
|
||||
var ret []NodeInfo
|
||||
unique := make(map[string]NodeInfo)
|
||||
func FilterByName(set []NodeConf, searchList []string) []NodeConf {
|
||||
var ret []NodeConf
|
||||
unique := make(map[string]NodeConf)
|
||||
|
||||
if len(searchList) > 0 {
|
||||
for _, search := range searchList {
|
||||
for _, entry := range set {
|
||||
if match, _ := regexp.MatchString("^"+search+"$", entry.Id.Get()); match {
|
||||
unique[entry.Id.Get()] = entry
|
||||
if match, _ := regexp.MatchString("^"+search+"$", entry.id); match {
|
||||
unique[entry.id] = entry
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,7 +55,7 @@ func FilterByName(set []NodeInfo, searchList []string) []NodeInfo {
|
||||
/*
|
||||
Filter a given map of NodeConf against given regular expression.
|
||||
*/
|
||||
func FilterMapByName(inputMap map[string]*NodeConf, searchList []string) (retMap map[string]*NodeConf) {
|
||||
func FilterNodesByName(inputMap map[string]*NodeConf, searchList []string) (retMap map[string]*NodeConf) {
|
||||
retMap = map[string]*NodeConf{}
|
||||
if len(searchList) > 0 {
|
||||
for _, search := range searchList {
|
||||
@@ -72,443 +69,192 @@ func FilterMapByName(inputMap map[string]*NodeConf, searchList []string) (retMap
|
||||
return retMap
|
||||
}
|
||||
|
||||
/**********
|
||||
*
|
||||
* Sets
|
||||
*
|
||||
*********/
|
||||
|
||||
/*
|
||||
Set value. If argument is 'UNDEF', 'DELETE',
|
||||
'UNSET" or '--' the value is removed.
|
||||
N.B. the '--' might never ever happen as '--'
|
||||
is parsed out by cobra
|
||||
Filter a given map of NodeConf against given regular expression.
|
||||
*/
|
||||
func (ent *Entry) Set(val string) {
|
||||
if val == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if util.InSlice(GetUnsetVerbs(), val) {
|
||||
wwlog.Debug("Removing value for %v", *ent)
|
||||
ent.value = []string{""}
|
||||
} else {
|
||||
ent.value = []string{val}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Set bool
|
||||
*/
|
||||
func (ent *Entry) SetB(val bool) {
|
||||
if val {
|
||||
ent.value = []string{"true"}
|
||||
} else {
|
||||
ent.value = []string{"false"}
|
||||
}
|
||||
}
|
||||
|
||||
func (ent *Entry) SetSlice(val []string) {
|
||||
if len(val) == 0 {
|
||||
return
|
||||
} else if len(val) == 1 && val[0] == "" { // check also for an "empty" slice
|
||||
return
|
||||
}
|
||||
ent.isSlice = true
|
||||
if util.InSlice(GetUnsetVerbs(), val[0]) {
|
||||
ent.value = []string{}
|
||||
} else {
|
||||
ent.value = val
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Set alternative value
|
||||
*/
|
||||
func (ent *Entry) SetAlt(val string, from string) {
|
||||
if val == "" {
|
||||
return
|
||||
}
|
||||
ent.altvalue = []string{val}
|
||||
ent.from = from
|
||||
}
|
||||
|
||||
/*
|
||||
Sets alternative bool
|
||||
*/
|
||||
func (ent *Entry) SetAltB(val bool, from string) {
|
||||
if val {
|
||||
ent.altvalue = []string{"true"}
|
||||
ent.from = from
|
||||
} else {
|
||||
ent.altvalue = []string{"false"}
|
||||
ent.from = from
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Sets alternative slice
|
||||
*/
|
||||
func (ent *Entry) SetAltSlice(val []string, from string) {
|
||||
if len(val) == 0 {
|
||||
return
|
||||
}
|
||||
ent.isSlice = true
|
||||
ent.altvalue = append(ent.altvalue, val...)
|
||||
if ent.from == "" {
|
||||
ent.from = from
|
||||
} else {
|
||||
ent.from = ent.from + "," + from
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the default value of an entry.
|
||||
*/
|
||||
func (ent *Entry) SetDefault(val string) {
|
||||
if val == "" {
|
||||
return
|
||||
}
|
||||
ent.def = []string{val}
|
||||
}
|
||||
|
||||
/*
|
||||
Set the default entry as slice
|
||||
*/
|
||||
func (ent *Entry) SetDefaultSlice(val []string) {
|
||||
if len(val) == 0 {
|
||||
return
|
||||
}
|
||||
ent.isSlice = true
|
||||
ent.def = val
|
||||
}
|
||||
|
||||
/*
|
||||
Set default entry as bool
|
||||
*/
|
||||
func (ent *Entry) SetDefaultB(val bool) {
|
||||
if val {
|
||||
ent.def = []string{"true"}
|
||||
} else {
|
||||
ent.def = []string{"false"}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Remove a element from a slice
|
||||
*/
|
||||
func (ent *Entry) SliceRemoveElement(val string) {
|
||||
util.SliceRemoveElement(ent.value, val)
|
||||
}
|
||||
|
||||
/**********
|
||||
*
|
||||
* Gets
|
||||
*
|
||||
*********/
|
||||
/*
|
||||
Gets the the entry of the value in following order
|
||||
* node value if set
|
||||
* profile value if set
|
||||
* default value if set
|
||||
*/
|
||||
func (ent *Entry) Get() string {
|
||||
if len(ent.value) != 0 {
|
||||
return ent.value[0]
|
||||
}
|
||||
if len(ent.altvalue) != 0 {
|
||||
return ent.altvalue[0]
|
||||
}
|
||||
if len(ent.def) != 0 {
|
||||
return ent.def[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
/*
|
||||
Get the bool value of an entry.
|
||||
*/
|
||||
func (ent *Entry) GetB() bool {
|
||||
if len(ent.value) > 0 {
|
||||
return !(strings.ToLower(ent.value[0]) == "false" ||
|
||||
strings.ToLower(ent.value[0]) == "no" ||
|
||||
ent.value[0] == "0")
|
||||
} else if len(ent.altvalue) > 0 {
|
||||
return !(strings.ToLower(ent.altvalue[0]) == "false" ||
|
||||
strings.ToLower(ent.altvalue[0]) == "no" ||
|
||||
ent.altvalue[0] == "0")
|
||||
} else {
|
||||
return !(len(ent.def) == 0 ||
|
||||
strings.ToLower(ent.def[0]) == "false" ||
|
||||
strings.ToLower(ent.def[0]) == "no" ||
|
||||
ent.def[0] == "0")
|
||||
}
|
||||
}
|
||||
|
||||
// returns all negated elemets which are marked with ! as prefix
|
||||
// from a list
|
||||
func negList(list []string) (ret []string) {
|
||||
for _, tok := range list {
|
||||
if strings.HasPrefix(tok, "~") {
|
||||
ret = append(ret, tok[1:])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// clean a list from negated tokens
|
||||
func cleanList(list []string) (ret []string) {
|
||||
neg := negList(list)
|
||||
for _, listTok := range list {
|
||||
notNegate := true
|
||||
for _, negTok := range neg {
|
||||
if listTok == negTok || listTok == "~"+negTok {
|
||||
notNegate = false
|
||||
func FilterProfilesByName(inputMap map[string]*ProfileConf, searchList []string) (retMap map[string]*ProfileConf) {
|
||||
retMap = map[string]*ProfileConf{}
|
||||
if len(searchList) > 0 {
|
||||
for _, search := range searchList {
|
||||
for name, nConf := range inputMap {
|
||||
if match, _ := regexp.MatchString("^"+search+"$", name); match {
|
||||
retMap[name] = nConf
|
||||
}
|
||||
}
|
||||
}
|
||||
if notNegate {
|
||||
ret = append(ret, listTok)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
return retMap
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a string slice created from a comma separated list of the value.
|
||||
Creates an NodeConf with the given id. Doesn't add it to the database
|
||||
*/
|
||||
func (ent *Entry) GetSlice() []string {
|
||||
retval := append(ent.altvalue, ent.value...)
|
||||
if len(retval) != 0 {
|
||||
return cleanList(retval)
|
||||
}
|
||||
if len(ent.def) != 0 {
|
||||
return ent.def
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
/*
|
||||
Get the real value, not the alternative of default one.
|
||||
*/
|
||||
func (ent *Entry) GetReal() string {
|
||||
if len(ent.value) == 0 {
|
||||
return ""
|
||||
}
|
||||
return ent.value[0]
|
||||
}
|
||||
|
||||
/*
|
||||
Get the real value, not the alternative of default one.
|
||||
*/
|
||||
func (ent *Entry) GetRealSlice() []string {
|
||||
if len(ent.value) == 0 {
|
||||
return []string{}
|
||||
}
|
||||
return ent.value
|
||||
}
|
||||
|
||||
/*
|
||||
true if the entry has set a real value, else false.
|
||||
*/
|
||||
func (ent *Entry) GotReal() bool {
|
||||
return len(ent.value) != 0
|
||||
}
|
||||
|
||||
/*
|
||||
Get a pointer to the value
|
||||
*/
|
||||
func (ent *Entry) GetPointer() *string {
|
||||
ret := ent.Get()
|
||||
return &ret
|
||||
}
|
||||
|
||||
/*
|
||||
Try to get a int of a value, 0 if value can't be parsed!
|
||||
*/
|
||||
func (ent *Entry) GetInt() int {
|
||||
var ret int
|
||||
if len(ent.value) != 0 {
|
||||
ret, _ = strconv.Atoi(ent.value[0])
|
||||
} else if len(ent.altvalue) != 0 {
|
||||
ret, _ = strconv.Atoi(ent.altvalue[0])
|
||||
} else if len(ent.def) != 0 {
|
||||
ret, _ = strconv.Atoi(ent.def[0])
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
/*
|
||||
Ptr to int
|
||||
*/
|
||||
func (ent *Entry) GetIntPtr() *int {
|
||||
ret := ent.GetInt()
|
||||
return &ret
|
||||
}
|
||||
|
||||
/**********
|
||||
*
|
||||
* Misc
|
||||
*
|
||||
*********/
|
||||
/*
|
||||
Gets the the entry of the value in following order
|
||||
* node value if set
|
||||
* profile value if set
|
||||
* default value if set
|
||||
*/
|
||||
func (ent *Entry) Print() string {
|
||||
if !ent.isSlice {
|
||||
if len(ent.value) != 0 {
|
||||
return ent.value[0]
|
||||
}
|
||||
if len(ent.altvalue) != 0 {
|
||||
return ent.altvalue[0]
|
||||
}
|
||||
if len(ent.def) != 0 {
|
||||
return "(" + ent.def[0] + ")"
|
||||
}
|
||||
} else {
|
||||
var ret string
|
||||
if len(ent.value) != 0 || len(ent.altvalue) != 0 {
|
||||
combList := append(ent.altvalue, ent.value...)
|
||||
ret = strings.Join(cleanList(combList), ",")
|
||||
if len(negList(combList)) > 0 {
|
||||
ret += " ~{" + strings.Join(negList(combList), ",") + "}"
|
||||
}
|
||||
|
||||
}
|
||||
if ret != "" {
|
||||
return ret
|
||||
}
|
||||
if len(ent.def) != 0 {
|
||||
return "(" + strings.Join(ent.def, ",") + ")"
|
||||
}
|
||||
|
||||
}
|
||||
return "--"
|
||||
}
|
||||
|
||||
/*
|
||||
Was used for combined stringSlice
|
||||
|
||||
func (ent *Entry) PrintComb() string {
|
||||
if ent.value != "" && ent.altvalue != "" {
|
||||
return "[" + ent.value + "," + ent.altvalue + "]"
|
||||
}
|
||||
return ent.Print()
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
same as GetB()
|
||||
*/
|
||||
func (ent *Entry) PrintB() string {
|
||||
if len(ent.value) != 0 || len(ent.altvalue) != 0 {
|
||||
return fmt.Sprintf("%t", ent.GetB())
|
||||
}
|
||||
return fmt.Sprintf("(%t)", ent.GetB())
|
||||
}
|
||||
|
||||
/*
|
||||
Returns SUPERSEDED if value was set per node or
|
||||
per profile. Else -- is returned.
|
||||
*/
|
||||
func (ent *Entry) Source() string {
|
||||
if len(ent.value) != 0 && len(ent.altvalue) != 0 {
|
||||
return "SUPERSEDED"
|
||||
// return fmt.Sprintf("[%s]", ent.from)
|
||||
} else if ent.from == "" {
|
||||
return "--"
|
||||
}
|
||||
return ent.from
|
||||
}
|
||||
|
||||
/*
|
||||
Check if value was defined.
|
||||
*/
|
||||
func (ent *Entry) Defined() bool {
|
||||
if len(ent.value) != 0 {
|
||||
return true
|
||||
}
|
||||
if len(ent.altvalue) != 0 {
|
||||
return true
|
||||
}
|
||||
if len(ent.def) != 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
Create an empty node NodeConf
|
||||
*/
|
||||
func NewConf() (nodeconf NodeConf) {
|
||||
nodeconf.Ipmi = new(IpmiConf)
|
||||
nodeconf.Ipmi.Tags = map[string]string{}
|
||||
nodeconf.Kernel = new(KernelConf)
|
||||
nodeconf.NetDevs = make(map[string]*NetDevs)
|
||||
nodeconf.Tags = map[string]string{}
|
||||
func NewNode(id string) (nodeconf NodeConf) {
|
||||
nodeconf = EmptyNode()
|
||||
nodeconf.id = id
|
||||
return nodeconf
|
||||
}
|
||||
|
||||
/*
|
||||
Create an empty node NodeInfo
|
||||
Creates a ProfileConf but doesn't add it to the database.
|
||||
*/
|
||||
func NewInfo() (nodeInfo NodeInfo) {
|
||||
nodeInfo.Ipmi = new(IpmiEntry)
|
||||
nodeInfo.Ipmi.Tags = map[string]*Entry{}
|
||||
nodeInfo.Kernel = new(KernelEntry)
|
||||
nodeInfo.NetDevs = make(map[string]*NetDevEntry)
|
||||
nodeInfo.Tags = make(map[string]*Entry)
|
||||
return nodeInfo
|
||||
func EmptyProfile() (profileconf ProfileConf) {
|
||||
profileconf.Ipmi = new(IpmiConf)
|
||||
profileconf.Ipmi.Tags = map[string]string{}
|
||||
profileconf.Kernel = new(KernelConf)
|
||||
profileconf.NetDevs = make(map[string]*NetDevs)
|
||||
profileconf.Tags = map[string]string{}
|
||||
return profileconf
|
||||
}
|
||||
|
||||
/*
|
||||
Get a entry by its name
|
||||
Flattens out a NodeConf, which means if there are no explicit values in *IpmiConf
|
||||
or *KernelConf, these pointer will set to nil. This will remove something like
|
||||
ipmi: {} from nodes.conf
|
||||
*/
|
||||
func GetByName(node interface{}, name string) (string, error) {
|
||||
valEntry := reflect.ValueOf(node)
|
||||
entryField := valEntry.Elem().FieldByName(name)
|
||||
if entryField == (reflect.Value{}) {
|
||||
return "", fmt.Errorf("couldn't find field with name: %s", name)
|
||||
}
|
||||
if entryField.Type() != reflect.TypeOf(Entry{}) {
|
||||
return "", fmt.Errorf("field %s is not of type node.Entry", name)
|
||||
}
|
||||
myEntry := entryField.Interface().(Entry)
|
||||
return myEntry.Get(), nil
|
||||
func (info *NodeConf) Flatten() {
|
||||
recursiveFlatten(info)
|
||||
}
|
||||
|
||||
/*
|
||||
Check if the Netdev is empty, so has no values set
|
||||
Flattens out a ProfileConf, which means if there are no explicit values in *IpmiConf
|
||||
or *KernelConf, these pointer will set to nil. This will remove something like
|
||||
ipmi: {} from nodes.conf
|
||||
*/
|
||||
func ObjectIsEmpty(obj interface{}) bool {
|
||||
if obj == nil {
|
||||
return true
|
||||
}
|
||||
varType := reflect.TypeOf(obj)
|
||||
varVal := reflect.ValueOf(obj)
|
||||
if varType.Kind() == reflect.Ptr && !varVal.IsNil() {
|
||||
return ObjectIsEmpty(varVal.Elem().Interface())
|
||||
}
|
||||
if varVal.IsZero() {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < varType.NumField(); i++ {
|
||||
if varType.Field(i).Type.Kind() == reflect.String && !varVal.Field(i).IsZero() {
|
||||
val := varVal.Field(i).Interface().(string)
|
||||
if val != "" {
|
||||
return false
|
||||
func (info *ProfileConf) Flatten() {
|
||||
recursiveFlatten(info)
|
||||
}
|
||||
|
||||
// abstract flatten
|
||||
func recursiveFlatten(strct interface{}) {
|
||||
confType := reflect.TypeOf(strct)
|
||||
confVal := reflect.ValueOf(strct)
|
||||
for j := 0; j < confType.Elem().NumField(); j++ {
|
||||
if confVal.Elem().Field(j).Type().Kind() == reflect.Ptr && !confVal.Elem().Field(j).IsNil() {
|
||||
// iterate now over the ptr fields
|
||||
setToNil := true
|
||||
nestedType := reflect.TypeOf(confVal.Elem().Field(j).Interface())
|
||||
nestedVal := reflect.ValueOf(confVal.Elem().Field(j).Interface())
|
||||
for i := 0; i < nestedType.Elem().NumField(); i++ {
|
||||
if nestedType.Elem().Field(i).Type.Kind() == reflect.String &&
|
||||
nestedVal.Elem().Field(i).Interface().(string) != "" &&
|
||||
nestedVal.Elem().Field(i).Interface().(string) != undef {
|
||||
setToNil = false
|
||||
} else if nestedType.Elem().Field(i).Type == reflect.TypeOf([]string{}) &&
|
||||
len(nestedVal.Elem().Field(i).Interface().([]string)) != 0 {
|
||||
setToNil = false
|
||||
} else if nestedType.Elem().Field(i).Type == reflect.TypeOf(map[string]string{}) &&
|
||||
len(nestedVal.Elem().Field(i).Interface().(map[string]string)) != 0 {
|
||||
setToNil = false
|
||||
} else if nestedType.Elem().Field(i).Type == reflect.TypeOf(net.IP{}) {
|
||||
val := nestedVal.Elem().Field(i).Interface().(net.IP)
|
||||
if len(val) != 0 && !val.IsUnspecified() {
|
||||
setToNil = false
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if varType.Field(i).Type == reflect.TypeOf(map[string]string{}) {
|
||||
if len(varVal.Field(i).Interface().(map[string]string)) != 0 {
|
||||
return false
|
||||
if setToNil {
|
||||
confVal.Elem().Field(j).Set(reflect.Zero(confVal.Elem().Field(j).Type()))
|
||||
}
|
||||
} else if varType.Field(i).Type.Kind() == reflect.Ptr {
|
||||
if !ObjectIsEmpty(varVal.Field(i).Interface()) {
|
||||
return false
|
||||
} else if confType.Elem().Field(j).Anonymous {
|
||||
recursiveFlatten(confVal.Elem().Field(j).Addr().Interface())
|
||||
} else if confType.Elem().Field(j).IsExported() && confType.Elem().Field(j).Type.Kind() == reflect.String {
|
||||
if confVal.Elem().Field(j).Interface().(string) == undef {
|
||||
confVal.Elem().Field(j).SetString("")
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
Create a string slice, where every element represents a yaml entry, used for node/profile edit
|
||||
in order to get a summary of all available elements
|
||||
*/
|
||||
func UnmarshalConf(obj interface{}, excludeList []string) (lines []string) {
|
||||
objType := reflect.TypeOf(obj)
|
||||
// now iterate of every field
|
||||
for i := 0; i < objType.NumField(); i++ {
|
||||
if objType.Field(i).Tag.Get("comment") != "" {
|
||||
if ymlStr, ok := getYamlString(objType.Field(i), excludeList); ok {
|
||||
lines = append(lines, ymlStr...)
|
||||
}
|
||||
}
|
||||
if objType.Field(i).Type.Kind() == reflect.Ptr && objType.Field(i).Tag.Get("yaml") != "" {
|
||||
typeLine := objType.Field(i).Tag.Get("yaml")
|
||||
if len(strings.Split(typeLine, ",")) > 1 {
|
||||
typeLine = strings.Split(typeLine, ",")[0] + ":"
|
||||
}
|
||||
lines = append(lines, typeLine)
|
||||
nestedLine := UnmarshalConf(reflect.New(objType.Field(i).Type.Elem()).Elem().Interface(), excludeList)
|
||||
for _, ln := range nestedLine {
|
||||
lines = append(lines, " "+ln)
|
||||
}
|
||||
} else if objType.Field(i).Type.Kind() == reflect.Map && objType.Field(i).Type.Elem().Kind() == reflect.Ptr {
|
||||
typeLine := objType.Field(i).Tag.Get("yaml")
|
||||
if len(strings.Split(typeLine, ",")) > 1 {
|
||||
typeLine = strings.Split(typeLine, ",")[0] + ":"
|
||||
}
|
||||
lines = append(lines, typeLine, " element:")
|
||||
nestedLine := UnmarshalConf(reflect.New(objType.Field(i).Type.Elem().Elem()).Elem().Interface(), excludeList)
|
||||
for _, ln := range nestedLine {
|
||||
lines = append(lines, " "+ln)
|
||||
}
|
||||
}
|
||||
}
|
||||
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("comment") == "" && myType.Type.Kind() == reflect.String {
|
||||
return []string{""}, false
|
||||
}
|
||||
if myType.Type.Kind() == reflect.String {
|
||||
fieldType := myType.Tag.Get("type")
|
||||
if fieldType == "" {
|
||||
fieldType = "string"
|
||||
}
|
||||
ymlStr += ": " + fieldType
|
||||
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
|
||||
}
|
||||
|
||||
/*
|
||||
Getters for unexported fields
|
||||
*/
|
||||
|
||||
/*
|
||||
Returns the id of the node/profile
|
||||
*/
|
||||
func (node *NodeConf) Id() string {
|
||||
return node.id
|
||||
}
|
||||
|
||||
/*
|
||||
Returns if the node is a valid in the database
|
||||
*/
|
||||
func (node *NodeConf) Valid() bool {
|
||||
return node.valid
|
||||
}
|
||||
|
||||
/*
|
||||
Check if the netdev is the primary one
|
||||
*/
|
||||
func (dev *NetDevs) Primary() bool {
|
||||
return dev.primary
|
||||
}
|
||||
|
||||
@@ -9,37 +9,26 @@ import (
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
/****
|
||||
*
|
||||
* NODE MODIFIERS
|
||||
*
|
||||
****/
|
||||
|
||||
func (config *NodeYaml) AddNode(nodeID string) (NodeInfo, error) {
|
||||
var node NodeConf
|
||||
var n NodeInfo
|
||||
|
||||
/*
|
||||
Add a node with the given ID and return a pointer to it
|
||||
*/
|
||||
func (config *NodeYaml) AddNode(nodeID string) (*NodeConf, error) {
|
||||
newNode := NewNode(nodeID)
|
||||
wwlog.Verbose("Adding new node: %s", nodeID)
|
||||
|
||||
if _, ok := config.Nodes[nodeID]; ok {
|
||||
return n, errors.New("Nodename already exists: " + nodeID)
|
||||
return nil, errors.New("nodename already exists: " + nodeID)
|
||||
} else {
|
||||
config.Nodes[nodeID] = &newNode
|
||||
}
|
||||
|
||||
config.Nodes[nodeID] = &node
|
||||
config.Nodes[nodeID].Profiles = []string{"default"}
|
||||
config.Nodes[nodeID].NetDevs = make(map[string]*NetDevs)
|
||||
n.Id.Set(nodeID)
|
||||
n.Profiles.SetSlice([]string{"default"})
|
||||
n.NetDevs = make(map[string]*NetDevEntry)
|
||||
n.Ipmi = new(IpmiEntry)
|
||||
n.Kernel = new(KernelEntry)
|
||||
|
||||
return n, nil
|
||||
return &newNode, nil
|
||||
}
|
||||
|
||||
/*
|
||||
delete node with the given id
|
||||
*/
|
||||
func (config *NodeYaml) DelNode(nodeID string) error {
|
||||
if _, ok := config.Nodes[nodeID]; !ok {
|
||||
return errors.New("Nodename does not exist: " + nodeID)
|
||||
return errors.New("nodename does not exist: " + nodeID)
|
||||
}
|
||||
|
||||
wwlog.Verbose("Deleting node: %s", nodeID)
|
||||
@@ -49,79 +38,33 @@ func (config *NodeYaml) DelNode(nodeID string) error {
|
||||
}
|
||||
|
||||
/*
|
||||
update the node in the database
|
||||
Add a node with the given ID and return a pointer to it
|
||||
*/
|
||||
func (config *NodeYaml) NodeUpdate(node NodeInfo) error {
|
||||
nodeID := node.Id.Get()
|
||||
wwlog.Debug("updating node %s: %v", nodeID, node)
|
||||
if _, ok := config.Nodes[nodeID]; !ok {
|
||||
return errors.New("Nodename does not exist: " + nodeID)
|
||||
func (config *NodeYaml) AddProfile(profileId string) (*ProfileConf, error) {
|
||||
profile := NewProfile(profileId)
|
||||
wwlog.Verbose("adding new profile: %s", profileId)
|
||||
if _, ok := config.NodeProfiles[profileId]; ok {
|
||||
return nil, errors.New("profile already exists: " + profileId)
|
||||
} else {
|
||||
config.NodeProfiles[profileId] = &profile
|
||||
}
|
||||
// maps may have deleted elements which will not be updated
|
||||
// so we delete the node and call GetRealFrom on an empty NodeConv
|
||||
delete(config.Nodes, nodeID)
|
||||
config.Nodes[nodeID] = new(NodeConf)
|
||||
config.Nodes[nodeID].GetRealFrom(node)
|
||||
return nil
|
||||
}
|
||||
|
||||
/****
|
||||
*
|
||||
* PROFILE MODIFIERS
|
||||
*
|
||||
****/
|
||||
|
||||
func (config *NodeYaml) AddProfile(profileID string) (NodeInfo, error) {
|
||||
var node NodeConf
|
||||
var n NodeInfo
|
||||
|
||||
wwlog.Verbose("Adding new profile: %s", profileID)
|
||||
|
||||
if _, ok := config.NodeProfiles[profileID]; ok {
|
||||
return n, errors.New("Profile name already exists: " + profileID)
|
||||
}
|
||||
|
||||
config.NodeProfiles[profileID] = &node
|
||||
config.NodeProfiles[profileID].NetDevs = make(map[string]*NetDevs)
|
||||
n.Id.Set(profileID)
|
||||
n.NetDevs = make(map[string]*NetDevEntry)
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (config *NodeYaml) DelProfile(profileID string) error {
|
||||
if _, ok := config.NodeProfiles[profileID]; !ok {
|
||||
return errors.New("Profile does not exist: " + profileID)
|
||||
}
|
||||
|
||||
wwlog.Verbose("Deleting profile: %s", profileID)
|
||||
delete(config.NodeProfiles, profileID)
|
||||
|
||||
return nil
|
||||
return &profile, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Update the the config for the given profile so that it can unmarshalled.
|
||||
delete node with the given id
|
||||
*/
|
||||
func (config *NodeYaml) ProfileUpdate(profile NodeInfo) error {
|
||||
profileID := profile.Id.Get()
|
||||
|
||||
if _, ok := config.NodeProfiles[profileID]; !ok {
|
||||
return errors.New("Profile name does not exist: " + profileID)
|
||||
func (config *NodeYaml) DelProfile(nodeID string) error {
|
||||
if _, ok := config.Nodes[nodeID]; !ok {
|
||||
return errors.New("profile does not exist: " + nodeID)
|
||||
}
|
||||
// maps may have deleted elements which will not be updated
|
||||
// so we delete the profile and call GetRealFrom on an empty NodeConv
|
||||
delete(config.NodeProfiles, profileID)
|
||||
config.NodeProfiles[profileID] = new(NodeConf)
|
||||
config.NodeProfiles[profileID].GetRealFrom(profile)
|
||||
|
||||
wwlog.Verbose("deleting profile: %s", nodeID)
|
||||
delete(config.Nodes, nodeID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/****
|
||||
*
|
||||
* PERSISTENCE
|
||||
*
|
||||
****/
|
||||
/*
|
||||
Write the the NodeYaml to disk.
|
||||
*/
|
||||
@@ -144,8 +87,10 @@ func (config *NodeYaml) Persist() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Dump returns a YAML document representing the nodeDb
|
||||
// instance. Passes through any errors generated by yaml.Marshal.
|
||||
/*
|
||||
Dump returns a YAML document representing the nodeDb
|
||||
instance. Passes through any errors generated by yaml.Marshal.
|
||||
*/
|
||||
func (config *NodeYaml) Dump() ([]byte, error) {
|
||||
// flatten out profiles and nodes
|
||||
for _, val := range config.NodeProfiles {
|
||||
|
||||
@@ -1,397 +0,0 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/warewulf/warewulf/internal/pkg/util"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
/*
|
||||
Populates a NodeConf struct (the one which goes to disk) from a
|
||||
NodeInfo (which just lives in memory), with the values from all
|
||||
the underlying entries using GetReal, so just the explicit values
|
||||
go do disk.
|
||||
*/
|
||||
func (nodeConf *NodeConf) GetRealFrom(nodeInfo NodeInfo) {
|
||||
recursiveGetter(&nodeInfo, nodeConf, (*Entry).GetReal, (*Entry).GetRealSlice)
|
||||
}
|
||||
|
||||
/*
|
||||
Populates a NodeConf struct from a NodeInfo, with the combined
|
||||
values from the underlying entries using Get.
|
||||
*/
|
||||
func (nodeConf *NodeConf) GetFrom(nodeInfo NodeInfo) {
|
||||
recursiveGetter(&nodeInfo, nodeConf, (*Entry).Get, (*Entry).GetSlice)
|
||||
}
|
||||
|
||||
/*
|
||||
Abstract function which populates a NodeConf from the given NodeInfo
|
||||
via getter functions. Calls recursive itself for nested structures.
|
||||
Panics if the NodeConf has fields which are not type of string,[]string,map[string]*ptr
|
||||
*/
|
||||
func recursiveGetter(
|
||||
source, target interface{},
|
||||
getter func(*Entry) string,
|
||||
getterSlice func(*Entry) []string) {
|
||||
sourceValue := reflect.ValueOf(source)
|
||||
targetType := reflect.TypeOf(target)
|
||||
targetValue := reflect.ValueOf(target)
|
||||
if targetValue.Elem().Kind() == reflect.Struct && sourceValue.Elem().Kind() == reflect.Struct {
|
||||
for i := 0; i < targetType.Elem().NumField(); i++ {
|
||||
sourceValueMatched := sourceValue.Elem().FieldByName(targetType.Elem().Field(i).Name)
|
||||
if sourceValueMatched.IsValid() {
|
||||
if sourceValueMatched.Type() == reflect.TypeOf(Entry{}) {
|
||||
// get the fields which are part of the struct
|
||||
switch targetValue.Elem().Field(i).Type() {
|
||||
case reflect.TypeOf(""):
|
||||
newValue := (targetValue.Elem().Field(i).Addr().Interface()).(*string)
|
||||
source := sourceValueMatched.Interface().(Entry)
|
||||
*newValue = getter(&source)
|
||||
case reflect.TypeOf([]string{}):
|
||||
newValue := (targetValue.Elem().Field(i).Addr().Interface()).(*[]string)
|
||||
source := sourceValueMatched.Interface().(Entry)
|
||||
*newValue = getterSlice(&source)
|
||||
default:
|
||||
panic(fmt.Errorf("can't convert an Entry to %s", targetValue.Elem().Field(i).Type()))
|
||||
}
|
||||
} else if sourceValueMatched.Kind() == reflect.Ptr {
|
||||
// if we get a pointer, initialize if empty and then have a recursive call
|
||||
if targetValue.Elem().Field(i).IsZero() {
|
||||
targetValue.Elem().Field(i).Set(reflect.New(targetType.Elem().Field(i).Type.Elem()))
|
||||
}
|
||||
recursiveGetter(sourceValueMatched.Interface(), targetValue.Elem().Field(i).Interface(), getter, getterSlice)
|
||||
} else if sourceValueMatched.Type().Kind() == reflect.Map {
|
||||
if targetValue.Elem().Field(i).IsZero() {
|
||||
targetValue.Elem().Field(i).Set(reflect.MakeMap(targetType.Elem().Field(i).Type))
|
||||
}
|
||||
sourceIter := sourceValueMatched.MapRange()
|
||||
if sourceValueMatched.Type() == reflect.TypeOf(map[string]*Entry{}) {
|
||||
// go over a simple map with strings
|
||||
for sourceIter.Next() {
|
||||
if !targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).IsValid() {
|
||||
// Only write entries for which have real values. This matters for
|
||||
// tags, as empty map elements can be created without this check
|
||||
// The alternative was following check:
|
||||
// if ((sourceIter.Value().Interface()).(*Entry)).GotReal() {
|
||||
// but this one failed for tags in templates
|
||||
str := getter((sourceIter.Value().Interface()).(*Entry))
|
||||
if str != "" {
|
||||
targetValue.Elem().Field(i).SetMapIndex(sourceIter.Key(), reflect.ValueOf(str))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// now the complicated map which contains pointers to objects
|
||||
for sourceIter.Next() {
|
||||
if !targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).IsValid() {
|
||||
newPtr := reflect.New(targetType.Elem().Field(i).Type.Elem().Elem())
|
||||
targetValue.Elem().Field(i).SetMapIndex(sourceIter.Key(), newPtr)
|
||||
}
|
||||
recursiveGetter(sourceIter.Value().Interface(), targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).Interface(), getter, getterSlice)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Populates all fields of NodeInfo with Set from the
|
||||
values of NodeConf.
|
||||
*/
|
||||
func (node *NodeInfo) SetFrom(n *NodeConf) {
|
||||
setWrap := func(entr *Entry, val string, nameArg string) {
|
||||
entr.Set(val)
|
||||
}
|
||||
setSliceWrap := func(entr *Entry, val []string, nameArg string) {
|
||||
entr.SetSlice(val)
|
||||
}
|
||||
recursiveSetter(n, node, "", setWrap, setSliceWrap)
|
||||
}
|
||||
|
||||
/*
|
||||
Populates all fields of NodeInfo with SetAlt from the
|
||||
values of NodeConf. The string profileName is used to
|
||||
determine from which source/NodeInfo the entry came
|
||||
from.
|
||||
*/
|
||||
func (node *NodeInfo) SetAltFrom(n *NodeConf, profileName string) {
|
||||
recursiveSetter(n, node, 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)
|
||||
}
|
||||
recursiveSetter(n, node, "", setWrap, setSliceWrap)
|
||||
}
|
||||
|
||||
func SetDefFrom(source, target interface{}) {
|
||||
setWrap := func(entr *Entry, val string, nameArg string) {
|
||||
entr.SetDefault(val)
|
||||
}
|
||||
setSliceWrap := func(entr *Entry, val []string, nameArg string) {
|
||||
entr.SetDefaultSlice(val)
|
||||
}
|
||||
recursiveSetter(source, target, "", setWrap, setSliceWrap)
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Abstract function which populates a NodeInfo from a NodeConf via
|
||||
setter functions. Panics if other type than string, []string *ptr is used in NodeConf.
|
||||
*/
|
||||
func recursiveSetter(source, target interface{}, nameArg string, setter func(*Entry, string, string),
|
||||
setterSlice func(*Entry, []string, string)) {
|
||||
sourceValue := reflect.ValueOf(source)
|
||||
targetType := reflect.TypeOf(target)
|
||||
targetValue := reflect.ValueOf(target)
|
||||
if targetValue.Elem().Kind() == reflect.Struct && sourceValue.Elem().Kind() == reflect.Struct {
|
||||
for i := 0; i < targetType.Elem().NumField(); i++ {
|
||||
sourceValueMatched := sourceValue.Elem().FieldByName(targetType.Elem().Field(i).Name)
|
||||
if sourceValueMatched.IsValid() {
|
||||
if targetValue.Elem().Field(i).Type() == reflect.TypeOf(Entry{}) {
|
||||
// get the fields which are part of the struct
|
||||
switch sourceValueMatched.Type() {
|
||||
case reflect.TypeOf(""):
|
||||
setter(targetValue.Elem().Field(i).Addr().Interface().(*Entry), sourceValueMatched.String(), nameArg)
|
||||
case reflect.TypeOf([]string{}):
|
||||
setterSlice(targetValue.Elem().Field(i).Addr().Interface().(*Entry), sourceValueMatched.Interface().([]string), nameArg)
|
||||
default:
|
||||
panic(fmt.Errorf("can't convert an Entry to %s", targetValue.Elem().Field(i).Type()))
|
||||
}
|
||||
} else if sourceValueMatched.Kind() == reflect.Ptr {
|
||||
// if we get a pointer, initialize if empty and then have a recursive call
|
||||
if targetValue.Elem().Field(i).IsZero() {
|
||||
targetValue.Elem().Field(i).Set(reflect.New(targetType.Elem().Field(i).Type.Elem()))
|
||||
}
|
||||
recursiveSetter(sourceValueMatched.Interface(), targetValue.Elem().Field(i).Interface(), nameArg, setter, setterSlice)
|
||||
} else if sourceValueMatched.Type().Kind() == reflect.Map {
|
||||
if targetValue.Elem().Field(i).IsZero() {
|
||||
targetValue.Elem().Field(i).Set(reflect.MakeMap(targetType.Elem().Field(i).Type))
|
||||
}
|
||||
// delete a map element which is only in the target
|
||||
if targetValue.Elem().Field(i).Len() > 0 && targetValue.Elem().Field(i).Len() < 0 {
|
||||
sourceIter := sourceValueMatched.MapRange()
|
||||
targetIter := targetValue.Elem().Field(i).MapRange()
|
||||
for targetIter.Next() {
|
||||
sameKey := false
|
||||
for sourceIter.Next() {
|
||||
if sourceIter.Key() == targetIter.Key() {
|
||||
sameKey = true
|
||||
}
|
||||
}
|
||||
if !sameKey {
|
||||
targetValue.Elem().Field(i).SetMapIndex(targetIter.Key(), reflect.Value{})
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceIter := sourceValueMatched.MapRange()
|
||||
if sourceValueMatched.Type().Elem() == reflect.TypeOf("") {
|
||||
// go over a simple map with strings
|
||||
for sourceIter.Next() {
|
||||
if !targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).IsValid() {
|
||||
newEntr := new(Entry)
|
||||
setter(newEntr, sourceIter.Value().String(), nameArg)
|
||||
targetValue.Elem().Field(i).SetMapIndex(sourceIter.Key(), reflect.ValueOf(newEntr))
|
||||
} else {
|
||||
// update the entry with latest value
|
||||
if entry, ok := targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).Interface().(*Entry); ok {
|
||||
setter(entry, sourceIter.Value().String(), nameArg)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// now the complicated map which contains pointers to objects
|
||||
for sourceIter.Next() {
|
||||
if !targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).IsValid() {
|
||||
newPtr := reflect.New(targetType.Elem().Field(i).Type.Elem().Elem())
|
||||
targetValue.Elem().Field(i).SetMapIndex(sourceIter.Key(), newPtr)
|
||||
}
|
||||
recursiveSetter(sourceIter.Value().Interface(), targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).Interface(), nameArg, setter, setterSlice)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Flattens out a NodeConf, which means if there are no explicit values in *IpmiConf
|
||||
or *KernelConf, these pointer will set to nil. This will remove something like
|
||||
ipmi: {} from nodes.conf
|
||||
*/
|
||||
func (info *NodeConf) Flatten() {
|
||||
recursiveFlatten(info)
|
||||
}
|
||||
func recursiveFlatten(strct interface{}) {
|
||||
confType := reflect.TypeOf(strct)
|
||||
confVal := reflect.ValueOf(strct)
|
||||
for j := 0; j < confType.Elem().NumField(); j++ {
|
||||
if confVal.Elem().Field(j).Type().Kind() == reflect.Ptr && !confVal.Elem().Field(j).IsNil() {
|
||||
// iterate now over the ptr fields
|
||||
setToNil := true
|
||||
nestedType := reflect.TypeOf(confVal.Elem().Field(j).Interface())
|
||||
nestedVal := reflect.ValueOf(confVal.Elem().Field(j).Interface())
|
||||
for i := 0; i < nestedType.Elem().NumField(); i++ {
|
||||
if nestedType.Elem().Field(i).Type.Kind() == reflect.String &&
|
||||
nestedVal.Elem().Field(i).Interface().(string) != "" {
|
||||
setToNil = false
|
||||
} else if nestedType.Elem().Field(i).Type == reflect.TypeOf([]string{}) &&
|
||||
len(nestedVal.Elem().Field(i).Interface().([]string)) != 0 {
|
||||
setToNil = false
|
||||
} else if nestedType.Elem().Field(i).Type == reflect.TypeOf(map[string]string{}) &&
|
||||
len(nestedVal.Elem().Field(i).Interface().(map[string]string)) != 0 {
|
||||
setToNil = false
|
||||
}
|
||||
}
|
||||
if setToNil {
|
||||
confVal.Elem().Field(j).Set(reflect.Zero(confVal.Elem().Field(j).Type()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Create a string slice, where every element represents a yaml entry, used for node/profile edit
|
||||
in order to get a summary of all available elements
|
||||
*/
|
||||
func UnmarshalConf(obj interface{}, excludeList []string) (lines []string) {
|
||||
objType := reflect.TypeOf(obj)
|
||||
// now iterate of every field
|
||||
for i := 0; i < objType.NumField(); i++ {
|
||||
if objType.Field(i).Tag.Get("comment") != "" {
|
||||
if ymlStr, ok := getYamlString(objType.Field(i), excludeList); ok {
|
||||
lines = append(lines, ymlStr...)
|
||||
}
|
||||
}
|
||||
if objType.Field(i).Type.Kind() == reflect.Ptr && objType.Field(i).Tag.Get("yaml") != "" {
|
||||
typeLine := objType.Field(i).Tag.Get("yaml")
|
||||
if len(strings.Split(typeLine, ",")) > 1 {
|
||||
typeLine = strings.Split(typeLine, ",")[0] + ":"
|
||||
}
|
||||
lines = append(lines, typeLine)
|
||||
nestedLine := UnmarshalConf(reflect.New(objType.Field(i).Type.Elem()).Elem().Interface(), excludeList)
|
||||
for _, ln := range nestedLine {
|
||||
lines = append(lines, " "+ln)
|
||||
}
|
||||
} else if objType.Field(i).Type.Kind() == reflect.Map && objType.Field(i).Type.Elem().Kind() == reflect.Ptr {
|
||||
typeLine := objType.Field(i).Tag.Get("yaml")
|
||||
if len(strings.Split(typeLine, ",")) > 1 {
|
||||
typeLine = strings.Split(typeLine, ",")[0] + ":"
|
||||
}
|
||||
lines = append(lines, typeLine, " element:")
|
||||
nestedLine := UnmarshalConf(reflect.New(objType.Field(i).Type.Elem().Elem()).Elem().Interface(), excludeList)
|
||||
for _, ln := range nestedLine {
|
||||
lines = append(lines, " "+ln)
|
||||
}
|
||||
}
|
||||
}
|
||||
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("comment") == "" && myType.Type.Kind() == reflect.String {
|
||||
return []string{""}, false
|
||||
}
|
||||
if myType.Type.Kind() == reflect.String {
|
||||
fieldType := myType.Tag.Get("type")
|
||||
if fieldType == "" {
|
||||
fieldType = "string"
|
||||
}
|
||||
ymlStr += ": " + fieldType
|
||||
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++ {
|
||||
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
|
||||
}
|
||||
@@ -3,61 +3,93 @@ package node
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/*
|
||||
get node by its hardware/MAC address, return error otherwise
|
||||
Gets a node by its hardware(mac) address
|
||||
*/
|
||||
func (config *NodeYaml) FindByHwaddr(hwa string) (NodeInfo, error) {
|
||||
func (config *NodeYaml) FindByHwaddr(hwa string) (NodeConf, error) {
|
||||
if _, err := net.ParseMAC(hwa); err != nil {
|
||||
return NodeInfo{}, errors.New("invalid hardware address: " + hwa)
|
||||
return NodeConf{}, errors.New("invalid hardware address: " + hwa)
|
||||
}
|
||||
|
||||
var ret NodeInfo
|
||||
|
||||
n, _ := config.FindAllNodes()
|
||||
|
||||
for _, node := range n {
|
||||
nodeList, _ := config.FindAllNodes()
|
||||
for _, node := range nodeList {
|
||||
for _, dev := range node.NetDevs {
|
||||
if strings.EqualFold(dev.Hwaddr.Get(), hwa) {
|
||||
if strings.EqualFold(dev.Hwaddr, hwa) {
|
||||
return node, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret, errors.New("No nodes found with HW Addr: " + hwa)
|
||||
return NodeConf{}, ErrNotFound
|
||||
}
|
||||
|
||||
/*
|
||||
get node by its ip address, return error otherwise
|
||||
Find a node by its ip address
|
||||
*/
|
||||
func (config *NodeYaml) FindByIpaddr(ipaddr string) (NodeInfo, error) {
|
||||
if addr := net.ParseIP(ipaddr); addr == nil {
|
||||
return NodeInfo{}, errors.New("invalid IP:" + ipaddr)
|
||||
} else {
|
||||
ipaddr = addr.String()
|
||||
func (config *NodeYaml) FindByIpaddr(ipaddr string) (NodeConf, error) {
|
||||
addr := net.ParseIP(ipaddr)
|
||||
if addr == nil {
|
||||
return NodeConf{}, errors.New("invalid IP:" + ipaddr)
|
||||
}
|
||||
var ret NodeInfo
|
||||
|
||||
n, _ := config.FindAllNodes()
|
||||
|
||||
for _, node := range n {
|
||||
nodeList, err := config.FindAllNodes()
|
||||
if err != nil {
|
||||
return NodeConf{}, err
|
||||
}
|
||||
for _, node := range nodeList {
|
||||
for _, dev := range node.NetDevs {
|
||||
if dev.Ipaddr.Get() == ipaddr {
|
||||
if dev.Ipaddr.Equal(addr) {
|
||||
return node, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret, errors.New("No nodes found with IP Addr: " + ipaddr)
|
||||
return NodeConf{}, ErrNotFound
|
||||
}
|
||||
|
||||
// Return just the node list as string slice
|
||||
func (config *NodeYaml) NodeList() []string {
|
||||
ret := make([]string, len(config.Nodes))
|
||||
for key := range config.Nodes {
|
||||
ret = append(ret, key)
|
||||
/*
|
||||
Check if the Object is empty, has no valid values
|
||||
*/
|
||||
func ObjectIsEmpty(obj interface{}) bool {
|
||||
if obj == nil {
|
||||
return true
|
||||
}
|
||||
return ret
|
||||
varType := reflect.TypeOf(obj)
|
||||
varVal := reflect.ValueOf(obj)
|
||||
if varType.Kind() == reflect.Ptr && !varVal.IsNil() {
|
||||
return ObjectIsEmpty(varVal.Elem().Interface())
|
||||
}
|
||||
if varVal.IsZero() {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < varType.NumField(); i++ {
|
||||
if varType.Field(i).Type.Kind() == reflect.String && !varVal.Field(i).IsZero() {
|
||||
val := varVal.Field(i).Interface().(string)
|
||||
if val != "" {
|
||||
return false
|
||||
}
|
||||
} else if varType.Field(i).Type == reflect.TypeOf(map[string]string{}) {
|
||||
if len(varVal.Field(i).Interface().(map[string]string)) != 0 {
|
||||
return false
|
||||
}
|
||||
} else if varType.Field(i).Type.Kind() == reflect.Ptr {
|
||||
if !ObjectIsEmpty(varVal.Field(i).Interface()) {
|
||||
return false
|
||||
}
|
||||
} else if varType.Field(i).Type == reflect.TypeOf(net.IP{}) {
|
||||
val := varVal.Field(i).Interface().(net.IP)
|
||||
if len(val) != 0 && !val.IsUnspecified() {
|
||||
return false
|
||||
}
|
||||
} else if varType.Field(i).Type == reflect.TypeOf(net.IPMask{}) {
|
||||
o, b := varVal.Field(i).Interface().(net.IPMask).Size()
|
||||
if o == 0 && b == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package oci
|
||||
|
||||
import (
|
||||
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
|
||||
"path/filepath"
|
||||
|
||||
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
|
||||
)
|
||||
|
||||
var defaultCachePath = filepath.Join(warewulfconf.Get().Warewulf.DataStore, "/container-cache/oci/")
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package overlay
|
||||
|
||||
import (
|
||||
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
|
||||
"testing"
|
||||
|
||||
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
|
||||
)
|
||||
|
||||
var overlayImageTests = []struct {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package overlay
|
||||
|
||||
import (
|
||||
"net"
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
|
||||
"github.com/warewulf/warewulf/internal/pkg/node"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -35,31 +35,26 @@ type TemplateStruct struct {
|
||||
Warewulf warewulfconf.WarewulfConf
|
||||
Tftp warewulfconf.TFTPConf
|
||||
Paths warewulfconf.BuildConfig
|
||||
AllNodes []node.NodeInfo
|
||||
AllNodes []node.NodeConf
|
||||
node.NodeConf
|
||||
// backward compatiblity
|
||||
Container string
|
||||
ThisNode *node.NodeInfo
|
||||
ThisNode *node.NodeConf
|
||||
}
|
||||
|
||||
/*
|
||||
Initialize an TemplateStruct with the given node.NodeInfo
|
||||
*/
|
||||
func InitStruct(nodeInfo *node.NodeInfo) TemplateStruct {
|
||||
func InitStruct(nodeData node.NodeConf) (TemplateStruct, error) {
|
||||
var tstruct TemplateStruct
|
||||
tstruct.ThisNode = nodeInfo
|
||||
hostname, _ := os.Hostname()
|
||||
tstruct.BuildHost = hostname
|
||||
controller := warewulfconf.Get()
|
||||
nodeDB, err := node.New()
|
||||
if err != nil {
|
||||
wwlog.Warn("Problems opening nodes.conf: %s", err)
|
||||
return tstruct, err
|
||||
}
|
||||
tstruct.AllNodes, err = nodeDB.FindAllNodes()
|
||||
if err != nil {
|
||||
wwlog.Warn("couldn't get all nodes: %s", err)
|
||||
}
|
||||
// init some convenience vars
|
||||
tstruct.Id = nodeInfo.Id.Get()
|
||||
tstruct.Hostname = nodeInfo.Id.Get()
|
||||
tstruct.ThisNode = &nodeData
|
||||
tstruct.Nfs = *controller.NFS
|
||||
tstruct.Ssh = *controller.SSH
|
||||
tstruct.Dhcp = *controller.DHCP
|
||||
@@ -70,31 +65,30 @@ func InitStruct(nodeInfo *node.NodeInfo) TemplateStruct {
|
||||
tstruct.Ipaddr6 = controller.Ipaddr6
|
||||
tstruct.Netmask = controller.Netmask
|
||||
tstruct.Network = controller.Network
|
||||
netaddrStruct := net.IPNet{IP: net.ParseIP(controller.Network), Mask: net.IPMask(net.ParseIP(controller.Netmask))}
|
||||
tstruct.NetworkCIDR = netaddrStruct.String()
|
||||
if controller.Ipaddr6 != "" {
|
||||
tstruct.Ipv6 = true
|
||||
} else {
|
||||
tstruct.Ipv6 = false
|
||||
allNodes, err := nodeDB.FindAllNodes()
|
||||
if err != nil {
|
||||
return tstruct, err
|
||||
}
|
||||
hostname, _ := os.Hostname()
|
||||
tstruct.BuildHost = hostname
|
||||
// init some convenience vars
|
||||
tstruct.Id = nodeData.Id()
|
||||
tstruct.Hostname = nodeData.Id()
|
||||
// Backwards compatibility for templates using "Keys"
|
||||
tstruct.AllNodes = allNodes
|
||||
dt := time.Now()
|
||||
tstruct.BuildTime = dt.Format("01-02-2006 15:04:05 MST")
|
||||
tstruct.BuildTimeUnix = strconv.FormatInt(dt.Unix(), 10)
|
||||
tstruct.NodeConf.Tags = map[string]string{}
|
||||
tstruct.NodeConf.GetFrom(*nodeInfo)
|
||||
// FIXME: Set ipCIDR address at this point, will fail with
|
||||
// invalid ipv4 addr
|
||||
for _, network := range tstruct.NodeConf.NetDevs {
|
||||
ipCIDR := net.IPNet{
|
||||
IP: net.ParseIP(network.Ipaddr),
|
||||
Mask: net.IPMask(net.ParseIP(network.Netmask))}
|
||||
network.IpCIDR = ipCIDR.String()
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
dec := gob.NewDecoder(&buf)
|
||||
err = enc.Encode(nodeData)
|
||||
if err != nil {
|
||||
return tstruct, err
|
||||
}
|
||||
// backward compatibilty
|
||||
tstruct.Container = tstruct.NodeConf.ContainerName
|
||||
|
||||
return tstruct
|
||||
err = dec.Decode(&tstruct)
|
||||
if err != nil {
|
||||
return tstruct, err
|
||||
}
|
||||
return tstruct, nil
|
||||
|
||||
}
|
||||
|
||||
@@ -104,7 +104,9 @@ func templateContainerFileInclude(containername string, filepath string) string
|
||||
return strings.TrimSuffix(string(content), "\n")
|
||||
}
|
||||
|
||||
func createIgnitionJson(node *node.NodeInfo) string {
|
||||
// don't return an error as we use this function for template evaluation,
|
||||
// so error will turn up there as the return string
|
||||
func createIgnitionJson(node *node.NodeConf) string {
|
||||
conf, rep, err := node.GetConfig()
|
||||
if len(conf.Storage.Disks) == 0 && len(conf.Storage.Filesystems) == 0 {
|
||||
wwlog.Debug("no disks or filesystems present, don't create a json object")
|
||||
|
||||
@@ -28,20 +28,20 @@ var (
|
||||
/*
|
||||
Build all overlays for a node
|
||||
*/
|
||||
func BuildAllOverlays(nodes []node.NodeInfo) error {
|
||||
func BuildAllOverlays(nodes []node.NodeConf) error {
|
||||
for _, n := range nodes {
|
||||
|
||||
sysOverlays := n.SystemOverlay.GetSlice()
|
||||
wwlog.Info("Building system overlays for %s: [%s]", n.Id.Get(), strings.Join(sysOverlays, ", "))
|
||||
sysOverlays := n.SystemOverlay
|
||||
wwlog.Info("Building system overlays for %s: [%s]", n.Id(), strings.Join(sysOverlays, ", "))
|
||||
err := BuildOverlay(n, "system", sysOverlays)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not build system overlays %v for node %s", sysOverlays, n.Id.Get())
|
||||
return errors.Wrapf(err, "could not build system overlays %v for node %s", sysOverlays, n.Id())
|
||||
}
|
||||
runOverlays := n.RuntimeOverlay.GetSlice()
|
||||
wwlog.Info("Building runtime overlays for %s: [%s]", n.Id.Get(), strings.Join(runOverlays, ", "))
|
||||
runOverlays := n.RuntimeOverlay
|
||||
wwlog.Info("Building runtime overlays for %s: [%s]", n.Id(), strings.Join(runOverlays, ", "))
|
||||
err = BuildOverlay(n, "runtime", runOverlays)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not build runtime overlays %v for node %s", runOverlays, n.Id.Get())
|
||||
return errors.Wrapf(err, "could not build runtime overlays %v for node %s", runOverlays, n.Id())
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,16 +50,15 @@ func BuildAllOverlays(nodes []node.NodeInfo) error {
|
||||
|
||||
// TODO: Add an Overlay Delete for both sourcedir and image
|
||||
|
||||
func BuildSpecificOverlays(nodes []node.NodeInfo, overlayNames []string) error {
|
||||
func BuildSpecificOverlays(nodes []node.NodeConf, overlayNames []string) error {
|
||||
for _, n := range nodes {
|
||||
wwlog.Info("Building overlay for %s: %v", n.Id.Get(), overlayNames)
|
||||
wwlog.Info("Building overlay for %s: %v", n, overlayNames)
|
||||
for _, overlayName := range overlayNames {
|
||||
err := BuildOverlay(n, "", []string{overlayName})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not build overlay %s for node %s", overlayName, n.Id.Get())
|
||||
return errors.Wrapf(err, "could not build overlay %s for node %s", overlayName, n.Id())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -68,10 +67,8 @@ func BuildSpecificOverlays(nodes []node.NodeInfo, overlayNames []string) error {
|
||||
Build overlay for the host, so no argument needs to be given
|
||||
*/
|
||||
func BuildHostOverlay() error {
|
||||
host := node.NewInfo()
|
||||
hostname, _ := os.Hostname()
|
||||
host.Id.Set(hostname)
|
||||
|
||||
hostData := node.NewNode(hostname)
|
||||
wwlog.Info("Building overlay for %s: host", hostname)
|
||||
hostdir := OverlaySourceDir("host")
|
||||
stats, err := os.Stat(hostdir)
|
||||
@@ -81,7 +78,7 @@ func BuildHostOverlay() error {
|
||||
if !(stats.Mode() == os.FileMode(0750|os.ModeDir) || stats.Mode() == os.FileMode(0700|os.ModeDir)) {
|
||||
wwlog.SecWarn("Permissions of host overlay dir %s are %s (750 is considered as secure)", hostdir, stats.Mode())
|
||||
}
|
||||
return BuildOverlayIndir(host, []string{"host"}, "/")
|
||||
return BuildOverlayIndir(hostData, []string{"host"}, "/")
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -126,14 +123,14 @@ func OverlayInit(overlayName string) error {
|
||||
/*
|
||||
Build the given overlays for a node and create a Image for them
|
||||
*/
|
||||
func BuildOverlay(nodeInfo node.NodeInfo, context string, overlayNames []string) error {
|
||||
func BuildOverlay(nodeConf node.NodeConf, context string, overlayNames []string) error {
|
||||
if len(overlayNames) == 0 && context == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// create the dir where the overlay images will reside
|
||||
name := fmt.Sprintf("overlay %s/%v", nodeInfo.Id.Get(), overlayNames)
|
||||
overlayImage := OverlayImage(nodeInfo.Id.Get(), context, overlayNames)
|
||||
name := fmt.Sprintf("overlay %s/%v", nodeConf.Id(), overlayNames)
|
||||
overlayImage := OverlayImage(nodeConf.Id(), context, overlayNames)
|
||||
overlayImageDir := path.Dir(overlayImage)
|
||||
|
||||
err := os.MkdirAll(overlayImageDir, 0750)
|
||||
@@ -151,7 +148,7 @@ func BuildOverlay(nodeInfo node.NodeInfo, context string, overlayNames []string)
|
||||
|
||||
wwlog.Debug("Created temporary directory for %s: %s", name, buildDir)
|
||||
|
||||
err = BuildOverlayIndir(nodeInfo, overlayNames, buildDir)
|
||||
err = BuildOverlayIndir(nodeConf, overlayNames, buildDir)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Failed to generate files for %s", name)
|
||||
}
|
||||
@@ -175,7 +172,7 @@ func BuildOverlay(nodeInfo node.NodeInfo, context string, overlayNames []string)
|
||||
Build the given overlays for a node in the given directory. If the given does not
|
||||
exists it will be created.
|
||||
*/
|
||||
func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir string) error {
|
||||
func BuildOverlayIndir(nodeData node.NodeConf, overlayNames []string, outputDir string) error {
|
||||
if len(overlayNames) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -190,9 +187,9 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir
|
||||
// Temporarily set umask to 0000, so directories in the overlay retain permissions
|
||||
defer syscall.Umask(syscall.Umask(0))
|
||||
|
||||
wwlog.Verbose("Processing node/overlay: %s/%s", nodeInfo.Id.Get(), strings.Join(overlayNames, "-"))
|
||||
wwlog.Verbose("Processing node/overlay: %s/%s", nodeData.Id(), strings.Join(overlayNames, "-"))
|
||||
for _, overlayName := range overlayNames {
|
||||
wwlog.Verbose("Building overlay %s for node %s in %s", overlayName, nodeInfo.Id.Get(), outputDir)
|
||||
wwlog.Verbose("Building overlay %s for node %s in %s", overlayName, nodeData.Id(), outputDir)
|
||||
overlaySourceDir := OverlaySourceDir(overlayName)
|
||||
wwlog.Debug("Changing directory to OverlayDir: %s", overlaySourceDir)
|
||||
err := os.Chdir(overlaySourceDir)
|
||||
@@ -223,7 +220,10 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir
|
||||
wwlog.Debug("Created directory in overlay: %s", location)
|
||||
|
||||
} else if filepath.Ext(location) == ".ww" {
|
||||
tstruct := InitStruct(&nodeInfo)
|
||||
tstruct, err := InitStruct(nodeData)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("failed to initial data for %s", nodeData.Id()))
|
||||
}
|
||||
tstruct.BuildSource = path.Join(overlaySourceDir, location)
|
||||
wwlog.Verbose("Evaluating overlay template file: %s", location)
|
||||
destFile := strings.TrimSuffix(location, ".ww")
|
||||
|
||||
@@ -491,9 +491,8 @@ func SplitValidPaths(input, delim string) []string {
|
||||
return (ret)
|
||||
}
|
||||
|
||||
func IncrementIPv4(start string, inc uint) string {
|
||||
ip_start := net.ParseIP(start)
|
||||
ipv4 := ip_start.To4()
|
||||
func IncrementIPv4(start net.IP, inc uint) net.IP {
|
||||
ipv4 := start.To4()
|
||||
v4_int := uint(ipv4[0])<<24 + uint(ipv4[1])<<16 + uint(ipv4[2])<<8 + uint(ipv4[3])
|
||||
v4_int += inc
|
||||
v4_o3 := byte(v4_int & 0xFF)
|
||||
@@ -501,7 +500,7 @@ func IncrementIPv4(start string, inc uint) string {
|
||||
v4_o1 := byte((v4_int >> 16) & 0xFF)
|
||||
v4_o0 := byte((v4_int >> 24) & 0xFF)
|
||||
ipv4_new := net.IPv4(v4_o0, v4_o1, v4_o2, v4_o3)
|
||||
return ipv4_new.String()
|
||||
return ipv4_new
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -12,7 +12,8 @@ import (
|
||||
|
||||
type nodeDB struct {
|
||||
lock sync.RWMutex
|
||||
NodeInfo map[string]node.NodeInfo
|
||||
NodeInfo map[string]node.NodeConf
|
||||
yml node.NodeYaml
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -25,55 +26,62 @@ func LoadNodeDB() error {
|
||||
return loadNodeDB()
|
||||
}
|
||||
|
||||
func loadNodeDB() error {
|
||||
TmpMap := make(map[string]node.NodeInfo)
|
||||
func loadNodeDB() (err error) {
|
||||
TmpMap := make(map[string]node.NodeConf)
|
||||
|
||||
DB, err := node.New()
|
||||
db.yml, err = node.New()
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
nodes, err := DB.FindAllNodes()
|
||||
nodes, err := db.yml.FindAllNodes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, n := range nodes {
|
||||
if n.Discoverable {
|
||||
continue
|
||||
}
|
||||
for _, netdev := range n.NetDevs {
|
||||
hwaddr := strings.ToLower(netdev.Hwaddr.Get())
|
||||
hwaddr := strings.ToLower(netdev.Hwaddr)
|
||||
TmpMap[hwaddr] = n
|
||||
}
|
||||
}
|
||||
|
||||
db.NodeInfo = TmpMap
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetNode(val string) (node.NodeInfo, error) {
|
||||
func GetNode(val string) (node node.NodeConf, err error) {
|
||||
db.lock.RLock()
|
||||
defer db.lock.RUnlock()
|
||||
return getNode(val)
|
||||
}
|
||||
|
||||
func getNode(val string) (node.NodeInfo, error) {
|
||||
func getNode(val string) (node node.NodeConf, err error) {
|
||||
|
||||
if _, ok := db.NodeInfo[val]; ok {
|
||||
|
||||
return db.NodeInfo[val], nil
|
||||
}
|
||||
|
||||
var empty node.NodeInfo
|
||||
return empty, errors.New("No node found")
|
||||
return node, errors.New("No node found")
|
||||
}
|
||||
|
||||
func GetNodeOrSetDiscoverable(hwaddr string) (node.NodeInfo, error) {
|
||||
func GetNodeOrSetDiscoverable(hwaddr string) (node.NodeConf, error) {
|
||||
db.lock.Lock()
|
||||
defer db.lock.Unlock()
|
||||
return getNodeOrSetDiscoverable(hwaddr)
|
||||
}
|
||||
|
||||
func getNodeOrSetDiscoverable(hwaddr string) (node.NodeInfo, error) {
|
||||
func PersistDb() error {
|
||||
db.lock.Lock()
|
||||
defer db.lock.Unlock()
|
||||
return db.yml.Persist()
|
||||
}
|
||||
|
||||
func getNodeOrSetDiscoverable(hwaddr string) (node.NodeConf, error) {
|
||||
// NOTE: since discoverable nodes will write an updated DB to file and then
|
||||
// reload, it is not enough to lock individual reads from the DB
|
||||
// to ensure the condition on which the node is updated is still satisfied
|
||||
@@ -89,30 +97,23 @@ func getNodeOrSetDiscoverable(hwaddr string) (node.NodeInfo, error) {
|
||||
|
||||
wwlog.WarnExc(err, "%s (node not configured)", hwaddr)
|
||||
|
||||
config, err := node.New()
|
||||
if err != nil {
|
||||
return n, errors.Wrapf(err, "%s (failed to read node configuration file)", hwaddr)
|
||||
}
|
||||
|
||||
_n, netdev, err := config.FindDiscoverableNode()
|
||||
nodeId, netdev, err := db.yml.FindDiscoverableNode()
|
||||
if err != nil {
|
||||
// NOTE: this is taken as there is no discoverable node, so return the
|
||||
// empty one
|
||||
return n, nil
|
||||
}
|
||||
|
||||
_n.NetDevs[netdev].Hwaddr.Set(hwaddr)
|
||||
_n.Discoverable.SetB(false)
|
||||
|
||||
// NOTE: errors here should return the empty node if the state cannot
|
||||
// be saved and re-loaded, since subsequent requests will be made on invalid
|
||||
// assumption that the database is up to date.
|
||||
err = config.NodeUpdate(_n)
|
||||
discoverdNode, err := getNode(nodeId)
|
||||
if err != nil {
|
||||
return n, errors.Wrapf(err, "%s (failed to set node configuration)", hwaddr)
|
||||
return n, err
|
||||
}
|
||||
// update data on disk and in memory
|
||||
discoverdNode.NetDevs[netdev].Hwaddr = hwaddr
|
||||
db.yml.Nodes[discoverdNode.Id()].NetDevs[netdev].Hwaddr = hwaddr
|
||||
discoverdNode.Discoverable = false
|
||||
db.yml.Nodes[discoverdNode.Id()].Discoverable = false
|
||||
|
||||
err = config.Persist()
|
||||
err = PersistDb()
|
||||
if err != nil {
|
||||
return n, errors.Wrapf(err, "%s (failed to persist node configuration)", hwaddr)
|
||||
}
|
||||
@@ -129,5 +130,5 @@ func getNodeOrSetDiscoverable(hwaddr string) (node.NodeInfo, error) {
|
||||
wwlog.Serv("%s (node automatically configured)", hwaddr)
|
||||
|
||||
// return the discovered node
|
||||
return _n, nil
|
||||
return discoverdNode, nil
|
||||
}
|
||||
|
||||
@@ -73,21 +73,21 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
// TODO: when module version is upgraded to go1.18, should be 'any' type
|
||||
var tmpl_data interface{}
|
||||
|
||||
node, err := GetNodeOrSetDiscoverable(rinfo.hwaddr)
|
||||
if err != nil {
|
||||
remoteNode, err := GetNodeOrSetDiscoverable(rinfo.hwaddr)
|
||||
if err != nil && err != node.ErrNoUnconfigured {
|
||||
wwlog.ErrorExc(err, "")
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
if node.AssetKey.Defined() && node.AssetKey.Get() != rinfo.assetkey {
|
||||
if remoteNode.AssetKey != "" && remoteNode.AssetKey != rinfo.assetkey {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
wwlog.Denied("Incorrect asset key for node: %s", node.Id.Get())
|
||||
updateStatus(node.Id.Get(), status_stage, "BAD_ASSET", rinfo.ipaddr)
|
||||
wwlog.Denied("Incorrect asset key for node: %s", remoteNode.Id())
|
||||
updateStatus(remoteNode.Id(), status_stage, "BAD_ASSET", rinfo.ipaddr)
|
||||
return
|
||||
}
|
||||
|
||||
if !node.Id.Defined() {
|
||||
if !remoteNode.Valid() {
|
||||
wwlog.Error("%s (unknown/unconfigured node)", rinfo.hwaddr)
|
||||
if rinfo.stage == "ipxe" {
|
||||
stage_file = path.Join(conf.Paths.Sysconfdir, "/warewulf/ipxe/unconfigured.ipxe")
|
||||
@@ -96,45 +96,44 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
} else if rinfo.stage == "ipxe" {
|
||||
stage_file = path.Join(conf.Paths.Sysconfdir, "warewulf/ipxe/"+node.Ipxe.Get()+".ipxe")
|
||||
tstruct := overlay.InitStruct(&node)
|
||||
stage_file = path.Join(conf.Paths.Sysconfdir, "warewulf/ipxe/"+remoteNode.Ipxe+".ipxe")
|
||||
tmpl_data = templateVars{
|
||||
Id: node.Id.Get(),
|
||||
Cluster: node.ClusterName.Get(),
|
||||
Fqdn: node.Id.Get(),
|
||||
Id: remoteNode.Id(),
|
||||
Cluster: remoteNode.ClusterName,
|
||||
Fqdn: remoteNode.Id(),
|
||||
Ipaddr: conf.Ipaddr,
|
||||
Port: strconv.Itoa(conf.Warewulf.Port),
|
||||
Hostname: node.Id.Get(),
|
||||
Hostname: remoteNode.Id(),
|
||||
Hwaddr: rinfo.hwaddr,
|
||||
ContainerName: node.ContainerName.Get(),
|
||||
KernelArgs: node.Kernel.Args.Get(),
|
||||
KernelOverride: node.Kernel.Override.Get(),
|
||||
NetDevs: tstruct.NetDevs,
|
||||
Tags: tstruct.Tags}
|
||||
ContainerName: remoteNode.ContainerName,
|
||||
KernelArgs: remoteNode.Kernel.Args,
|
||||
KernelOverride: remoteNode.Kernel.Override,
|
||||
NetDevs: remoteNode.NetDevs,
|
||||
Tags: remoteNode.Tags}
|
||||
} else if rinfo.stage == "kernel" {
|
||||
if node.Kernel.Override.Defined() {
|
||||
stage_file = kernel.KernelImage(node.Kernel.Override.Get())
|
||||
} else if node.ContainerName.Defined() {
|
||||
stage_file, _, err = kernel.FindKernel(container.RootFsDir(node.ContainerName.Get()))
|
||||
if remoteNode.Kernel.Override != "" {
|
||||
stage_file = kernel.KernelImage(remoteNode.Kernel.Override)
|
||||
} else if remoteNode.ContainerName != "" {
|
||||
stage_file, _, err = kernel.FindKernel(container.RootFsDir(remoteNode.ContainerName))
|
||||
if err != nil {
|
||||
wwlog.Error("No kernel found for container %s: %s", node.ContainerName.Get(), err)
|
||||
wwlog.Error("No kernel found for container %s: %s", remoteNode.ContainerName, err)
|
||||
}
|
||||
} else {
|
||||
wwlog.Warn("No kernel version set for node %s", node.Id.Get())
|
||||
wwlog.Warn("No kernel version set for node %s", remoteNode.Id())
|
||||
}
|
||||
|
||||
} else if rinfo.stage == "kmods" {
|
||||
if node.Kernel.Override.Defined() {
|
||||
stage_file = kernel.KmodsImage(node.Kernel.Override.Get())
|
||||
if remoteNode.Kernel.Override != "" {
|
||||
stage_file = kernel.KmodsImage(remoteNode.Kernel.Override)
|
||||
} else {
|
||||
wwlog.Warn("No kernel override modules set for node %s", node.Id.Get())
|
||||
wwlog.Warn("No kernel override modules set for node %s", remoteNode.Id())
|
||||
}
|
||||
|
||||
} else if rinfo.stage == "container" {
|
||||
if node.ContainerName.Defined() {
|
||||
stage_file = container.ImageFile(node.ContainerName.Get())
|
||||
if remoteNode.ContainerName != "" {
|
||||
stage_file = container.ImageFile(remoteNode.ContainerName)
|
||||
} else {
|
||||
wwlog.Warn("No container set for node %s", node.Id.Get())
|
||||
wwlog.Warn("No container set for node %s", remoteNode.Id())
|
||||
}
|
||||
|
||||
} else if rinfo.stage == "system" || rinfo.stage == "runtime" {
|
||||
@@ -147,7 +146,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
context = rinfo.stage
|
||||
}
|
||||
stage_file, err = getOverlayFile(
|
||||
node,
|
||||
remoteNode,
|
||||
context,
|
||||
request_overlays,
|
||||
conf.Warewulf.AutobuildOverlays)
|
||||
@@ -164,7 +163,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
} else if rinfo.stage == "efiboot" {
|
||||
wwlog.Debug("requested method: %s", req.Method)
|
||||
containerName := node.ContainerName.Get()
|
||||
containerName := remoteNode.ContainerName
|
||||
switch rinfo.efifile {
|
||||
case "shim.efi":
|
||||
stage_file = container.ShimFind(containerName)
|
||||
@@ -182,20 +181,17 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
case "grub.cfg":
|
||||
stage_file = path.Join(conf.Paths.Sysconfdir, "warewulf/grub/grub.cfg.ww")
|
||||
tstruct := overlay.InitStruct(&node)
|
||||
tmpl_data = templateVars{
|
||||
Id: node.Id.Get(),
|
||||
Cluster: node.ClusterName.Get(),
|
||||
Fqdn: node.Id.Get(),
|
||||
Id: remoteNode.Id(),
|
||||
Cluster: remoteNode.ClusterName,
|
||||
Fqdn: remoteNode.Id(),
|
||||
Ipaddr: conf.Ipaddr,
|
||||
Port: strconv.Itoa(conf.Warewulf.Port),
|
||||
Hostname: node.Id.Get(),
|
||||
Hostname: remoteNode.Id(),
|
||||
Hwaddr: rinfo.hwaddr,
|
||||
ContainerName: node.ContainerName.Get(),
|
||||
KernelArgs: node.Kernel.Args.Get(),
|
||||
KernelOverride: node.Kernel.Override.Get(),
|
||||
NetDevs: tstruct.NetDevs,
|
||||
Tags: tstruct.Tags}
|
||||
ContainerName: remoteNode.ContainerName,
|
||||
KernelArgs: remoteNode.Kernel.Args,
|
||||
KernelOverride: remoteNode.Kernel.Override}
|
||||
if stage_file == "" {
|
||||
wwlog.ErrorExc(fmt.Errorf("could't find grub.cfg template"), containerName)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
@@ -205,36 +201,36 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
wwlog.ErrorExc(fmt.Errorf("could't find efiboot file: %s", rinfo.efifile), "")
|
||||
}
|
||||
} else if rinfo.stage == "shim" {
|
||||
if node.ContainerName.Defined() {
|
||||
stage_file = container.ShimFind(node.ContainerName.Get())
|
||||
if remoteNode.ContainerName != "" {
|
||||
stage_file = container.ShimFind(remoteNode.ContainerName)
|
||||
|
||||
if stage_file == "" {
|
||||
wwlog.Error("No kernel found for container %s", node.ContainerName.Get())
|
||||
wwlog.Error("No kernel found for container %s", remoteNode.ContainerName)
|
||||
}
|
||||
} else {
|
||||
wwlog.Warn("No container set for this %s", node.Id.Get())
|
||||
wwlog.Warn("No container set for this %s", remoteNode.Id())
|
||||
}
|
||||
} else if rinfo.stage == "grub" {
|
||||
if node.ContainerName.Defined() {
|
||||
stage_file = container.GrubFind(node.ContainerName.Get())
|
||||
if remoteNode.ContainerName != "" {
|
||||
stage_file = container.GrubFind(remoteNode.ContainerName)
|
||||
if stage_file == "" {
|
||||
wwlog.Error("No grub found for container %s", node.ContainerName.Get())
|
||||
wwlog.Error("No grub found for container %s", remoteNode.ContainerName)
|
||||
}
|
||||
} else {
|
||||
wwlog.Warn("No conainer set for node %s", node.Id.Get())
|
||||
wwlog.Warn("No conainer set for node %s", remoteNode.Id())
|
||||
}
|
||||
} else if rinfo.stage == "initramfs" {
|
||||
if node.ContainerName.Defined() {
|
||||
_, kver, err := kernel.FindKernel(container.RootFsDir(node.ContainerName.Get()))
|
||||
if remoteNode.ContainerName != "" {
|
||||
_, kver, err := kernel.FindKernel(container.RootFsDir(remoteNode.ContainerName))
|
||||
if err != nil {
|
||||
wwlog.Error("No kernel found for initramfs for container %s: %s", node.ContainerName.Get(), err)
|
||||
wwlog.Error("No kernel found for initramfs for container %s: %s", remoteNode.ContainerName, err)
|
||||
}
|
||||
stage_file, err = container.InitramfsBootPath(node.ContainerName.Get(), kver)
|
||||
stage_file, err = container.InitramfsBootPath(remoteNode.ContainerName, kver)
|
||||
if err != nil {
|
||||
wwlog.Error("No initramfs found for container %s: %s", node.ContainerName.Get(), err)
|
||||
wwlog.Error("No initramfs found for container %s: %s", remoteNode.ContainerName, err)
|
||||
}
|
||||
} else {
|
||||
wwlog.Warn("No container set for node %s", node.Id.Get())
|
||||
wwlog.Warn("No container set for node %s", remoteNode.Id())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +274,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
wwlog.ErrorExc(err, "")
|
||||
}
|
||||
|
||||
wwlog.Send("%15s: %s", node.Id.Get(), stage_file)
|
||||
wwlog.Send("%15s: %s", remoteNode.Id(), stage_file)
|
||||
|
||||
} else {
|
||||
if rinfo.compress == "gz" {
|
||||
@@ -296,24 +292,24 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
|
||||
err = sendFile(w, req, stage_file, node.Id.Get())
|
||||
err = sendFile(w, req, stage_file, remoteNode.Id())
|
||||
if err != nil {
|
||||
wwlog.ErrorExc(err, "")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
updateStatus(node.Id.Get(), status_stage, path.Base(stage_file), rinfo.ipaddr)
|
||||
updateStatus(remoteNode.Id(), status_stage, path.Base(stage_file), rinfo.ipaddr)
|
||||
|
||||
} else if stage_file == "" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
wwlog.Error("No resource selected")
|
||||
updateStatus(node.Id.Get(), status_stage, "BAD_REQUEST", rinfo.ipaddr)
|
||||
updateStatus(remoteNode.Id(), status_stage, "BAD_REQUEST", rinfo.ipaddr)
|
||||
|
||||
} else {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
wwlog.Error("Not found: %s", stage_file)
|
||||
updateStatus(node.Id.Get(), status_stage, "NOT_FOUND", rinfo.ipaddr)
|
||||
updateStatus(remoteNode.Id(), status_stage, "NOT_FOUND", rinfo.ipaddr)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
|
||||
"github.com/warewulf/warewulf/internal/pkg/node"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
|
||||
@@ -49,11 +49,11 @@ func LoadNodeStatus() error {
|
||||
}
|
||||
|
||||
for _, n := range nodes {
|
||||
if _, ok := statusDB.Nodes[n.Id.Get()]; !ok {
|
||||
newDB.Nodes[n.Id.Get()] = &NodeStatus{}
|
||||
newDB.Nodes[n.Id.Get()].NodeName = n.Id.Get()
|
||||
if _, ok := statusDB.Nodes[n.Id()]; !ok {
|
||||
newDB.Nodes[n.Id()] = &NodeStatus{}
|
||||
newDB.Nodes[n.Id()].NodeName = n.Id()
|
||||
} else {
|
||||
newDB.Nodes[n.Id.Get()] = statusDB.Nodes[n.Id.Get()]
|
||||
newDB.Nodes[n.Id()] = statusDB.Nodes[n.Id()]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,13 +44,9 @@ func sendFile(
|
||||
return nil
|
||||
}
|
||||
|
||||
func getOverlayFile(
|
||||
n node.NodeInfo,
|
||||
context string,
|
||||
stage_overlays []string,
|
||||
autobuild bool) (stage_file string, err error) {
|
||||
func getOverlayFile(n node.NodeConf, context string, stage_overlays []string, autobuild bool) (stage_file string, err error) {
|
||||
|
||||
stage_file = overlay.OverlayImage(n.Id.Get(), context, stage_overlays)
|
||||
stage_file = overlay.OverlayImage(n.Id(), context, stage_overlays)
|
||||
err = nil
|
||||
build := !util.IsFile(stage_file)
|
||||
wwlog.Verbose("stage file: %s", stage_file)
|
||||
@@ -64,13 +60,13 @@ func getOverlayFile(
|
||||
|
||||
if build {
|
||||
if len(stage_overlays) > 0 {
|
||||
err = overlay.BuildSpecificOverlays([]node.NodeInfo{n}, stage_overlays)
|
||||
err = overlay.BuildSpecificOverlays([]node.NodeConf{n}, stage_overlays)
|
||||
} else {
|
||||
err = overlay.BuildAllOverlays([]node.NodeInfo{n})
|
||||
err = overlay.BuildAllOverlays([]node.NodeConf{n})
|
||||
}
|
||||
if err != nil {
|
||||
wwlog.Error("Failed to build overlay: %s, %s, %s\n%s",
|
||||
n.Id.Get(), stage_overlays, stage_file, err)
|
||||
n.Id(), stage_overlays, stage_file, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user