Merge pull request #1306 from anderbubble/split-test-overlays

Split overlays into discrete functionality and test
This commit is contained in:
Christian Goll
2024-09-27 14:59:37 +02:00
committed by GitHub
110 changed files with 3584 additions and 283 deletions

View File

@@ -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/coreos/ignition/v2 from 2.15.0 to 2.19.0 #1239
- Disable building containers by default when calling `wwctl container copy`. #1378
- Split wwinit and generic overlays into discrete functionality. #987
### Fixed

View File

@@ -100,6 +100,7 @@ install: build docs
install -d -m 0755 $(DESTDIR)$(WWCHROOTDIR)
install -d -m 0755 $(DESTDIR)$(WWPROVISIONDIR)
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)$(WWCONFIGDIR)/examples
install -d -m 0755 $(DESTDIR)$(WWCONFIGDIR)/ipxe
@@ -122,15 +123,15 @@ install: build docs
install -m 0644 etc/grub/grub.cfg.ww $(DESTDIR)$(WWCONFIGDIR)/grub/grub.cfg.ww
install -m 0644 etc/grub/chainload.ww $(DESTDIR)$(WWOVERLAYDIR)/host/rootfs$(TFTPDIR)/warewulf/grub.cfg.ww
install -m 0644 etc/logrotate.d/warewulfd.conf $(DESTDIR)$(LOGROTATEDIR)/warewulfd.conf
(cd overlays && find * -type f -exec install -D -m 0644 {} $(DESTDIR)$(WWOVERLAYDIR)/{} \;)
(cd overlays && find * -type d -exec mkdir -pv $(DESTDIR)$(WWOVERLAYDIR)/{} \;)
(cd overlays && find * -type l -exec cp -av {} $(DESTDIR)$(WWOVERLAYDIR)/{} \;)
(cd overlays && find * -path '*/internal' -prune -o -type f -exec install -D -m 0644 {} $(DESTDIR)$(WWOVERLAYDIR)/{} \;)
(cd overlays && find * -path '*/internal' -prune -o -type d -exec mkdir -pv $(DESTDIR)$(WWOVERLAYDIR)/{} \;)
(cd overlays && find * -path '*/internal' -prune -o -type l -exec cp -av {} $(DESTDIR)$(WWOVERLAYDIR)/{} \;)
chmod 0755 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/init
chmod 0755 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/$(WWCLIENTDIR)/wwinit
chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/etc/ssh/ssh*
chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/etc/NetworkManager/system-connections/ww4-managed.ww
chmod 0644 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/etc/ssh/ssh*.pub.ww
chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/warewulf/config.ww
chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/$(WWCLIENTDIR)/config.ww
chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/ssh.host_keys/rootfs/etc/ssh/ssh*
chmod 0644 $(DESTDIR)$(WWOVERLAYDIR)/ssh.host_keys/rootfs/etc/ssh/ssh*.pub.ww
chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/NetworkManager/rootfs/etc/NetworkManager/system-connections/ww4-managed.ww
chmod 0750 $(DESTDIR)$(WWOVERLAYDIR)/host/rootfs
install -m 0755 wwctl $(DESTDIR)$(BINDIR)
install -m 0755 wwclient $(DESTDIR)$(WWOVERLAYDIR)/wwinit/rootfs/$(WWCLIENTDIR)/wwclient

View File

@@ -18,9 +18,24 @@ structure of a node within \fBnodes.pp\fP.
.EX
defaultnode:
runtime overlay:
- generic
- hosts
- ssh.authorized_keys
- syncuser
system overlay:
- wwinit
- wwclient
- fstab
- hostname
- ssh.host_keys
- issue
- resolv
- udev.netname
- systemd.netname
- ifcfg
- NetworkManager
- debian.interfaces
- wicked
- ignition
kernel:
args: quiet crashkernel=no vga=791 net.naming-scheme=v238
init: /sbin/init

View File

