Fix handling of CIDR values in warewulf.conf

- Fixes: #1541
- Fixes: #1594

Signed-off-by: Jonathon Anderson <janderson@ciq.com>
This commit is contained in:
Jonathon Anderson
2025-02-03 17:01:57 -07:00
parent 8088112c30
commit 2e1466fc7e
11 changed files with 520 additions and 192 deletions

View File

@@ -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 completion for profile list. #1695
- Add OPTIONS argument for `warewulfd.service`. #1707 - Add OPTIONS argument for `warewulfd.service`. #1707
- Document `warewulf.conf:dhcp.template`. #1701 - Document `warewulf.conf:dhcp.template`. #1701
- New template field `IpCIDR`. #1700
### Changed ### 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 - Only calculate image chroot size when requested. #1504
- Create temporary files in overlay directory during `wwctl overlay edit`. #1473 - Create temporary files in overlay directory during `wwctl overlay edit`. #1473
- Re-order SSH key types to make ed25519 default. #981 - 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 ### Fixed
@@ -46,6 +50,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Removed ### Removed
- Remove `warewulf.conf:syslog`. #1606 - 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 ## v4.6.0rc1, 2025-01-29

View File

@@ -92,8 +92,5 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) (err error) {
return return
} }
} }
if err = conf.SetDynamicDefaults(); err != nil {
wwlog.Error("error setting default configuration: %s", err)
}
return return
} }

View File

@@ -10,12 +10,9 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"net" "net"
"net/netip"
"os" "os"
"reflect" "reflect"
"github.com/pkg/errors"
"github.com/creasty/defaults" "github.com/creasty/defaults"
"github.com/warewulf/warewulf/internal/pkg/wwlog" "github.com/warewulf/warewulf/internal/pkg/wwlog"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
@@ -103,98 +100,55 @@ func (conf *WarewulfYaml) Parse(data []byte) error {
conf.TFTP.IpxeBinaries = defIpxe 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 { if ip, network, err := net.ParseCIDR(conf.Ipaddr); err == nil {
conf.Ipaddr = ip.String() conf.Ipaddr = ip.String()
if conf.Network == "" { if conf.Network == "" {
conf.Network = network.IP.String() conf.Network = network.IP.String()
} }
if conf.Netmask == "" { if conf.Netmask == "" {
mask := network.Mask conf.Netmask = net.IP(network.Mask).String()
conf.Netmask = fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3])
} }
} }
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 return nil
} }
// SetDynamicDefaults populates [WarewulfYaml] with plausible defaults for func (config *WarewulfYaml) NetworkCIDR() string {
// the runtime environment. if config.Network == "" || config.Netmask == "" {
func (conf *WarewulfYaml) SetDynamicDefaults() (err error) { return ""
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()
} }
if conf.DHCP.RangeStart == "" && conf.DHCP.RangeEnd == "" { cidr := net.IPNet{
start := net.ParseIP(conf.Network).To4() IP: net.ParseIP(config.Network),
start[3] += 1 Mask: net.IPMask(net.ParseIP(config.Netmask)),
if start.Equal(net.ParseIP(conf.Ipaddr)) { }
start[3] += 1 if cidr.IP == nil || cidr.Mask == nil {
} return ""
conf.DHCP.RangeStart = start.String() }
wwlog.Verbose("dhpd start is not configured in warewulf.conf, using %s", conf.DHCP.RangeStart) return cidr.String()
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)
func (config *WarewulfYaml) IpCIDR() string {
if config.Ipaddr == "" || config.Netmask == "" {
return ""
} }
// check validity of ipv6 net cidr := net.IPNet{
if conf.Ipaddr6 != "" { IP: net.ParseIP(config.Ipaddr),
_, ipv6net, err := net.ParseCIDR(conf.Ipaddr6) Mask: net.IPMask(net.ParseIP(config.Netmask)),
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")
}
} }
return if cidr.IP == nil || cidr.Mask == nil {
return ""
}
return cidr.String()
} }
// InitializedFromFile returns true if [WarewulfYaml] memory was read from // InitializedFromFile returns true if [WarewulfYaml] memory was read from

View File

