diff --git a/CHANGELOG.md b/CHANGELOG.md index 29459bb9..a3e5d86f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Add completion for profile list. #1695 - Add OPTIONS argument for `warewulfd.service`. #1707 - Document `warewulf.conf:dhcp.template`. #1701 +- New template field `IpCIDR`. #1700 ### Changed @@ -22,6 +23,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Only calculate image chroot size when requested. #1504 - Create temporary files in overlay directory during `wwctl overlay edit`. #1473 - Re-order SSH key types to make ed25519 default. #981 +- Don't assume default values for `warewulf.conf` network settings. #1700 +- Omit DHCP pool from `dhcpd.conf` if any required fields are missing. #1700 +- `warewulf.conf:ipaddr6` is no longer required to be a `/64` or smaller. #1700 ### Fixed @@ -46,6 +50,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Removed - Remove `warewulf.conf:syslog`. #1606 +- Properly handle parsing of server network and netmask from CIDR `warewulf.conf:ipaddr`. #1541, #1594 +- Populate template field `NetworkCIDR`. #1700 ## v4.6.0rc1, 2025-01-29 diff --git a/internal/app/wwctl/root.go b/internal/app/wwctl/root.go index 6941309e..cfbeeedf 100644 --- a/internal/app/wwctl/root.go +++ b/internal/app/wwctl/root.go @@ -92,8 +92,5 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) (err error) { return } } - if err = conf.SetDynamicDefaults(); err != nil { - wwlog.Error("error setting default configuration: %s", err) - } return } diff --git a/internal/pkg/config/root.go b/internal/pkg/config/root.go index 50796e8b..b41c7db5 100644 --- a/internal/pkg/config/root.go +++ b/internal/pkg/config/root.go @@ -10,12 +10,9 @@ import ( "bytes" "fmt" "net" - "net/netip" "os" "reflect" - "github.com/pkg/errors" - "github.com/creasty/defaults" "github.com/warewulf/warewulf/internal/pkg/wwlog" "gopkg.in/yaml.v3" @@ -103,98 +100,55 @@ func (conf *WarewulfYaml) Parse(data []byte) error { conf.TFTP.IpxeBinaries = defIpxe } - // check whether ip addr is CIDR type and configure related fields as required if ip, network, err := net.ParseCIDR(conf.Ipaddr); err == nil { conf.Ipaddr = ip.String() if conf.Network == "" { conf.Network = network.IP.String() } if conf.Netmask == "" { - mask := network.Mask - conf.Netmask = fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]) + conf.Netmask = net.IP(network.Mask).String() } } + + if conf.Ipaddr6 != "" { + if _, network, err := net.ParseCIDR(conf.Ipaddr6); err == nil { + if conf.Ipv6net == "" { + conf.Ipv6net = network.IP.String() + } + } else { + return fmt.Errorf("invalid ipv6 address: must use CIDR notation: %s", conf.Ipaddr6) + } + } + return nil } -// SetDynamicDefaults populates [WarewulfYaml] with plausible defaults for -// the runtime environment. -func (conf *WarewulfYaml) SetDynamicDefaults() (err error) { - if conf.Ipaddr == "" || conf.Netmask == "" || conf.Network == "" { - var mask net.IPMask - var network *net.IPNet - var ip net.IP - cidr := conf.Ipaddr - - if conf.Ipaddr == "" { - wwlog.Verbose("Configuration has no valid network, going to dynamic values") - conn, err := net.Dial("udp", "8.8.8.8:80") - if err == nil { - defer conn.Close() - ipaddr := conn.LocalAddr().(*net.UDPAddr).IP - mask = ipaddr.DefaultMask() - sz, _ := mask.Size() - cidr = ipaddr.String() + fmt.Sprintf("/%d", sz) - } else { - cidr = "192.168.1.1/24" - } - } else if addr, err := netip.ParseAddr(conf.Ipaddr); err == nil { - // if the ipaddr does not have mask appended, update it with default generated mask - ipaddr := net.IP(addr.AsSlice()) - sz, _ := ipaddr.DefaultMask().Size() - cidr = fmt.Sprintf("%s/%d", conf.Ipaddr, sz) - // otherwise, the following code will handle the ipaddr format: invalid or xxx.xxx.xxx.xxx/xx - } - - ip, network, err = net.ParseCIDR(cidr) - if err == nil { - mask = network.Mask - } else { - return fmt.Errorf("Couldn't parse IP address: %w", err) - } - if conf.Netmask == "" { - conf.Netmask = fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]) - wwlog.Verbose("Netmask address is not configured in warewulf.conf, using %s", conf.Netmask) - } - if conf.Network == "" { - conf.Network = network.IP.String() - wwlog.Verbose("Network is not configured in warewulf.conf, using %s", conf.Network) - } - // always update the ipaddr to valid ip - conf.Ipaddr = ip.String() +func (config *WarewulfYaml) NetworkCIDR() string { + if config.Network == "" || config.Netmask == "" { + return "" } - if conf.DHCP.RangeStart == "" && conf.DHCP.RangeEnd == "" { - start := net.ParseIP(conf.Network).To4() - start[3] += 1 - if start.Equal(net.ParseIP(conf.Ipaddr)) { - start[3] += 1 - } - conf.DHCP.RangeStart = start.String() - wwlog.Verbose("dhpd start is not configured in warewulf.conf, using %s", conf.DHCP.RangeStart) - sz, _ := net.IPMask(net.ParseIP(conf.Netmask).To4()).Size() - range_end := (1 << (32 - sz)) / 8 - if range_end > 127 { - range_end = 127 - } - end := net.ParseIP(conf.Network).To4() - end[3] += byte(range_end) - conf.DHCP.RangeEnd = end.String() - wwlog.Verbose("dhpd end is not configured in warewulf.conf, using %s", conf.DHCP.RangeEnd) + cidr := net.IPNet{ + IP: net.ParseIP(config.Network), + Mask: net.IPMask(net.ParseIP(config.Netmask)), + } + if cidr.IP == nil || cidr.Mask == nil { + return "" + } + return cidr.String() +} +func (config *WarewulfYaml) IpCIDR() string { + if config.Ipaddr == "" || config.Netmask == "" { + return "" } - // check validity of ipv6 net - if conf.Ipaddr6 != "" { - _, ipv6net, err := net.ParseCIDR(conf.Ipaddr6) - if err != nil { - wwlog.Error("Invalid ipv6 address specified, mut be CIDR notation: %s", conf.Ipaddr6) - return errors.New("invalid ipv6 network") - } - if msize, _ := ipv6net.Mask.Size(); msize > 64 { - wwlog.Error("ipv6 mask size must be smaller than 64") - return errors.New("invalid ipv6 network size") - } + cidr := net.IPNet{ + IP: net.ParseIP(config.Ipaddr), + Mask: net.IPMask(net.ParseIP(config.Netmask)), } - return + if cidr.IP == nil || cidr.Mask == nil { + return "" + } + return cidr.String() } // InitializedFromFile returns true if [WarewulfYaml] memory was read from diff --git a/internal/pkg/config/root_test.go b/internal/pkg/config/root_test.go index 9e75b92d..533002be 100644 --- a/internal/pkg/config/root_test.go +++ b/internal/pkg/config/root_test.go @@ -7,69 +7,289 @@ import ( "github.com/stretchr/testify/assert" ) -func TestDefaultWarewulfYaml(t *testing.T) { - conf := New() - - assert.Equal(t, 9873, conf.Warewulf.Port) - assert.True(t, conf.Warewulf.Secure()) - assert.Equal(t, 60, conf.Warewulf.UpdateInterval) - assert.True(t, conf.Warewulf.AutobuildOverlays()) - assert.True(t, conf.Warewulf.EnableHostOverlay()) - - assert.True(t, conf.DHCP.Enabled()) - assert.Equal(t, "default", conf.DHCP.Template) - assert.Empty(t, conf.DHCP.RangeStart) - assert.Empty(t, conf.DHCP.RangeEnd) - assert.Equal(t, "dhcpd", conf.DHCP.SystemdName) - - assert.True(t, conf.TFTP.Enabled()) - assert.NotEmpty(t, conf.TFTP.TftpRoot) - assert.Equal(t, "tftp", conf.TFTP.SystemdName) - assert.NotEmpty(t, conf.TFTP.IpxeBinaries["00:00"]) - assert.NotEmpty(t, conf.TFTP.IpxeBinaries["00:07"]) - assert.NotEmpty(t, conf.TFTP.IpxeBinaries["00:09"]) - assert.NotEmpty(t, conf.TFTP.IpxeBinaries["00:0B"]) - - assert.True(t, conf.NFS.Enabled()) - assert.Empty(t, conf.NFS.ExportsExtended) - assert.Equal(t, "nfsd", conf.NFS.SystemdName) - - assert.Equal(t, "/etc/resolv.conf", conf.MountsImage[0].Source) - assert.Equal(t, "/etc/resolv.conf", conf.MountsImage[0].Dest) - assert.False(t, conf.MountsImage[0].ReadOnly()) - assert.Empty(t, conf.MountsImage[0].Options) - - assert.NotEmpty(t, conf.Paths.Bindir) - assert.NotEmpty(t, conf.Paths.Sysconfdir) - assert.NotEmpty(t, conf.Paths.Localstatedir) - assert.NotEmpty(t, conf.Paths.Srvdir) - assert.NotEmpty(t, conf.Paths.Firewallddir) - assert.NotEmpty(t, conf.Paths.Systemddir) - assert.NotEmpty(t, conf.Paths.WWOverlaydir) - assert.NotEmpty(t, conf.Paths.WWChrootdir) - assert.NotEmpty(t, conf.Paths.WWProvisiondir) - assert.NotEmpty(t, conf.Paths.WWClientdir) - assert.NotEmpty(t, conf.Paths.Cachedir) -} - -func TestInitializedFromFile(t *testing.T) { - example_warewulf_conf := "" - tempWarewulfConf, warewulfConfErr := os.CreateTemp("", "warewulf.conf-") - assert.NoError(t, warewulfConfErr) - defer os.Remove(tempWarewulfConf.Name()) - _, warewulfConfErr = tempWarewulfConf.Write([]byte(example_warewulf_conf)) - assert.NoError(t, warewulfConfErr) - assert.NoError(t, tempWarewulfConf.Sync()) - - conf := New() - assert.False(t, conf.InitializedFromFile()) - assert.NoError(t, conf.Read(tempWarewulfConf.Name())) - assert.True(t, conf.InitializedFromFile()) - assert.Equal(t, conf.GetWarewulfConf(), tempWarewulfConf.Name()) -} - -func TestExampleWarewulfYaml(t *testing.T) { - example_warewulf_conf := ` +func TestParse(t *testing.T) { + tests := map[string]struct { + input string + result string + }{ + "default": { + input: ``, + result: ` +warewulf: + autobuild overlays: true + grubboot: false + host overlay: true + port: 9873 + secure: true + update interval: 60 +nfs: + enabled: true + systemd name: nfsd +dhcp: + enabled: true + systemd name: dhcpd + template: default +image mounts: +- dest: /etc/resolv.conf + source: /etc/resolv.conf +paths: + bindir: /usr/local/bin + cachedir: /var/local/cache + datadir: /usr/local/share + firewallddir: /usr/lib/firewalld/services + ipxesource: /usr/local/share/ipxe + localstatedir: /var/local + srvdir: /srv + sysconfdir: /usr/local/etc + systemddir: /usr/lib/systemd/system + wwchrootdir: /var/local/warewulf/chroots + wwclientdir: /warewulf + wwoverlaydir: /var/local/warewulf/overlays + wwprovisiondir: /var/local/warewulf/provision +ssh: + key types: + - ed25519 + - ecdsa + - rsa + - dsa +tftp: + enabled: true + ipxe: + "00:00": undionly.kpxe + "00:07": ipxe-snponly-x86_64.efi + "00:09": ipxe-snponly-x86_64.efi + "00:0B": arm64-efi/snponly.efi + systemd name: tftp + tftproot: /var/lib/tftpboot +`, + }, + "cidr": { + input: ` +ipaddr: 192.168.0.1/24 +`, + result: ` +ipaddr: 192.168.0.1 +network: 192.168.0.0 +netmask: 255.255.255.0 +warewulf: + autobuild overlays: true + grubboot: false + host overlay: true + port: 9873 + secure: true + update interval: 60 +nfs: + enabled: true + systemd name: nfsd +dhcp: + enabled: true + systemd name: dhcpd + template: default +image mounts: +- dest: /etc/resolv.conf + source: /etc/resolv.conf +paths: + bindir: /usr/local/bin + cachedir: /var/local/cache + datadir: /usr/local/share + firewallddir: /usr/lib/firewalld/services + ipxesource: /usr/local/share/ipxe + localstatedir: /var/local + srvdir: /srv + sysconfdir: /usr/local/etc + systemddir: /usr/lib/systemd/system + wwchrootdir: /var/local/warewulf/chroots + wwclientdir: /warewulf + wwoverlaydir: /var/local/warewulf/overlays + wwprovisiondir: /var/local/warewulf/provision +ssh: + key types: + - ed25519 + - ecdsa + - rsa + - dsa +tftp: + enabled: true + ipxe: + "00:00": undionly.kpxe + "00:07": ipxe-snponly-x86_64.efi + "00:09": ipxe-snponly-x86_64.efi + "00:0B": arm64-efi/snponly.efi + systemd name: tftp + tftproot: /var/lib/tftpboot +`, + }, + "cidr with conflicts": { + input: ` +ipaddr: 192.168.1.1/24 +network: 192.168.0.0 +netmask: 255.255.0.0 +`, + result: ` +ipaddr: 192.168.1.1 +network: 192.168.0.0 +netmask: 255.255.0.0 +warewulf: + autobuild overlays: true + grubboot: false + host overlay: true + port: 9873 + secure: true + update interval: 60 +nfs: + enabled: true + systemd name: nfsd +dhcp: + enabled: true + systemd name: dhcpd + template: default +image mounts: +- dest: /etc/resolv.conf + source: /etc/resolv.conf +paths: + bindir: /usr/local/bin + cachedir: /var/local/cache + datadir: /usr/local/share + firewallddir: /usr/lib/firewalld/services + ipxesource: /usr/local/share/ipxe + localstatedir: /var/local + srvdir: /srv + sysconfdir: /usr/local/etc + systemddir: /usr/lib/systemd/system + wwchrootdir: /var/local/warewulf/chroots + wwclientdir: /warewulf + wwoverlaydir: /var/local/warewulf/overlays + wwprovisiondir: /var/local/warewulf/provision +ssh: + key types: + - ed25519 + - ecdsa + - rsa + - dsa +tftp: + enabled: true + ipxe: + "00:00": undionly.kpxe + "00:07": ipxe-snponly-x86_64.efi + "00:09": ipxe-snponly-x86_64.efi + "00:0B": arm64-efi/snponly.efi + systemd name: tftp + tftproot: /var/lib/tftpboot +`, + }, + "ipv6 cidr": { + input: ` +ipaddr6: "2001:db8::1/64" +`, + result: ` +ipaddr6: "2001:db8::1/64" +ipv6net: "2001:db8::" +warewulf: + autobuild overlays: true + grubboot: false + host overlay: true + port: 9873 + secure: true + update interval: 60 +nfs: + enabled: true + systemd name: nfsd +dhcp: + enabled: true + systemd name: dhcpd + template: default +image mounts: +- dest: /etc/resolv.conf + source: /etc/resolv.conf +paths: + bindir: /usr/local/bin + cachedir: /var/local/cache + datadir: /usr/local/share + firewallddir: /usr/lib/firewalld/services + ipxesource: /usr/local/share/ipxe + localstatedir: /var/local + srvdir: /srv + sysconfdir: /usr/local/etc + systemddir: /usr/lib/systemd/system + wwchrootdir: /var/local/warewulf/chroots + wwclientdir: /warewulf + wwoverlaydir: /var/local/warewulf/overlays + wwprovisiondir: /var/local/warewulf/provision +ssh: + key types: + - ed25519 + - ecdsa + - rsa + - dsa +tftp: + enabled: true + ipxe: + "00:00": undionly.kpxe + "00:07": ipxe-snponly-x86_64.efi + "00:09": ipxe-snponly-x86_64.efi + "00:0B": arm64-efi/snponly.efi + systemd name: tftp + tftproot: /var/lib/tftpboot +`, + }, + "ipv6 cidr conflict": { + input: ` +ipaddr6: "2001:db8:1::1/64" +ipv6net: "2001:db8:2::" +`, + result: ` +ipaddr6: "2001:db8:1::1/64" +ipv6net: "2001:db8:2::" +warewulf: + autobuild overlays: true + grubboot: false + host overlay: true + port: 9873 + secure: true + update interval: 60 +nfs: + enabled: true + systemd name: nfsd +dhcp: + enabled: true + systemd name: dhcpd + template: default +image mounts: +- dest: /etc/resolv.conf + source: /etc/resolv.conf +paths: + bindir: /usr/local/bin + cachedir: /var/local/cache + datadir: /usr/local/share + firewallddir: /usr/lib/firewalld/services + ipxesource: /usr/local/share/ipxe + localstatedir: /var/local + srvdir: /srv + sysconfdir: /usr/local/etc + systemddir: /usr/lib/systemd/system + wwchrootdir: /var/local/warewulf/chroots + wwclientdir: /warewulf + wwoverlaydir: /var/local/warewulf/overlays + wwprovisiondir: /var/local/warewulf/provision +ssh: + key types: + - ed25519 + - ecdsa + - rsa + - dsa +tftp: + enabled: true + ipxe: + "00:00": undionly.kpxe + "00:07": ipxe-snponly-x86_64.efi + "00:09": ipxe-snponly-x86_64.efi + "00:0B": arm64-efi/snponly.efi + systemd name: tftp + tftproot: /var/lib/tftpboot +`, + }, + "example": { + input: ` ipaddr: 192.168.200.1 netmask: 255.255.255.0 network: 192.168.200.0 @@ -98,8 +318,84 @@ nfs: image mounts: - source: /etc/resolv.conf dest: /etc/resolv.conf - readonly: true` + readonly: true +`, + result: ` +ipaddr: 192.168.200.1 +netmask: 255.255.255.0 +network: 192.168.200.0 +warewulf: + autobuild overlays: true + grubboot: false + host overlay: true + port: 9873 + secure: false + update interval: 60 +nfs: + enabled: true + systemd name: nfs-server + export paths: + - path: /home + export options: rw,sync + - path: /opt + export options: ro,sync,no_root_squash +dhcp: + enabled: true + systemd name: dhcpd + template: default + range end: 192.168.200.99 + range start: 192.168.200.50 +image mounts: +- dest: /etc/resolv.conf + readonly: true + source: /etc/resolv.conf +paths: + bindir: /usr/local/bin + cachedir: /var/local/cache + datadir: /usr/local/share + firewallddir: /usr/lib/firewalld/services + ipxesource: /usr/local/share/ipxe + localstatedir: /var/local + srvdir: /srv + sysconfdir: /usr/local/etc + systemddir: /usr/lib/systemd/system + wwchrootdir: /var/local/warewulf/chroots + wwclientdir: /warewulf + wwoverlaydir: /var/local/warewulf/overlays + wwprovisiondir: /var/local/warewulf/provision +ssh: + key types: + - ed25519 + - ecdsa + - rsa + - dsa +tftp: + enabled: true + ipxe: + "00:00": undionly.kpxe + "00:07": ipxe-snponly-x86_64.efi + "00:09": ipxe-snponly-x86_64.efi + "00:0B": arm64-efi/snponly.efi + systemd name: tftp + tftproot: /var/lib/tftpboot +`, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + conf := New() + err := conf.Parse([]byte(tt.input)) + assert.NoError(t, err) + result, err := conf.Dump() + assert.NoError(t, err) + assert.YAMLEq(t, tt.result, string(result)) + }) + } +} + +func TestInitializedFromFile(t *testing.T) { + example_warewulf_conf := "" tempWarewulfConf, warewulfConfErr := os.CreateTemp("", "warewulf.conf-") assert.NoError(t, warewulfConfErr) defer os.Remove(tempWarewulfConf.Name()) @@ -108,36 +404,10 @@ image mounts: assert.NoError(t, tempWarewulfConf.Sync()) conf := New() + assert.False(t, conf.InitializedFromFile()) assert.NoError(t, conf.Read(tempWarewulfConf.Name())) - - assert.Equal(t, "192.168.200.1", conf.Ipaddr) - assert.Equal(t, "255.255.255.0", conf.Netmask) - assert.Equal(t, "192.168.200.0", conf.Network) - - assert.Equal(t, 9873, conf.Warewulf.Port) - assert.False(t, conf.Warewulf.Secure()) - assert.Equal(t, 60, conf.Warewulf.UpdateInterval) - assert.True(t, conf.Warewulf.AutobuildOverlays()) - assert.True(t, conf.Warewulf.EnableHostOverlay()) - - assert.True(t, conf.DHCP.Enabled()) - assert.Equal(t, "192.168.200.50", conf.DHCP.RangeStart) - assert.Equal(t, "192.168.200.99", conf.DHCP.RangeEnd) - assert.Equal(t, "dhcpd", conf.DHCP.SystemdName) - - assert.True(t, conf.TFTP.Enabled()) - assert.Equal(t, "tftp", conf.TFTP.SystemdName) - - assert.True(t, conf.NFS.Enabled()) - assert.Equal(t, "/home", conf.NFS.ExportsExtended[0].Path) - assert.Equal(t, "rw,sync", conf.NFS.ExportsExtended[0].ExportOptions) - assert.Equal(t, "/opt", conf.NFS.ExportsExtended[1].Path) - assert.Equal(t, "ro,sync,no_root_squash", conf.NFS.ExportsExtended[1].ExportOptions) - assert.Equal(t, "nfs-server", conf.NFS.SystemdName) - - assert.Equal(t, "/etc/resolv.conf", conf.MountsImage[0].Source) - assert.Equal(t, "/etc/resolv.conf", conf.MountsImage[0].Dest) - assert.True(t, conf.MountsImage[0].ReadOnly()) + assert.True(t, conf.InitializedFromFile()) + assert.Equal(t, conf.GetWarewulfConf(), tempWarewulfConf.Name()) } func TestCache(t *testing.T) { @@ -154,3 +424,99 @@ func TestCache(t *testing.T) { New() assert.NotEqual(t, 9999, Get().Warewulf.Port) } + +func TestIpCIDR(t *testing.T) { + tests := map[string]struct { + ipaddr string + netmask string + cidr string + }{ + "blank": { + ipaddr: "", + netmask: "", + cidr: "", + }, + "ip only": { + ipaddr: "192.168.0.1", + netmask: "", + cidr: "", + }, + "netmask only": { + ipaddr: "", + netmask: "255.255.255.0", + cidr: "", + }, + "full": { + ipaddr: "192.168.0.1", + netmask: "255.255.255.0", + cidr: "192.168.0.1/24", + }, + "invalid ip": { + ipaddr: "asdf", + netmask: "255.255.255.0", + cidr: "", + }, + "invalid netmask": { + ipaddr: "192.168.0.1", + netmask: "asdf", + cidr: "", + }, + } + + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + conf := New() + conf.Ipaddr = tt.ipaddr + conf.Netmask = tt.netmask + assert.Equal(t, tt.cidr, conf.IpCIDR()) + }) + } +} + +func TestNetworkCIDR(t *testing.T) { + tests := map[string]struct { + network string + netmask string + cidr string + }{ + "blank": { + network: "", + netmask: "", + cidr: "", + }, + "network only": { + network: "192.168.0.0", + netmask: "", + cidr: "", + }, + "netmask only": { + network: "", + netmask: "255.255.255.0", + cidr: "", + }, + "full": { + network: "192.168.0.0", + netmask: "255.255.255.0", + cidr: "192.168.0.0/24", + }, + "invalid network": { + network: "asdf", + netmask: "255.255.255.0", + cidr: "", + }, + "invalid netmask": { + network: "192.168.0.0", + netmask: "asdf", + cidr: "", + }, + } + + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + conf := New() + conf.Network = tt.network + conf.Netmask = tt.netmask + assert.Equal(t, tt.cidr, conf.NetworkCIDR()) + }) + } +} diff --git a/internal/pkg/overlay/datastructure.go b/internal/pkg/overlay/datastructure.go index 00e0a0db..25779c25 100644 --- a/internal/pkg/overlay/datastructure.go +++ b/internal/pkg/overlay/datastructure.go @@ -24,6 +24,7 @@ type TemplateStruct struct { BuildTimeUnix string BuildSource string Ipaddr string + IpCIDR string Ipaddr6 string Netmask string Network string @@ -67,9 +68,11 @@ func InitStruct(overlayName string, nodeData node.Node, allNodes []node.Node) (T tstruct.Paths = *controller.Paths tstruct.Warewulf = *controller.Warewulf tstruct.Ipaddr = controller.Ipaddr + tstruct.IpCIDR = controller.IpCIDR() tstruct.Ipaddr6 = controller.Ipaddr6 tstruct.Netmask = controller.Netmask tstruct.Network = controller.Network + tstruct.NetworkCIDR = controller.NetworkCIDR() // init some convenience vars tstruct.Id = nodeData.Id() tstruct.Hostname = nodeData.Id() diff --git a/overlays/debug/internal/debug_test.go b/overlays/debug/internal/debug_test.go index 504b9a9a..16dab229 100644 --- a/overlays/debug/internal/debug_test.go +++ b/overlays/debug/internal/debug_test.go @@ -19,8 +19,10 @@ func Test_debugOverlay(t *testing.T) { env := testenv.New(t) defer env.RemoveAll() + env.ImportFile("etc/warewulf/warewulf.conf", "warewulf.conf") env.ImportFile("etc/warewulf/nodes.conf", "nodes.conf") env.ImportFile("var/lib/warewulf/overlays/debug/rootfs/tstruct.md.ww", "../rootfs/tstruct.md.ww") + env.Configure() tests := []struct { name string @@ -86,11 +88,12 @@ data from other structures. ### Network -- Ipaddr: +- Ipaddr: 192.168.0.1 +- IpCIDR: 192.168.0.1/24 - Ipaddr6: -- Netmask: -- Network: -- NetworkCIDR: +- Netmask: 255.255.255.0 +- Network: 192.168.0.0 +- NetworkCIDR: 192.168.0.0/24 - Ipv6: false ### DHCP diff --git a/overlays/debug/internal/warewulf.conf b/overlays/debug/internal/warewulf.conf new file mode 100644 index 00000000..89c6ce11 --- /dev/null +++ b/overlays/debug/internal/warewulf.conf @@ -0,0 +1 @@ +ipaddr: 192.168.0.1/24 diff --git a/overlays/debug/rootfs/tstruct.md.ww b/overlays/debug/rootfs/tstruct.md.ww index 3684dde1..b70bd838 100644 --- a/overlays/debug/rootfs/tstruct.md.ww +++ b/overlays/debug/rootfs/tstruct.md.ww @@ -29,6 +29,7 @@ data from other structures. ### Network - Ipaddr: {{ .Ipaddr }} +- IpCIDR: {{ .IpCIDR }} - Ipaddr6: {{ .Ipaddr6 }} - Netmask: {{ .Netmask }} - Network: {{ .Network }} diff --git a/overlays/host/internal/host_test.go b/overlays/host/internal/host_test.go index c1c13a83..5478a363 100644 --- a/overlays/host/internal/host_test.go +++ b/overlays/host/internal/host_test.go @@ -152,7 +152,6 @@ 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"; } @@ -166,7 +165,6 @@ if exists user-class and option user-class = "iPXE" { 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; @@ -201,7 +199,6 @@ 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"; } @@ -215,7 +212,6 @@ if exists user-class and option user-class = "iPXE" { 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; @@ -278,7 +274,6 @@ 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) diff --git a/overlays/host/rootfs/etc/dhcp/dhcpd.conf.ww b/overlays/host/rootfs/etc/dhcp/dhcpd.conf.ww index 8c611416..d9cd43b2 100644 --- a/overlays/host/rootfs/etc/dhcp/dhcpd.conf.ww +++ b/overlays/host/rootfs/etc/dhcp/dhcpd.conf.ww @@ -1,4 +1,4 @@ -{{if $.Dhcp.Enabled -}} +{{ if $.Dhcp.Enabled -}} # This file is autogenerated by warewulf allow booting; @@ -38,19 +38,21 @@ if substring (option vendor-class-identifier, 0, 9) = "PXEClient" { if exists user-class and option user-class = "iPXE" { filename "http://{{$.Ipaddr}}:{{$.Warewulf.Port}}/ipxe/${mac:hexhyp}?assetkey=${asset}&uuid=${uuid}"; } else { -{{range $type,$name := $.Tftp.IpxeBinaries }} +{{- range $type, $name := $.Tftp.IpxeBinaries }} if option architecture-type = {{ $type }} { filename "/warewulf/{{ basename $name }}"; } -{{- end }}{{/* range IpxeBinaries */}} +{{- end }} } -{{- end }}{{/* BootMethod */}} +{{- end }} -subnet {{$.Network}} netmask {{$.Netmask}} { +{{- if and .Network .Netmask .Dhcp.RangeStart .Dhcp.RangeEnd }} +subnet {{.Network}} netmask {{.Netmask}} { max-lease-time 120; - range {{$.Dhcp.RangeStart}} {{$.Dhcp.RangeEnd}}; + range {{.Dhcp.RangeStart}} {{.Dhcp.RangeEnd}}; next-server {{.Ipaddr}}; } +{{- end }} {{- if eq .Dhcp.Template "static" }} {{- range $nodes := $.AllNodes}} diff --git a/overlays/host/rootfs/etc/exports.ww b/overlays/host/rootfs/etc/exports.ww index 7507c37f..b8c44fff 100644 --- a/overlays/host/rootfs/etc/exports.ww +++ b/overlays/host/rootfs/etc/exports.ww @@ -1,4 +1,4 @@ -{{- if .Nfs.Enabled }} +{{ if .Nfs.Enabled -}} # This file is autogenerated by warewulf {{- $network := .Network }} {{- $netmask := .Netmask }}