@@ -145,8 +145,8 @@ nodes:
n01 Id -- n01
n01 Comment default profilecomment
n01 Ipxe -- (default)
n01 RuntimeOverlay -- (generic)
n01 SystemOverlay -- (wwinit)
n01 RuntimeOverlay -- (hosts,ssh.authorized_keys,syncuser)
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 Init -- (/sbin/init)
n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238)
@@ -168,8 +168,8 @@ nodes:
n01 Id -- n01
n01 Comment SUPERSEDED nodecomment
n01 Ipxe -- (default)
n01 RuntimeOverlay -- (generic)
n01 SystemOverlay -- (wwinit)
n01 RuntimeOverlay -- (hosts,ssh.authorized_keys,syncuser)
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 Init -- (/sbin/init)
n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238)
@@ -251,8 +251,8 @@ nodes:
stdout: `NODE FIELD PROFILE VALUE
n01 Id -- n01
n01 Ipxe -- (default)
n01 RuntimeOverlay -- (generic)
n01 SystemOverlay -- (wwinit)
n01 RuntimeOverlay -- (hosts,ssh.authorized_keys,syncuser)
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 Init -- (/sbin/init)
n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238)
@@ -273,12 +273,14 @@ nodes:
name: "multiple overlays list",
args: []string{"-l"},
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
nodeprofiles:
p1:
system overlay:
- sop1
runtime overlay:
- rop1
- rop2
@@ -293,12 +295,14 @@ nodes:
name: "multiple overlays list",
args: []string{"-l"},
stdout: `NODE NAME KERNEL OVERRIDE CONTAINER OVERLAYS (S/R)
n01 -- -- (wwinit)/rop2,nop1 ~{rop1}
n01 -- -- sop1/rop2,nop1 ~{rop1}
`},
{
inDb: `WW_INTERNAL: 45
nodeprofiles:
p1:
system overlay:
- sop1
runtime overlay:
- rop1
- rop2
@@ -316,7 +320,7 @@ nodes:
n01 Id -- n01
n01 Ipxe -- (default)
n01 RuntimeOverlay SUPERSEDED rop2,nop1 ~{rop1}
n01 SystemOverlay -- (wwinit)
n01 SystemOverlay p1 sop1
n01 Root -- (initramfs)
n01 Init -- (/sbin/init)
n01 Kernel.Args -- (quiet crashkernel=no vga=791 net.naming-scheme=v238)
@@ -340,7 +344,7 @@ nodes:
stdout: `NODE FIELD PROFILE VALUE
n01 Id -- n01
n01 Ipxe -- (default)
n01 RuntimeOverlay -- (generic)
n01 RuntimeOverlay -- (hosts,ssh.authorized_keys,syncuser)
n01 SystemOverlay SUPERSEDED profileinit,nodeinit
n01 Root -- (initramfs)
n01 Init -- (/sbin/init)

View File

@@ -3,6 +3,7 @@ package show
import (
"bufio"
"bytes"
"errors"
"os"
"path"
"path/filepath"
@@ -24,30 +25,33 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
overlaySourceDir = overlay.OverlaySourceDir(overlayName)
if !util.IsDir(overlaySourceDir) {
wwlog.Error("Overlay does not exist: %s", overlayName)
os.Exit(1)
err := errors.New("Overlay does not exist")
wwlog.Error("%s: %s", err, overlayName)
return err
}
overlayFile := path.Join(overlaySourceDir, fileName)
if !util.IsFile(overlayFile) {
wwlog.Error("File does not exist within overlay: %s:%s", overlayName, fileName)
os.Exit(1)
err := errors.New("File does not exist within overlay")
wwlog.Error("%s: %s:%s", err, overlayName, fileName)
return err
}
if NodeName == "" {
f, err := os.ReadFile(overlayFile)
if err != nil {
wwlog.Error("Could not read file: %s", err)
os.Exit(1)
return err
}
wwlog.Info(string(f))
wwlog.Output("%s", string(f))
} else {
if !util.IsFile(overlayFile) {
wwlog.Debug("%s is not a file", overlayFile)
wwlog.Error("%s:%s is not a file", overlayName, fileName)
os.Exit(1)
err := errors.New("Not a file")
wwlog.Error("%s: %s:%s", err, overlayName, fileName)
return err
}
if filepath.Ext(overlayFile) != ".ww" {
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()
if err != nil {
wwlog.Error("Could not open node configuration: %s", err)
os.Exit(1)
return err
}
nodes, err := nodeDB.FindAllNodes()
if err != nil {
wwlog.Error("Could not get node list: %s", err)
os.Exit(1)
return err
}
filteredNodes := node.FilterByName(nodes, []string{NodeName})
if hostName, err := os.Hostname(); err != nil {
@@ -73,8 +77,9 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
hostNodeInfo.ClusterName.Set(hostName)
filteredNodes = append(filteredNodes, *hostNodeInfo)
} else if len(filteredNodes) != 1 {
wwlog.Error("%v does not identify a single node", NodeName)
os.Exit(1)
err := errors.New("Not a single node")
wwlog.Error("%s: %v", err, NodeName)
return err
}
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])
if foundFileComment {
if !Quiet {
wwlog.Info("backupFile: %v\nwriteFile: %v", backupFile, writeFile)
wwlog.Info("Filename: %s\n", destFileName)
wwlog.Info("backupFile: %v", backupFile)
wwlog.Info("writeFile: %v", writeFile)
wwlog.Info("Filename: %s", destFileName)
}
wwlog.Info("%s", outBuffer.String())
wwlog.Output("%s", outBuffer.String())
outBuffer.Reset()
}
destFileName = filenameFromTemplate[0][1]
@@ -110,10 +116,11 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
}
}
if !Quiet {
wwlog.Info("backupFile: %v\nwriteFile: %v", backupFile, writeFile)
wwlog.Info("Filename: %s\n", destFileName)
wwlog.Info("backupFile: %v", backupFile)
wwlog.Info("writeFile: %v", writeFile)
wwlog.Info("Filename: %s", destFileName)
}
wwlog.Info("%s", outBuffer.String())
wwlog.Output("%s", outBuffer.String())
}
return nil
}

View File

@@ -7,10 +7,9 @@ import (
"testing"
"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/warewulfd"
"github.com/warewulf/warewulf/internal/pkg/wwlog"
)
var (
@@ -24,6 +23,35 @@ overlay name {{ .Overlay }}
func Test_Overlay_List(t *testing.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",
`WW_INTERNAL: 45
nodeprofiles:

