diff --git a/CHANGELOG.md b/CHANGELOG.md index 237693a3..88ab83ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - `wwclient.aarch64` overlay always provides an aarch64 wwclient executable. - `wwclient.x86_64` overlay always provides an x86_64 wwclient executable. +- systemd-networkd overlay with IPv6 support ### Changed diff --git a/overlays/systemd.networkd/internal/nodes-no-hwaddr.conf b/overlays/systemd.networkd/internal/nodes-no-hwaddr.conf new file mode 100644 index 00000000..289055c6 --- /dev/null +++ b/overlays/systemd.networkd/internal/nodes-no-hwaddr.conf @@ -0,0 +1,20 @@ +nodes: + node1: + network devices: + default: + device: wwnet0 + ipaddr: 192.168.3.21 + netmask: 255.255.255.0 + gateway: 192.168.3.1 + mtu: 1500 + secondary: + device: wwnet1 + ipaddr: 192.168.3.22 + netmask: 255.255.255.0 + gateway: 192.168.3.1 + mtu: 9000 + tags: + route1: "192.168.1.0/24,192.168.3.254" + ipaddr6: 2001:db8::23 + prefixlen6: 64 + gateway6: 2001:db8::1 diff --git a/overlays/systemd.networkd/internal/nodes.conf b/overlays/systemd.networkd/internal/nodes.conf new file mode 100644 index 00000000..df712539 --- /dev/null +++ b/overlays/systemd.networkd/internal/nodes.conf @@ -0,0 +1,22 @@ +nodes: + node1: + 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 + mtu: 1500 + 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 + mtu: 9000 + tags: + route1: "192.168.1.0/24,192.168.3.254" + ipaddr6: 2001:db8::23 + prefixlen6: 64 + gateway6: 2001:db8::1 diff --git a/overlays/systemd.networkd/internal/systemd_networkd_test.go b/overlays/systemd.networkd/internal/systemd_networkd_test.go new file mode 100644 index 00000000..cc5ae922 --- /dev/null +++ b/overlays/systemd.networkd/internal/systemd_networkd_test.go @@ -0,0 +1,137 @@ +package systemd_networkd + +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_systemdNetworkOverlay(t *testing.T) { + env := testenv.New(t) + defer env.RemoveAll() + + env.ImportFile("var/lib/warewulf/overlays/systemd.networkd/rootfs/etc/systemd/network/default.network.ww", "../rootfs/etc/systemd/network/default.network.ww") + + tests := []struct { + name string + nodes_conf string + args []string + log string + }{ + { + name: "systemd.networkd with hwaddr", + nodes_conf: "nodes.conf", + args: []string{"--render", "node1", "systemd.networkd", "etc/systemd/network/default.network.ww"}, + log: expected_log_with_hwaddr, + }, + { + name: "systemd.networkd without hwaddr", + nodes_conf: "nodes-no-hwaddr.conf", + args: []string{"--render", "node1", "systemd.networkd", "etc/systemd/network/default.network.ww"}, + log: expected_log_without_hwaddr, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + env.ImportFile("etc/warewulf/nodes.conf", tt.nodes_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()) + assert.Equal(t, tt.log, logbuf.String()) + }) + } +} + +const expected_log_with_hwaddr string = `backupFile: true +writeFile: true +Filename: 10-ww4-networkd-default.network + +# This file is autogenerated by warewulf +[Match] +Name=wwnet0 +MACAddress=e6:92:39:49:7b:03 + +[Network] +DHCP=no +Address=192.168.3.21/24 +Gateway=192.168.3.1 +[Link] +MTUBytes=1500 +backupFile: true +writeFile: true +Filename: 10-ww4-networkd-secondary.network +# This file is autogenerated by warewulf +[Match] +Name=wwnet1 +MACAddress=9a:77:29:73:14:f1 + +[Network] +DHCP=no +Address=192.168.3.22/24 +Gateway=192.168.3.1 +IPv6PrivacyExtensions=false +Address=2001:db8::23/64 +Gateway=2001:db8::1 +[Link] +MTUBytes=9000 + +[Route] +Destination=192.168.1.0/24 +Gateway=192.168.3.254 + +[DHCPv6] +UseAddress=no +` + +const expected_log_without_hwaddr string = `backupFile: true +writeFile: true +Filename: 10-ww4-networkd-default.network + +# This file is autogenerated by warewulf +[Match] +Name=wwnet0 + +[Network] +DHCP=no +Address=192.168.3.21/24 +Gateway=192.168.3.1 +[Link] +MTUBytes=1500 +backupFile: true +writeFile: true +Filename: 10-ww4-networkd-secondary.network +# This file is autogenerated by warewulf +[Match] +Name=wwnet1 + +[Network] +DHCP=no +Address=192.168.3.22/24 +Gateway=192.168.3.1 +IPv6PrivacyExtensions=false +Address=2001:db8::23/64 +Gateway=2001:db8::1 +[Link] +MTUBytes=9000 + +[Route] +Destination=192.168.1.0/24 +Gateway=192.168.3.254 + +[DHCPv6] +UseAddress=no +` diff --git a/overlays/systemd.networkd/rootfs/etc/systemd/network/71-default.network b/overlays/systemd.networkd/rootfs/etc/systemd/network/71-default.network new file mode 100644 index 00000000..e69de29b diff --git a/overlays/systemd.networkd/rootfs/etc/systemd/network/default.network.ww b/overlays/systemd.networkd/rootfs/etc/systemd/network/default.network.ww new file mode 100644 index 00000000..8301fa18 --- /dev/null +++ b/overlays/systemd.networkd/rootfs/etc/systemd/network/default.network.ww @@ -0,0 +1,55 @@ +{{- range $devname, $netdev := .ThisNode.NetDevs }} +{{ file (print "10-ww4-networkd-" $devname ".network") }} +# This file is autogenerated by warewulf +[Match] +{{- if $netdev.Device }} +Name={{ $netdev.Device }} +{{- end }} +{{- if $netdev.Hwaddr }} +MACAddress={{ $netdev.Hwaddr }} +{{- end }} + +[Network] +{{- if or $netdev.IpCIDR $netdev.IpCIDR6 }} +DHCP=no +{{- end }} +{{- if $netdev.IpCIDR }} +Address={{ $netdev.IpCIDR }} +{{- end }} +{{- if $netdev.Gateway }} +Gateway={{ $netdev.Gateway }} +{{- end }} +{{- if $netdev.IpCIDR6 }} +IPv6PrivacyExtensions=false +Address={{ $netdev.IpCIDR6 }} +{{- end }} +{{- if $netdev.Gateway6 }} +Gateway={{ $netdev.Gateway6 }} +{{- end }} +{{- range $tk, $tv := $netdev.Tags }} +{{- if regexMatch "^DNS[0-9]*$" $tk }} +DNS={{ $tv }} +{{- end }} +{{- end }} + +{{- if $netdev.MTU }} +[Link] +MTUBytes={{ $netdev.MTU }} +{{- end }} +{{- range $tk, $tv := $netdev.Tags }} +{{- if eq (substr 0 5 $tk) "route" }} +{{- $route := splitList "," $tv }} + +[Route] +Destination={{ index $route 0 }} +Gateway={{ index $route 1 }} +{{- end }} +{{- end }} + +{{- if $netdev.Ipaddr6 }} + +[DHCPv6] +UseAddress=no +{{ end -}} + +{{ end -}}