Restore server network auto-detection

Signed-off-by: Jonathon Anderson <janderson@ciq.com>
This commit is contained in:
Jonathon Anderson
2025-02-05 17:14:40 -07:00
parent abc3a0ee2d
commit 73f34d0099
13 changed files with 113 additions and 15 deletions

View File

@@ -41,6 +41,7 @@ type WarewulfYaml struct {
WWClient *WWClientConf `yaml:"wwclient,omitempty"`
warewulfconf string
autodetected bool
}
// New caches and returns a new [WarewulfYaml] initialized with empty
@@ -73,12 +74,12 @@ func Get() *WarewulfYaml {
// Read populates [WarewulfYaml] with the values from a configuration
// file.
func (conf *WarewulfYaml) Read(confFileName string) error {
func (conf *WarewulfYaml) Read(confFileName string, autodetect bool) error {
wwlog.Debug("Reading warewulf.conf from: %s", confFileName)
conf.warewulfconf = confFileName
if data, err := os.ReadFile(confFileName); err != nil {
return err
} else if err := conf.Parse(data); err != nil {
} else if err := conf.Parse(data, autodetect); err != nil {
return err
} else {
return nil
@@ -86,7 +87,7 @@ func (conf *WarewulfYaml) Read(confFileName string) error {
}
// Parse populates [WarewulfYaml] with the values from a yaml document.
func (conf *WarewulfYaml) Parse(data []byte) error {
func (conf *WarewulfYaml) Parse(data []byte, autodetect bool) error {
// ipxe binaries are merged not overwritten, store defaults separate
defIpxe := make(map[string]string)
for k, v := range conf.TFTP.IpxeBinaries {
@@ -110,6 +111,33 @@ func (conf *WarewulfYaml) Parse(data []byte) error {
}
}
if autodetect {
if conf.Ipaddr == "" {
if ip := GetOutboundIP(); ip != nil {
conf.Ipaddr = ip.String()
conf.autodetected = true
}
}
if conf.Netmask == "" {
if ip := net.ParseIP(conf.Ipaddr); ip != nil {
if network, err := GetIPNetForIP(ip); err == nil {
conf.Netmask = net.IP(network.Mask).String()
conf.autodetected = true
}
}
}
if conf.Network == "" {
if ip := net.ParseIP(conf.Ipaddr); ip != nil {
if mask := net.IPMask(net.ParseIP(conf.Netmask)); mask != nil {
conf.Network = ip.Mask(mask).String()
conf.autodetected = true
}
}
}
}
if conf.Ipaddr6 != "" {
if _, network, err := net.ParseCIDR(conf.Ipaddr6); err == nil {
if conf.Ipv6net == "" {
@@ -161,6 +189,10 @@ func (conf *WarewulfYaml) GetWarewulfConf() string {
return conf.warewulfconf
}
func (conf *WarewulfYaml) Autodetected() bool {
return conf.autodetected
}
func (config *WarewulfYaml) Dump() ([]byte, error) {
var buf bytes.Buffer
yamlEncoder := yaml.NewEncoder(&buf)

View File

@@ -385,7 +385,7 @@ tftp:
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
conf := New()
err := conf.Parse([]byte(tt.input))
err := conf.Parse([]byte(tt.input), false)
assert.NoError(t, err)
result, err := conf.Dump()
assert.NoError(t, err)
@@ -405,7 +405,7 @@ func TestInitializedFromFile(t *testing.T) {
conf := New()
assert.False(t, conf.InitializedFromFile())
assert.NoError(t, conf.Read(tempWarewulfConf.Name()))
assert.NoError(t, conf.Read(tempWarewulfConf.Name(), false))
assert.True(t, conf.InitializedFromFile())
assert.Equal(t, conf.GetWarewulfConf(), tempWarewulfConf.Name())
}

View File

@@ -1,5 +1,62 @@
package config
import (
"fmt"
"net"
)
func BoolP(p *bool) bool {
return p != nil && *p
}
func GetOutboundIP() net.IP {
conn, err := net.Dial("udp", "192.0.2.1:80")
if err != nil {
return nil
}
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
return localAddr.IP
}
func GetIPNetForIP(ip net.IP) (*net.IPNet, error) {
interfaces, err := net.Interfaces()
if err != nil {
return nil, fmt.Errorf("failed to get network interfaces: %v", err)
}
for _, iface := range interfaces {
// skip interfaces that are down or not relevant
if iface.Flags&net.FlagUp == 0 {
continue
}
addrs, err := iface.Addrs()
if err != nil {
continue // try next interface
}
for _, addr := range addrs {
// We expect addr to be of type *net.IPNet.
var ipNet *net.IPNet
switch v := addr.(type) {
case *net.IPNet:
ipNet = v
case *net.IPAddr:
// Wrap net.IPAddr in an IPNet with its default mask.
ipNet = &net.IPNet{IP: v.IP, Mask: v.IP.DefaultMask()}
}
if ipNet == nil {
continue
}
// Check if the IP matches (for IPv4, Equal works well)
if ipNet.IP.Equal(ip) {
return ipNet, nil
}
}
}
return nil, fmt.Errorf("could not find IPNet for IP %v", ip)
}