View File

@@ -23,9 +23,24 @@ var (
var FallBackConf = `---
defaultnode:
runtime overlay:
- generic
- hosts
- ssh.authorized_keys
- syncuser
system overlay:
- wwinit
- wwclient
- fstab
- hostname
- ssh.host_keys
- issue
- resolv
- udev.netname
- systemd.netname
- ifcfg
- NetworkManager
- debian.interfaces
- wicked
- ignition
kernel:
args: quiet crashkernel=no vga=791 net.naming-scheme=v238
init: /sbin/init

View File

@@ -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 {
for _, n := range nodes {

View File

@@ -42,7 +42,6 @@ const Systemddir = "usr/lib/systemd/system"
const WWOverlaydir = "var/lib/warewulf/overlays"
const WWChrootdir = "var/lib/warewulf/chroots"
const WWProvisiondir = "srv/warewulf"
const WWClientdir = "warewulf"
// New creates a test environment in a temporary directory and configures
// Warewulf to use it.
@@ -79,7 +78,7 @@ func New(t *testing.T) (env *TestEnv) {
conf.Paths.WWOverlaydir = env.GetPath(WWOverlaydir)
conf.Paths.WWChrootdir = env.GetPath(WWChrootdir)
conf.Paths.WWProvisiondir = env.GetPath(WWProvisiondir)
conf.Paths.WWClientdir = env.GetPath(WWClientdir)
conf.Paths.WWClientdir = "/warewulf"
for _, confPath := range []string{
conf.Paths.Sysconfdir,
@@ -93,7 +92,6 @@ func New(t *testing.T) (env *TestEnv) {
conf.Paths.WWOverlaydir,
conf.Paths.WWChrootdir,
conf.Paths.WWProvisiondir,
conf.Paths.WWClientdir,
} {
env.MkdirAll(t, confPath)
}
@@ -138,6 +136,15 @@ func (env *TestEnv) WriteFile(t *testing.T, fileName string, content string) {
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
// string.
//

View File

@@ -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
// to not pollute the messages unless something bad happens (by default).
// if logLevel > INFO, then level == INFO messages should not be printed anyway,

View 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
`

View 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

View File

@@ -1,12 +1,6 @@
{{- $host := .BuildHost }}
{{- $time := .BuildTime }}
{{- $source := .BuildSource }}
{{- $filename := print "warewulf-unmanaged.conf" }}
{{- file $filename }}
# This file is autogenerated by warewulf
# Host: {{ $host }}
# Time: {{ $time }}
# Source: {{ $source }}
[main]
plugins=keyfile

View File

@@ -1,13 +1,7 @@
{{- $host := .BuildHost }}
{{- $time := .BuildTime }}
{{- $source := .BuildSource }}
{{- range $connection_id, $netdev := .NetDevs }}
{{- $filename := print "warewulf-" $connection_id ".conf" }}
{{- file $filename }}
# This file is autogenerated by warewulf
# Host: {{ $host }}
# Time: {{ $time }}
# Source: {{ $source }}
[connection]
id={{ $connection_id }}
interface-name={{ $netdev.Device }}

View File

@@ -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
`

View 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

View File

@@ -1,13 +1,7 @@
{{- $host := .BuildHost }}
{{- $time := .BuildTime }}
{{- $source := .BuildSource }}
{{range $devname, $netdev := .ThisNode.NetDevs -}}
{{- $filename := print $devname }}
{{- file $filename }}
# This file is autogenerated by warewulf
# Host: {{ $host }}
# Time: {{ $time }}
# Source: {{ $source }}
{{ if $netdev.OnBoot.GetB -}}
auto {{$netdev.Device.Get}}
{{ end -}}

View 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:
`

View 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

View File

@@ -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
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
`internal/pkg/overlay/datastructure.go`, though it also references
internal/pkg/overlay/datastructure.go, though it also references
data from other structures.

View 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
`

View 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

View 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

View File

@@ -1,7 +1,4 @@
# This file is autogenerated by warewulf
# Host: {{.BuildHost}}
# Time: {{.BuildTime}}
# Source: {{.BuildSource}}
rootfs / tmpfs defaults 0 0
devpts /dev/pts devpts gid=5,mode=620 0 0
tmpfs /run/shm tmpfs defaults 0 0

View File

@@ -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 */}}

View 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
`

View 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

View 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

View 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

View File

@@ -1,8 +1,5 @@
{{if $.Dhcp.Enabled -}}
# This file is autogenerated by warewulf
# Host: {{.BuildHost}}
# Time: {{.BuildTime}}
# Source: {{.BuildSource}}
allow booting;
allow bootp;

View File

@@ -1,7 +1,4 @@
# This file was autgenerated by warewulf
# Host: {{.BuildHost}}
# Time: {{.BuildTime}}
# Source {{.BuildSource}}
{{ nobackup }}
# select the x86 hosts which will get the iXPE binary
dhcp-match=set:bios,option:client-arch,0 #legacy boot

View File

@@ -1,8 +1,5 @@
{{- if .Nfs.Enabled }}
# This file is autogenerated by warewulf
# Host: {{.BuildHost}}
# Time: {{.BuildTime}}
# Source: {{.BuildSource}}
{{- $network := .Network }}
{{- $netmask := .Netmask }}
{{- range .Nfs.ExportsExtended }}

View File

@@ -1,17 +1,14 @@
{{ IncludeBlock "/etc/hosts" "# Do not edit after this line" }}
# This block is autogenerated by warewulf
# Host: {{.BuildHost}}
# Time: {{.BuildTime}}
# Source: {{.BuildSource}}
# 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 */}}
{{- 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 */}}

View 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
`

View 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

View 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
`

View 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

View 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

View 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 }}

