use yaml/v3 and don't export Nodes
introduced wwbool and don't export Nodes and NodeConfs. This requires new explict Yaml (un)marshaling as the standard marshaller won't touch these fields Signed-off-by: Christian Goll <cgoll@suse.com>
This commit is contained in:
committed by
Jonathon Anderson
parent
ac6cd69ca6
commit
5bd4fd4712
@@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/warewulf/warewulf/internal/pkg/util"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwtype"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -60,7 +61,7 @@ func (nodeConf *NodeConf) Check() (err error) {
|
||||
}
|
||||
|
||||
func checker(value string, valType string) (niceValue string, err error) {
|
||||
if valType == "" || value == "" || util.InSlice(GetUnsetVerbs(), value) {
|
||||
if valType == "" || value == "" || util.InSlice(wwtype.GetUnsetVerbs(), value) {
|
||||
return "", nil
|
||||
}
|
||||
//wwlog.Debug("checker: %s is %s", value, valType)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"dario.cat/mergo"
|
||||
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -37,79 +37,82 @@ func New() (NodeYaml, error) {
|
||||
// document. Passes any errors return from yaml.Unmarshal. Returns an
|
||||
// error if any parsed value is not of a valid type for the given
|
||||
// parameter.
|
||||
func Parse(data []byte) (NodeYaml, error) {
|
||||
var nodeList NodeYaml
|
||||
var err error
|
||||
func Parse(data []byte) (nodeList NodeYaml, err error) {
|
||||
wwlog.Debug("Unmarshaling the node configuration")
|
||||
err = yaml.Unmarshal(data, &nodeList)
|
||||
if err != nil {
|
||||
return nodeList, err
|
||||
}
|
||||
wwlog.Debug("Checking profiles for types")
|
||||
if nodeList.Nodes == nil {
|
||||
nodeList.Nodes = map[string]*NodeConf{}
|
||||
wwlog.Debug("Checking nodes for types")
|
||||
if nodeList.nodes == nil {
|
||||
nodeList.nodes = map[string]*NodeConf{}
|
||||
}
|
||||
/*
|
||||
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 nodeList.nodeProfiles == nil {
|
||||
nodeList.nodeProfiles = map[string]*ProfileConf{}
|
||||
}
|
||||
/*
|
||||
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")
|
||||
wwlog.Debug("returning node object")
|
||||
return nodeList, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Get a node with its merged in profiles
|
||||
Get a node with its merged in nodes
|
||||
*/
|
||||
func (config *NodeYaml) GetNode(id string) (node NodeConf, err error) {
|
||||
if _, ok := config.Nodes[id]; !ok {
|
||||
|
||||
if _, ok := config.nodes[id]; !ok {
|
||||
return node, ErrNotFound
|
||||
}
|
||||
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)
|
||||
node = *config.nodes[id]
|
||||
for _, p := range cleanList(config.nodes[id].Profiles) {
|
||||
includedProfile, err := config.GetProfile(p)
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
err = enc.Encode(includedProfile)
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
err = dec.Decode(&node)
|
||||
err = mergo.Merge(&node.ProfileConf, includedProfile, mergo.WithAppendSlice)
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
}
|
||||
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)
|
||||
// err = mergo.Merge(&node, config.nodes[id], mergo.WithOverride, mergo.WithoutDereference)
|
||||
// err = mergo.Merge(&node, config.nodes[id], mergo.WithOverride)
|
||||
// err = mergo.Merge(&node, config.nodes[id])
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
/*
|
||||
node = EmptyNode()
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
dec := gob.NewDecoder(&buf)
|
||||
includedProfile, err := config.GetProfile(p)
|
||||
appendStringSlices(&node, &includedProfile)
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
err = enc.Encode(includedProfile)
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
err = dec.Decode(&node)
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
wwlog.Debug("merged in profile: %s", p)
|
||||
}
|
||||
var bufNode bytes.Buffer
|
||||
encNode := gob.NewEncoder(&bufNode)
|
||||
decNode := gob.NewDecoder(&bufNode)
|
||||
appendStringSlices(&node, &config.nodes[id].ProfileConf)
|
||||
|
||||
err = encNode.Encode(config.nodes[id])
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
err = decNode.Decode(&node)
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
*/
|
||||
// finally set no exported values
|
||||
node.id = id
|
||||
node.valid = true
|
||||
@@ -127,46 +130,69 @@ func (config *NodeYaml) GetNode(id string) (node NodeConf, err error) {
|
||||
node.PrimaryNetDev = keys[0]
|
||||
}
|
||||
}
|
||||
wwlog.Debug("constructed node: %s", id)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Get the profile with all the profiles merged in
|
||||
Return the node with the id string without the merged in nodes, return ErrNotFound
|
||||
otherwise
|
||||
*/
|
||||
func (config *NodeYaml) GetProfile(id string) (profile NodeConf, err error) {
|
||||
if _, ok := config.NodeProfiles[id]; !ok {
|
||||
return profile, ErrNotFound
|
||||
func (config *NodeYaml) GetNodeOnly(id string) (node NodeConf, err error) {
|
||||
node = EmptyNode()
|
||||
if found, ok := config.nodes[id]; ok {
|
||||
return *found, nil
|
||||
}
|
||||
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
|
||||
return node, ErrNotFound
|
||||
}
|
||||
|
||||
/*
|
||||
Get the profiles from the loaded configuration. This function also merges
|
||||
the profiles with the given profiles.
|
||||
Return pointer to the node with the id string without the merged in nodes, return ErrMotFound
|
||||
otherwise
|
||||
*/
|
||||
func (config *NodeYaml) FindAllNodes(profiles ...string) (nodeList []NodeConf, err error) {
|
||||
if len(profiles) == 0 {
|
||||
for n := range config.Nodes {
|
||||
profiles = append(profiles, n)
|
||||
func (config *NodeYaml) GetNodeOnlyPtr(id string) (*NodeConf, error) {
|
||||
node := EmptyNode()
|
||||
if found, ok := config.nodes[id]; ok {
|
||||
return found, nil
|
||||
}
|
||||
return &node, ErrNotFound
|
||||
}
|
||||
|
||||
/*
|
||||
Get the profile with id, return ErrNotFound otherwise
|
||||
*/
|
||||
func (config *NodeYaml) GetProfile(id string) (profile ProfileConf, err error) {
|
||||
if found, ok := config.nodeProfiles[id]; ok {
|
||||
found.id = id
|
||||
return *found, nil
|
||||
}
|
||||
return profile, ErrNotFound
|
||||
}
|
||||
|
||||
/*
|
||||
Get the profile with id, return ErrNotFound otherwise
|
||||
*/
|
||||
func (config *NodeYaml) GetProfilePtr(id string) (profile *ProfileConf, err error) {
|
||||
if found, ok := config.nodeProfiles[id]; ok {
|
||||
found.id = id
|
||||
return found, nil
|
||||
}
|
||||
return profile, ErrNotFound
|
||||
}
|
||||
|
||||
/*
|
||||
Get the nodes from the loaded configuration. This function also merges
|
||||
the nodes with the given nodes.
|
||||
*/
|
||||
func (config *NodeYaml) FindAllNodes(nodes ...string) (nodeList []NodeConf, err error) {
|
||||
if len(nodes) == 0 {
|
||||
for n := range config.nodes {
|
||||
nodes = append(nodes, n)
|
||||
}
|
||||
}
|
||||
wwlog.Debug("Finding profiles: %s", profiles)
|
||||
for _, profileId := range profiles {
|
||||
node, err := config.GetNode(profileId)
|
||||
wwlog.Debug("Finding nodes: %s", nodes)
|
||||
for _, nodeId := range nodes {
|
||||
node, err := config.GetNode(nodeId)
|
||||
if err != nil {
|
||||
return nodeList, err
|
||||
}
|
||||
@@ -186,16 +212,16 @@ func (config *NodeYaml) FindAllNodes(profiles ...string) (nodeList []NodeConf, e
|
||||
}
|
||||
|
||||
/*
|
||||
Return all profiles as NodeInfo
|
||||
Return all nodes as ProfileConf
|
||||
*/
|
||||
func (config *NodeYaml) FindAllProfiles(profiles ...string) (profileList []NodeConf, err error) {
|
||||
if len(profiles) == 0 {
|
||||
for n := range config.NodeProfiles {
|
||||
profiles = append(profiles, n)
|
||||
func (config *NodeYaml) FindAllProfiles(nodes ...string) (profileList []ProfileConf, err error) {
|
||||
if len(nodes) == 0 {
|
||||
for n := range config.nodeProfiles {
|
||||
nodes = append(nodes, n)
|
||||
}
|
||||
}
|
||||
wwlog.Debug("Finding profiles: %s", profiles)
|
||||
for _, profileId := range profiles {
|
||||
wwlog.Debug("Finding nodes: %s", nodes)
|
||||
for _, profileId := range nodes {
|
||||
node, err := config.GetProfile(profileId)
|
||||
if err != nil {
|
||||
return profileList, err
|
||||
@@ -220,19 +246,19 @@ func (config *NodeYaml) FindAllProfiles(profiles ...string) (profileList []NodeC
|
||||
Return the names of all available nodes
|
||||
*/
|
||||
func (config *NodeYaml) ListAllNodes() []string {
|
||||
nodeList := make([]string, len(config.Nodes))
|
||||
for name := range config.Nodes {
|
||||
nodeList := make([]string, len(config.nodes))
|
||||
for name := range config.nodes {
|
||||
nodeList = append(nodeList, name)
|
||||
}
|
||||
return nodeList
|
||||
}
|
||||
|
||||
/*
|
||||
Return the names of all available profiles
|
||||
Return the names of all available nodes
|
||||
*/
|
||||
func (config *NodeYaml) ListAllProfiles() []string {
|
||||
var nodeList []string
|
||||
for name := range config.NodeProfiles {
|
||||
for name := range config.nodeProfiles {
|
||||
nodeList = append(nodeList, name)
|
||||
}
|
||||
return nodeList
|
||||
@@ -246,7 +272,7 @@ without a hardware address is returned.
|
||||
|
||||
If no unconfigured node is found, an error is returned.
|
||||
*/
|
||||
func (config *NodeYaml) FindDiscoverableNode() (string, string, error) {
|
||||
func (config *NodeYaml) FindDiscoverableNode() (NodeConf, string, error) {
|
||||
|
||||
nodes, _ := config.FindAllNodes()
|
||||
|
||||
@@ -255,14 +281,41 @@ func (config *NodeYaml) FindDiscoverableNode() (string, string, error) {
|
||||
continue
|
||||
}
|
||||
if _, ok := node.NetDevs[node.PrimaryNetDev]; ok {
|
||||
return node.Id(), node.PrimaryNetDev, nil
|
||||
return node, node.PrimaryNetDev, nil
|
||||
}
|
||||
for netdev, dev := range node.NetDevs {
|
||||
if dev.Hwaddr != "" {
|
||||
return node.Id(), netdev, nil
|
||||
return node, netdev, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", "", ErrNoUnconfigured
|
||||
return EmptyNode(), "", ErrNoUnconfigured
|
||||
}
|
||||
func appendStringSlices(src, dst any) {
|
||||
//srcType := reflect.TypeOf(src)
|
||||
srcVal := reflect.ValueOf(src)
|
||||
dstType := reflect.TypeOf(dst)
|
||||
dstVal := reflect.ValueOf(dst)
|
||||
for i := 0; i < dstType.Elem().NumField(); i++ {
|
||||
// wwlog.Debug("dstType.Name: %s", dstType.Elem().Field(i).Name)
|
||||
srcValField := srcVal.Elem().FieldByName(dstType.Elem().Field(i).Name)
|
||||
if !srcValField.IsZero() {
|
||||
if srcValField.Type() == reflect.TypeOf([]string{}) {
|
||||
// wwlog.Debug("dstType.Name: %s", dstType.Elem().Field(i).Name)
|
||||
if srcValField.Len() > 0 {
|
||||
// wwlog.Debug("srcValField.Len(): %d", srcValField.Len())
|
||||
for _, elem := range srcValField.Interface().([]string) {
|
||||
dstVal.Elem().Field(i).Set(reflect.Append(dstVal.Elem().Field(i), reflect.ValueOf(elem)))
|
||||
wwlog.Debug("elem: %s", elem)
|
||||
}
|
||||
lst := dstVal.Elem().Field(i).Addr().Interface().(*[]string)
|
||||
*lst = cleanList(*lst)
|
||||
}
|
||||
|
||||
}
|
||||
} else if srcValField.Type().Kind() == reflect.Ptr {
|
||||
appendStringSlices(srcValField, dstVal.Elem().Field(i).Interface())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
package node
|
||||
|
||||
import "net"
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwtype"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const undef string = "UNDEF"
|
||||
|
||||
@@ -14,14 +20,18 @@ type NodeYaml struct {
|
||||
WWInternal int `yaml:"WW_INTERNAL,omitempty" json:"WW_INTERNAL,omitempty"`
|
||||
NodeProfiles map[string]*ProfileConf
|
||||
Nodes map[string]*NodeConf
|
||||
WWInternal int `yaml:"WW_INTERNAL"`
|
||||
nodeProfiles map[string]*ProfileConf
|
||||
nodes map[string]*NodeConf
|
||||
}
|
||||
|
||||
/*
|
||||
NodeConf is the datastructure describing a node and a profile which in disk format.
|
||||
*/
|
||||
type NodeConf struct {
|
||||
id string
|
||||
valid bool // Is set true, if called by the constructor
|
||||
id string
|
||||
valid bool // Is set true, if called by the constructor
|
||||
// exported values
|
||||
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)"`
|
||||
@@ -32,6 +42,7 @@ type NodeConf struct {
|
||||
Holds the data which can be set for profiles and nodes.
|
||||
*/
|
||||
type ProfileConf struct {
|
||||
id string
|
||||
// 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"`
|
||||
@@ -59,9 +70,10 @@ type IpmiConf struct {
|
||||
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)"`
|
||||
Write wwtype.WWbool `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"`
|
||||
Override string `yaml:"override,omitempty" lopt:"kerneloverride" sopt:"K" comment:"Set kernel override version" json:"override,omitempty"`
|
||||
@@ -118,3 +130,52 @@ type FileSystem struct {
|
||||
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"`
|
||||
}
|
||||
|
||||
/*
|
||||
interface so that nodes and profiles which aren't exported will
|
||||
be marshaled
|
||||
*/
|
||||
type ExportedYml struct {
|
||||
WWInternal int `yaml:"WW_INTERNAL"`
|
||||
NodeProfiles map[string]*ProfileConf `yaml:"nodeprofiles"`
|
||||
Nodes map[string]*NodeConf `yaml:"nodes"`
|
||||
}
|
||||
|
||||
/*
|
||||
Marshall Exported stuff, not NodeYaml directly
|
||||
*/
|
||||
func (yml *NodeYaml) MarshalYAML() (interface{}, error) {
|
||||
wwlog.Debug("marshall yml")
|
||||
var exp ExportedYml
|
||||
exp.WWInternal = yml.WWInternal
|
||||
exp.Nodes = yml.nodes
|
||||
exp.NodeProfiles = yml.nodeProfiles
|
||||
node := yaml.Node{}
|
||||
err := node.Encode(exp)
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
return node, err
|
||||
}
|
||||
|
||||
/*
|
||||
Unmarshal to intermediate format
|
||||
*/
|
||||
func (yml *NodeYaml) UnmarshalYAML(
|
||||
unmarshal func(interface{}) (err error),
|
||||
) (err error) {
|
||||
wwlog.Debug("UnmarshalYAML called")
|
||||
var exp ExportedYml
|
||||
err = unmarshal(&exp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
yml.WWInternal = exp.WWInternal
|
||||
yml.nodes = exp.Nodes
|
||||
yml.nodeProfiles = exp.NodeProfiles
|
||||
return nil
|
||||
}
|
||||
|
||||
func (yml NodeYaml) IsZero() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"reflect"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwtype"
|
||||
)
|
||||
|
||||
type NodeConfDel struct {
|
||||
@@ -71,6 +72,8 @@ func recursiveCreateFlags(obj interface{}, baseCmd *cobra.Command) {
|
||||
recursiveCreateFlags(nodeInfoVal.Elem().Field(i).MapIndex(key).Interface(), baseCmd)
|
||||
} else if nodeInfoType.Elem().Field(i).Anonymous {
|
||||
recursiveCreateFlags(nodeInfoVal.Elem().Field(i).Addr().Interface(), baseCmd)
|
||||
} else if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.Struct {
|
||||
recursiveCreateFlags(nodeInfoVal.Elem().Field(i).Addr().Interface(), baseCmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,6 +185,20 @@ func createFlags(baseCmd *cobra.Command,
|
||||
net.IPMask{}, // empty default!
|
||||
myType.Tag.Get("comment"))
|
||||
}
|
||||
} else if myType.Type == reflect.TypeOf(wwtype.WWbool{}) {
|
||||
ptr := myVal.Addr().Interface().(*wwtype.WWbool)
|
||||
if myType.Tag.Get("sopt") != "" {
|
||||
baseCmd.PersistentFlags().VarP(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("sopt"),
|
||||
myType.Tag.Get("comment"))
|
||||
baseCmd.Flag(myType.Tag.Get("lopt")).NoOptDefVal = "true"
|
||||
} else {
|
||||
baseCmd.PersistentFlags().Var(ptr,
|
||||
myType.Tag.Get("lopt"),
|
||||
myType.Tag.Get("comment"))
|
||||
baseCmd.Flag(myType.Tag.Get("lopt")).NoOptDefVal = "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -13,10 +13,10 @@ Calculate the hash of NodeYaml in an orderder fashion
|
||||
*/
|
||||
func (config *NodeYaml) Hash() [32]byte {
|
||||
// flatten out profiles and nodes
|
||||
for _, val := range config.NodeProfiles {
|
||||
for _, val := range config.nodeProfiles {
|
||||
val.Flatten()
|
||||
}
|
||||
for _, val := range config.Nodes {
|
||||
for _, val := range config.nodes {
|
||||
val.Flatten()
|
||||
}
|
||||
data, err := yaml.Marshal(config)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -13,55 +16,91 @@ type NodeFields struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
type fieldMap map[string]NodeFields
|
||||
|
||||
/*
|
||||
Get all the info out of NodeConf. If emptyFields is set true, all fields are shown not only the ones with effective values
|
||||
*/
|
||||
func (nodeYml *NodeYaml) GetFields(node NodeConf, emptyFields bool) (output []NodeFields) {
|
||||
fieldMap := make(map[string]NodeFields)
|
||||
nodeMap := make(fieldMap)
|
||||
for _, p := range node.Profiles {
|
||||
if profile, ok := nodeYml.NodeProfiles[p]; ok {
|
||||
recursiveFields(profile, emptyFields, "", fieldMap, p)
|
||||
if profile, ok := nodeYml.nodeProfiles[p]; ok {
|
||||
nodeMap.recursiveFields(profile, emptyFields, "", p)
|
||||
}
|
||||
}
|
||||
recursiveFields(node, emptyFields, "", fieldMap, "")
|
||||
nodeMap.recursiveFields(&node, emptyFields, "", "")
|
||||
for _, elem := range nodeMap {
|
||||
output = append(output, elem)
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
/*
|
||||
Get all the info out of ProfileConf. If emptyFields is set true, all fields are shown not only the ones with effective values
|
||||
*/
|
||||
func (nodeYml *NodeYaml) GetFieldsProfile(profile ProfileConf, emptyFields bool) (output []NodeFields) {
|
||||
profileMap := make(fieldMap)
|
||||
profileMap.recursiveFields(&profile, emptyFields, "", "")
|
||||
for _, elem := range profileMap {
|
||||
output = append(output, elem)
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
/*
|
||||
Internal function which travels through all fields of a NodeConf and for this
|
||||
reason needs tb called via interface{}
|
||||
reason needs to be called via interface{}
|
||||
*/
|
||||
func recursiveFields(obj interface{}, emptyFields bool, prefix string,
|
||||
fieldMap map[string]NodeFields, source string) {
|
||||
func (fieldMap *fieldMap) recursiveFields(obj interface{}, emptyFields bool, prefix string, source string) {
|
||||
valObj := reflect.ValueOf(obj)
|
||||
typeObj := reflect.TypeOf(obj)
|
||||
for i := 0; i < typeObj.Elem().NumField(); i++ {
|
||||
fmt.Printf("name: %s\n", typeObj.Elem().Field(i).Name)
|
||||
if valObj.Elem().Field(i).IsValid() {
|
||||
if valObj.Elem().Field(i).String() != "" {
|
||||
fieldMap[prefix+typeObj.Elem().Field(i).Name] = NodeFields{
|
||||
if !typeObj.Elem().Field(i).IsExported() {
|
||||
continue
|
||||
}
|
||||
if valObj.Elem().Field(i).Kind() == reflect.String && valObj.Elem().Field(i).String() != "" {
|
||||
fmt.Printf("string: %s\n", valObj.Elem().Field(i).String())
|
||||
(*fieldMap)[prefix+typeObj.Elem().Field(i).Name] = NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name,
|
||||
Source: source,
|
||||
Value: valObj.Elem().Field(i).String(),
|
||||
}
|
||||
} else if emptyFields {
|
||||
fieldMap[prefix+typeObj.Elem().Field(i).Name] = NodeFields{
|
||||
(*fieldMap)[prefix+typeObj.Elem().Field(i).Name] = NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name + "[]",
|
||||
Source: source,
|
||||
}
|
||||
}
|
||||
} else if typeObj.Elem().Field(i).Type.Kind() == reflect.Map {
|
||||
mapIter := valObj.Elem().Field(i).MapRange()
|
||||
for mapIter.Next() {
|
||||
recursiveFields(mapIter.Value().Interface(),
|
||||
emptyFields, prefix+typeObj.Elem().Field(i).Name+"["+mapIter.Key().String()+"].", fieldMap, source)
|
||||
}
|
||||
if valObj.Elem().Field(i).Len() == 0 && emptyFields {
|
||||
fieldMap[prefix+typeObj.Elem().Field(i).Name] = NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name + "[]",
|
||||
} else if typeObj.Elem().Field(i).Type == reflect.TypeOf([]string{}) && valObj.Elem().Field(i).Len() != 0 {
|
||||
vals := (valObj.Elem().Field(i).Interface()).([]string)
|
||||
(*fieldMap)[prefix+typeObj.Elem().Field(i).Name] = NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name,
|
||||
Source: source,
|
||||
Value: strings.Join(vals, ","),
|
||||
}
|
||||
}
|
||||
} else if typeObj.Elem().Field(i).Type.Kind() == reflect.Ptr {
|
||||
recursiveFields(valObj.Elem().Field(i).Interface(), emptyFields, prefix+typeObj.Elem().Field(i).Name+".", fieldMap, source)
|
||||
} else if typeObj.Elem().Field(i).Type == reflect.TypeOf(net.IP{}) {
|
||||
val := (valObj.Elem().Field(i).Interface()).(net.IP)
|
||||
(*fieldMap)[prefix+typeObj.Elem().Field(i).Name] = NodeFields{
|
||||
Field: prefix + typeObj.Elem().Field(i).Name,
|
||||
Source: source,
|
||||
Value: val.String(),
|
||||
}
|
||||
} else if typeObj.Elem().Field(i).Type.Kind() == reflect.Map {
|
||||
mapIter := valObj.Elem().Field(i).MapRange()
|
||||
for mapIter.Next() {
|
||||
fieldMap.recursiveFields(mapIter.Value().Interface(),
|
||||
emptyFields, prefix+typeObj.Elem().Field(i).Name+"["+mapIter.Key().String()+"].", source)
|
||||
}
|
||||
if valObj.Elem().Field(i).Len() == 0 && emptyFields {
|
||||
(*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 {
|
||||
fieldMap.recursiveFields(valObj.Elem().Field(i).Interface(), emptyFields, prefix+typeObj.Elem().Field(i).Name+".", source)
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"net"
|
||||
"reflect"
|
||||
"regexp"
|
||||
@@ -8,6 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/warewulf/warewulf/internal/pkg/util"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwtype"
|
||||
)
|
||||
|
||||
type sortByName []NodeConf
|
||||
@@ -15,9 +18,6 @@ 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 < a[j].id }
|
||||
func GetUnsetVerbs() []string {
|
||||
return []string{"UNSET", "DELETE", "UNDEF", "undef", "--", "nil", "0.0.0.0"}
|
||||
}
|
||||
|
||||
/**********
|
||||
*
|
||||
@@ -95,6 +95,15 @@ func NewNode(id string) (nodeconf NodeConf) {
|
||||
return nodeconf
|
||||
}
|
||||
|
||||
func EmptyNode() (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{}
|
||||
return nodeconf
|
||||
}
|
||||
|
||||
/*
|
||||
Creates a ProfileConf but doesn't add it to the database.
|
||||
*/
|
||||
@@ -136,6 +145,7 @@ func recursiveFlatten(strct interface{}) {
|
||||
nestedType := reflect.TypeOf(confVal.Elem().Field(j).Interface())
|
||||
nestedVal := reflect.ValueOf(confVal.Elem().Field(j).Interface())
|
||||
for i := 0; i < nestedType.Elem().NumField(); i++ {
|
||||
// wwlog.Debug("checking %s", nestedType.Elem().Field(i).Type.String())
|
||||
if nestedType.Elem().Field(i).Type.Kind() == reflect.String &&
|
||||
nestedVal.Elem().Field(i).Interface().(string) != "" &&
|
||||
nestedVal.Elem().Field(i).Interface().(string) != undef {
|
||||
@@ -151,6 +161,11 @@ func recursiveFlatten(strct interface{}) {
|
||||
if len(val) != 0 && !val.IsUnspecified() {
|
||||
setToNil = false
|
||||
}
|
||||
} else if nestedType.Elem().Field(i).Type == reflect.TypeOf(wwtype.WWbool{}) {
|
||||
val := nestedVal.Elem().Field(i).Interface().(wwtype.WWbool)
|
||||
if !val.IsZero() {
|
||||
setToNil = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if setToNil {
|
||||
@@ -239,12 +254,19 @@ Getters for unexported fields
|
||||
*/
|
||||
|
||||
/*
|
||||
Returns the id of the node/profile
|
||||
Returns the id of the node
|
||||
*/
|
||||
func (node *NodeConf) Id() string {
|
||||
return node.id
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the id of the profile
|
||||
*/
|
||||
func (node *ProfileConf) Id() string {
|
||||
return node.id
|
||||
}
|
||||
|
||||
/*
|
||||
Returns if the node is a valid in the database
|
||||
*/
|
||||
@@ -258,3 +280,66 @@ Check if the netdev is the primary one
|
||||
func (dev *NetDevs) Primary() bool {
|
||||
return dev.primary
|
||||
}
|
||||
|
||||
// returns all negated elements 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
|
||||
}
|
||||
}
|
||||
if notNegate {
|
||||
ret = append(ret, listTok)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Clone (deep copy) a node via gob
|
||||
func (src *NodeConf) Clone() (dst NodeConf, err error) {
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
dec := gob.NewDecoder(&buf)
|
||||
err = enc.Encode(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = dec.Decode(&dst)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
dst.id = src.id
|
||||
dst.valid = src.valid
|
||||
return
|
||||
}
|
||||
|
||||
// Clone (deep copy) a node via gob
|
||||
func (src ProfileConf) Clone() (dst ProfileConf, err error) {
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
dec := gob.NewDecoder(&buf)
|
||||
err = enc.Encode(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = dec.Decode(&dst)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
dst.id = src.id
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
@@ -15,10 +17,10 @@ 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 {
|
||||
if _, ok := config.nodes[nodeID]; ok {
|
||||
return nil, errors.New("nodename already exists: " + nodeID)
|
||||
} else {
|
||||
config.Nodes[nodeID] = &newNode
|
||||
config.nodes[nodeID] = &newNode
|
||||
}
|
||||
return &newNode, nil
|
||||
}
|
||||
@@ -27,26 +29,64 @@ func (config *NodeYaml) AddNode(nodeID string) (*NodeConf, error) {
|
||||
delete node with the given id
|
||||
*/
|
||||
func (config *NodeYaml) DelNode(nodeID string) error {
|
||||
if _, ok := config.Nodes[nodeID]; !ok {
|
||||
if _, ok := config.nodes[nodeID]; !ok {
|
||||
return errors.New("nodename does not exist: " + nodeID)
|
||||
}
|
||||
|
||||
wwlog.Verbose("Deleting node: %s", nodeID)
|
||||
delete(config.Nodes, nodeID)
|
||||
delete(config.nodes, nodeID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
set node for the node with id the values of vals
|
||||
*/
|
||||
func (config *NodeYaml) SetNode(nodeID string, vals NodeConf) error {
|
||||
node, ok := config.nodes[nodeID]
|
||||
if !ok {
|
||||
return ErrNotFound
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
dec := gob.NewDecoder(&buf)
|
||||
err := enc.Encode(vals)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dec.Decode(node)
|
||||
return err
|
||||
}
|
||||
|
||||
/*
|
||||
set profile for the node with id the values of vals
|
||||
*/
|
||||
func (config *NodeYaml) SetProfile(profileId string, vals ProfileConf) error {
|
||||
profile, ok := config.nodeProfiles[profileId]
|
||||
if !ok {
|
||||
return ErrNotFound
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
dec := gob.NewDecoder(&buf)
|
||||
err := enc.Encode(vals)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dec.Decode(profile)
|
||||
return err
|
||||
}
|
||||
|
||||
/*
|
||||
Add a node with the given ID and return a pointer to it
|
||||
*/
|
||||
func (config *NodeYaml) AddProfile(profileId string) (*ProfileConf, error) {
|
||||
profile := NewProfile(profileId)
|
||||
wwlog.Verbose("adding new profile: %s", profileId)
|
||||
if _, ok := config.NodeProfiles[profileId]; ok {
|
||||
if _, ok := config.nodeProfiles[profileId]; ok {
|
||||
return nil, errors.New("profile already exists: " + profileId)
|
||||
} else {
|
||||
config.NodeProfiles[profileId] = &profile
|
||||
config.nodeProfiles[profileId] = &profile
|
||||
}
|
||||
return &profile, nil
|
||||
}
|
||||
@@ -55,12 +95,12 @@ func (config *NodeYaml) AddProfile(profileId string) (*ProfileConf, error) {
|
||||
delete node with the given id
|
||||
*/
|
||||
func (config *NodeYaml) DelProfile(nodeID string) error {
|
||||
if _, ok := config.Nodes[nodeID]; !ok {
|
||||
if _, ok := config.nodes[nodeID]; !ok {
|
||||
return errors.New("profile does not exist: " + nodeID)
|
||||
}
|
||||
|
||||
wwlog.Verbose("deleting profile: %s", nodeID)
|
||||
delete(config.Nodes, nodeID)
|
||||
delete(config.nodes, nodeID)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -84,6 +124,7 @@ func (config *NodeYaml) Persist() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wwlog.Debug("persisted: %s", ConfigFile)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -93,11 +134,16 @@ 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 {
|
||||
for _, val := range config.nodeProfiles {
|
||||
val.Flatten()
|
||||
}
|
||||
for _, val := range config.Nodes {
|
||||
for _, val := range config.nodes {
|
||||
val.Flatten()
|
||||
}
|
||||
return yaml.Marshal(config)
|
||||
var buf bytes.Buffer
|
||||
// Run through encoder
|
||||
yamlEncoder := yaml.NewEncoder(&buf)
|
||||
yamlEncoder.SetIndent(2)
|
||||
err := yamlEncoder.Encode(config)
|
||||
return buf.Bytes(), err //yaml.Marshal(config)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user