Split overlays into discrete functionality and test
- Closes #987 To make the overlays testable in isolation, and after discussion with the TSC, removed host/time/source from overlay files. Signed-off-by: Jonathon Anderson <janderson@ciq.com>
This commit is contained in:
@@ -31,6 +31,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
- Bump github.com/fatih/color from 1.15.0 to 1.17.0 #1224
|
- Bump github.com/fatih/color from 1.15.0 to 1.17.0 #1224
|
||||||
- Bump github.com/coreos/ignition/v2 from 2.15.0 to 2.19.0 #1239
|
- Bump github.com/coreos/ignition/v2 from 2.15.0 to 2.19.0 #1239
|
||||||
- Disable building containers by default when calling `wwctl container copy`. #1378
|
- Disable building containers by default when calling `wwctl container copy`. #1378
|
||||||
|
- Split wwinit and generic overlays into discrete functionality. #987
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|||||||
9
Makefile
9
Makefile
@@ -100,6 +100,7 @@ install: build docs
|
|||||||
install -d -m 0755 $(DESTDIR)$(WWCHROOTDIR)
|
install -d -m 0755 $(DESTDIR)$(WWCHROOTDIR)
|
||||||
install -d -m 0755 $(DESTDIR)$(WWPROVISIONDIR)
|
install -d -m 0755 $(DESTDIR)$(WWPROVISIONDIR)
|
||||||
install -d -m 0755 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/$(WWCLIENTDIR)
|
install -d -m 0755 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/$(WWCLIENTDIR)
|
||||||
|
install -d -m 0755 $(DESTDIR)$(WWOVERLAYDIR)/wwclient/rootfs/$(WWCLIENTDIR)
|
||||||
install -d -m 0755 $(DESTDIR)$(WWOVERLAYDIR)/host/rootfs/$(TFTPDIR)/warewulf/
|
install -d -m 0755 $(DESTDIR)$(WWOVERLAYDIR)/host/rootfs/$(TFTPDIR)/warewulf/
|
||||||
install -d -m 0755 $(DESTDIR)$(WWCONFIGDIR)/examples
|
install -d -m 0755 $(DESTDIR)$(WWCONFIGDIR)/examples
|
||||||
install -d -m 0755 $(DESTDIR)$(WWCONFIGDIR)/ipxe
|
install -d -m 0755 $(DESTDIR)$(WWCONFIGDIR)/ipxe
|
||||||
@@ -127,10 +128,10 @@ install: build docs
|
|||||||
(cd overlays && find * -type l -exec cp -av {} $(DESTDIR)$(WWOVERLAYDIR)/{} \;)
|
(cd overlays && find * -type l -exec cp -av {} $(DESTDIR)$(WWOVERLAYDIR)/{} \;)
|
||||||
chmod 0755 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/init
|
chmod 0755 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/init
|
||||||
chmod 0755 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/$(WWCLIENTDIR)/wwinit
|
chmod 0755 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/$(WWCLIENTDIR)/wwinit
|
||||||
chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/etc/ssh/ssh*
|
chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/$(WWCLIENTDIR)/config.ww
|
||||||
chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/etc/NetworkManager/system-connections/ww4-managed.ww
|
chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/ssh.host_keys/rootfs/etc/ssh/ssh*
|
||||||
chmod 0644 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/etc/ssh/ssh*.pub.ww
|
chmod 0644 $(DESTDIR)$(WWOVERLAYDIR)/ssh.host_keys/rootfs/etc/ssh/ssh*.pub.ww
|
||||||
chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/warewulf/config.ww
|
chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/NetworkManager/rootfs/etc/NetworkManager/system-connections/ww4-managed.ww
|
||||||
chmod 0750 $(DESTDIR)$(WWOVERLAYDIR)/host/rootfs
|
chmod 0750 $(DESTDIR)$(WWOVERLAYDIR)/host/rootfs
|
||||||
install -m 0755 wwctl $(DESTDIR)$(BINDIR)
|
install -m 0755 wwctl $(DESTDIR)$(BINDIR)
|
||||||
install -m 0755 wwclient $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/$(WWCLIENTDIR)/wwclient
|
install -m 0755 wwclient $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/$(WWCLIENTDIR)/wwclient
|
||||||
|
|||||||
@@ -18,9 +18,24 @@ structure of a node within \fBnodes.pp\fP.
|
|||||||
.EX
|
.EX
|
||||||
defaultnode:
|
defaultnode:
|
||||||
runtime overlay:
|
runtime overlay:
|
||||||
- generic
|
- hosts
|
||||||
|
- ssh.authorized_keys
|
||||||
|
- syncuser
|
||||||
system overlay:
|
system overlay:
|
||||||
- wwinit
|
- wwinit
|
||||||
|
- wwclient
|
||||||
|
- fstab
|
||||||
|
- hostname
|
||||||
|
- ssh.host_keys
|
||||||
|
- issue
|
||||||
|
- resolv
|
||||||
|
- udev.netname
|
||||||
|
- systemd.netname
|
||||||
|
- ifcfg
|
||||||
|
- NetworkManager
|
||||||
|
- debian.interfaces
|
||||||
|
- wicked
|
||||||
|
- ignition
|
||||||
kernel:
|
kernel:
|
||||||
args: quiet crashkernel=no vga=791 net.naming-scheme=v238
|
args: quiet crashkernel=no vga=791 net.naming-scheme=v238
|
||||||
init: /sbin/init
|
init: /sbin/init
|
||||||
|
|||||||
@@ -145,8 +145,8 @@ nodes:
|
|||||||
n01 Id -- n01
|
n01 Id -- n01
|
||||||
n01 Comment default profilecomment
|
n01 Comment default profilecomment
|
||||||
n01 Ipxe -- (default)
|
n01 Ipxe -- (default)
|
||||||
n01 RuntimeOverlay -- (generic)
|
n01 RuntimeOverlay -- (hosts,ssh.authorized_keys,syncuser)
|
||||||
n01 SystemOverlay -- (wwinit)
|
n01 SystemOverlay -- (wwinit,wwclient,fstab,hostname,ssh.host_keys,issue,resolv,udev.netname,systemd.netname,ifcfg,NetworkManager,debian.interfaces,wicked,ignition)
|
||||||
n01 Root -- (initramfs)
|
n01 Root -- (initramfs)
|
||||||
n01 Init -- (/sbin/init)
|
n01 Init -- (/sbin/init)
|
||||||
n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238)
|
n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238)
|
||||||
@@ -168,8 +168,8 @@ nodes:
|
|||||||
n01 Id -- n01
|
n01 Id -- n01
|
||||||
n01 Comment SUPERSEDED nodecomment
|
n01 Comment SUPERSEDED nodecomment
|
||||||
n01 Ipxe -- (default)
|
n01 Ipxe -- (default)
|
||||||
n01 RuntimeOverlay -- (generic)
|
n01 RuntimeOverlay -- (hosts,ssh.authorized_keys,syncuser)
|
||||||
n01 SystemOverlay -- (wwinit)
|
n01 SystemOverlay -- (wwinit,wwclient,fstab,hostname,ssh.host_keys,issue,resolv,udev.netname,systemd.netname,ifcfg,NetworkManager,debian.interfaces,wicked,ignition)
|
||||||
n01 Root -- (initramfs)
|
n01 Root -- (initramfs)
|
||||||
n01 Init -- (/sbin/init)
|
n01 Init -- (/sbin/init)
|
||||||
n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238)
|
n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238)
|
||||||
@@ -251,8 +251,8 @@ nodes:
|
|||||||
stdout: `NODE FIELD PROFILE VALUE
|
stdout: `NODE FIELD PROFILE VALUE
|
||||||
n01 Id -- n01
|
n01 Id -- n01
|
||||||
n01 Ipxe -- (default)
|
n01 Ipxe -- (default)
|
||||||
n01 RuntimeOverlay -- (generic)
|
n01 RuntimeOverlay -- (hosts,ssh.authorized_keys,syncuser)
|
||||||
n01 SystemOverlay -- (wwinit)
|
n01 SystemOverlay -- (wwinit,wwclient,fstab,hostname,ssh.host_keys,issue,resolv,udev.netname,systemd.netname,ifcfg,NetworkManager,debian.interfaces,wicked,ignition)
|
||||||
n01 Root -- (initramfs)
|
n01 Root -- (initramfs)
|
||||||
n01 Init -- (/sbin/init)
|
n01 Init -- (/sbin/init)
|
||||||
n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238)
|
n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238)
|
||||||
@@ -273,12 +273,14 @@ nodes:
|
|||||||
name: "multiple overlays list",
|
name: "multiple overlays list",
|
||||||
args: []string{"-l"},
|
args: []string{"-l"},
|
||||||
stdout: `NODE NAME KERNEL OVERRIDE CONTAINER OVERLAYS (S/R)
|
stdout: `NODE NAME KERNEL OVERRIDE CONTAINER OVERLAYS (S/R)
|
||||||
n01 -- -- (wwinit)/rop1,rop2
|
n01 -- -- (wwinit,wwclient,fstab,hostname,ssh.host_keys,issue,resolv,udev.netname,systemd.netname,ifcfg,NetworkManager,debian.interfaces,wicked,ignition)/rop1,rop2
|
||||||
`},
|
`},
|
||||||
{
|
{
|
||||||
inDb: `WW_INTERNAL: 45
|
inDb: `WW_INTERNAL: 45
|
||||||
nodeprofiles:
|
nodeprofiles:
|
||||||
p1:
|
p1:
|
||||||
|
system overlay:
|
||||||
|
- sop1
|
||||||
runtime overlay:
|
runtime overlay:
|
||||||
- rop1
|
- rop1
|
||||||
- rop2
|
- rop2
|
||||||
@@ -293,12 +295,14 @@ nodes:
|
|||||||
name: "multiple overlays list",
|
name: "multiple overlays list",
|
||||||
args: []string{"-l"},
|
args: []string{"-l"},
|
||||||
stdout: `NODE NAME KERNEL OVERRIDE CONTAINER OVERLAYS (S/R)
|
stdout: `NODE NAME KERNEL OVERRIDE CONTAINER OVERLAYS (S/R)
|
||||||
n01 -- -- (wwinit)/rop2,nop1 ~{rop1}
|
n01 -- -- sop1/rop2,nop1 ~{rop1}
|
||||||
`},
|
`},
|
||||||
{
|
{
|
||||||
inDb: `WW_INTERNAL: 45
|
inDb: `WW_INTERNAL: 45
|
||||||
nodeprofiles:
|
nodeprofiles:
|
||||||
p1:
|
p1:
|
||||||
|
system overlay:
|
||||||
|
- sop1
|
||||||
runtime overlay:
|
runtime overlay:
|
||||||
- rop1
|
- rop1
|
||||||
- rop2
|
- rop2
|
||||||
@@ -316,7 +320,7 @@ nodes:
|
|||||||
n01 Id -- n01
|
n01 Id -- n01
|
||||||
n01 Ipxe -- (default)
|
n01 Ipxe -- (default)
|
||||||
n01 RuntimeOverlay SUPERSEDED rop2,nop1 ~{rop1}
|
n01 RuntimeOverlay SUPERSEDED rop2,nop1 ~{rop1}
|
||||||
n01 SystemOverlay -- (wwinit)
|
n01 SystemOverlay p1 sop1
|
||||||
n01 Root -- (initramfs)
|
n01 Root -- (initramfs)
|
||||||
n01 Init -- (/sbin/init)
|
n01 Init -- (/sbin/init)
|
||||||
n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238)
|
n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238)
|
||||||
@@ -340,7 +344,7 @@ nodes:
|
|||||||
stdout: `NODE FIELD PROFILE VALUE
|
stdout: `NODE FIELD PROFILE VALUE
|
||||||
n01 Id -- n01
|
n01 Id -- n01
|
||||||
n01 Ipxe -- (default)
|
n01 Ipxe -- (default)
|
||||||
n01 RuntimeOverlay -- (generic)
|
n01 RuntimeOverlay -- (hosts,ssh.authorized_keys,syncuser)
|
||||||
n01 SystemOverlay SUPERSEDED profileinit,nodeinit
|
n01 SystemOverlay SUPERSEDED profileinit,nodeinit
|
||||||
n01 Root -- (initramfs)
|
n01 Root -- (initramfs)
|
||||||
n01 Init -- (/sbin/init)
|
n01 Init -- (/sbin/init)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package show
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -24,30 +25,33 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
|||||||
overlaySourceDir = overlay.OverlaySourceDir(overlayName)
|
overlaySourceDir = overlay.OverlaySourceDir(overlayName)
|
||||||
|
|
||||||
if !util.IsDir(overlaySourceDir) {
|
if !util.IsDir(overlaySourceDir) {
|
||||||
wwlog.Error("Overlay does not exist: %s", overlayName)
|
err := errors.New("Overlay does not exist")
|
||||||
os.Exit(1)
|
wwlog.Error("%s: %s", err, overlayName)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
overlayFile := path.Join(overlaySourceDir, fileName)
|
overlayFile := path.Join(overlaySourceDir, fileName)
|
||||||
|
|
||||||
if !util.IsFile(overlayFile) {
|
if !util.IsFile(overlayFile) {
|
||||||
wwlog.Error("File does not exist within overlay: %s:%s", overlayName, fileName)
|
err := errors.New("File does not exist within overlay")
|
||||||
os.Exit(1)
|
wwlog.Error("%s: %s:%s", err, overlayName, fileName)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if NodeName == "" {
|
if NodeName == "" {
|
||||||
f, err := os.ReadFile(overlayFile)
|
f, err := os.ReadFile(overlayFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Error("Could not read file: %s", err)
|
wwlog.Error("Could not read file: %s", err)
|
||||||
os.Exit(1)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
wwlog.Info(string(f))
|
wwlog.Output(string(f))
|
||||||
} else {
|
} else {
|
||||||
if !util.IsFile(overlayFile) {
|
if !util.IsFile(overlayFile) {
|
||||||
wwlog.Debug("%s is not a file", overlayFile)
|
wwlog.Debug("%s is not a file", overlayFile)
|
||||||
wwlog.Error("%s:%s is not a file", overlayName, fileName)
|
err := errors.New("Not a file")
|
||||||
os.Exit(1)
|
wwlog.Error("%s: %s:%s", err, overlayName, fileName)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if filepath.Ext(overlayFile) != ".ww" {
|
if filepath.Ext(overlayFile) != ".ww" {
|
||||||
wwlog.Warn("%s lacks the '.ww' suffix, will not be rendered in an overlay", fileName)
|
wwlog.Warn("%s lacks the '.ww' suffix, will not be rendered in an overlay", fileName)
|
||||||
@@ -56,12 +60,12 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
|||||||
nodeDB, err := node.New()
|
nodeDB, err := node.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Error("Could not open node configuration: %s", err)
|
wwlog.Error("Could not open node configuration: %s", err)
|
||||||
os.Exit(1)
|
return err
|
||||||
}
|
}
|
||||||
nodes, err := nodeDB.FindAllNodes()
|
nodes, err := nodeDB.FindAllNodes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Error("Could not get node list: %s", err)
|
wwlog.Error("Could not get node list: %s", err)
|
||||||
os.Exit(1)
|
return err
|
||||||
}
|
}
|
||||||
filteredNodes := node.FilterByName(nodes, []string{NodeName})
|
filteredNodes := node.FilterByName(nodes, []string{NodeName})
|
||||||
if hostName, err := os.Hostname(); err != nil {
|
if hostName, err := os.Hostname(); err != nil {
|
||||||
@@ -73,8 +77,9 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
|||||||
hostNodeInfo.ClusterName.Set(hostName)
|
hostNodeInfo.ClusterName.Set(hostName)
|
||||||
filteredNodes = append(filteredNodes, *hostNodeInfo)
|
filteredNodes = append(filteredNodes, *hostNodeInfo)
|
||||||
} else if len(filteredNodes) != 1 {
|
} else if len(filteredNodes) != 1 {
|
||||||
wwlog.Error("%v does not identify a single node", NodeName)
|
err := errors.New("Not a single node")
|
||||||
os.Exit(1)
|
wwlog.Error("%s: %v", err, NodeName)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tstruct := overlay.InitStruct(&filteredNodes[0])
|
tstruct := overlay.InitStruct(&filteredNodes[0])
|
||||||
@@ -97,10 +102,11 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
|||||||
wwlog.Debug("Found multifile comment, new filename %s", filenameFromTemplate[0][1])
|
wwlog.Debug("Found multifile comment, new filename %s", filenameFromTemplate[0][1])
|
||||||
if foundFileComment {
|
if foundFileComment {
|
||||||
if !Quiet {
|
if !Quiet {
|
||||||
wwlog.Info("backupFile: %v\nwriteFile: %v", backupFile, writeFile)
|
wwlog.Info("backupFile: %v", backupFile)
|
||||||
wwlog.Info("Filename: %s\n", destFileName)
|
wwlog.Info("writeFile: %v", writeFile)
|
||||||
|
wwlog.Info("Filename: %s", destFileName)
|
||||||
}
|
}
|
||||||
wwlog.Info("%s", outBuffer.String())
|
wwlog.Output(outBuffer.String())
|
||||||
outBuffer.Reset()
|
outBuffer.Reset()
|
||||||
}
|
}
|
||||||
destFileName = filenameFromTemplate[0][1]
|
destFileName = filenameFromTemplate[0][1]
|
||||||
@@ -110,10 +116,11 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !Quiet {
|
if !Quiet {
|
||||||
wwlog.Info("backupFile: %v\nwriteFile: %v", backupFile, writeFile)
|
wwlog.Info("backupFile: %v", backupFile)
|
||||||
wwlog.Info("Filename: %s\n", destFileName)
|
wwlog.Info("writeFile: %v", writeFile)
|
||||||
|
wwlog.Info("Filename: %s", destFileName)
|
||||||
}
|
}
|
||||||
wwlog.Info("%s", outBuffer.String())
|
wwlog.Output("%s", outBuffer.String())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
|
||||||
|
|
||||||
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
"github.com/warewulf/warewulf/internal/pkg/warewulfd"
|
"github.com/warewulf/warewulf/internal/pkg/warewulfd"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -24,6 +23,35 @@ overlay name {{ .Overlay }}
|
|||||||
|
|
||||||
func Test_Overlay_List(t *testing.T) {
|
func Test_Overlay_List(t *testing.T) {
|
||||||
env := testenv.New(t)
|
env := testenv.New(t)
|
||||||
|
env.WriteFile(t, "etc/warewulf/warewulf.conf", `WW_INTERNAL: 43
|
||||||
|
ipaddr: 192.168.0.1/24
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
network: 192.168.0.0
|
||||||
|
warewulf:
|
||||||
|
port: 9873
|
||||||
|
secure: false
|
||||||
|
update interval: 60
|
||||||
|
autobuild overlays: true
|
||||||
|
host overlay: true
|
||||||
|
syslog: true
|
||||||
|
dhcp:
|
||||||
|
enabled: true
|
||||||
|
range start: 192.168.0.100
|
||||||
|
range end: 192.168.0.199
|
||||||
|
tftp:
|
||||||
|
enabled: false
|
||||||
|
nfs:
|
||||||
|
enabled: true
|
||||||
|
export paths:
|
||||||
|
- path: /home
|
||||||
|
export options: rw,sync
|
||||||
|
mount options: defaults
|
||||||
|
mount: true
|
||||||
|
- path: /opt
|
||||||
|
export options: ro,sync,no_root_squash
|
||||||
|
mount options: defaults
|
||||||
|
mount: false`)
|
||||||
|
|
||||||
env.WriteFile(t, "etc/warewulf/nodes.conf",
|
env.WriteFile(t, "etc/warewulf/nodes.conf",
|
||||||
`WW_INTERNAL: 45
|
`WW_INTERNAL: 45
|
||||||
nodeprofiles:
|
nodeprofiles:
|
||||||
|
|||||||
@@ -23,9 +23,24 @@ var (
|
|||||||
var FallBackConf = `---
|
var FallBackConf = `---
|
||||||
defaultnode:
|
defaultnode:
|
||||||
runtime overlay:
|
runtime overlay:
|
||||||
- generic
|
- hosts
|
||||||
|
- ssh.authorized_keys
|
||||||
|
- syncuser
|
||||||
system overlay:
|
system overlay:
|
||||||
- wwinit
|
- wwinit
|
||||||
|
- wwclient
|
||||||
|
- fstab
|
||||||
|
- hostname
|
||||||
|
- ssh.host_keys
|
||||||
|
- issue
|
||||||
|
- resolv
|
||||||
|
- udev.netname
|
||||||
|
- systemd.netname
|
||||||
|
- ifcfg
|
||||||
|
- NetworkManager
|
||||||
|
- debian.interfaces
|
||||||
|
- wicked
|
||||||
|
- ignition
|
||||||
kernel:
|
kernel:
|
||||||
args: quiet crashkernel=no vga=791 net.naming-scheme=v238
|
args: quiet crashkernel=no vga=791 net.naming-scheme=v238
|
||||||
init: /sbin/init
|
init: /sbin/init
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Build all overlays (runtime and generic) for a node
|
Build all overlays for a node
|
||||||
*/
|
*/
|
||||||
func BuildAllOverlays(nodes []node.NodeInfo) error {
|
func BuildAllOverlays(nodes []node.NodeInfo) error {
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ const Systemddir = "usr/lib/systemd/system"
|
|||||||
const WWOverlaydir = "var/lib/warewulf/overlays"
|
const WWOverlaydir = "var/lib/warewulf/overlays"
|
||||||
const WWChrootdir = "var/lib/warewulf/chroots"
|
const WWChrootdir = "var/lib/warewulf/chroots"
|
||||||
const WWProvisiondir = "srv/warewulf"
|
const WWProvisiondir = "srv/warewulf"
|
||||||
const WWClientdir = "warewulf"
|
|
||||||
|
|
||||||
// New creates a test environment in a temporary directory and configures
|
// New creates a test environment in a temporary directory and configures
|
||||||
// Warewulf to use it.
|
// Warewulf to use it.
|
||||||
@@ -78,7 +77,7 @@ func New(t *testing.T) (env *TestEnv) {
|
|||||||
conf.Paths.WWOverlaydir = env.GetPath(WWOverlaydir)
|
conf.Paths.WWOverlaydir = env.GetPath(WWOverlaydir)
|
||||||
conf.Paths.WWChrootdir = env.GetPath(WWChrootdir)
|
conf.Paths.WWChrootdir = env.GetPath(WWChrootdir)
|
||||||
conf.Paths.WWProvisiondir = env.GetPath(WWProvisiondir)
|
conf.Paths.WWProvisiondir = env.GetPath(WWProvisiondir)
|
||||||
conf.Paths.WWClientdir = env.GetPath(WWClientdir)
|
conf.Paths.WWClientdir = "/warewulf"
|
||||||
|
|
||||||
for _, confPath := range []string{
|
for _, confPath := range []string{
|
||||||
conf.Paths.Sysconfdir,
|
conf.Paths.Sysconfdir,
|
||||||
@@ -92,7 +91,6 @@ func New(t *testing.T) (env *TestEnv) {
|
|||||||
conf.Paths.WWOverlaydir,
|
conf.Paths.WWOverlaydir,
|
||||||
conf.Paths.WWChrootdir,
|
conf.Paths.WWChrootdir,
|
||||||
conf.Paths.WWProvisiondir,
|
conf.Paths.WWProvisiondir,
|
||||||
conf.Paths.WWClientdir,
|
|
||||||
} {
|
} {
|
||||||
env.MkdirAll(t, confPath)
|
env.MkdirAll(t, confPath)
|
||||||
}
|
}
|
||||||
@@ -133,6 +131,15 @@ func (env *TestEnv) WriteFile(t *testing.T, fileName string, content string) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImportFile writes the contents of inputFileName to fileName,
|
||||||
|
// creating any necessary intermediate directories relative to the
|
||||||
|
// test environment.
|
||||||
|
func (env *TestEnv) ImportFile(t *testing.T, fileName string, inputFileName string) {
|
||||||
|
buffer, err := os.ReadFile(inputFileName)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
env.WriteFile(t, fileName, string(buffer))
|
||||||
|
}
|
||||||
|
|
||||||
// ReadFile returns the content of fileName as converted to a
|
// ReadFile returns the content of fileName as converted to a
|
||||||
// string.
|
// string.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ func DefaultFormatter(logLevel int, rec *LogRecord) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if rec.Level == INFO && logLevel == INFO {
|
if (rec.Level == INFO || rec.Level == OUT) && logLevel == INFO {
|
||||||
// NOTE: this is a bit strange, but for user-friendliness it makes sense
|
// NOTE: this is a bit strange, but for user-friendliness it makes sense
|
||||||
// to not pollute the messages unless something bad happens (by default).
|
// to not pollute the messages unless something bad happens (by default).
|
||||||
// if logLevel > INFO, then level == INFO messages should not be printed anyway,
|
// if logLevel > INFO, then level == INFO messages should not be printed anyway,
|
||||||
|
|||||||
112
overlays/NetworkManager/internal/networkmanager_test.go
Normal file
112
overlays/NetworkManager/internal/networkmanager_test.go
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
package networkmanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_networkmanagerOverlay(t *testing.T) {
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/NetworkManager/rootfs/etc/NetworkManager/conf.d/ww4-unmanaged.ww", "../rootfs/etc/NetworkManager/conf.d/ww4-unmanaged.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/NetworkManager/rootfs/etc/NetworkManager/system-connections/ww4-managed.ww", "../rootfs/etc/NetworkManager/system-connections/ww4-managed.ww")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "NetworkManager:ww4-unmanaged.ww",
|
||||||
|
args: []string{"--render", "node1", "NetworkManager", "etc/NetworkManager/conf.d/ww4-unmanaged.ww"},
|
||||||
|
log: networkmanager_unmanaged,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "NetworkManager:ww4-managed.ww",
|
||||||
|
args: []string{"--render", "node1", "NetworkManager", "etc/NetworkManager/system-connections/ww4-managed.ww"},
|
||||||
|
log: networkmanager_managed,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
assert.Equal(t, tt.log, logbuf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const networkmanager_unmanaged string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: warewulf-unmanaged.conf
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
[main]
|
||||||
|
plugins=keyfile
|
||||||
|
|
||||||
|
[keyfile]
|
||||||
|
unmanaged-devices=except:interface-name:wwnet0,except:interface-name:wwnet1,
|
||||||
|
`
|
||||||
|
|
||||||
|
const networkmanager_managed string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: warewulf-default.conf
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
[connection]
|
||||||
|
id=default
|
||||||
|
interface-name=wwnet0
|
||||||
|
|
||||||
|
type=ethernet
|
||||||
|
autoconnect=true
|
||||||
|
[ethernet]
|
||||||
|
mac-address=e6:92:39:49:7b:03
|
||||||
|
# bond
|
||||||
|
[ipv4]
|
||||||
|
address=192.168.3.21/24
|
||||||
|
gateway=192.168.3.1
|
||||||
|
method=manual
|
||||||
|
|
||||||
|
[ipv6]
|
||||||
|
addr-gen-mode=stable-privacy
|
||||||
|
method=ignore
|
||||||
|
never-default=true
|
||||||
|
backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: warewulf-secondary.conf
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
[connection]
|
||||||
|
id=secondary
|
||||||
|
interface-name=wwnet1
|
||||||
|
|
||||||
|
type=ethernet
|
||||||
|
autoconnect=true
|
||||||
|
[ethernet]
|
||||||
|
mac-address=9a:77:29:73:14:f1
|
||||||
|
# bond
|
||||||
|
[ipv4]
|
||||||
|
address=192.168.3.22/24
|
||||||
|
gateway=192.168.3.1
|
||||||
|
method=manual
|
||||||
|
dns=8.8.8.8;8.8.4.4;
|
||||||
|
|
||||||
|
[ipv6]
|
||||||
|
addr-gen-mode=stable-privacy
|
||||||
|
method=ignore
|
||||||
|
never-default=true
|
||||||
|
`
|
||||||
58
overlays/NetworkManager/internal/nodes.conf
Normal file
58
overlays/NetworkManager/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
@@ -1,12 +1,6 @@
|
|||||||
{{- $host := .BuildHost }}
|
|
||||||
{{- $time := .BuildTime }}
|
|
||||||
{{- $source := .BuildSource }}
|
|
||||||
{{- $filename := print "warewulf-unmanaged.conf" }}
|
{{- $filename := print "warewulf-unmanaged.conf" }}
|
||||||
{{- file $filename }}
|
{{- file $filename }}
|
||||||
# This file is autogenerated by warewulf
|
# This file is autogenerated by warewulf
|
||||||
# Host: {{ $host }}
|
|
||||||
# Time: {{ $time }}
|
|
||||||
# Source: {{ $source }}
|
|
||||||
[main]
|
[main]
|
||||||
plugins=keyfile
|
plugins=keyfile
|
||||||
|
|
||||||
@@ -1,13 +1,7 @@
|
|||||||
{{- $host := .BuildHost }}
|
|
||||||
{{- $time := .BuildTime }}
|
|
||||||
{{- $source := .BuildSource }}
|
|
||||||
{{- range $connection_id, $netdev := .NetDevs }}
|
{{- range $connection_id, $netdev := .NetDevs }}
|
||||||
{{- $filename := print "warewulf-" $connection_id ".conf" }}
|
{{- $filename := print "warewulf-" $connection_id ".conf" }}
|
||||||
{{- file $filename }}
|
{{- file $filename }}
|
||||||
# This file is autogenerated by warewulf
|
# This file is autogenerated by warewulf
|
||||||
# Host: {{ $host }}
|
|
||||||
# Time: {{ $time }}
|
|
||||||
# Source: {{ $source }}
|
|
||||||
[connection]
|
[connection]
|
||||||
id={{ $connection_id }}
|
id={{ $connection_id }}
|
||||||
interface-name={{ $netdev.Device }}
|
interface-name={{ $netdev.Device }}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package debian_interfaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_wickedOverlay(t *testing.T) {
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/debian.interfaces/rootfs/etc/network/interfaces.d/default.ww", "../rootfs/etc/network/interfaces.d/default.ww")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "debian.interfaces",
|
||||||
|
args: []string{"--render", "node1", "debian.interfaces", "etc/network/interfaces.d/default.ww"},
|
||||||
|
log: debian_interfaces,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
assert.Equal(t, tt.log, logbuf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const debian_interfaces string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: default
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
auto wwnet0
|
||||||
|
allow-hotplug wwnet0
|
||||||
|
iface wwnet0 inet static
|
||||||
|
address 192.168.3.21
|
||||||
|
netmask 255.255.255.0
|
||||||
|
gateway 192.168.3.1
|
||||||
|
mtu
|
||||||
|
backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: secondary
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
auto wwnet1
|
||||||
|
allow-hotplug wwnet1
|
||||||
|
iface wwnet1 inet static
|
||||||
|
address 192.168.3.22
|
||||||
|
netmask 255.255.255.0
|
||||||
|
gateway 192.168.3.1
|
||||||
|
mtu
|
||||||
|
up ifmetric wwnet1 30
|
||||||
|
`
|
||||||
58
overlays/debian.interfaces/internal/nodes.conf
Normal file
58
overlays/debian.interfaces/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
@@ -1,13 +1,7 @@
|
|||||||
{{- $host := .BuildHost }}
|
|
||||||
{{- $time := .BuildTime }}
|
|
||||||
{{- $source := .BuildSource }}
|
|
||||||
{{range $devname, $netdev := .ThisNode.NetDevs -}}
|
{{range $devname, $netdev := .ThisNode.NetDevs -}}
|
||||||
{{- $filename := print $devname }}
|
{{- $filename := print $devname }}
|
||||||
{{- file $filename }}
|
{{- file $filename }}
|
||||||
# This file is autogenerated by warewulf
|
# This file is autogenerated by warewulf
|
||||||
# Host: {{ $host }}
|
|
||||||
# Time: {{ $time }}
|
|
||||||
# Source: {{ $source }}
|
|
||||||
{{ if $netdev.OnBoot.GetB -}}
|
{{ if $netdev.OnBoot.GetB -}}
|
||||||
auto {{$netdev.Device.Get}}
|
auto {{$netdev.Device.Get}}
|
||||||
{{ end -}}
|
{{ end -}}
|
||||||
292
overlays/debug/internal/debug_test.go
Normal file
292
overlays/debug/internal/debug_test.go
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
package debug
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_debugOverlay(t *testing.T) {
|
||||||
|
variableData := regexp.MustCompile(`(?m)(BuildTime|BuildTimeUnix|BuildSource|DataStore):.*$`)
|
||||||
|
hostname, _ := os.Hostname()
|
||||||
|
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/debug/rootfs/warewulf/template-variables.md.ww", "../rootfs/warewulf/template-variables.md.ww")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "debug",
|
||||||
|
args: []string{"--render", "node1", "debug", "warewulf/template-variables.md.ww"},
|
||||||
|
log: debug,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
assert.Equal(t, strings.Replace(tt.log, "%HOSTNAME%", hostname, -1), variableData.ReplaceAllString(logbuf.String(), "${1}: REMOVED_BY_TEST"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const debug = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: warewulf/template-variables.md
|
||||||
|
# Warewulf template variables
|
||||||
|
|
||||||
|
This Warewulf template serves as a complete example of the variables
|
||||||
|
available to Warewulf templates. It may also be rendered against a
|
||||||
|
node to debug its apparent configuration.
|
||||||
|
|
||||||
|
wwctl overlay show --render $nodename debug /warewulf/template-variables.md.ww
|
||||||
|
|
||||||
|
The template data structure is defined in
|
||||||
|
internal/pkg/overlay/datastructure.go, though it also references
|
||||||
|
data from other structures.
|
||||||
|
|
||||||
|
|
||||||
|
## Node
|
||||||
|
|
||||||
|
- Id: node1
|
||||||
|
- Hostname: node1
|
||||||
|
- Comment:
|
||||||
|
- ClusterName:
|
||||||
|
- ContainerName: rockylinux-9
|
||||||
|
- Ipxe: default
|
||||||
|
- RuntimeOverlay: hosts,ssh.authorized_keys,syncuser
|
||||||
|
- SystemOverlay: wwinit,wwclient,fstab,hostname,ssh.host_keys,issue,resolv,udev.netname,systemd.netname,ifcfg,NetworkManager,debian.interfaces,wicked,ignition
|
||||||
|
- Init: /sbin/init
|
||||||
|
- Root: initramfs
|
||||||
|
- AssetKey:
|
||||||
|
- Discoverable:
|
||||||
|
- Profiles: empty
|
||||||
|
- Tags:
|
||||||
|
- TagsDel:
|
||||||
|
- Keys:
|
||||||
|
- Kernel:
|
||||||
|
- Version:
|
||||||
|
- Override:
|
||||||
|
- Args: quiet crashkernel=no vga=791 net.naming-scheme=v238
|
||||||
|
- Ipmi:
|
||||||
|
- UserName: user
|
||||||
|
- Password: password
|
||||||
|
- Ipaddr: 192.168.4.21
|
||||||
|
- Netmask: 255.255.255.0
|
||||||
|
- Port:
|
||||||
|
- Gateway: 192.168.4.1
|
||||||
|
- Interface:
|
||||||
|
- Write: true
|
||||||
|
- Tags:
|
||||||
|
- TagsDel:
|
||||||
|
- NetDevs[default]:
|
||||||
|
- Type: ethernet
|
||||||
|
- OnBoot: true
|
||||||
|
- Device: wwnet0
|
||||||
|
- Hwaddr: e6:92:39:49:7b:03
|
||||||
|
- Ipaddr: 192.168.3.21
|
||||||
|
- IpCIDR: 192.168.3.21/24
|
||||||
|
- Ipaddr6:
|
||||||
|
- Prefix:
|
||||||
|
- Netmask: 255.255.255.0
|
||||||
|
- Gateway: 192.168.3.1
|
||||||
|
- MTU:
|
||||||
|
- Primary: true
|
||||||
|
- Default:
|
||||||
|
- Tags:
|
||||||
|
- TagsDel:
|
||||||
|
- NetDevs[secondary]:
|
||||||
|
- Type: ethernet
|
||||||
|
- OnBoot: true
|
||||||
|
- Device: wwnet1
|
||||||
|
- Hwaddr: 9a:77:29:73:14:f1
|
||||||
|
- Ipaddr: 192.168.3.22
|
||||||
|
- IpCIDR: 192.168.3.22/24
|
||||||
|
- Ipaddr6:
|
||||||
|
- Prefix:
|
||||||
|
- Netmask: 255.255.255.0
|
||||||
|
- Gateway: 192.168.3.1
|
||||||
|
- MTU:
|
||||||
|
- Primary:
|
||||||
|
- Default:
|
||||||
|
- Tags: DNS1=8.8.8.8 DNS2=8.8.4.4
|
||||||
|
- TagsDel:
|
||||||
|
|
||||||
|
|
||||||
|
## Build variables
|
||||||
|
|
||||||
|
- BuildHost: %HOSTNAME%
|
||||||
|
- BuildTime: REMOVED_BY_TEST
|
||||||
|
- BuildTimeUnix: REMOVED_BY_TEST
|
||||||
|
- BuildSource: REMOVED_BY_TEST
|
||||||
|
- Overlay: debug
|
||||||
|
|
||||||
|
|
||||||
|
## Network
|
||||||
|
|
||||||
|
- Ipaddr:
|
||||||
|
- Ipaddr6:
|
||||||
|
- Netmask:
|
||||||
|
- Network:
|
||||||
|
- NetworkCIDR: <nil>
|
||||||
|
- Ipv6: false
|
||||||
|
|
||||||
|
|
||||||
|
## Services
|
||||||
|
|
||||||
|
### DHCP
|
||||||
|
|
||||||
|
- Dhcp.Enabled: true
|
||||||
|
- Dhcp.Template: default
|
||||||
|
- Dhcp.RangeStart:
|
||||||
|
- Dhcp.RangeEnd:
|
||||||
|
- Dhcp.SystemdName: dhcpd
|
||||||
|
|
||||||
|
|
||||||
|
### NFS
|
||||||
|
|
||||||
|
- Enabled: true
|
||||||
|
- SystemdName: nfsd
|
||||||
|
|
||||||
|
### SSH
|
||||||
|
- Key types:
|
||||||
|
- rsa
|
||||||
|
- dsa
|
||||||
|
- ecdsa
|
||||||
|
- ed25519
|
||||||
|
- First key type: rsa
|
||||||
|
|
||||||
|
### Warewulf
|
||||||
|
|
||||||
|
- Port: 9983
|
||||||
|
- Secure: true
|
||||||
|
- UpdateInterval: 60
|
||||||
|
- AutobuildOverlays: true
|
||||||
|
- EnableHostOverlay: true
|
||||||
|
- Syslog: false
|
||||||
|
- DataStore: REMOVED_BY_TEST
|
||||||
|
|
||||||
|
|
||||||
|
### Other nodes
|
||||||
|
|
||||||
|
- AllNodes[0]:
|
||||||
|
- Id: node1
|
||||||
|
- Comment:
|
||||||
|
- ClusterName:
|
||||||
|
- ContainerName: rockylinux-9
|
||||||
|
- Ipxe: default
|
||||||
|
- RuntimeOverlay: hosts
|
||||||
|
- SystemOverlay: wwinit
|
||||||
|
- Root: initramfs
|
||||||
|
- Discoverable:
|
||||||
|
- Init: /sbin/init
|
||||||
|
- AssetKey:
|
||||||
|
- Profiles: empty
|
||||||
|
- Tags:
|
||||||
|
- Kernel
|
||||||
|
- Override:
|
||||||
|
- Args: quiet crashkernel=no vga=791 net.naming-scheme=v238
|
||||||
|
- Ipmi:
|
||||||
|
- Ipaddr: 192.168.4.21
|
||||||
|
- Netmask: 255.255.255.0
|
||||||
|
- Port:
|
||||||
|
- Gateway: 192.168.4.1
|
||||||
|
- UserName: user
|
||||||
|
- Password: password
|
||||||
|
- Interface:
|
||||||
|
- Write: true
|
||||||
|
- Tags:
|
||||||
|
- NetDevs[default]:
|
||||||
|
- Type: ethernet
|
||||||
|
- OnBoot: true
|
||||||
|
- Device: wwnet0
|
||||||
|
- Hwaddr: e6:92:39:49:7b:03
|
||||||
|
- Ipaddr: 192.168.3.21
|
||||||
|
- Ipaddr6:
|
||||||
|
- IpCIDR:
|
||||||
|
- Prefix:
|
||||||
|
- Netmask: 255.255.255.0
|
||||||
|
- Gateway: 192.168.3.1
|
||||||
|
- MTU:
|
||||||
|
- Primary: true
|
||||||
|
- Tags:
|
||||||
|
- NetDevs[secondary]:
|
||||||
|
- Type: ethernet
|
||||||
|
- OnBoot: true
|
||||||
|
- Device: wwnet1
|
||||||
|
- Hwaddr: 9a:77:29:73:14:f1
|
||||||
|
- Ipaddr: 192.168.3.22
|
||||||
|
- Ipaddr6:
|
||||||
|
- IpCIDR:
|
||||||
|
- Prefix:
|
||||||
|
- Netmask: 255.255.255.0
|
||||||
|
- Gateway: 192.168.3.1
|
||||||
|
- MTU:
|
||||||
|
- Primary:
|
||||||
|
- Tags: DNS1=8.8.8.8 DNS2=8.8.4.4
|
||||||
|
- AllNodes[1]:
|
||||||
|
- Id: node2
|
||||||
|
- Comment:
|
||||||
|
- ClusterName:
|
||||||
|
- ContainerName:
|
||||||
|
- Ipxe: default
|
||||||
|
- RuntimeOverlay: hosts
|
||||||
|
- SystemOverlay: wwinit
|
||||||
|
- Root: initramfs
|
||||||
|
- Discoverable:
|
||||||
|
- Init: /sbin/init
|
||||||
|
- AssetKey:
|
||||||
|
- Profiles: empty
|
||||||
|
- Tags:
|
||||||
|
- Kernel
|
||||||
|
- Override:
|
||||||
|
- Args: quiet crashkernel=no vga=791 net.naming-scheme=v238
|
||||||
|
- Ipmi:
|
||||||
|
- Ipaddr:
|
||||||
|
- Netmask:
|
||||||
|
- Port:
|
||||||
|
- Gateway:
|
||||||
|
- UserName:
|
||||||
|
- Password:
|
||||||
|
- Interface:
|
||||||
|
- Write:
|
||||||
|
- Tags:
|
||||||
|
- NetDevs[default]:
|
||||||
|
- Type: ethernet
|
||||||
|
- OnBoot: true
|
||||||
|
- Device: wwnet0
|
||||||
|
- Hwaddr: e6:92:39:49:7b:04
|
||||||
|
- Ipaddr: 192.168.3.23
|
||||||
|
- Ipaddr6:
|
||||||
|
- IpCIDR:
|
||||||
|
- Prefix:
|
||||||
|
- Netmask: 255.255.255.0
|
||||||
|
- Gateway: 192.168.3.1
|
||||||
|
- MTU:
|
||||||
|
- Primary: true
|
||||||
|
- Tags:
|
||||||
|
|
||||||
|
`
|
||||||
58
overlays/debug/internal/nodes.conf
Normal file
58
overlays/debug/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
@@ -4,12 +4,10 @@ This Warewulf template serves as a complete example of the variables
|
|||||||
available to Warewulf templates. It may also be rendered against a
|
available to Warewulf templates. It may also be rendered against a
|
||||||
node to debug its apparent configuration.
|
node to debug its apparent configuration.
|
||||||
|
|
||||||
```sh
|
wwctl overlay show --render $nodename debug /warewulf/template-variables.md.ww
|
||||||
wwctl overlay show --render $nodename debug /warewulf/template-variables.md.ww
|
|
||||||
```
|
|
||||||
|
|
||||||
The template data structure is defined in
|
The template data structure is defined in
|
||||||
`internal/pkg/overlay/datastructure.go`, though it also references
|
internal/pkg/overlay/datastructure.go, though it also references
|
||||||
data from other structures.
|
data from other structures.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
68
overlays/fstab/internal/fstab_test.go
Normal file
68
overlays/fstab/internal/fstab_test.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package fstab
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/config"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_fstabOverlay(t *testing.T) {
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "etc/warewulf/warewulf.conf", "warewulf.conf")
|
||||||
|
assert.NoError(t, config.Get().Read(env.GetPath("etc/warewulf/warewulf.conf")))
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/fstab/rootfs/etc/fstab.ww", "../rootfs/etc/fstab.ww")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "/etc/fstab",
|
||||||
|
args: []string{"--render", "node1", "fstab", "etc/fstab.ww"},
|
||||||
|
log: fstab,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
assert.Equal(t, tt.log, logbuf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fstab string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/fstab
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
rootfs / tmpfs defaults 0 0
|
||||||
|
devpts /dev/pts devpts gid=5,mode=620 0 0
|
||||||
|
tmpfs /run/shm tmpfs defaults 0 0
|
||||||
|
sysfs /sys sysfs defaults 0 0
|
||||||
|
proc /proc proc defaults 0 0
|
||||||
|
# mounts for local file systems created with ignition in nodes.conf
|
||||||
|
# all with noauto as mounts happens with systemd units
|
||||||
|
/dev/disk/by-partlabel/scratch /scratch btrfs noauto,defaults 0 0
|
||||||
|
/dev/disk/by-partlabel/swap swap swap noauto,defaults 0 0
|
||||||
|
# nfs mounts provided in warewulf.conf
|
||||||
|
192.168.0.1:/home /home nfs defaults 0 0
|
||||||
|
`
|
||||||
58
overlays/fstab/internal/nodes.conf
Normal file
58
overlays/fstab/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
28
overlays/fstab/internal/warewulf.conf
Normal file
28
overlays/fstab/internal/warewulf.conf
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
WW_INTERNAL: 43
|
||||||
|
ipaddr: 192.168.0.1/24
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
network: 192.168.0.0
|
||||||
|
warewulf:
|
||||||
|
port: 9873
|
||||||
|
secure: false
|
||||||
|
update interval: 60
|
||||||
|
autobuild overlays: true
|
||||||
|
host overlay: true
|
||||||
|
syslog: true
|
||||||
|
dhcp:
|
||||||
|
enabled: true
|
||||||
|
range start: 192.168.0.100
|
||||||
|
range end: 192.168.0.199
|
||||||
|
tftp:
|
||||||
|
enabled: false
|
||||||
|
nfs:
|
||||||
|
enabled: true
|
||||||
|
export paths:
|
||||||
|
- path: /home
|
||||||
|
export options: rw,sync
|
||||||
|
mount options: defaults
|
||||||
|
mount: true
|
||||||
|
- path: /opt
|
||||||
|
export options: ro,sync,no_root_squash
|
||||||
|
mount options: defaults
|
||||||
|
mount: false
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
# This file is autogenerated by warewulf
|
# This file is autogenerated by warewulf
|
||||||
# Host: {{.BuildHost}}
|
|
||||||
# Time: {{.BuildTime}}
|
|
||||||
# Source: {{.BuildSource}}
|
|
||||||
rootfs / tmpfs defaults 0 0
|
rootfs / tmpfs defaults 0 0
|
||||||
devpts /dev/pts devpts gid=5,mode=620 0 0
|
devpts /dev/pts devpts gid=5,mode=620 0 0
|
||||||
tmpfs /run/shm tmpfs defaults 0 0
|
tmpfs /run/shm tmpfs defaults 0 0
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
|
|
||||||
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Warewulf Server
|
|
||||||
{{$.Ipaddr}} {{$.BuildHost}} warewulf
|
|
||||||
|
|
||||||
{{- range $node := $.AllNodes}} {{/* for each node */}}
|
|
||||||
# Entry for {{$node.Id.Get}}
|
|
||||||
{{- range $devname, $netdev := $node.NetDevs}} {{/* for each network device on the node */}}
|
|
||||||
{{- if $netdev.Ipaddr.Defined}} {{/* if we have an ip address on this network device */}}
|
|
||||||
{{- /* emit the node name as hostname if this is the primary */}}
|
|
||||||
{{$netdev.Ipaddr.Get}} {{if $netdev.Primary.GetB}}{{$node.Id.Get}}{{end}} {{$node.Id.Get}}-{{$devname}} {{if $netdev.Device.Defined}}{{$node.Id.Get}}-{{$netdev.Device.Get}}{{end}}
|
|
||||||
{{- end }}{{/* if ip */}}
|
|
||||||
{{- end }}{{/* for each network device */}}
|
|
||||||
{{- end }}{{/* for each node */}}
|
|
||||||
359
overlays/host/internal/host_test.go
Normal file
359
overlays/host/internal/host_test.go
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
package host
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/config"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_hostOverlay(t *testing.T) {
|
||||||
|
hostname, _ := os.Hostname()
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/host/rootfs/etc/dhcp/dhcpd.conf.ww", "../rootfs/etc/dhcp/dhcpd.conf.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/host/rootfs/etc/dnsmasq.d/ww4-hosts.conf.ww", "../rootfs/etc/dnsmasq.d/ww4-hosts.conf.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/host/rootfs/etc/exports.ww", "../rootfs/etc/exports.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/host/rootfs/etc/hosts.ww", "../rootfs/etc/hosts.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/host/rootfs/etc/profile.d/ssh_setup.csh.ww", "../rootfs/etc/profile.d/ssh_setup.csh.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/host/rootfs/etc/profile.d/ssh_setup.sh.ww", "../rootfs/etc/profile.d/ssh_setup.sh.ww")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
conf string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
header string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "host:dhcp",
|
||||||
|
conf: "warewulf.conf",
|
||||||
|
args: []string{"--render", "host", "host", "etc/dhcp/dhcpd.conf.ww"},
|
||||||
|
log: host_dhcp,
|
||||||
|
header: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "host:dhcp(static)",
|
||||||
|
conf: "warewulf.conf-static",
|
||||||
|
args: []string{"--render", "host", "host", "etc/dhcp/dhcpd.conf.ww"},
|
||||||
|
log: host_dhcp_static,
|
||||||
|
header: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "host:dnsmasq",
|
||||||
|
conf: "warewulf.conf",
|
||||||
|
args: []string{"--render", "host", "host", "etc/dnsmasq.d/ww4-hosts.conf.ww"},
|
||||||
|
log: host_dnsmasq,
|
||||||
|
header: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "host:/etc/exports",
|
||||||
|
conf: "",
|
||||||
|
args: []string{"--render", "host", "host", "etc/exports.ww"},
|
||||||
|
log: host_exports,
|
||||||
|
header: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "host:/etc/hosts",
|
||||||
|
args: []string{"--render", "host", "host", "etc/hosts.ww"},
|
||||||
|
log: host_hosts,
|
||||||
|
header: "# Do not edit after this line",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "host:ssh_setup.csh",
|
||||||
|
conf: "",
|
||||||
|
args: []string{"--render", "host", "host", "etc/profile.d/ssh_setup.csh.ww"},
|
||||||
|
log: host_ssh_setup_csh,
|
||||||
|
header: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "host:ssh_setup.sh",
|
||||||
|
conf: "",
|
||||||
|
args: []string{"--render", "host", "host", "etc/profile.d/ssh_setup.sh.ww"},
|
||||||
|
log: host_ssh_setup_sh,
|
||||||
|
header: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if tt.conf != "" {
|
||||||
|
env.ImportFile(t, "etc/warewulf/warewulf.conf", tt.conf)
|
||||||
|
assert.NoError(t, config.Get().Read(env.GetPath("etc/warewulf/warewulf.conf")))
|
||||||
|
}
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
var log string
|
||||||
|
if tt.header != "" {
|
||||||
|
log = skipHeader(t, logbuf.String(), tt.header)
|
||||||
|
} else {
|
||||||
|
log = logbuf.String()
|
||||||
|
}
|
||||||
|
assert.Equal(t, strings.Replace(tt.log, "%HOSTNAME%", hostname, -1), log)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipHeader(t *testing.T, log string, header string) (newlog string) {
|
||||||
|
newlog = ""
|
||||||
|
found := false
|
||||||
|
for _, line := range strings.SplitAfter(log, "\n") {
|
||||||
|
if strings.Contains(line, header) {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
if found || strings.HasPrefix(line, "backupFile:") || strings.HasPrefix(line, "writeFile:") || strings.HasPrefix(line, "Filename:") {
|
||||||
|
newlog = newlog + line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const host_dhcp string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/dhcp/dhcpd.conf
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
|
||||||
|
allow booting;
|
||||||
|
allow bootp;
|
||||||
|
ddns-update-style interim;
|
||||||
|
authoritative;
|
||||||
|
|
||||||
|
option space ipxe;
|
||||||
|
|
||||||
|
# Tell iPXE to not wait for ProxyDHCP requests to speed up boot.
|
||||||
|
option ipxe.no-pxedhcp code 176 = unsigned integer 8;
|
||||||
|
option ipxe.no-pxedhcp 1;
|
||||||
|
|
||||||
|
option space PXE;
|
||||||
|
option PXE.mtftp-ip code 1 = ip-address;
|
||||||
|
option PXE.mtftp-cport code 2 = unsigned integer 16;
|
||||||
|
option PXE.mtftp-sport code 3 = unsigned integer 16;
|
||||||
|
option PXE.mtftp-tmout code 4 = unsigned integer 8;
|
||||||
|
option PXE.mtftp-delay code 5 = unsigned integer 8;
|
||||||
|
|
||||||
|
option architecture-type code 93 = unsigned integer 16;
|
||||||
|
if exists user-class and option user-class = "iPXE" {
|
||||||
|
filename "http://192.168.0.1:9873/ipxe/${mac:hexhyp}?assetkey=${asset}&uuid=${uuid}";
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if option architecture-type = 00:00 {
|
||||||
|
filename "/warewulf/undionly.kpxe";
|
||||||
|
}
|
||||||
|
if option architecture-type = 00:07 {
|
||||||
|
filename "/warewulf/ipxe-snponly-x86_64.efi";
|
||||||
|
}
|
||||||
|
if option architecture-type = 00:09 {
|
||||||
|
filename "/warewulf/ipxe-snponly-x86_64.efi";
|
||||||
|
}
|
||||||
|
if option architecture-type = 00:0B {
|
||||||
|
filename "/warewulf/snponly.efi";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subnet 192.168.0.0 netmask 255.255.255.0 {
|
||||||
|
max-lease-time 120;
|
||||||
|
range 192.168.0.100 192.168.0.199;
|
||||||
|
next-server 192.168.0.1;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const host_dhcp_static string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/dhcp/dhcpd.conf
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
|
||||||
|
allow booting;
|
||||||
|
allow bootp;
|
||||||
|
ddns-update-style interim;
|
||||||
|
authoritative;
|
||||||
|
|
||||||
|
option space ipxe;
|
||||||
|
|
||||||
|
# Tell iPXE to not wait for ProxyDHCP requests to speed up boot.
|
||||||
|
option ipxe.no-pxedhcp code 176 = unsigned integer 8;
|
||||||
|
option ipxe.no-pxedhcp 1;
|
||||||
|
|
||||||
|
option space PXE;
|
||||||
|
option PXE.mtftp-ip code 1 = ip-address;
|
||||||
|
option PXE.mtftp-cport code 2 = unsigned integer 16;
|
||||||
|
option PXE.mtftp-sport code 3 = unsigned integer 16;
|
||||||
|
option PXE.mtftp-tmout code 4 = unsigned integer 8;
|
||||||
|
option PXE.mtftp-delay code 5 = unsigned integer 8;
|
||||||
|
|
||||||
|
option architecture-type code 93 = unsigned integer 16;
|
||||||
|
if exists user-class and option user-class = "iPXE" {
|
||||||
|
filename "http://192.168.0.1:9873/ipxe/${mac:hexhyp}?assetkey=${asset}&uuid=${uuid}";
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if option architecture-type = 00:00 {
|
||||||
|
filename "/warewulf/undionly.kpxe";
|
||||||
|
}
|
||||||
|
if option architecture-type = 00:07 {
|
||||||
|
filename "/warewulf/ipxe-snponly-x86_64.efi";
|
||||||
|
}
|
||||||
|
if option architecture-type = 00:09 {
|
||||||
|
filename "/warewulf/ipxe-snponly-x86_64.efi";
|
||||||
|
}
|
||||||
|
if option architecture-type = 00:0B {
|
||||||
|
filename "/warewulf/snponly.efi";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subnet 192.168.0.0 netmask 255.255.255.0 {
|
||||||
|
max-lease-time 120;
|
||||||
|
}
|
||||||
|
host node1-default
|
||||||
|
{
|
||||||
|
hardware ethernet e6:92:39:49:7b:03;
|
||||||
|
fixed-address 192.168.3.21;
|
||||||
|
option host-name "node1";
|
||||||
|
}
|
||||||
|
|
||||||
|
host node1-secondary
|
||||||
|
{
|
||||||
|
hardware ethernet 9a:77:29:73:14:f1;
|
||||||
|
fixed-address 192.168.3.22;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
host node2-default
|
||||||
|
{
|
||||||
|
hardware ethernet e6:92:39:49:7b:04;
|
||||||
|
fixed-address 192.168.3.23;
|
||||||
|
option host-name "node2";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
const host_dnsmasq string = `backupFile: false
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/dnsmasq.d/ww4-hosts.conf
|
||||||
|
# This file was autgenerated by warewulf
|
||||||
|
|
||||||
|
# select the x86 hosts which will get the iXPE binary
|
||||||
|
dhcp-match=set:bios,option:client-arch,0 #legacy boot
|
||||||
|
dhcp-match=set:x86PC,option:client-arch, 7 #EFI x86-64
|
||||||
|
dhcp-match=set:x86PC,option:client-arch, 6 #EFI x86-64
|
||||||
|
dhcp-match=set:x86PC,option:client-arch, 9 #EFI x86-64
|
||||||
|
dhcp-match=set:aarch64,option:client-arch, 11 #EFI aarch64
|
||||||
|
dhcp-match=set:iPXE,77,"iPXE"
|
||||||
|
dhcp-userclass=set:iPXE,iPXE
|
||||||
|
dhcp-vendorclass=set:efi-http,HTTPClient:Arch:00016
|
||||||
|
dhcp-option-force=tag:efi-http,60,HTTPClient
|
||||||
|
# for http boot always use shim/grub
|
||||||
|
dhcp-boot=tag:efi-http,"http://192.168.0.1:9873/efiboot/shim.efi"
|
||||||
|
dhcp-boot=tag:x86PC,"/warewulf/ipxe-snponly-x86_64.efi"
|
||||||
|
dhcp-boot=tag:aarch64,"/warewulf/arm64-efi/snponly.efi"
|
||||||
|
# iPXE binary will get the following configuration file
|
||||||
|
dhcp-boot=tag:iPXE,"http://192.168.0.1:9873/ipxe/${mac:hexhyp}?assetkey=${asset}&uuid=${uuid}"
|
||||||
|
dhcp-no-override
|
||||||
|
# define the the range
|
||||||
|
dhcp-range=192.168.0.100,192.168.0.199,255.255.255.0,6h
|
||||||
|
|
||||||
|
dhcp-host=e6:92:39:49:7b:03,set:warewulf,node1,192.168.3.21,infinite
|
||||||
|
dhcp-host=9a:77:29:73:14:f1,set:warewulf,node1,192.168.3.22,infinite
|
||||||
|
dhcp-host=e6:92:39:49:7b:04,set:warewulf,node2,192.168.3.23,infinite
|
||||||
|
`
|
||||||
|
|
||||||
|
const host_exports string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/exports
|
||||||
|
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
/home 192.168.0.0/255.255.255.0(rw,sync)
|
||||||
|
/opt 192.168.0.0/255.255.255.0(ro,sync,no_root_squash)
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
const host_hosts string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/hosts
|
||||||
|
# Do not edit after this line
|
||||||
|
# This block is autogenerated by warewulf
|
||||||
|
|
||||||
|
|
||||||
|
# Warewulf Server
|
||||||
|
192.168.0.1 %HOSTNAME% warewulf
|
||||||
|
# Entry for node1
|
||||||
|
192.168.3.21 node1 node1-default node1-wwnet0
|
||||||
|
192.168.3.22 node1-secondary node1-wwnet1
|
||||||
|
# Entry for node2
|
||||||
|
192.168.3.23 node2 node2-default node2-wwnet0
|
||||||
|
`
|
||||||
|
|
||||||
|
const host_ssh_setup_csh = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/profile.d/ssh_setup.csh
|
||||||
|
#!/bin/csh
|
||||||
|
|
||||||
|
## Automatically configure SSH keys for a user on C SHell login
|
||||||
|
## Copy this file to /etc/profile.d along with ssh_setup.sh
|
||||||
|
|
||||||
|
` + "set _UID=`id -u`" + `
|
||||||
|
if ( ( $_UID > 500 || $_UID == 0 ) && ( ! -f "$HOME/.ssh/config" && ! -f "$HOME/.ssh/cluster" ) ) then
|
||||||
|
echo "Configuring SSH for cluster access"
|
||||||
|
install -d -m 700 $HOME/.ssh
|
||||||
|
ssh-keygen -t rsa -f $HOME/.ssh/cluster -N '' -C "Warewulf Cluster key" >& /dev/null
|
||||||
|
cat $HOME/.ssh/cluster.pub >>! $HOME/.ssh/authorized_keys
|
||||||
|
chmod 0600 $HOME/.ssh/authorized_keys
|
||||||
|
|
||||||
|
touch $HOME/.ssh/config
|
||||||
|
echo -n "# Added by Warewulf " >>! $HOME/.ssh/config
|
||||||
|
(date +%Y-%m-%d >> $HOME/.ssh/config) >& /dev/null
|
||||||
|
echo "Host *" >> $HOME/.ssh/config
|
||||||
|
echo " IdentityFile ~/.ssh/cluster" >> $HOME/.ssh/config
|
||||||
|
echo " StrictHostKeyChecking=no" >> $HOME/.ssh/config
|
||||||
|
chmod 0600 $HOME/.ssh/config
|
||||||
|
endif
|
||||||
|
`
|
||||||
|
|
||||||
|
const host_ssh_setup_sh = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/profile.d/ssh_setup.sh
|
||||||
|
#!/bin/sh
|
||||||
|
##
|
||||||
|
## Copyright (c) 2001-2003 Gregory M. Kurtzer
|
||||||
|
##
|
||||||
|
## Copyright (c) 2003-2012, The Regents of the University of California,
|
||||||
|
## through Lawrence Berkeley National Laboratory (subject to receipt of any
|
||||||
|
## required approvals from the U.S. Dept. of Energy). All rights reserved.
|
||||||
|
##
|
||||||
|
## Copied from https://github.com/warewulf/warewulf3/blob/master/cluster/bin/cluster-env
|
||||||
|
|
||||||
|
## Automatically configure SSH keys for a user on login
|
||||||
|
## Copy this file to /etc/profile.d
|
||||||
|
|
||||||
|
` + "_UID=`id -u`" + `
|
||||||
|
if [ $_UID -ge 500 -o $_UID -eq 0 ] && [ ! -f "$HOME/.ssh/config" -a ! -f "$HOME/.ssh/cluster" ]; then
|
||||||
|
echo "Configuring SSH for cluster access"
|
||||||
|
install -d -m 700 $HOME/.ssh
|
||||||
|
ssh-keygen -t rsa -f $HOME/.ssh/cluster -N '' -C "Warewulf Cluster key" > /dev/null 2>&1
|
||||||
|
cat $HOME/.ssh/cluster.pub >> $HOME/.ssh/authorized_keys
|
||||||
|
chmod 0600 $HOME/.ssh/authorized_keys
|
||||||
|
|
||||||
|
echo "# Added by Warewulf ` + "`date +%Y-%m-%d 2>/dev/null`" + `" >> $HOME/.ssh/config
|
||||||
|
echo "Host *" >> $HOME/.ssh/config
|
||||||
|
echo " IdentityFile ~/.ssh/cluster" >> $HOME/.ssh/config
|
||||||
|
echo " StrictHostKeyChecking=no" >> $HOME/.ssh/config
|
||||||
|
chmod 0600 $HOME/.ssh/config
|
||||||
|
fi
|
||||||
|
`
|
||||||
58
overlays/host/internal/nodes.conf
Normal file
58
overlays/host/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
28
overlays/host/internal/warewulf.conf
Normal file
28
overlays/host/internal/warewulf.conf
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
WW_INTERNAL: 43
|
||||||
|
ipaddr: 192.168.0.1/24
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
network: 192.168.0.0
|
||||||
|
warewulf:
|
||||||
|
port: 9873
|
||||||
|
secure: false
|
||||||
|
update interval: 60
|
||||||
|
autobuild overlays: true
|
||||||
|
host overlay: true
|
||||||
|
syslog: true
|
||||||
|
dhcp:
|
||||||
|
enabled: true
|
||||||
|
range start: 192.168.0.100
|
||||||
|
range end: 192.168.0.199
|
||||||
|
tftp:
|
||||||
|
enabled: false
|
||||||
|
nfs:
|
||||||
|
enabled: true
|
||||||
|
export paths:
|
||||||
|
- path: /home
|
||||||
|
export options: rw,sync
|
||||||
|
mount options: defaults
|
||||||
|
mount: true
|
||||||
|
- path: /opt
|
||||||
|
export options: ro,sync,no_root_squash
|
||||||
|
mount options: defaults
|
||||||
|
mount: false
|
||||||
27
overlays/host/internal/warewulf.conf-static
Normal file
27
overlays/host/internal/warewulf.conf-static
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
WW_INTERNAL: 43
|
||||||
|
ipaddr: 192.168.0.1/24
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
network: 192.168.0.0
|
||||||
|
warewulf:
|
||||||
|
port: 9873
|
||||||
|
secure: false
|
||||||
|
update interval: 60
|
||||||
|
autobuild overlays: true
|
||||||
|
host overlay: true
|
||||||
|
syslog: true
|
||||||
|
dhcp:
|
||||||
|
enabled: true
|
||||||
|
template: static
|
||||||
|
tftp:
|
||||||
|
enabled: false
|
||||||
|
nfs:
|
||||||
|
enabled: true
|
||||||
|
export paths:
|
||||||
|
- path: /home
|
||||||
|
export options: rw,sync
|
||||||
|
mount options: defaults
|
||||||
|
mount: true
|
||||||
|
- path: /opt
|
||||||
|
export options: ro,sync,no_root_squash
|
||||||
|
mount options: defaults
|
||||||
|
mount: false
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
{{if $.Dhcp.Enabled -}}
|
{{if $.Dhcp.Enabled -}}
|
||||||
# This file is autogenerated by warewulf
|
# This file is autogenerated by warewulf
|
||||||
# Host: {{.BuildHost}}
|
|
||||||
# Time: {{.BuildTime}}
|
|
||||||
# Source: {{.BuildSource}}
|
|
||||||
|
|
||||||
allow booting;
|
allow booting;
|
||||||
allow bootp;
|
allow bootp;
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
# This file was autgenerated by warewulf
|
# This file was autgenerated by warewulf
|
||||||
# Host: {{.BuildHost}}
|
|
||||||
# Time: {{.BuildTime}}
|
|
||||||
# Source {{.BuildSource}}
|
|
||||||
{{ nobackup }}
|
{{ nobackup }}
|
||||||
# select the x86 hosts which will get the iXPE binary
|
# select the x86 hosts which will get the iXPE binary
|
||||||
dhcp-match=set:bios,option:client-arch,0 #legacy boot
|
dhcp-match=set:bios,option:client-arch,0 #legacy boot
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
{{- if .Nfs.Enabled }}
|
{{- if .Nfs.Enabled }}
|
||||||
# This file is autogenerated by warewulf
|
# This file is autogenerated by warewulf
|
||||||
# Host: {{.BuildHost}}
|
|
||||||
# Time: {{.BuildTime}}
|
|
||||||
# Source: {{.BuildSource}}
|
|
||||||
{{- $network := .Network }}
|
{{- $network := .Network }}
|
||||||
{{- $netmask := .Netmask }}
|
{{- $netmask := .Netmask }}
|
||||||
{{- range .Nfs.ExportsExtended }}
|
{{- range .Nfs.ExportsExtended }}
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
{{ IncludeBlock "/etc/hosts" "# Do not edit after this line" }}
|
{{ IncludeBlock "/etc/hosts" "# Do not edit after this line" }}
|
||||||
# This block is autogenerated by warewulf
|
# This block is autogenerated by warewulf
|
||||||
# Host: {{.BuildHost}}
|
|
||||||
# Time: {{.BuildTime}}
|
|
||||||
# Source: {{.BuildSource}}
|
|
||||||
|
|
||||||
|
|
||||||
# Warewulf Server
|
# Warewulf Server
|
||||||
{{$.Ipaddr}} {{$.BuildHost}} warewulf
|
{{$.Ipaddr}} {{$.BuildHost}} warewulf
|
||||||
|
|
||||||
{{- range $node := $.AllNodes}} {{/* for each node */}}
|
{{- range $node := $.AllNodes }}{{/* for each node */}}
|
||||||
# Entry for {{$node.Id.Get}}
|
# Entry for {{ $node.Id.Get }}
|
||||||
{{- range $devname, $netdev := $node.NetDevs}} {{/* for each network device on the node */}}
|
{{- range $devname, $netdev := $node.NetDevs }}{{/* for each network device on the node */}}
|
||||||
{{- if $netdev.Ipaddr.Defined}} {{/* if we have an ip address on this network device */}}
|
{{- if $netdev.Ipaddr.Defined }}{{/* if we have an ip address on this network device */}}
|
||||||
{{- /* emit the node name as hostname if this is the primary */}}
|
{{- /* emit the node name as hostname if this is the primary */}}
|
||||||
{{$netdev.Ipaddr.Get}} {{if $netdev.Primary.GetB}}{{$node.Id.Get}}{{end}} {{$node.Id.Get}}-{{$devname}} {{if $netdev.Device.Defined}}{{$node.Id.Get}}-{{$netdev.Device.Get}}{{end}}
|
{{$netdev.Ipaddr.Get}} {{if $netdev.Primary.GetB}}{{$node.Id.Get}}{{end}} {{$node.Id.Get}}-{{$devname}} {{if $netdev.Device.Defined}}{{$node.Id.Get}}-{{$netdev.Device.Get}}{{end}}
|
||||||
{{- end }}{{/* if ip */}}
|
{{- end }}{{/* if ip */}}
|
||||||
|
|||||||
54
overlays/hostname/internal/hostname_test.go
Normal file
54
overlays/hostname/internal/hostname_test.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package hostname
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_hostnameOverlay(t *testing.T) {
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/hostname/rootfs/etc/hostname.ww", "../rootfs/etc/hostname.ww")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "/etc/hostname",
|
||||||
|
args: []string{"--render", "node1", "hostname", "etc/hostname.ww"},
|
||||||
|
log: hostname,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
assert.Equal(t, tt.log, logbuf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hostname string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/hostname
|
||||||
|
node1
|
||||||
|
`
|
||||||
58
overlays/hostname/internal/nodes.conf
Normal file
58
overlays/hostname/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
70
overlays/hosts/internal/hosts_test.go
Normal file
70
overlays/hosts/internal/hosts_test.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package hosts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/config"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_hostsOverlay(t *testing.T) {
|
||||||
|
hostname, _ := os.Hostname()
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/warewulf.conf", "warewulf.conf")
|
||||||
|
assert.NoError(t, config.Get().Read(env.GetPath("etc/warewulf/warewulf.conf")))
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/hosts/rootfs/etc/hosts.ww", "../rootfs/etc/hosts.ww")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "/etc/hosts",
|
||||||
|
args: []string{"--render", "node1", "hosts", "etc/hosts.ww"},
|
||||||
|
log: hosts,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
assert.Equal(t, strings.Replace(tt.log, "%HOSTNAME%", hostname, -1), logbuf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hosts string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/hosts
|
||||||
|
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
|
||||||
|
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
|
||||||
|
|
||||||
|
# Warewulf Server
|
||||||
|
192.168.0.1 %HOSTNAME% warewulf
|
||||||
|
# Entry for node1
|
||||||
|
192.168.3.21 node1 node1-default node1-wwnet0
|
||||||
|
192.168.3.22 node1-secondary node1-wwnet1
|
||||||
|
# Entry for node2
|
||||||
|
192.168.3.23 node2 node2-default node2-wwnet0
|
||||||
|
`
|
||||||
58
overlays/hosts/internal/nodes.conf
Normal file
58
overlays/hosts/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
28
overlays/hosts/internal/warewulf.conf
Normal file
28
overlays/hosts/internal/warewulf.conf
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
WW_INTERNAL: 43
|
||||||
|
ipaddr: 192.168.0.1/24
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
network: 192.168.0.0
|
||||||
|
warewulf:
|
||||||
|
port: 9873
|
||||||
|
secure: false
|
||||||
|
update interval: 60
|
||||||
|
autobuild overlays: true
|
||||||
|
host overlay: true
|
||||||
|
syslog: true
|
||||||
|
dhcp:
|
||||||
|
enabled: true
|
||||||
|
range start: 192.168.0.100
|
||||||
|
range end: 192.168.0.199
|
||||||
|
tftp:
|
||||||
|
enabled: false
|
||||||
|
nfs:
|
||||||
|
enabled: true
|
||||||
|
export paths:
|
||||||
|
- path: /home
|
||||||
|
export options: rw,sync
|
||||||
|
mount options: defaults
|
||||||
|
mount: true
|
||||||
|
- path: /opt
|
||||||
|
export options: ro,sync,no_root_squash
|
||||||
|
mount options: defaults
|
||||||
|
mount: false
|
||||||
14
overlays/hosts/rootfs/etc/hosts.ww
Normal file
14
overlays/hosts/rootfs/etc/hosts.ww
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
|
||||||
|
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
|
||||||
|
|
||||||
|
# Warewulf Server
|
||||||
|
{{$.Ipaddr}} {{$.BuildHost}} warewulf
|
||||||
|
|
||||||
|
{{- range $node := $.AllNodes }}
|
||||||
|
# Entry for {{$node.Id.Get}}
|
||||||
|
{{- range $devname, $netdev := $node.NetDevs }}
|
||||||
|
{{- if $netdev.Ipaddr.Defined }}
|
||||||
|
{{$netdev.Ipaddr.Get}} {{if $netdev.Primary.GetB}}{{$node.Id.Get}}{{end}} {{$node.Id.Get}}-{{$devname}} {{if $netdev.Device.Defined}}{{$node.Id.Get}}-{{$netdev.Device.Get}}{{end}}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
103
overlays/ifcfg/internal/ifcfg_test.go
Normal file
103
overlays/ifcfg/internal/ifcfg_test.go
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package ifcfg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_ifcfgOverlay(t *testing.T) {
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/ifcfg/rootfs/etc/sysconfig/network-scripts/ifcfg.ww", "../rootfs/etc/sysconfig/network-scripts/ifcfg.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/ifcfg/rootfs/etc/sysconfig/network.ww", "../rootfs/etc/sysconfig/network.ww")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "ifcfg:ifcfg.ww",
|
||||||
|
args: []string{"--render", "node1", "ifcfg", "etc/sysconfig/network-scripts/ifcfg.ww"},
|
||||||
|
log: ifcfg,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ifcfg:network.ww",
|
||||||
|
args: []string{"--render", "node1", "ifcfg", "etc/sysconfig/network.ww"},
|
||||||
|
log: ifcfg_network,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
assert.Equal(t, tt.log, logbuf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ifcfg string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: ifcfg-default.conf
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
TYPE=ethernet
|
||||||
|
DEVICE=wwnet0
|
||||||
|
NAME=default
|
||||||
|
BOOTPROTO=static
|
||||||
|
DEVTIMEOUT=10
|
||||||
|
IPADDR=192.168.3.21
|
||||||
|
NETMASK=255.255.255.0
|
||||||
|
GATEWAY=192.168.3.1
|
||||||
|
HWADDR=e6:92:39:49:7b:03
|
||||||
|
TYPE=ethernet
|
||||||
|
ONBOOT=true
|
||||||
|
IPV6INIT=yes
|
||||||
|
IPV6_AUTOCONF=yes
|
||||||
|
IPV6_DEFROUTE=yes
|
||||||
|
IPV6_FAILURE_FATAL=no
|
||||||
|
backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: ifcfg-secondary.conf
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
TYPE=ethernet
|
||||||
|
DEVICE=wwnet1
|
||||||
|
NAME=secondary
|
||||||
|
BOOTPROTO=static
|
||||||
|
DEVTIMEOUT=10
|
||||||
|
IPADDR=192.168.3.22
|
||||||
|
NETMASK=255.255.255.0
|
||||||
|
GATEWAY=192.168.3.1
|
||||||
|
HWADDR=9a:77:29:73:14:f1
|
||||||
|
TYPE=ethernet
|
||||||
|
ONBOOT=true
|
||||||
|
IPV6INIT=yes
|
||||||
|
IPV6_AUTOCONF=yes
|
||||||
|
IPV6_DEFROUTE=yes
|
||||||
|
IPV6_FAILURE_FATAL=no
|
||||||
|
DNS1=8.8.8.8
|
||||||
|
DNS2=8.8.4.4
|
||||||
|
`
|
||||||
|
|
||||||
|
const ifcfg_network string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/sysconfig/network
|
||||||
|
NETWORKING=yes
|
||||||
|
HOSTNAME=node1
|
||||||
|
`
|
||||||
58
overlays/ifcfg/internal/nodes.conf
Normal file
58
overlays/ifcfg/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
@@ -1,13 +1,7 @@
|
|||||||
{{- $host := .BuildHost }}
|
|
||||||
{{- $time := .BuildTime }}
|
|
||||||
{{- $source := .BuildSource }}
|
|
||||||
{{range $devname, $netdev := .NetDevs -}}
|
{{range $devname, $netdev := .NetDevs -}}
|
||||||
{{- $filename := print "ifcfg-" $devname ".conf" }}
|
{{- $filename := print "ifcfg-" $devname ".conf" }}
|
||||||
{{- file $filename }}
|
{{- file $filename }}
|
||||||
# This file is autogenerated by warewulf
|
# This file is autogenerated by warewulf
|
||||||
# Host: {{ $host }}
|
|
||||||
# Time: {{ $time }}
|
|
||||||
# Source: {{ $source }}
|
|
||||||
TYPE={{ $netdev.Type }}
|
TYPE={{ $netdev.Type }}
|
||||||
DEVICE={{ $netdev.Device }}
|
DEVICE={{ $netdev.Device }}
|
||||||
NAME={{ $devname }}
|
NAME={{ $devname }}
|
||||||
120
overlays/ignition/internal/ignition_test.go
Normal file
120
overlays/ignition/internal/ignition_test.go
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
package ignition
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_ignitionOverlay(t *testing.T) {
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/ignition/rootfs/etc/systemd/system/ww4-disks.target.ww", "../rootfs/etc/systemd/system/ww4-disks.target.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/ignition/rootfs/etc/systemd/system/ww4-mounts.ww", "../rootfs/etc/systemd/system/ww4-mounts.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/ignition/rootfs/warewulf/ignition.json.ww", "../rootfs/warewulf/ignition.json.ww")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
json bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "ignition:ww4-disks.target",
|
||||||
|
args: []string{"--render", "node1", "ignition", "etc/systemd/system/ww4-disks.target.ww"},
|
||||||
|
log: ignition_disks,
|
||||||
|
json: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ignition:ww4-mounts",
|
||||||
|
args: []string{"--render", "node1", "ignition", "etc/systemd/system/ww4-mounts.ww"},
|
||||||
|
log: ignition_mounts,
|
||||||
|
json: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ignition:ignition.json",
|
||||||
|
args: []string{"--quiet", "--render", "node1", "ignition", "warewulf/ignition.json.ww"},
|
||||||
|
log: ignition_json,
|
||||||
|
json: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
if tt.json {
|
||||||
|
var expected, actual interface{}
|
||||||
|
assert.NoError(t, json.Unmarshal([]byte(tt.log), &expected))
|
||||||
|
assert.NoError(t, json.Unmarshal(logbuf.Bytes(), &actual))
|
||||||
|
assert.True(t, reflect.DeepEqual(expected, actual), "expected: %v\nactual : %v", expected, actual)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, tt.log, logbuf.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ignition_disks string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/systemd/system/ww4-disks.target
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
[Unit]
|
||||||
|
Description=mount ww4 disks
|
||||||
|
# make sure that the disks are available
|
||||||
|
Requires=ignition-ww4-disks.service
|
||||||
|
After=ignition-ww4-disks.service
|
||||||
|
Requisite=ignition-ww4-disks.service
|
||||||
|
# Get the mounts
|
||||||
|
Wants=scratch.mount
|
||||||
|
Wants=dev-disk-by\x2dpartlabel-swap.swap
|
||||||
|
`
|
||||||
|
|
||||||
|
const ignition_mounts string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: scratch.mount
|
||||||
|
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
[Unit]
|
||||||
|
ConditionPathExists=/warewulf/ignition.json
|
||||||
|
Before=local-fs.target
|
||||||
|
After=ignition-ww4-disks.service
|
||||||
|
[Mount]
|
||||||
|
Where=/scratch
|
||||||
|
What=/dev/disk/by-partlabel/scratch
|
||||||
|
Type=btrfs
|
||||||
|
[Install]
|
||||||
|
RequiredBy=local-fs.target
|
||||||
|
backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: dev-disk-by\x2dpartlabel-swap.swap
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
[Unit]
|
||||||
|
ConditionPathExists=/warewulf/ignition.json
|
||||||
|
After=ignition-ww4-disks.service
|
||||||
|
Before=swap.target
|
||||||
|
[Swap]
|
||||||
|
What=/dev/disk/by-partlabel/swap
|
||||||
|
[Install]
|
||||||
|
RequiredBy=swap.target
|
||||||
|
`
|
||||||
|
|
||||||
|
const ignition_json string = `{"ignition":{"version":"3.1.0"},"storage":{"disks":[{"device":"/dev/vda","partitions":[{"label":"scratch","shouldExist":true,"wipePartitionEntry":false},{"label":"swap","number":1,"shouldExist":false,"sizeMiB":1024,"wipePartitionEntry":false}],"wipeTable":true}],"filesystems":[{"device":"/dev/disk/by-partlabel/scratch","format":"btrfs","path":"/scratch","wipeFilesystem":true},{"device":"/dev/disk/by-partlabel/swap","format":"swap","path":"swap","wipeFilesystem":false}]}}
|
||||||
|
`
|
||||||
58
overlays/ignition/internal/nodes.conf
Normal file
58
overlays/ignition/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
# This file is autogenerated by warewulf
|
# This file is autogenerated by warewulf
|
||||||
# Host: {{.BuildHost}}
|
|
||||||
# Time: {{.BuildTime}}
|
|
||||||
# Source: {{.BuildSource}}
|
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=mount ww4 disks
|
Description=mount ww4 disks
|
||||||
# make sure that the disks are available
|
# make sure that the disks are available
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
{{- $host := .BuildHost }}
|
|
||||||
{{- $time := .BuildTime }}
|
|
||||||
{{- $source := .BuildSource }}
|
|
||||||
{{- range $fsdevice,$fs := .FileSystems }}
|
{{- range $fsdevice,$fs := .FileSystems }}
|
||||||
{{- $prefix := replace "-" "\\x2d" $fs.Path }}
|
{{- $prefix := replace "-" "\\x2d" $fs.Path }}
|
||||||
{{- $prefix = replace "/" "-" $prefix }}
|
{{- $prefix = replace "/" "-" $prefix }}
|
||||||
@@ -11,9 +8,6 @@
|
|||||||
{{- $filename := print $prefix "." $suffix }}
|
{{- $filename := print $prefix "." $suffix }}
|
||||||
{{- file $filename }}
|
{{- file $filename }}
|
||||||
# This file is autogenerated by warewulf
|
# This file is autogenerated by warewulf
|
||||||
# Host: {{ $host }}
|
|
||||||
# Time: {{ $time }}
|
|
||||||
# Source: {{ $source }}
|
|
||||||
{{- if eq $fs.Format "swap"}}
|
{{- if eq $fs.Format "swap"}}
|
||||||
[Unit]
|
[Unit]
|
||||||
ConditionPathExists=/warewulf/ignition.json
|
ConditionPathExists=/warewulf/ignition.json
|
||||||
64
overlays/issue/internal/issue_test.go
Normal file
64
overlays/issue/internal/issue_test.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package issue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_issueOverlay(t *testing.T) {
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/issue/rootfs/etc/issue.ww", "../rootfs/etc/issue.ww")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "/etc/issue",
|
||||||
|
args: []string{"--render", "node1", "issue", "etc/issue.ww"},
|
||||||
|
log: issue,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
assert.Equal(t, tt.log, logbuf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const issue string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/issue
|
||||||
|
Warewulf Node: node1
|
||||||
|
Container: rockylinux-9
|
||||||
|
Kernelargs: quiet crashkernel=no vga=791 net.naming-scheme=v238
|
||||||
|
|
||||||
|
Network:
|
||||||
|
default: wwnet0
|
||||||
|
default: 192.168.3.21/24
|
||||||
|
default: e6:92:39:49:7b:03
|
||||||
|
secondary: wwnet1
|
||||||
|
secondary: 192.168.3.22/24
|
||||||
|
secondary: 9a:77:29:73:14:f1
|
||||||
|
`
|
||||||
58
overlays/issue/internal/nodes.conf
Normal file
58
overlays/issue/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
58
overlays/resolv/internal/nodes.conf
Normal file
58
overlays/resolv/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
56
overlays/resolv/internal/resolv_test.go
Normal file
56
overlays/resolv/internal/resolv_test.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package resolv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_resolvOverlay(t *testing.T) {
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/resolv/rootfs/etc/resolv.conf.ww", "../rootfs/etc/resolv.conf.ww")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "/etc/resolv.conf",
|
||||||
|
args: []string{"--render", "node1", "resolv", "etc/resolv.conf.ww"},
|
||||||
|
log: resolv_conf,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
assert.Equal(t, tt.log, logbuf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolv_conf string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/resolv.conf
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
nameserver 8.8.8.8
|
||||||
|
nameserver 8.8.4.4
|
||||||
|
`
|
||||||
@@ -1,10 +1,4 @@
|
|||||||
{{- $host := .BuildHost }}
|
|
||||||
{{- $time := .BuildTime }}
|
|
||||||
{{- $source := .BuildSource }}
|
|
||||||
# This file is autogenerated by warewulf
|
# This file is autogenerated by warewulf
|
||||||
# Host: {{ $host }}
|
|
||||||
# Time: {{ $time }}
|
|
||||||
# Source: {{ $source }}
|
|
||||||
{{range $devname, $netdev := .NetDevs -}}
|
{{range $devname, $netdev := .NetDevs -}}
|
||||||
{{range $tk, $tv := $netdev.Tags -}}
|
{{range $tk, $tv := $netdev.Tags -}}
|
||||||
{{ $prefix := substr 0 3 $tk -}}
|
{{ $prefix := substr 0 3 $tk -}}
|
||||||
58
overlays/ssh.authorized_keys/internal/nodes.conf
Normal file
58
overlays/ssh.authorized_keys/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package ssh_authorized_keys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_ssh_authorized_keysOverlay(t *testing.T) {
|
||||||
|
t.Skip("ssh.authorized_keys is not yet isolated from the host")
|
||||||
|
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/ssh.authorized_keys/rootfs/root/.ssh/authorized_keys.ww", "../rootfs/root/.ssh/authorized_keys.ww")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "ssh.authorized_keys:authorized_keys.ww",
|
||||||
|
args: []string{"--render", "node1", "ssh.authorized_keys", "root/.ssh/authorized_keys.ww"},
|
||||||
|
log: ssh_authorized_keys,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
assert.Equal(t, tt.log, logbuf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ssh_authorized_keys string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: root/.ssh/authorized_keys
|
||||||
|
|
||||||
|
`
|
||||||
58
overlays/ssh.host_keys/internal/nodes.conf
Normal file
58
overlays/ssh.host_keys/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
146
overlays/ssh.host_keys/internal/ssh_host_keys_test.go
Normal file
146
overlays/ssh.host_keys/internal/ssh_host_keys_test.go
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
package ssh_host_keys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_ssh_host_keysOverlay(t *testing.T) {
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/ssh.host_keys/rootfs/etc/ssh/ssh_host_dsa_key.pub.ww", "../rootfs/etc/ssh/ssh_host_dsa_key.pub.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/ssh.host_keys/rootfs/etc/ssh/ssh_host_dsa_key.ww", "../rootfs/etc/ssh/ssh_host_dsa_key.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/ssh.host_keys/rootfs/etc/ssh/ssh_host_ecdsa_key.pub.ww", "../rootfs/etc/ssh/ssh_host_ecdsa_key.pub.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/ssh.host_keys/rootfs/etc/ssh/ssh_host_ecdsa_key.ww", "../rootfs/etc/ssh/ssh_host_ecdsa_key.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/ssh.host_keys/rootfs/etc/ssh/ssh_host_ed25519_key.pub.ww", "../rootfs/etc/ssh/ssh_host_ed25519_key.pub.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/ssh.host_keys/rootfs/etc/ssh/ssh_host_ed25519_key.ww", "../rootfs/etc/ssh/ssh_host_ed25519_key.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/ssh.host_keys/rootfs/etc/ssh/ssh_host_rsa_key.pub.ww", "../rootfs/etc/ssh/ssh_host_rsa_key.pub.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/ssh.host_keys/rootfs/etc/ssh/ssh_host_rsa_key.ww", "../rootfs/etc/ssh/ssh_host_rsa_key.ww")
|
||||||
|
env.WriteFile(t, "etc/warewulf/keys/ssh_host_dsa_key.pub", `dsa pubkey sentinel`)
|
||||||
|
env.WriteFile(t, "etc/warewulf/keys/ssh_host_dsa_key", `dsa key sentinel`)
|
||||||
|
env.WriteFile(t, "etc/warewulf/keys/ssh_host_ecdsa_key.pub", `ecdsa pubkey sentinel`)
|
||||||
|
env.WriteFile(t, "etc/warewulf/keys/ssh_host_ecdsa_key", `ecdsa key sentinel`)
|
||||||
|
env.WriteFile(t, "etc/warewulf/keys/ssh_host_ed25519_key.pub", `ed25519 pubkey sentinel`)
|
||||||
|
env.WriteFile(t, "etc/warewulf/keys/ssh_host_ed25519_key", `ed25519 key sentinel`)
|
||||||
|
env.WriteFile(t, "etc/warewulf/keys/ssh_host_rsa_key.pub", `rsa pubkey sentinel`)
|
||||||
|
env.WriteFile(t, "etc/warewulf/keys/ssh_host_rsa_key", `rsa key sentinel`)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "ssh.host_keys:dsa pub",
|
||||||
|
args: []string{"--render", "node1", "ssh.host_keys", "etc/ssh/ssh_host_dsa_key.pub.ww"},
|
||||||
|
log: ssh_host_dsa_key_pub,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ssh.host_keys:dsa",
|
||||||
|
args: []string{"--render", "node1", "ssh.host_keys", "etc/ssh/ssh_host_dsa_key.ww"},
|
||||||
|
log: ssh_host_dsa_key,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ssh.host_keys:ecdsa pub",
|
||||||
|
args: []string{"--render", "node1", "ssh.host_keys", "etc/ssh/ssh_host_ecdsa_key.pub.ww"},
|
||||||
|
log: ssh_host_ecdsa_key_pub,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ssh.host_keys:ecdsa",
|
||||||
|
args: []string{"--render", "node1", "ssh.host_keys", "etc/ssh/ssh_host_ecdsa_key.ww"},
|
||||||
|
log: ssh_host_ecdsa_key,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ssh.host_keys:rsa pub",
|
||||||
|
args: []string{"--render", "node1", "ssh.host_keys", "etc/ssh/ssh_host_rsa_key.pub.ww"},
|
||||||
|
log: ssh_host_rsa_key_pub,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ssh.host_keys:dsa",
|
||||||
|
args: []string{"--render", "node1", "ssh.host_keys", "etc/ssh/ssh_host_rsa_key.ww"},
|
||||||
|
log: ssh_host_rsa_key,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ssh.host_keys:ed25519 pub",
|
||||||
|
args: []string{"--render", "node1", "ssh.host_keys", "etc/ssh/ssh_host_ed25519_key.pub.ww"},
|
||||||
|
log: ssh_host_ed25519_key_pub,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ssh.host_keys:ed25519",
|
||||||
|
args: []string{"--render", "node1", "ssh.host_keys", "etc/ssh/ssh_host_ed25519_key.ww"},
|
||||||
|
log: ssh_host_ed25519_key,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
assert.Equal(t, tt.log, logbuf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ssh_host_dsa_key_pub string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/ssh/ssh_host_dsa_key.pub
|
||||||
|
dsa pubkey sentinel
|
||||||
|
`
|
||||||
|
|
||||||
|
const ssh_host_dsa_key string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/ssh/ssh_host_dsa_key
|
||||||
|
dsa key sentinel
|
||||||
|
`
|
||||||
|
|
||||||
|
const ssh_host_ecdsa_key_pub string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/ssh/ssh_host_ecdsa_key.pub
|
||||||
|
ecdsa pubkey sentinel
|
||||||
|
`
|
||||||
|
|
||||||
|
const ssh_host_ecdsa_key string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/ssh/ssh_host_ecdsa_key
|
||||||
|
ecdsa key sentinel
|
||||||
|
`
|
||||||
|
|
||||||
|
const ssh_host_ed25519_key_pub string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/ssh/ssh_host_ed25519_key.pub
|
||||||
|
ed25519 pubkey sentinel
|
||||||
|
`
|
||||||
|
|
||||||
|
const ssh_host_ed25519_key string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/ssh/ssh_host_ed25519_key
|
||||||
|
ed25519 key sentinel
|
||||||
|
`
|
||||||
|
|
||||||
|
const ssh_host_rsa_key_pub string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/ssh/ssh_host_rsa_key.pub
|
||||||
|
rsa pubkey sentinel
|
||||||
|
`
|
||||||
|
|
||||||
|
const ssh_host_rsa_key string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/ssh/ssh_host_rsa_key
|
||||||
|
rsa key sentinel
|
||||||
|
`
|
||||||
58
overlays/syncuser/internal/nodes.conf
Normal file
58
overlays/syncuser/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
72
overlays/syncuser/internal/syncuser_test.go
Normal file
72
overlays/syncuser/internal/syncuser_test.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package syncuser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_syncuserOverlay(t *testing.T) {
|
||||||
|
t.Skip("syncuser is not yet isolated from the host")
|
||||||
|
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/syncuser/rootfs/etc/passwd.ww", "../../../../../overlays/syncuser/rootfs/etc/passwd.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/syncuser/rootfs/etc/group.ww", "../../../../../overlays/syncuser/rootfs/etc/group.ww")
|
||||||
|
env.WriteFile(t, "var/lib/warewulf/chroots/rockylinux-9/rootfs/etc/passwd", `root:x:0:0:root:/root:/bin/bash`)
|
||||||
|
env.WriteFile(t, "var/lib/warewulf/chroots/rockylinux-9/rootfs/etc/group", `root:x:0:`)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "syncuser:passwd.ww",
|
||||||
|
args: []string{"--render", "node1", "syncuser", "etc/passwd.ww"},
|
||||||
|
log: syncuser_passwd,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "syncuser:group.ww",
|
||||||
|
args: []string{"--render", "node1", "syncuser", "etc/group.ww"},
|
||||||
|
log: syncuser_group,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
assert.Equal(t, tt.log, logbuf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const syncuser_passwd string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/passwd
|
||||||
|
# Uncomment the following line to enable passwordless root login
|
||||||
|
# root::0:0:root:/root:/bin/bash
|
||||||
|
root:x:0:0:root:/root:/bin/bash
|
||||||
|
`
|
||||||
|
|
||||||
|
const syncuser_group string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/group
|
||||||
|
root:x:0:
|
||||||
|
`
|
||||||
58
overlays/systemd.netname/internal/nodes.conf
Normal file
58
overlays/systemd.netname/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
66
overlays/systemd.netname/internal/systemd_netname_test.go
Normal file
66
overlays/systemd.netname/internal/systemd_netname_test.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package systemd_netname
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_udev_netnameOverlay(t *testing.T) {
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/systemd.netname/rootfs/etc/systemd/network/10-ww4-netname.link.ww", "../rootfs/etc/systemd/network/10-ww4-netname.link.ww")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "systemd network links",
|
||||||
|
args: []string{"--render", "node1", "systemd.netname", "etc/systemd/network/10-ww4-netname.link.ww"},
|
||||||
|
log: systemd_network_links,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
assert.Equal(t, tt.log, logbuf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const systemd_network_links string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: 10-ww4-netname-default.link
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
[Match]
|
||||||
|
MACAddress=e6:92:39:49:7b:03
|
||||||
|
[Link]
|
||||||
|
Name=wwnet0
|
||||||
|
backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: 10-ww4-netname-secondary.link
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
[Match]
|
||||||
|
MACAddress=9a:77:29:73:14:f1
|
||||||
|
[Link]
|
||||||
|
Name=wwnet1
|
||||||
|
`
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
{{ range $devname, $netdev := .NetDevs -}}
|
{{ range $devname, $netdev := .NetDevs -}}
|
||||||
{{- $filename := print "10-persistent-net-" $devname ".link" }}
|
{{- $filename := print "10-ww4-netname-" $devname ".link" }}
|
||||||
{{- file $filename }}
|
{{- file $filename }}
|
||||||
# This file is autogenerated by warewulf
|
# This file is autogenerated by warewulf
|
||||||
# Host: {{$.BuildHost}}
|
|
||||||
# Time: {{$.BuildTime}}
|
|
||||||
# Source: {{$.BuildSource}}
|
|
||||||
{{- if and $netdev.Hwaddr $netdev.Device }}
|
{{- if and $netdev.Hwaddr $netdev.Device }}
|
||||||
[Match]
|
[Match]
|
||||||
MACAddress={{$netdev.Hwaddr}}
|
MACAddress={{$netdev.Hwaddr}}
|
||||||
58
overlays/udev.netname/internal/nodes.conf
Normal file
58
overlays/udev.netname/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
58
overlays/udev.netname/internal/udev_netname_test.go
Normal file
58
overlays/udev.netname/internal/udev_netname_test.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package udev_netname
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_udev_netnameOverlay(t *testing.T) {
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/udev.netname/rootfs/etc/udev/rules.d/70-ww4-netname.rules.ww", "../rootfs/etc/udev/rules.d/70-ww4-netname.rules.ww")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "udev.netname",
|
||||||
|
args: []string{"--render", "node1", "udev.netname", "etc/udev/rules.d/70-ww4-netname.rules.ww"},
|
||||||
|
log: udev_netname,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
assert.Equal(t, tt.log, logbuf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const udev_netname string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/udev/rules.d/70-ww4-netname.rules
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
|
||||||
|
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="e6:92:39:49:7b:03", NAME="wwnet0"
|
||||||
|
|
||||||
|
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="9a:77:29:73:14:f1", NAME="wwnet1"
|
||||||
|
`
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
# This file is autogenerated by warewulf
|
# This file is autogenerated by warewulf
|
||||||
# Host: {{.BuildHost}}
|
|
||||||
# Time: {{.BuildTime}}
|
|
||||||
# Source: {{.BuildSource}}
|
|
||||||
{{range $devname, $netdev := .NetDevs}}
|
{{range $devname, $netdev := .NetDevs}}
|
||||||
{{- if $netdev.Hwaddr }}
|
{{- if $netdev.Hwaddr }}
|
||||||
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="{{ $netdev.Hwaddr }}", NAME="{{ $netdev.Device }}"
|
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="{{ $netdev.Hwaddr }}", NAME="{{ $netdev.Device }}"
|
||||||
58
overlays/wicked/internal/nodes.conf
Normal file
58
overlays/wicked/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
118
overlays/wicked/internal/wicked_test.go
Normal file
118
overlays/wicked/internal/wicked_test.go
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
package wicked
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_wickedOverlay(t *testing.T) {
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/wicked/rootfs/etc/wicked/ifconfig/ifcfg.xml.ww", "../rootfs/etc/wicked/ifconfig/ifcfg.xml.ww")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "wicked",
|
||||||
|
args: []string{"--render", "node1", "wicked", "etc/wicked/ifconfig/ifcfg.xml.ww"},
|
||||||
|
log: wicked,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
assert.Equal(t, tt.log, logbuf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wicked string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: ifcfg-default.xml
|
||||||
|
<!--
|
||||||
|
This file is autogenerated by warewulf
|
||||||
|
-->
|
||||||
|
<interface origin="static generated warewulf config">
|
||||||
|
<name>wwnet0</name>
|
||||||
|
<link-type>ethernet</link-type>
|
||||||
|
<control>
|
||||||
|
<mode>boot</mode>
|
||||||
|
</control>
|
||||||
|
<firewall/>
|
||||||
|
<link/>
|
||||||
|
<ipv4>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
<arp-verify>true</arp-verify>
|
||||||
|
</ipv4>
|
||||||
|
<ipv4:static>
|
||||||
|
<address>
|
||||||
|
<local>192.168.3.21/24</local>
|
||||||
|
</address>
|
||||||
|
<route>
|
||||||
|
<nexthop>
|
||||||
|
<gateway>192.168.3.1</gateway>
|
||||||
|
</nexthop>
|
||||||
|
</route>
|
||||||
|
</ipv4:static>
|
||||||
|
<ipv6>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
<privacy>prefer-public</privacy>
|
||||||
|
<accept-redirects>false</accept-redirects>
|
||||||
|
</ipv6>
|
||||||
|
</interface>
|
||||||
|
backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: ifcfg-secondary.xml
|
||||||
|
<!--
|
||||||
|
This file is autogenerated by warewulf
|
||||||
|
-->
|
||||||
|
<interface origin="static generated warewulf config">
|
||||||
|
<name>wwnet1</name>
|
||||||
|
<link-type>ethernet</link-type>
|
||||||
|
<control>
|
||||||
|
<mode>boot</mode>
|
||||||
|
</control>
|
||||||
|
<firewall/>
|
||||||
|
<link/>
|
||||||
|
<ipv4>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
<arp-verify>true</arp-verify>
|
||||||
|
</ipv4>
|
||||||
|
<ipv4:static>
|
||||||
|
<address>
|
||||||
|
<local>192.168.3.22/24</local>
|
||||||
|
</address>
|
||||||
|
<route>
|
||||||
|
<nexthop>
|
||||||
|
<gateway>192.168.3.1</gateway>
|
||||||
|
</nexthop>
|
||||||
|
</route>
|
||||||
|
</ipv4:static>
|
||||||
|
<ipv6>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
<privacy>prefer-public</privacy>
|
||||||
|
<accept-redirects>false</accept-redirects>
|
||||||
|
</ipv6>
|
||||||
|
</interface>
|
||||||
|
`
|
||||||
@@ -1,15 +1,9 @@
|
|||||||
{{- $host := .BuildHost }}
|
|
||||||
{{- $time := .BuildTime }}
|
|
||||||
{{- $source := .BuildSource }}
|
|
||||||
{{ $NetDevs := .NetDevs -}}{{/* FIX: 1413 */ -}}
|
{{ $NetDevs := .NetDevs -}}{{/* FIX: 1413 */ -}}
|
||||||
{{range $devname, $netdev := .ThisNode.NetDevs -}}
|
{{range $devname, $netdev := .ThisNode.NetDevs -}}
|
||||||
{{- $filename := print "ifcfg-" $devname ".xml" }}
|
{{- $filename := print "ifcfg-" $devname ".xml" }}
|
||||||
{{- file $filename }}
|
{{- file $filename }}
|
||||||
<!--
|
<!--
|
||||||
This file is autogenerated by warewulf
|
This file is autogenerated by warewulf
|
||||||
Host: {{ $host }}
|
|
||||||
Time: {{ $time }}
|
|
||||||
Source: {{ $source }}
|
|
||||||
-->
|
-->
|
||||||
<interface origin="static generated warewulf config">
|
<interface origin="static generated warewulf config">
|
||||||
<name>{{$netdev.Device.Get}}</name>
|
<name>{{$netdev.Device.Get}}</name>
|
||||||
58
overlays/wwclient/internal/nodes.conf
Normal file
58
overlays/wwclient/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
61
overlays/wwclient/internal/wwclient_test.go
Normal file
61
overlays/wwclient/internal/wwclient_test.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package wwclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_wwclientOverlay(t *testing.T) {
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/wwclient/rootfs/warewulf/init.d/80-wwclient.ww", "../rootfs/warewulf/init.d/80-wwclient.ww")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "wwclient:wwclient.ww",
|
||||||
|
args: []string{"--render", "node1", "wwclient", "warewulf/init.d/80-wwclient.ww"},
|
||||||
|
log: wwinit_wwclient,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
assert.Equal(t, tt.log, logbuf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wwinit_wwclient string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: warewulf/init.d/80-wwclient
|
||||||
|
#!/bin/sh
|
||||||
|
# This file is autogenerated by warewulf
|
||||||
|
|
||||||
|
. /warewulf/config
|
||||||
|
# Only start if the systemd is not available
|
||||||
|
test -e /usr/lib/systemd/systemd && exit 0
|
||||||
|
echo "Starting wwclient"
|
||||||
|
nohup /warewulf/wwclient >/var/log/wwclient.log 2>&1 </dev/null &
|
||||||
|
`
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# This file is autogenerated by warewulf
|
# This file is autogenerated by warewulf
|
||||||
# Host: {{.BuildHost}}
|
|
||||||
# Time: {{.BuildTime}}
|
|
||||||
# Source: {{.BuildSource}}
|
|
||||||
|
|
||||||
. /warewulf/config
|
. /warewulf/config
|
||||||
# Only start if the systemd is not available
|
# Only start if the systemd is not available
|
||||||
58
overlays/wwinit/internal/nodes.conf
Normal file
58
overlays/wwinit/internal/nodes.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
WW_INTERNAL: 45
|
||||||
|
nodeprofiles:
|
||||||
|
empty: {}
|
||||||
|
nodes:
|
||||||
|
node1:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
container name: rockylinux-9
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:03
|
||||||
|
ipaddr: 192.168.3.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
secondary:
|
||||||
|
device: wwnet1
|
||||||
|
hwaddr: 9a:77:29:73:14:f1
|
||||||
|
ipaddr: 192.168.3.22
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
|
tags:
|
||||||
|
DNS1: 8.8.8.8
|
||||||
|
DNS2: 8.8.4.4
|
||||||
|
ipmi:
|
||||||
|
username: user
|
||||||
|
password: password
|
||||||
|
ipaddr: 192.168.4.21
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.4.1
|
||||||
|
write: "true"
|
||||||
|
disks:
|
||||||
|
/dev/vda:
|
||||||
|
wipe_table: "true"
|
||||||
|
partitions:
|
||||||
|
scratch:
|
||||||
|
should_exist: "true"
|
||||||
|
swap:
|
||||||
|
number: "1"
|
||||||
|
size_mib: "1024"
|
||||||
|
filesystems:
|
||||||
|
/dev/disk/by-partlabel/scratch:
|
||||||
|
format: btrfs
|
||||||
|
path: /scratch
|
||||||
|
wipe_filesystem: "true"
|
||||||
|
/dev/disk/by-partlabel/swap:
|
||||||
|
format: swap
|
||||||
|
path: swap
|
||||||
|
node2:
|
||||||
|
profiles:
|
||||||
|
- empty
|
||||||
|
network devices:
|
||||||
|
default:
|
||||||
|
device: wwnet0
|
||||||
|
hwaddr: e6:92:39:49:7b:04
|
||||||
|
ipaddr: 192.168.3.23
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
gateway: 192.168.3.1
|
||||||
28
overlays/wwinit/internal/warewulf.conf
Normal file
28
overlays/wwinit/internal/warewulf.conf
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
WW_INTERNAL: 43
|
||||||
|
ipaddr: 192.168.0.1/24
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
network: 192.168.0.0
|
||||||
|
warewulf:
|
||||||
|
port: 9873
|
||||||
|
secure: false
|
||||||
|
update interval: 60
|
||||||
|
autobuild overlays: true
|
||||||
|
host overlay: true
|
||||||
|
syslog: true
|
||||||
|
dhcp:
|
||||||
|
enabled: true
|
||||||
|
range start: 192.168.0.100
|
||||||
|
range end: 192.168.0.199
|
||||||
|
tftp:
|
||||||
|
enabled: false
|
||||||
|
nfs:
|
||||||
|
enabled: true
|
||||||
|
export paths:
|
||||||
|
- path: /home
|
||||||
|
export options: rw,sync
|
||||||
|
mount options: defaults
|
||||||
|
mount: true
|
||||||
|
- path: /opt
|
||||||
|
export options: ro,sync,no_root_squash
|
||||||
|
mount options: defaults
|
||||||
|
mount: false
|
||||||
103
overlays/wwinit/internal/wwinit_test.go
Normal file
103
overlays/wwinit/internal/wwinit_test.go
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package wwinit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_wwinitOverlay(t *testing.T) {
|
||||||
|
env := testenv.New(t)
|
||||||
|
defer env.RemoveAll(t)
|
||||||
|
env.ImportFile(t, "etc/warewulf/warewulf.conf", "warewulf.conf")
|
||||||
|
env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/wwinit/rootfs/etc/warewulf/warewulf.conf.ww", "../rootfs/etc/warewulf/warewulf.conf.ww")
|
||||||
|
env.ImportFile(t, "var/lib/warewulf/overlays/wwinit/rootfs/warewulf/config.ww", "../rootfs/warewulf/config.ww")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "wwinit:warewulf.conf.ww",
|
||||||
|
args: []string{"--render", "node1", "wwinit", "etc/warewulf/warewulf.conf.ww"},
|
||||||
|
log: wwinit_warewulf_conf,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wwinit:config.ww",
|
||||||
|
args: []string{"--render", "node1", "wwinit", "warewulf/config.ww"},
|
||||||
|
log: wwinit_config,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := show.GetCommand()
|
||||||
|
cmd.SetArgs(tt.args)
|
||||||
|
stdout := bytes.NewBufferString("")
|
||||||
|
stderr := bytes.NewBufferString("")
|
||||||
|
logbuf := bytes.NewBufferString("")
|
||||||
|
cmd.SetOut(stdout)
|
||||||
|
cmd.SetErr(stderr)
|
||||||
|
wwlog.SetLogWriter(logbuf)
|
||||||
|
err := cmd.Execute()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, stdout.String())
|
||||||
|
assert.Empty(t, stderr.String())
|
||||||
|
assert.Equal(t, tt.log, logbuf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wwinit_warewulf_conf string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: etc/warewulf/warewulf.conf
|
||||||
|
WW_INTERNAL: 43
|
||||||
|
ipaddr: 192.168.0.1/24
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
network: 192.168.0.0
|
||||||
|
warewulf:
|
||||||
|
port: 9873
|
||||||
|
secure: false
|
||||||
|
update interval: 60
|
||||||
|
autobuild overlays: true
|
||||||
|
host overlay: true
|
||||||
|
syslog: true
|
||||||
|
dhcp:
|
||||||
|
enabled: true
|
||||||
|
range start: 192.168.0.100
|
||||||
|
range end: 192.168.0.199
|
||||||
|
tftp:
|
||||||
|
enabled: false
|
||||||
|
nfs:
|
||||||
|
enabled: true
|
||||||
|
export paths:
|
||||||
|
- path: /home
|
||||||
|
export options: rw,sync
|
||||||
|
mount options: defaults
|
||||||
|
mount: true
|
||||||
|
- path: /opt
|
||||||
|
export options: ro,sync,no_root_squash
|
||||||
|
mount options: defaults
|
||||||
|
mount: false
|
||||||
|
`
|
||||||
|
|
||||||
|
const wwinit_config string = `backupFile: true
|
||||||
|
writeFile: true
|
||||||
|
Filename: warewulf/config
|
||||||
|
WWCONTAINER=rockylinux-9
|
||||||
|
WWHOSTNAME=node1
|
||||||
|
WWROOT=initramfs
|
||||||
|
WWINIT=/sbin/init
|
||||||
|
WWIPMI_IPADDR="192.168.4.21"
|
||||||
|
WWIPMI_NETMASK="255.255.255.0"
|
||||||
|
WWIPMI_GATEWAY="192.168.4.1"
|
||||||
|
WWIPMI_USER="user"
|
||||||
|
WWIPMI_PASSWORD="password"
|
||||||
|
WWIPMI_WRITE="true"
|
||||||
|
`
|
||||||
@@ -98,7 +98,7 @@ explained as follows:
|
|||||||
When ``true``, ``wwclient`` uses TCP port 987.
|
When ``true``, ``wwclient`` uses TCP port 987.
|
||||||
|
|
||||||
Changing this option requires rebuilding node overlays and rebooting
|
Changing this option requires rebuilding node overlays and rebooting
|
||||||
compute nodes, to configure them to use a privileged port.
|
compute nodes to configure them to use a privileged port.
|
||||||
|
|
||||||
* ``warewulf:update interval``: This defines the frequency (in
|
* ``warewulf:update interval``: This defines the frequency (in
|
||||||
seconds) with which the Warewulf client on the compute node fetches
|
seconds) with which the Warewulf client on the compute node fetches
|
||||||
@@ -205,29 +205,7 @@ a "runtime overlay" specified, the respective value from
|
|||||||
``defaultnode`` is used. If a network device does not specify a
|
``defaultnode`` is used. If a network device does not specify a
|
||||||
"device," the device value of the ``dummy`` device is used.
|
"device," the device value of the ``dummy`` device is used.
|
||||||
|
|
||||||
If ``defaults.conf`` does not exist, the following values are used as
|
If ``defaults.conf`` does not exist, compiled-in defaults are used.
|
||||||
compiled into Warewulf at build-time:
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
--
|
|
||||||
defaultnode:
|
|
||||||
runtime overlay:
|
|
||||||
- generic
|
|
||||||
system overlay:
|
|
||||||
- wwinit
|
|
||||||
kernel:
|
|
||||||
args: quiet crashkernel=no vga=791 net.naming-scheme=v238
|
|
||||||
init: /sbin/init
|
|
||||||
root: initramfs
|
|
||||||
ipxe template: default
|
|
||||||
profiles:
|
|
||||||
- default
|
|
||||||
network devices:
|
|
||||||
dummy:
|
|
||||||
device: eth0
|
|
||||||
type: ethernet
|
|
||||||
netmask: 255.255.255.0
|
|
||||||
|
|
||||||
There should never be a need to change this file: all site-local
|
There should never be a need to change this file: all site-local
|
||||||
parameters should be specified using either nodes or profiles.
|
parameters should be specified using either nodes or profiles.
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ Ignition Implementation
|
|||||||
=======================
|
=======================
|
||||||
|
|
||||||
The ignition implementation uses systemd services, as the underlying ``sgdisk`` command relies on dbus notifications.
|
The ignition implementation uses systemd services, as the underlying ``sgdisk`` command relies on dbus notifications.
|
||||||
All necessary services are distributed by the ``wwinit`` overlay and depends on the existence of the file ``/warewulf/ignition.json``.
|
All necessary services are distributed by the ``ignition`` overlay and depends on the existence of the file ``/warewulf/ignition.json``.
|
||||||
This file is created by the template function ``{{ createIgnitionJson }}`` only if the configuration contains necessary specifications for disks, partitions, and file systems.
|
This file is created by the template function ``{{ createIgnitionJson }}`` only if the configuration contains necessary specifications for disks, partitions, and file systems.
|
||||||
If the file ``/warewulf/ignition.json`` exists, the service ``ignition-disks-ww4.service`` calls the ignition binary which takes creates partitions and file systems.
|
If the file ``/warewulf/ignition.json`` exists, the service ``ignition-disks-ww4.service`` calls the ignition binary which takes creates partitions and file systems.
|
||||||
A systemd ``.mount`` unit is created for each configured file system, which also creates the necessary mount points in the root file system.
|
A systemd ``.mount`` unit is created for each configured file system, which also creates the necessary mount points in the root file system.
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ properly. Here are the things it will do:
|
|||||||
the system service.
|
the system service.
|
||||||
* **hostfile**: Update the system's /etc/hosts file based on the
|
* **hostfile**: Update the system's /etc/hosts file based on the
|
||||||
**host** template ``/etc/hosts.ww``.
|
**host** template ``/etc/hosts.ww``.
|
||||||
* **nfs**: Configure the NFS server on the control node as well as the
|
* **nfs**: Configure the NFS server on the control node based on the
|
||||||
``/etc/fstab`` in the system overlay based on the configuration in
|
configuration in ``/etc/warewulf/warewulf.conf`` and enable the NFS
|
||||||
``/etc/warewulf/warewulf.conf`` and enable the system service. Also
|
service. Also the file ``/etc/exports.ww`` from the **host** template is
|
||||||
the file ``/etc/exports.ww`` from the **host** template is installed.
|
installed.
|
||||||
* **ssh**: Create the appropriate host keys (stored in
|
* **ssh**: Create the appropriate host keys (stored in
|
||||||
``/etc/warewulf/keys/``) and user keys for passwordless ``ssh`` into
|
``/etc/warewulf/keys/``) and user keys for passwordless ``ssh`` into
|
||||||
the nodes. Addionally the shell profiles
|
the nodes. Addionally the shell profiles
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ individual node would be the IP address.
|
|||||||
|
|
||||||
The settings are only written to the IPMI interface if ``--ipmiwrite``
|
The settings are only written to the IPMI interface if ``--ipmiwrite``
|
||||||
is set to `true`. The write process happens at every boot of the node
|
is set to `true`. The write process happens at every boot of the node
|
||||||
through the script ``/warewulf/init.d/50-ipmi`` in the **system**
|
through the script ``/warewulf/init.d/50-ipmi`` in the wwinit
|
||||||
overlay.
|
overlay.
|
||||||
|
|
||||||
If an individual node has different settings, you can set the IPMI
|
If an individual node has different settings, you can set the IPMI
|
||||||
@@ -99,8 +99,8 @@ Node View
|
|||||||
n001 cluster -- --
|
n001 cluster -- --
|
||||||
n001 container default sle-micro-5.3
|
n001 container default sle-micro-5.3
|
||||||
n001 ipxe -- (default)
|
n001 ipxe -- (default)
|
||||||
n001 runtime -- (generic)
|
n001 runtime -- (hosts,ssh.authorized_keys,syncuser)
|
||||||
n001 wwinit -- (wwinit)
|
n001 wwinit -- (wwinit,wwclient,fstab,hostname,ssh.host_keys,issue,resolv,udev.netname,systemd.netname,ifcfg,NetworkManager,debian.interfaces,wicked,ignition)
|
||||||
n001 root -- (initramfs)
|
n001 root -- (initramfs)
|
||||||
n001 discoverable -- --
|
n001 discoverable -- --
|
||||||
n001 init -- (/sbin/init)
|
n001 init -- (/sbin/init)
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ You can also see the node's full attribute list by specifying the
|
|||||||
n001 cluster -- --
|
n001 cluster -- --
|
||||||
n001 container default sle-micro-5.3
|
n001 container default sle-micro-5.3
|
||||||
n001 ipxe -- (default)
|
n001 ipxe -- (default)
|
||||||
n001 runtime -- (generic)
|
n001 runtime -- (hosts,ssh.authorized_keys,syncuser)
|
||||||
n001 wwinit -- (wwinit)
|
n001 wwinit -- (wwinit,wwclient,fstab,hostname,ssh.host_keys,issue,resolv,udev.netname,systemd.netname,ifcfg,NetworkManager,debian.interfaces,wicked,ignition)
|
||||||
n001 root -- (initramfs)
|
n001 root -- (initramfs)
|
||||||
n001 discoverable -- --
|
n001 discoverable -- --
|
||||||
n001 init -- (/sbin/init)
|
n001 init -- (/sbin/init)
|
||||||
@@ -193,8 +193,8 @@ container, kernel, and network:
|
|||||||
n001 cluster -- --
|
n001 cluster -- --
|
||||||
n001 container default sle-micro-5.3
|
n001 container default sle-micro-5.3
|
||||||
n001 ipxe -- (default)
|
n001 ipxe -- (default)
|
||||||
n001 runtime -- (generic)
|
n001 runtime -- (hosts,ssh.authorized_keys,syncuser)
|
||||||
n001 wwinit -- (wwinit)
|
n001 wwinit -- (wwinit,wwclient,fstab,hostname,ssh.host_keys,issue,resolv,udev.netname,systemd.netname,ifcfg,NetworkManager,debian.interfaces,wicked,ignition)
|
||||||
n001 root -- (initramfs)
|
n001 root -- (initramfs)
|
||||||
n001 discoverable -- --
|
n001 discoverable -- --
|
||||||
n001 init -- (/sbin/init)
|
n001 init -- (/sbin/init)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Warewulf Overlays
|
Warewulf Overlays
|
||||||
=================
|
=================
|
||||||
|
|
||||||
So at this point, we have discussed how Warewulf is designed to
|
At this point we have discussed how Warewulf is designed to
|
||||||
scalably provision and manage thousands of cluster nodes by utilizing
|
scalably provision and manage thousands of cluster nodes by utilizing
|
||||||
identical stateless boot images. And there-in lies a problem to
|
identical stateless boot images. And there-in lies a problem to
|
||||||
solve. If these boot images are completely identical, then how do we
|
solve. If these boot images are completely identical, then how do we
|
||||||
@@ -19,57 +19,114 @@ through the provisioning process. A node or profile can configure an
|
|||||||
overlay in two different ways:
|
overlay in two different ways:
|
||||||
|
|
||||||
* An overlay can be configured to run during boot as part of the
|
* An overlay can be configured to run during boot as part of the
|
||||||
``wwinit`` process. These overlays are called **system overlay** or
|
``wwinit`` process. These overlays are called **system overlays**.
|
||||||
**wwinit overlays**.
|
|
||||||
* An overlay can be configured to run periodically while the system is
|
* An overlay can be configured to run periodically while the system is
|
||||||
running. These overlays are called **runtime overlays** or **generic
|
running. These overlays are called **runtime overlays**.
|
||||||
overlays**.
|
|
||||||
|
|
||||||
The default profile has both a **wwinit** and a **runtime** overlay
|
The default profile has both a **wwinit** and a **runtime** overlay
|
||||||
configured.
|
configured.
|
||||||
|
|
||||||
Overlays are compiled for each compute node individually.
|
Overlays are compiled for each compute node individually.
|
||||||
|
|
||||||
Defined Overlays
|
Provided overlays
|
||||||
================
|
=================
|
||||||
|
|
||||||
System or wwinit overlay
|
These overlays are provided as part of Warewulf.
|
||||||
------------------------
|
|
||||||
|
|
||||||
This overlay contains all the necessary scripts to provision a
|
wwinit
|
||||||
Warewulf node. It is available before the ``systemd`` or other init is
|
------
|
||||||
called and contains all configurations which are needed to bring up
|
|
||||||
the compute node. It is not updated during run time. Besides the
|
|
||||||
network configurations for
|
|
||||||
|
|
||||||
* wicked
|
The **wwinit** overlay performs initial configuration of the Warewulf node.
|
||||||
* NetworkManager
|
Its `wwinit` script runs before ``systemd`` or other init is called and
|
||||||
* EL legacy network scripts
|
contains all configurations which are needed to boot.
|
||||||
|
|
||||||
it also contains udev rules, which will set the interface names
|
In particular:
|
||||||
based on the hardware addresses configured for the node.
|
|
||||||
Before the ``systemd`` init is called, the overlay loops through the scripts in
|
|
||||||
``/wwinit/warwulf/init.d/*`` which will setup
|
|
||||||
|
|
||||||
* Ipmi
|
- Configure the loopback interface
|
||||||
* wwclient
|
- Configure the BMC based on the node's configuration
|
||||||
* selinux
|
- Update PAM configuration to allow missing shadow entries
|
||||||
|
- Relabel the file system for SELinux
|
||||||
|
|
||||||
Runtime Overlay or generic Overlay
|
Other overlays may place additional scripts in ``/warewulf/init.d/`` to affect
|
||||||
----------------------------------
|
node configuration in this pre-boot environment.
|
||||||
|
|
||||||
The runtime overlay is updated by the ``wwclient`` service on a
|
wwclient
|
||||||
regular basis (by default, once per minute). In the standard
|
--------
|
||||||
configuration it includes updates for ``/etc/passwd``, ``/etc/group``
|
|
||||||
and ``/etc/hosts``. Additionally the ``authorized_keys`` file of the
|
|
||||||
root user is updated. It is recommended to use this overlay for
|
|
||||||
dynamic configuration files like ``slurm.conf``. Once the system is
|
|
||||||
provisioned and booted, the ``wwclient`` program (which is provisioned
|
|
||||||
as part of the ``wwinit`` system overlay) will continuously update the
|
|
||||||
node with updates in the runtime overlay.
|
|
||||||
|
|
||||||
Host Overlay
|
All configured overlays are provisioned initially along with the node image
|
||||||
------------
|
itself; but **wwclient** periodically fetches and applies the runtime overlay
|
||||||
|
to allow configuration of some settings without a reboot.
|
||||||
|
|
||||||
|
Network interfaces
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Warewulf ships with support for many different network interface configuration
|
||||||
|
systems. All of these are applied by default; but the list may be trimmed to
|
||||||
|
the desired system.
|
||||||
|
|
||||||
|
- ifcfg
|
||||||
|
- NetworkManager
|
||||||
|
- debian.interfaces
|
||||||
|
- wicked
|
||||||
|
|
||||||
|
Warewulf also configures both systemd and udev with the intended names of
|
||||||
|
configured network interfaces, typically based on a known MAC address.
|
||||||
|
|
||||||
|
- systemd.netname
|
||||||
|
- udev.netname
|
||||||
|
|
||||||
|
Basics
|
||||||
|
------
|
||||||
|
|
||||||
|
The **hostname** overlay sets the hostname based on the configured Warewulf
|
||||||
|
node name.
|
||||||
|
|
||||||
|
The **hosts** overlay configures ``/etc/hosts`` to include all Warewulf nodes.
|
||||||
|
|
||||||
|
The **issue** overlay configures a standard Warewulf status message for display
|
||||||
|
during login.
|
||||||
|
|
||||||
|
The **resolv** overlay configures ``/etc/resolv.conf`` based on the value of
|
||||||
|
"DNS" nettags. (In most situations this should be unnecessary, as the network
|
||||||
|
interface configuration should handle this dynamically.)
|
||||||
|
|
||||||
|
fstab
|
||||||
|
-----
|
||||||
|
|
||||||
|
The **fstab** overlay configures ``/etc/fstab`` to mount NFS shares defined in
|
||||||
|
``/etc/warewulf.conf`` and file systems created by Ignition.
|
||||||
|
|
||||||
|
ssh
|
||||||
|
---
|
||||||
|
|
||||||
|
Two SSH overlays configure host keys (one set for all node in the cluster) and
|
||||||
|
``authorized_keys`` for the root account.
|
||||||
|
|
||||||
|
- ssh.authorized_keys
|
||||||
|
- ssh.host_keys
|
||||||
|
|
||||||
|
syncuser
|
||||||
|
--------
|
||||||
|
|
||||||
|
The **syncuser** overlay updates ``/etc/passwd`` and ``/etc/group`` to include
|
||||||
|
all users on both the Warewulf server and from the container image. To function
|
||||||
|
properly, ``wwctl container syncuser`` must have also been run on the container
|
||||||
|
image to synchronize its user and group IDs with those of the server.
|
||||||
|
|
||||||
|
ignition
|
||||||
|
--------
|
||||||
|
|
||||||
|
The **ignition** overlay defines partitions and file systems on local disks.
|
||||||
|
|
||||||
|
debug
|
||||||
|
-----
|
||||||
|
|
||||||
|
The **debug** overlay is not intended to be used in configuration, but can be
|
||||||
|
used as an example and applied with ``wwctl overlay show --render <nodename>
|
||||||
|
debug /warewulf/template-variables.md.ww``.
|
||||||
|
|
||||||
|
host
|
||||||
|
----
|
||||||
|
|
||||||
Configuration files used for the configuration of the Warewulf host /
|
Configuration files used for the configuration of the Warewulf host /
|
||||||
server are stored in the **host** overlay. Unlike other overlays, it
|
server are stored in the **host** overlay. Unlike other overlays, it
|
||||||
@@ -91,25 +148,25 @@ The following services get configuration files via the host overlay:
|
|||||||
Combining Overlays
|
Combining Overlays
|
||||||
==================
|
==================
|
||||||
|
|
||||||
When changing the overlays, it is recommended not to change them, but
|
We recommended not changing the provided overlays. Rather,
|
||||||
to add the changed files to a new overlay and combine them in the
|
add the changed files to a new overlay and combine them in the
|
||||||
configuration. This is possible as the configuration fields for the
|
configuration. This is possible as the configuration fields for the
|
||||||
**wwinit** and **runtime** overlays are lists and can contain several
|
system and runtime overlays are lists and can contain several
|
||||||
overlays. As an example for this, we will overwrite the
|
overlays. As an example for this, we will overwrite the
|
||||||
``/etc/issue`` file from the **wwinit** overlay. For this we will
|
``/etc/issue`` file from the "issue" overlay. For this we will
|
||||||
create a new overlay called welcome and import the file ``/etc/issue``
|
create a new overlay called "welcome" and import the file ``/etc/issue``
|
||||||
from the host to it. This overlay is then combined with the existing
|
from the host to it. This overlay is then combined with the existing
|
||||||
**wwinit** overlay.
|
overlays.
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
# wwctl overlay create welcome
|
# wwctl overlay create welcome
|
||||||
# wwctl overlay mkdir welcome /etc
|
# wwctl overlay mkdir welcome /etc
|
||||||
# wwctl overlay import welcome /etc/issue
|
# wwctl overlay import welcome /etc/issue
|
||||||
# wwctl profile set default --wwinit=wwinit,welcome
|
# wwctl profile set default --wwinit=wwinit,wwclient,welcome
|
||||||
? Are you sure you want to modify 1 profile(s)? [y/N] y
|
? Are you sure you want to modify 1 profile(s)? [y/N] y
|
||||||
# wwctl profile list default -a |grep welcome
|
# wwctl profile list default -a |grep welcome
|
||||||
default SystemOverlay wwinit,welcome
|
default SystemOverlay wwinit,wwclient,welcome
|
||||||
|
|
||||||
Templates
|
Templates
|
||||||
=========
|
=========
|
||||||
@@ -143,7 +200,7 @@ permissions, etc.
|
|||||||
|
|
||||||
..
|
..
|
||||||
note::
|
note::
|
||||||
There is no possibility to delete files with an overlay!
|
It is not possible to delete files with an overlay.
|
||||||
|
|
||||||
Build
|
Build
|
||||||
-----
|
-----
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user