@@ -7,69 +7,289 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestDefaultWarewulfYaml(t *testing.T) { func TestParse(t *testing.T) {
conf := New() tests := map[string]struct {
input string
assert.Equal(t, 9873, conf.Warewulf.Port) result string
assert.True(t, conf.Warewulf.Secure()) }{
assert.Equal(t, 60, conf.Warewulf.UpdateInterval) "default": {
assert.True(t, conf.Warewulf.AutobuildOverlays()) input: ``,
assert.True(t, conf.Warewulf.EnableHostOverlay()) result: `
warewulf:
assert.True(t, conf.DHCP.Enabled()) autobuild overlays: true
assert.Equal(t, "default", conf.DHCP.Template) grubboot: false
assert.Empty(t, conf.DHCP.RangeStart) host overlay: true
assert.Empty(t, conf.DHCP.RangeEnd) port: 9873
assert.Equal(t, "dhcpd", conf.DHCP.SystemdName) secure: true
update interval: 60
assert.True(t, conf.TFTP.Enabled()) nfs:
assert.NotEmpty(t, conf.TFTP.TftpRoot) enabled: true
assert.Equal(t, "tftp", conf.TFTP.SystemdName) systemd name: nfsd
assert.NotEmpty(t, conf.TFTP.IpxeBinaries["00:00"]) dhcp:
assert.NotEmpty(t, conf.TFTP.IpxeBinaries["00:07"]) enabled: true
assert.NotEmpty(t, conf.TFTP.IpxeBinaries["00:09"]) systemd name: dhcpd
assert.NotEmpty(t, conf.TFTP.IpxeBinaries["00:0B"]) template: default
image mounts:
assert.True(t, conf.NFS.Enabled()) - dest: /etc/resolv.conf
assert.Empty(t, conf.NFS.ExportsExtended) source: /etc/resolv.conf
assert.Equal(t, "nfsd", conf.NFS.SystemdName) paths:
bindir: /usr/local/bin
assert.Equal(t, "/etc/resolv.conf", conf.MountsImage[0].Source) cachedir: /var/local/cache
assert.Equal(t, "/etc/resolv.conf", conf.MountsImage[0].Dest) datadir: /usr/local/share
assert.False(t, conf.MountsImage[0].ReadOnly()) firewallddir: /usr/lib/firewalld/services
assert.Empty(t, conf.MountsImage[0].Options) ipxesource: /usr/local/share/ipxe
localstatedir: /var/local
assert.NotEmpty(t, conf.Paths.Bindir) srvdir: /srv
assert.NotEmpty(t, conf.Paths.Sysconfdir) sysconfdir: /usr/local/etc
assert.NotEmpty(t, conf.Paths.Localstatedir) systemddir: /usr/lib/systemd/system
assert.NotEmpty(t, conf.Paths.Srvdir) wwchrootdir: /var/local/warewulf/chroots
assert.NotEmpty(t, conf.Paths.Firewallddir) wwclientdir: /warewulf
assert.NotEmpty(t, conf.Paths.Systemddir) wwoverlaydir: /var/local/warewulf/overlays
assert.NotEmpty(t, conf.Paths.WWOverlaydir) wwprovisiondir: /var/local/warewulf/provision
assert.NotEmpty(t, conf.Paths.WWChrootdir) ssh:
assert.NotEmpty(t, conf.Paths.WWProvisiondir) key types:
assert.NotEmpty(t, conf.Paths.WWClientdir) - ed25519
assert.NotEmpty(t, conf.Paths.Cachedir) - ecdsa
} - rsa
- dsa
func TestInitializedFromFile(t *testing.T) { tftp:
example_warewulf_conf := "" enabled: true
tempWarewulfConf, warewulfConfErr := os.CreateTemp("", "warewulf.conf-") ipxe:
assert.NoError(t, warewulfConfErr) "00:00": undionly.kpxe
defer os.Remove(tempWarewulfConf.Name()) "00:07": ipxe-snponly-x86_64.efi
_, warewulfConfErr = tempWarewulfConf.Write([]byte(example_warewulf_conf)) "00:09": ipxe-snponly-x86_64.efi
assert.NoError(t, warewulfConfErr) "00:0B": arm64-efi/snponly.efi
assert.NoError(t, tempWarewulfConf.Sync()) systemd name: tftp
tftproot: /var/lib/tftpboot
conf := New() `,
assert.False(t, conf.InitializedFromFile()) },
assert.NoError(t, conf.Read(tempWarewulfConf.Name())) "cidr": {
assert.True(t, conf.InitializedFromFile()) input: `
assert.Equal(t, conf.GetWarewulfConf(), tempWarewulfConf.Name()) ipaddr: 192.168.0.1/24
} `,
result: `
func TestExampleWarewulfYaml(t *testing.T) { ipaddr: 192.168.0.1
example_warewulf_conf := ` 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 ipaddr: 192.168.200.1
netmask: 255.255.255.0 netmask: 255.255.255.0
network: 192.168.200.0 network: 192.168.200.0
@@ -98,8 +318,84 @@ nfs:
image mounts: image mounts:
- source: /etc/resolv.conf - source: /etc/resolv.conf
dest: /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-") tempWarewulfConf, warewulfConfErr := os.CreateTemp("", "warewulf.conf-")
assert.NoError(t, warewulfConfErr) assert.NoError(t, warewulfConfErr)
defer os.Remove(tempWarewulfConf.Name()) defer os.Remove(tempWarewulfConf.Name())
@@ -108,36 +404,10 @@ image mounts:
assert.NoError(t, tempWarewulfConf.Sync()) assert.NoError(t, tempWarewulfConf.Sync())
conf := New() conf := New()
assert.False(t, conf.InitializedFromFile())
assert.NoError(t, conf.Read(tempWarewulfConf.Name())) assert.NoError(t, conf.Read(tempWarewulfConf.Name()))
assert.True(t, conf.InitializedFromFile())
assert.Equal(t, "192.168.200.1", conf.Ipaddr) assert.Equal(t, conf.GetWarewulfConf(), tempWarewulfConf.Name())
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())
} }
func TestCache(t *testing.T) { func TestCache(t *testing.T) {
@@ -154,3 +424,99 @@ func TestCache(t *testing.T) {
New() New()
assert.NotEqual(t, 9999, Get().Warewulf.Port) 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())
})
}
}