View 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
`

View 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

View File

@@ -1,13 +1,7 @@
{{- $host := .BuildHost }}
{{- $time := .BuildTime }}
{{- $source := .BuildSource }}
{{range $devname, $netdev := .NetDevs -}}
{{- $filename := print "ifcfg-" $devname ".conf" }}
{{- file $filename }}
# This file is autogenerated by warewulf
# Host: {{ $host }}
# Time: {{ $time }}
# Source: {{ $source }}
TYPE={{ $netdev.Type }}
DEVICE={{ $netdev.Device }}
NAME={{ $devname }}

View 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}]}}
`

View 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

View File

@@ -1,7 +1,4 @@
# This file is autogenerated by warewulf
# Host: {{.BuildHost}}
# Time: {{.BuildTime}}
# Source: {{.BuildSource}}
[Unit]
Description=mount ww4 disks
# make sure that the disks are available

View File

@@ -1,6 +1,3 @@
{{- $host := .BuildHost }}
{{- $time := .BuildTime }}
{{- $source := .BuildSource }}
{{- range $fsdevice,$fs := .FileSystems }}
{{- $prefix := replace "-" "\\x2d" $fs.Path }}
{{- $prefix = replace "/" "-" $prefix }}
@@ -11,9 +8,6 @@
{{- $filename := print $prefix "." $suffix }}
{{- file $filename }}
# This file is autogenerated by warewulf
# Host: {{ $host }}
# Time: {{ $time }}
# Source: {{ $source }}
{{- if eq $fs.Format "swap"}}
[Unit]
ConditionPathExists=/warewulf/ignition.json

