Add json output for ignition
Signed-off-by: Christian Goll <cgoll@suse.com>
This commit is contained in:
committed by
Jonathon Anderson
parent
45539a0d1f
commit
a7df560a30
@@ -69,7 +69,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
||||
wwlog.Error("%v does not identify a single node", NodeName)
|
||||
os.Exit(1)
|
||||
}
|
||||
tstruct := overlay.InitStruct(filteredNodes[0])
|
||||
tstruct := overlay.InitStruct(&filteredNodes[0])
|
||||
tstruct.BuildSource = overlayFile
|
||||
buffer, backupFile, writeFile, err := overlay.RenderTemplateFile(overlayFile, tstruct)
|
||||
if err != nil {
|
||||
|
||||
@@ -24,7 +24,7 @@ func Hostfile() error {
|
||||
nodeInfo := node.NewInfo()
|
||||
hostname, _ := os.Hostname()
|
||||
nodeInfo.Id.Set(hostname)
|
||||
tstruct := overlay.InitStruct(nodeInfo)
|
||||
tstruct := overlay.InitStruct(&nodeInfo)
|
||||
buffer, backupFile, writeFile, err := overlay.RenderTemplateFile(
|
||||
hostTemplate,
|
||||
tstruct)
|
||||
|
||||
127
internal/pkg/node/ignition.go
Normal file
127
internal/pkg/node/ignition.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
types_3_4 "github.com/coreos/ignition/v2/config/v3_2/types"
|
||||
"github.com/coreos/vcontext/path"
|
||||
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
/*
|
||||
Create a ignition struct class for ignition
|
||||
*/
|
||||
func (node *NodeInfo) 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() {
|
||||
mountOptions = append(mountOptions, types_3_4.MountOption(opt))
|
||||
}
|
||||
var fsOption []types_3_4.FilesystemOption
|
||||
for _, opt := range fs.Options.GetSlice() {
|
||||
fsOption = append(fsOption, types_3_4.FilesystemOption(opt))
|
||||
}
|
||||
wipe := fs.WipeFileSystem.GetB()
|
||||
myFs := types_3_4.Filesystem{
|
||||
Device: fsdevice,
|
||||
Path: fs.Path.GetPointer(),
|
||||
WipeFilesystem: &wipe,
|
||||
}
|
||||
if fs.Format.Get() != "" {
|
||||
myFs.Format = fs.Format.GetPointer()
|
||||
}
|
||||
if fs.Label.Get() != "" {
|
||||
myFs.Label = fs.Label.GetPointer()
|
||||
}
|
||||
if fs.MountOptions.Get() != "" {
|
||||
myFs.MountOptions = mountOptions
|
||||
}
|
||||
if fs.Options.Get() != "" {
|
||||
myFs.Options = fsOption
|
||||
}
|
||||
if fs.Options.Get() != "" {
|
||||
myFs.UUID = fs.Uuid.GetPointer()
|
||||
}
|
||||
wwlog.Debug("created file system struct: %v", myFs)
|
||||
fileSystems = append(fileSystems, myFs)
|
||||
}
|
||||
var disks []types_3_4.Disk
|
||||
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()
|
||||
label := partlabel
|
||||
myPart := types_3_4.Partition{
|
||||
Label: &label,
|
||||
Number: part.Number.GetInt(),
|
||||
ShouldExist: &shouldExist,
|
||||
WipePartitionEntry: &wipe,
|
||||
}
|
||||
if part.Guid.Get() != "" {
|
||||
myPart.GUID = part.Guid.GetPointer()
|
||||
}
|
||||
if part.Resize.Get() != "" {
|
||||
myPart.Resize = &resize
|
||||
}
|
||||
if part.SizeMiB.Get() != "" {
|
||||
myPart.SizeMiB = part.SizeMiB.GetIntPtr()
|
||||
}
|
||||
if part.StartMiB.Get() != "" {
|
||||
myPart.StartMiB = part.StartMiB.GetIntPtr()
|
||||
}
|
||||
if part.TypeGuid.Get() != "" {
|
||||
myPart.TypeGUID = part.TypeGuid.GetPointer()
|
||||
}
|
||||
partitions = append(partitions, myPart)
|
||||
}
|
||||
sort.SliceStable(partitions, func(i int, j int) bool {
|
||||
if partitions[i].Number == partitions[j].Number {
|
||||
if partitions[i].SizeMiB != nil && partitions[j].SizeMiB == nil {
|
||||
return true
|
||||
}
|
||||
if partitions[j].SizeMiB != nil && partitions[i].SizeMiB == nil {
|
||||
return false
|
||||
}
|
||||
return *partitions[i].SizeMiB < *partitions[j].SizeMiB
|
||||
}
|
||||
return partitions[i].Number < partitions[j].Number
|
||||
})
|
||||
wipe := disk.WipeTable.GetB()
|
||||
disks = append(disks, types_3_4.Disk{
|
||||
Device: diskDev,
|
||||
Partitions: partitions,
|
||||
WipeTable: &wipe,
|
||||
})
|
||||
}
|
||||
stor = types_3_4.Storage{
|
||||
Disks: disks,
|
||||
Filesystems: fileSystems,
|
||||
}
|
||||
report := stor.Validate(path.ContextPath{})
|
||||
if report.IsFatal() {
|
||||
err = fmt.Errorf(report.String())
|
||||
}
|
||||
rep = report.String()
|
||||
return
|
||||
}
|
||||
|
||||
type MyIgnition struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
type SimpleIgnitionConfig struct {
|
||||
Ignition MyIgnition `json:"ignition"`
|
||||
Storage types_3_4.Storage `json:"storage"`
|
||||
}
|
||||
|
||||
/*
|
||||
Get a simple config which can be marshalled to json
|
||||
*/
|
||||
func (node *NodeInfo) GetConfig() (conf SimpleIgnitionConfig, rep string, err error) {
|
||||
conf.Storage, err, rep = node.GetStorage()
|
||||
conf.Ignition.Version = "3.1.0"
|
||||
return
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hpcng/warewulf/internal/pkg/util"
|
||||
@@ -281,6 +282,37 @@ 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
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hpcng/warewulf/internal/pkg/node"
|
||||
warewulfconf "github.com/hpcng/warewulf/internal/pkg/config"
|
||||
"github.com/hpcng/warewulf/internal/pkg/node"
|
||||
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
@@ -37,13 +37,15 @@ type TemplateStruct struct {
|
||||
node.NodeConf
|
||||
// backward compatiblity
|
||||
Container string
|
||||
ThisNode *node.NodeInfo
|
||||
}
|
||||
|
||||
/*
|
||||
Initialize an TemplateStruct with the given node.NodeInfo
|
||||
*/
|
||||
func InitStruct(nodeInfo node.NodeInfo) TemplateStruct {
|
||||
func InitStruct(nodeInfo *node.NodeInfo) TemplateStruct {
|
||||
var tstruct TemplateStruct
|
||||
tstruct.ThisNode = nodeInfo
|
||||
controller := warewulfconf.Get()
|
||||
nodeDB, err := node.New()
|
||||
if err != nil {
|
||||
@@ -55,7 +57,7 @@ func InitStruct(nodeInfo node.NodeInfo) TemplateStruct {
|
||||
wwlog.Error("%s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
// init some convininence vars
|
||||
// init some convenience vars
|
||||
tstruct.Id = nodeInfo.Id.Get()
|
||||
tstruct.Hostname = nodeInfo.Id.Get()
|
||||
// Backwards compatibility for templates using "Keys"
|
||||
@@ -81,17 +83,17 @@ func InitStruct(nodeInfo node.NodeInfo) TemplateStruct {
|
||||
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)
|
||||
tstruct.NodeConf.GetFrom(*nodeInfo)
|
||||
// FIXME: Set ipCIDR address at this point, will fail with
|
||||
// invalid ipv4 addr
|
||||
for _, network := range tstruct.NetDevs {
|
||||
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()
|
||||
}
|
||||
// backward compatibilty
|
||||
tstruct.Container = tstruct.ContainerName
|
||||
tstruct.Container = tstruct.NodeConf.ContainerName
|
||||
|
||||
return tstruct
|
||||
|
||||
|
||||
@@ -2,13 +2,16 @@ package overlay
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/hpcng/warewulf/internal/pkg/container"
|
||||
"github.com/hpcng/warewulf/internal/pkg/util"
|
||||
warewulfconf "github.com/hpcng/warewulf/internal/pkg/config"
|
||||
"github.com/hpcng/warewulf/internal/pkg/container"
|
||||
"github.com/hpcng/warewulf/internal/pkg/node"
|
||||
"github.com/hpcng/warewulf/internal/pkg/util"
|
||||
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
@@ -100,3 +103,20 @@ func templateContainerFileInclude(containername string, filepath string) string
|
||||
}
|
||||
return strings.TrimSuffix(string(content), "\n")
|
||||
}
|
||||
|
||||
func createIgnitionJson(node *node.NodeInfo) 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")
|
||||
return ""
|
||||
}
|
||||
if err != nil {
|
||||
wwlog.Error("disk, filesystem configuration has following error: ", fmt.Sprint(err))
|
||||
return fmt.Sprint(err)
|
||||
}
|
||||
if rep != "" {
|
||||
wwlog.Warn("%s storage configuration has following non fatal problems: %s", node.Id, rep)
|
||||
}
|
||||
tmpYaml, _ := json.Marshal(&conf)
|
||||
return string(tmpYaml)
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ 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 := InitStruct(&nodeInfo)
|
||||
tstruct.BuildSource = path.Join(overlaySourceDir, location)
|
||||
wwlog.Verbose("Evaluating overlay template file: %s", location)
|
||||
destFile := strings.TrimSuffix(location, ".ww")
|
||||
@@ -266,7 +266,7 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir
|
||||
line := fileScanner.Text()
|
||||
filenameFromTemplate := reg.FindAllStringSubmatch(line, -1)
|
||||
if len(filenameFromTemplate) != 0 {
|
||||
wwlog.Debug("Found multifile comment, new filename %s", filenameFromTemplate[0][1])
|
||||
wwlog.Debug("Found multiple comment, new filename %s", filenameFromTemplate[0][1])
|
||||
if foundFileComment {
|
||||
err = CarefulWriteBuffer(path.Join(outputDir, destFileName),
|
||||
fileBuffer, backupFile, info.Mode())
|
||||
@@ -384,6 +384,14 @@ func RenderTemplateFile(fileName string, data TemplateStruct) (
|
||||
"inc": func(i int) int { return i + 1 },
|
||||
"dec": func(i int) int { return i - 1 },
|
||||
"file": func(str string) string { return fmt.Sprintf("{{ /* file \"%s\" */ }}", str) },
|
||||
"IgnitionJson": func() string {
|
||||
str := createIgnitionJson(data.ThisNode)
|
||||
if str != "" {
|
||||
return str
|
||||
}
|
||||
writeFile = false
|
||||
return ""
|
||||
},
|
||||
"abort": func() string {
|
||||
wwlog.Debug("abort file called in %s", fileName)
|
||||
writeFile = false
|
||||
@@ -397,6 +405,12 @@ func RenderTemplateFile(fileName string, data TemplateStruct) (
|
||||
"split": func(s string, d string) []string {
|
||||
return strings.Split(s, d)
|
||||
},
|
||||
"tr": func(source, old, new string) string {
|
||||
return strings.Replace(source, old, new, -1)
|
||||
},
|
||||
"replace": func(source, old, new string) string {
|
||||
return strings.Replace(source, old, new, -1)
|
||||
},
|
||||
// }).ParseGlob(path.Join(OverlayDir, destFile+".ww*"))
|
||||
}).ParseGlob(fileName)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user