View File

@@ -24,6 +24,7 @@ type TemplateStruct struct {
BuildTimeUnix string BuildTimeUnix string
BuildSource string BuildSource string
Ipaddr string Ipaddr string
IpCIDR string
Ipaddr6 string Ipaddr6 string
Netmask string Netmask string
Network string Network string
@@ -67,9 +68,11 @@ func InitStruct(overlayName string, nodeData node.Node, allNodes []node.Node) (T
tstruct.Paths = *controller.Paths tstruct.Paths = *controller.Paths
tstruct.Warewulf = *controller.Warewulf tstruct.Warewulf = *controller.Warewulf
tstruct.Ipaddr = controller.Ipaddr tstruct.Ipaddr = controller.Ipaddr
tstruct.IpCIDR = controller.IpCIDR()
tstruct.Ipaddr6 = controller.Ipaddr6 tstruct.Ipaddr6 = controller.Ipaddr6
tstruct.Netmask = controller.Netmask tstruct.Netmask = controller.Netmask
tstruct.Network = controller.Network tstruct.Network = controller.Network
tstruct.NetworkCIDR = controller.NetworkCIDR()
// init some convenience vars // init some convenience vars
tstruct.Id = nodeData.Id() tstruct.Id = nodeData.Id()
tstruct.Hostname = nodeData.Id() tstruct.Hostname = nodeData.Id()

View File

@@ -19,8 +19,10 @@ func Test_debugOverlay(t *testing.T) {
env := testenv.New(t) env := testenv.New(t)
defer env.RemoveAll() defer env.RemoveAll()
env.ImportFile("etc/warewulf/warewulf.conf", "warewulf.conf")
env.ImportFile("etc/warewulf/nodes.conf", "nodes.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.ImportFile("var/lib/warewulf/overlays/debug/rootfs/tstruct.md.ww", "../rootfs/tstruct.md.ww")
env.Configure()
tests := []struct { tests := []struct {
name string name string
@@ -86,11 +88,12 @@ data from other structures.
### Network ### Network
- Ipaddr: - Ipaddr: 192.168.0.1
- IpCIDR: 192.168.0.1/24
- Ipaddr6: - Ipaddr6:
- Netmask: - Netmask: 255.255.255.0
- Network: - Network: 192.168.0.0
- NetworkCIDR: - NetworkCIDR: 192.168.0.0/24
- Ipv6: false - Ipv6: false
### DHCP ### DHCP

View File

@@ -0,0 +1 @@
ipaddr: 192.168.0.1/24

View File

@@ -29,6 +29,7 @@ data from other structures.
### Network ### Network
- Ipaddr: {{ .Ipaddr }} - Ipaddr: {{ .Ipaddr }}
- IpCIDR: {{ .IpCIDR }}
- Ipaddr6: {{ .Ipaddr6 }} - Ipaddr6: {{ .Ipaddr6 }}
- Netmask: {{ .Netmask }} - Netmask: {{ .Netmask }}
- Network: {{ .Network }} - Network: {{ .Network }}

View File

@@ -152,7 +152,6 @@ option architecture-type code 93 = unsigned integer 16;
if exists user-class and option user-class = "iPXE" { if exists user-class and option user-class = "iPXE" {
filename "http://192.168.0.1:9873/ipxe/${mac:hexhyp}?assetkey=${asset}&uuid=${uuid}"; filename "http://192.168.0.1:9873/ipxe/${mac:hexhyp}?assetkey=${asset}&uuid=${uuid}";
} else { } else {
if option architecture-type = 00:00 { if option architecture-type = 00:00 {
filename "/warewulf/undionly.kpxe"; filename "/warewulf/undionly.kpxe";
} }
@@ -166,7 +165,6 @@ if exists user-class and option user-class = "iPXE" {
filename "/warewulf/snponly.efi"; filename "/warewulf/snponly.efi";
} }
} }
subnet 192.168.0.0 netmask 255.255.255.0 { subnet 192.168.0.0 netmask 255.255.255.0 {
max-lease-time 120; max-lease-time 120;
range 192.168.0.100 192.168.0.199; 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" { if exists user-class and option user-class = "iPXE" {
filename "http://192.168.0.1:9873/ipxe/${mac:hexhyp}?assetkey=${asset}&uuid=${uuid}"; filename "http://192.168.0.1:9873/ipxe/${mac:hexhyp}?assetkey=${asset}&uuid=${uuid}";
} else { } else {
if option architecture-type = 00:00 { if option architecture-type = 00:00 {
filename "/warewulf/undionly.kpxe"; filename "/warewulf/undionly.kpxe";
} }
@@ -215,7 +212,6 @@ if exists user-class and option user-class = "iPXE" {
filename "/warewulf/snponly.efi"; filename "/warewulf/snponly.efi";
} }
} }
subnet 192.168.0.0 netmask 255.255.255.0 { subnet 192.168.0.0 netmask 255.255.255.0 {
max-lease-time 120; max-lease-time 120;
range 192.168.0.100 192.168.0.199; 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 const host_exports string = `backupFile: true
writeFile: true writeFile: true
Filename: etc/exports Filename: etc/exports
# This file is autogenerated by warewulf # This file is autogenerated by warewulf
/home 192.168.0.0/255.255.255.0(rw,sync) /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) /opt 192.168.0.0/255.255.255.0(ro,sync,no_root_squash)

View File

@@ -1,4 +1,4 @@
{{if $.Dhcp.Enabled -}} {{ if $.Dhcp.Enabled -}}
# This file is autogenerated by warewulf # This file is autogenerated by warewulf
allow booting; allow booting;
@@ -38,19 +38,21 @@ if substring (option vendor-class-identifier, 0, 9) = "PXEClient" {
if exists user-class and option user-class = "iPXE" { if exists user-class and option user-class = "iPXE" {
filename "http://{{$.Ipaddr}}:{{$.Warewulf.Port}}/ipxe/${mac:hexhyp}?assetkey=${asset}&uuid=${uuid}"; filename "http://{{$.Ipaddr}}:{{$.Warewulf.Port}}/ipxe/${mac:hexhyp}?assetkey=${asset}&uuid=${uuid}";
} else { } else {
{{range $type,$name := $.Tftp.IpxeBinaries }} {{- range $type, $name := $.Tftp.IpxeBinaries }}
if option architecture-type = {{ $type }} { if option architecture-type = {{ $type }} {
filename "/warewulf/{{ basename $name }}"; 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; max-lease-time 120;
range {{$.Dhcp.RangeStart}} {{$.Dhcp.RangeEnd}}; range {{.Dhcp.RangeStart}} {{.Dhcp.RangeEnd}};
next-server {{.Ipaddr}}; next-server {{.Ipaddr}};
} }
{{- end }}
{{- if eq .Dhcp.Template "static" }} {{- if eq .Dhcp.Template "static" }}
{{- range $nodes := $.AllNodes}} {{- range $nodes := $.AllNodes}}

View File

@@ -1,4 +1,4 @@
{{- if .Nfs.Enabled }} {{ if .Nfs.Enabled -}}
# This file is autogenerated by warewulf # This file is autogenerated by warewulf
{{- $network := .Network }} {{- $network := .Network }}
{{- $netmask := .Netmask }} {{- $netmask := .Netmask }}