View 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
`

View 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

View 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

View 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
`

View File

@@ -1,10 +1,4 @@
{{- $host := .BuildHost }}
{{- $time := .BuildTime }}
{{- $source := .BuildSource }}
# This file is autogenerated by warewulf
# Host: {{ $host }}
# Time: {{ $time }}
# Source: {{ $source }}
{{range $devname, $netdev := .NetDevs -}}
{{range $tk, $tv := $netdev.Tags -}}
{{ $prefix := substr 0 3 $tk -}}

View 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

View File

@@ -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
`

View 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

View 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
`

View 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

View 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:
`

View 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

View 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
`

View File

@@ -1,10 +1,7 @@
{{ range $devname, $netdev := .NetDevs -}}
{{- $filename := print "10-persistent-net-" $devname ".link" }}
{{- $filename := print "10-ww4-netname-" $devname ".link" }}
{{- file $filename }}
# This file is autogenerated by warewulf
# Host: {{$.BuildHost}}
# Time: {{$.BuildTime}}
# Source: {{$.BuildSource}}
{{- if and $netdev.Hwaddr $netdev.Device }}
[Match]
MACAddress={{$netdev.Hwaddr}}

View 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

View 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"
`

View File

@@ -1,9 +1,6 @@
# This file is autogenerated by warewulf
# Host: {{.BuildHost}}
# Time: {{.BuildTime}}
# Source: {{.BuildSource}}
{{range $devname, $netdev := .NetDevs}}
{{- if $netdev.Hwaddr }}
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="{{ $netdev.Hwaddr }}", NAME="{{ $netdev.Device }}"
{{ end -}}
{{ end -}}
{{ end -}}

View 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

View 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>
`

View File

@@ -1,15 +1,9 @@
{{- $host := .BuildHost }}
{{- $time := .BuildTime }}
{{- $source := .BuildSource }}
{{ $NetDevs := .NetDevs -}}{{/* FIX: 1413 */ -}}
{{range $devname, $netdev := .ThisNode.NetDevs -}}
{{- $filename := print "ifcfg-" $devname ".xml" }}
{{- file $filename }}
<!--
This file is autogenerated by warewulf
Host: {{ $host }}
Time: {{ $time }}
Source: {{ $source }}
-->
<interface origin="static generated warewulf config">
<name>{{$netdev.Device.Get}}</name>

View 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

View 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 &
`

View File

@@ -1,8 +1,5 @@
#!/bin/sh
# This file is autogenerated by warewulf
# Host: {{.BuildHost}}
# Time: {{.BuildTime}}
# Source: {{.BuildSource}}
. /warewulf/config
# Only start if the systemd is not available

View 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

View 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

View 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"
`

View File

@@ -1,48 +0,0 @@
package main
import (
"fmt"
"runtime"
"time"
"github.com/warewulf/warewulf/internal/pkg/batch"
)
func exampleJob(name string, id int) string {
fmt.Printf("[%d] %s started...\n", id, name)
time.Sleep(time.Second * time.Duration(id))
fmt.Printf("[%d] %s ending...\n", id, name)
return name
}
func main() {
fmt.Printf("GOMAXPROCS=%d\n", runtime.GOMAXPROCS(0))
jobstorun := 100
// create batch pool to run 100 jobs at a time every 10 seconds
batchpool := batch.New(10)
retvalues := make(chan string, jobstorun)
// submit a bunch of jobs
for i := 0; i < jobstorun; i++ {
name := fmt.Sprintf("job-%04d", i)
id := i
batchpool.Submit(func() {
retvalues <- exampleJob(name, id)
})
}
// Run all batch jobs to completion
batchpool.Run()
close(retvalues)
for s := range retvalues {
fmt.Printf("Return value is %s\n", s)
}
}

View File

@@ -98,7 +98,7 @@ explained as follows:
When ``true``, ``wwclient`` uses TCP port 987.
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
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
"device," the device value of the ``dummy`` device is used.
If ``defaults.conf`` does not exist, the following values are used as
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
If ``defaults.conf`` does not exist, compiled-in defaults are used.
There should never be a need to change this file: all site-local
parameters should be specified using either nodes or profiles.

View File

@@ -57,7 +57,7 @@ Ignition Implementation
=======================
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.
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.

View File

@@ -22,10 +22,10 @@ properly. Here are the things it will do:
the system service.
* **hostfile**: Update the system's /etc/hosts file based on the
**host** template ``/etc/hosts.ww``.
* **nfs**: Configure the NFS server on the control node as well as the
``/etc/fstab`` in the system overlay based on the configuration in
``/etc/warewulf/warewulf.conf`` and enable the system service. Also
the file ``/etc/exports.ww`` from the **host** template is installed.
* **nfs**: Configure the NFS server on the control node based on the
configuration in ``/etc/warewulf/warewulf.conf`` and enable the NFS
service. Also the file ``/etc/exports.ww`` from the **host** template is
installed.
* **ssh**: Create the appropriate host keys (stored in
``/etc/warewulf/keys/``) and user keys for passwordless ``ssh`` into
the nodes. Addionally the shell profiles

View File

@@ -15,7 +15,7 @@ individual node would be the IP address.
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
through the script ``/warewulf/init.d/50-ipmi`` in the **system**
through the script ``/warewulf/init.d/50-ipmi`` in the wwinit
overlay.
If an individual node has different settings, you can set the IPMI
@@ -99,8 +99,8 @@ Node View
n001 cluster -- --
n001 container default sle-micro-5.3
n001 ipxe -- (default)
n001 runtime -- (generic)
n001 wwinit -- (wwinit)
n001 runtime -- (hosts,ssh.authorized_keys,syncuser)
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 discoverable -- --
n001 init -- (/sbin/init)

View File

@@ -87,8 +87,8 @@ You can also see the node's full attribute list by specifying the
n001 cluster -- --
n001 container default sle-micro-5.3
n001 ipxe -- (default)
n001 runtime -- (generic)
n001 wwinit -- (wwinit)
n001 runtime -- (hosts,ssh.authorized_keys,syncuser)
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 discoverable -- --
n001 init -- (/sbin/init)
@@ -193,8 +193,8 @@ container, kernel, and network:
n001 cluster -- --
n001 container default sle-micro-5.3
n001 ipxe -- (default)
n001 runtime -- (generic)
n001 wwinit -- (wwinit)
n001 runtime -- (hosts,ssh.authorized_keys,syncuser)
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 discoverable -- --
n001 init -- (/sbin/init)

Some files were not shown because too many files have changed in this diff Show More