Merge pull request #1958 from middelkoopt/tm-ipv6-pxe
Add IPv6 support for iPXE booting
This commit is contained in:
@@ -14,12 +14,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
- Added `DELETE /api/overlays/{name}/file?path={path}`
|
||||
- Added `wwctl configure warewulfd`
|
||||
- Added troubleshooting documentation regarding NUMA and tmpfs
|
||||
- Added support for IPv6 when booting with iPXE. #1852
|
||||
|
||||
### Changed
|
||||
|
||||
- Restore default idempotency of `PUT /api/nodes/{id}`
|
||||
- `DELETE /api/overlays/{name}?force=true` can delete overlays that are in use
|
||||
- `warewulfd` overlay autobuild rebuilds overlays after node discovery. #1468
|
||||
|
||||
### Fixed
|
||||
|
||||
- Improved netplan support. #1873
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -47,3 +47,4 @@
|
||||
* Stephen Simpson [@ssimpson89](https://github.com/ssimpson89)
|
||||
* Rafael Lopez <raflopez1@gmail.com> @rafalop
|
||||
* Arian Cabrera [@acabrera86](https://github.com/acabrera86)
|
||||
* Dacian Reece-Stremtan <dacianstremtan@gmail.com> [@dacianstremtan](https://github.com/dacianstremtan)
|
||||
|
||||
@@ -4,6 +4,7 @@ echo
|
||||
echo "Warewulf Server:"
|
||||
echo "* Ipaddr: {{.Ipaddr}}"
|
||||
echo "* Port: {{.Port}}"
|
||||
echo "* Authority: {{.Authority}}"
|
||||
echo
|
||||
echo "This node:"
|
||||
echo "* Fqdn: {{.Fqdn}}"
|
||||
@@ -28,7 +29,7 @@ reboot
|
||||
echo "Reading asset key..."
|
||||
smbios --type 3 --get-string 8 --set assetkey
|
||||
|
||||
uri="(http,{{.Ipaddr}}:{{.Port}})/provision/${net_default_mac}?assetkey=${assetkey}"
|
||||
uri="(http,{{.Authority}})/provision/${net_default_mac}?assetkey=${assetkey}"
|
||||
kernel="${uri}&stage=kernel"
|
||||
|
||||
set default={{ or .Tags.GrubMenuEntry "single-stage" }}
|
||||
|
||||
@@ -15,7 +15,7 @@ sleep 30
|
||||
reboot
|
||||
{{- end }}
|
||||
|
||||
set baseuri http://{{.Ipaddr}}:{{.Port}}/provision/{{.Hwaddr}}
|
||||
set baseuri http://{{.Authority}}/provision/{{.Hwaddr}}
|
||||
set uri ${baseuri}?assetkey=${asset}&uuid=${uuid}
|
||||
|
||||
echo Downloading kernel image...
|
||||
@@ -115,6 +115,7 @@ goto menu
|
||||
echo Warewulf Server:
|
||||
echo * Ipaddr: {{.Ipaddr}}
|
||||
echo * Port: {{.Port}}
|
||||
echo * Authority: {{.Authority}}
|
||||
echo
|
||||
echo This node:
|
||||
echo * Fqdn: {{.Fqdn}}
|
||||
|
||||
@@ -2,7 +2,7 @@ package warewulfd
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -44,9 +44,12 @@ func parseReq(req *http.Request) (parserInfo, error) {
|
||||
ret.efifile = path_parts[2]
|
||||
}
|
||||
ret.hwaddr = hwaddr
|
||||
ret.ipaddr = strings.Split(req.RemoteAddr, ":")[0]
|
||||
ret.remoteport, _ = strconv.Atoi(strings.Split(req.RemoteAddr, ":")[1])
|
||||
|
||||
remoteAddrPort, err := netip.ParseAddrPort(req.RemoteAddr)
|
||||
if err != nil {
|
||||
return ret, errors.New("could not parse remote address")
|
||||
}
|
||||
ret.ipaddr = remoteAddrPort.Addr().String()
|
||||
ret.remoteport = int(remoteAddrPort.Port())
|
||||
if len(req.URL.Query()["assetkey"]) > 0 {
|
||||
ret.assetkey = req.URL.Query()["assetkey"][0]
|
||||
}
|
||||
|
||||
53
internal/pkg/warewulfd/parser_test.go
Normal file
53
internal/pkg/warewulfd/parser_test.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package warewulfd
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var parseReqTests = []struct {
|
||||
description string
|
||||
url string
|
||||
remoteAddr string
|
||||
result parserInfo
|
||||
}{
|
||||
{
|
||||
description: "basic ipv4 request",
|
||||
url: "/provision/00:00:00:ff:ff:ff",
|
||||
remoteAddr: "10.5.1.1:9873",
|
||||
result: parserInfo{
|
||||
hwaddr: "00:00:00:ff:ff:ff",
|
||||
ipaddr: "10.5.1.1",
|
||||
remoteport: 9873,
|
||||
stage: "ipxe",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "basic ipv6 request",
|
||||
url: "/provision/00:00:00:ff:ff:ff",
|
||||
remoteAddr: "[fd00:5::1:1]:9873",
|
||||
result: parserInfo{
|
||||
hwaddr: "00:00:00:ff:ff:ff",
|
||||
ipaddr: "fd00:5::1:1",
|
||||
remoteport: 9873,
|
||||
stage: "ipxe",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func Test_ParseReq(t *testing.T) {
|
||||
for _, tt := range parseReqTests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
req := &http.Request{
|
||||
URL: &url.URL{Path: tt.url},
|
||||
RemoteAddr: tt.remoteAddr,
|
||||
}
|
||||
result, err := parseReq(req)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.result, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@@ -32,7 +33,9 @@ type templateVars struct {
|
||||
Ipxe string
|
||||
Hwaddr string
|
||||
Ipaddr string
|
||||
Ipaddr6 string
|
||||
Port string
|
||||
Authority string
|
||||
KernelArgs string
|
||||
KernelVersion string
|
||||
Root string
|
||||
@@ -115,12 +118,30 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
kernelVersion = kernel_.Version()
|
||||
}
|
||||
}
|
||||
authority := fmt.Sprintf("%s:%d", conf.Ipaddr, conf.Warewulf.Port)
|
||||
ipaddr6 := ""
|
||||
if confIpaddr6, err := netip.ParsePrefix(conf.Ipaddr6); err == nil {
|
||||
ipaddr6 = confIpaddr6.Addr().String()
|
||||
}
|
||||
if rinfoIpaddr, err := netip.ParseAddr(rinfo.ipaddr); err == nil {
|
||||
if rinfoIpaddr.Is6() {
|
||||
if ipaddr6 != "" {
|
||||
authority = fmt.Sprintf("[%s]:%d", ipaddr6, conf.Warewulf.Port)
|
||||
} else {
|
||||
wwlog.Error("No valid IPv6 address configured, but request is IPv6")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
wwlog.Error("Could not parse request IP address: %s", rinfo.ipaddr)
|
||||
}
|
||||
tmpl_data = &templateVars{
|
||||
Id: remoteNode.Id(),
|
||||
Cluster: remoteNode.ClusterName,
|
||||
Fqdn: remoteNode.Id(),
|
||||
Ipaddr: conf.Ipaddr,
|
||||
Ipaddr6: ipaddr6,
|
||||
Port: strconv.Itoa(conf.Warewulf.Port),
|
||||
Authority: authority,
|
||||
Hostname: remoteNode.Id(),
|
||||
Hwaddr: rinfo.hwaddr,
|
||||
ImageName: remoteNode.ImageName,
|
||||
@@ -203,12 +224,30 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
||||
kernelVersion = kernel_.Version()
|
||||
}
|
||||
}
|
||||
authority := fmt.Sprintf("%s:%d", conf.Ipaddr, conf.Warewulf.Port)
|
||||
ipaddr6 := ""
|
||||
if confIpaddr6, err := netip.ParsePrefix(conf.Ipaddr6); err == nil {
|
||||
ipaddr6 = confIpaddr6.Addr().String()
|
||||
}
|
||||
if rinfoIpaddr, err := netip.ParseAddr(rinfo.ipaddr); err == nil {
|
||||
if rinfoIpaddr.Is6() {
|
||||
if ipaddr6 != "" {
|
||||
authority = fmt.Sprintf("[%s]:%d", ipaddr6, conf.Warewulf.Port)
|
||||
} else {
|
||||
wwlog.Error("No valid IPv6 address configured, but request is IPv6")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
wwlog.Error("Could not parse request IP address: %s", rinfo.ipaddr)
|
||||
}
|
||||
tmpl_data = &templateVars{
|
||||
Id: remoteNode.Id(),
|
||||
Cluster: remoteNode.ClusterName,
|
||||
Fqdn: remoteNode.Id(),
|
||||
Ipaddr: conf.Ipaddr,
|
||||
Ipaddr6: ipaddr6,
|
||||
Port: strconv.Itoa(conf.Warewulf.Port),
|
||||
Authority: authority,
|
||||
Hostname: remoteNode.Id(),
|
||||
Hwaddr: rinfo.hwaddr,
|
||||
ImageName: remoteNode.ImageName,
|
||||
|
||||
@@ -23,22 +23,23 @@ var provisionSendTests = []struct {
|
||||
}{
|
||||
{"system overlay", "/overlay-system/00:00:00:ff:ff:ff", "system overlay", 200, "10.10.10.10:9873"},
|
||||
{"runtime overlay", "/overlay-runtime/00:00:00:ff:ff:ff", "runtime overlay", 200, "10.10.10.10:9873"},
|
||||
{"fake overlay", "/overlay-system/00:00:00:ff:ff:ff?overlay=fake", "", 404, "10.10.10.10:9873:9873"},
|
||||
{"fake overlay", "/overlay-system/00:00:00:ff:ff:ff?overlay=fake", "", 404, "10.10.10.10:9873"},
|
||||
{"specific overlay", "/overlay-system/00:00:00:ff:ff:ff?overlay=o1", "specific overlay", 200, "10.10.10.10:9873"},
|
||||
{"find shim", "/efiboot/shim.efi", "", 200, "10.10.10.10:9873"},
|
||||
{"find shim", "/efiboot/shim.efi", "", 404, "10.10.10.11:9873"},
|
||||
{"find grub", "/efiboot/grub.efi", "", 200, "10.10.10.10:9873"},
|
||||
{"find grub", "/efiboot/grub.efi", "", 404, "10.10.10.11:9873"},
|
||||
{"find initramfs", "/provision/00:00:00:ff:ff:ff?stage=initramfs", "", 200, "10.10.10.10:9873"},
|
||||
{"ipxe test with NetDevs and KernelVersion", "/provision/00:00:00:00:00:ff?stage=ipxe", "1.1.1 ifname=net:00:00:00:00:00:ff ", 200, "10.10.10.12:9873"},
|
||||
{"find grub.cfg", "/efiboot/grub.cfg", "dracut", 200, "10.10.10.11:9873"},
|
||||
{"ipxe test with NetDevs, KernelVersion, and Authority", "/provision/00:00:00:00:00:ff?stage=ipxe", "1.1.1 ifname=net:00:00:00:00:00:ff 10.10.0.1 fd00:10::1 10.10.0.1:9873", 200, "10.10.10.12:9873"},
|
||||
{"ipxe ipv6", "/provision/00:00:00:00:00:ff?stage=ipxe", "1.1.1 ifname=net:00:00:00:00:00:ff 10.10.0.1 fd00:10::1 [fd00:10::1]:9873", 200, "[fd00:10::10:12]:9873"},
|
||||
{"find grub.cfg", "/efiboot/grub.cfg", "dracut 10.10.0.1:9873", 200, "10.10.10.11:9873"},
|
||||
}
|
||||
|
||||
func Test_ProvisionSend(t *testing.T) {
|
||||
env := testenv.New(t)
|
||||
defer env.RemoveAll()
|
||||
|
||||
env.WriteFile("etc/warewulf/nodes.conf", `nodeprofiles:
|
||||
env.WriteFile("/etc/warewulf/nodes.conf", `nodeprofiles:
|
||||
default:
|
||||
image name: suse
|
||||
nodes:
|
||||
@@ -78,8 +79,8 @@ nodes:
|
||||
env.CreateFile("/var/lib/warewulf/chroots/suse/rootfs/usr/lib64/efi/shim.efi")
|
||||
env.CreateFile("/var/lib/warewulf/chroots/suse/rootfs/usr/share/efi/x86_64/grub.efi")
|
||||
env.CreateFile("/var/lib/warewulf/chroots/suse/rootfs/boot/initramfs-1.1.0.img")
|
||||
env.WriteFile("/etc/warewulf/ipxe/test.ipxe", "{{.KernelVersion}}{{range $devname, $netdev := .NetDevs}}{{if and $netdev.Hwaddr $netdev.Device}} ifname={{$netdev.Device}}:{{$netdev.Hwaddr}} {{end}}{{end}}")
|
||||
env.WriteFile("/etc/warewulf/grub/grub.cfg.ww", "{{ .Tags.GrubMenuEntry }}")
|
||||
env.WriteFile("/etc/warewulf/ipxe/test.ipxe", "{{.KernelVersion}}{{range $devname, $netdev := .NetDevs}}{{if and $netdev.Hwaddr $netdev.Device}} ifname={{$netdev.Device}}:{{$netdev.Hwaddr}} {{end}}{{end}} {{.Ipaddr}} {{.Ipaddr6}} {{.Authority}}")
|
||||
env.WriteFile("/etc/warewulf/grub/grub.cfg.ww", "{{ .Tags.GrubMenuEntry }} {{ .Authority }}")
|
||||
|
||||
dbErr := LoadNodeDB()
|
||||
assert.NoError(t, dbErr)
|
||||
@@ -87,6 +88,8 @@ nodes:
|
||||
conf := warewulfconf.Get()
|
||||
secureFalse := false
|
||||
conf.Warewulf.SecureP = &secureFalse
|
||||
conf.Ipaddr = "10.10.0.1"
|
||||
conf.Ipaddr6 = "fd00:10::1/64"
|
||||
assert.NoError(t, os.MkdirAll(path.Join(conf.Paths.OverlayProvisiondir(), "n1"), 0700))
|
||||
assert.NoError(t, os.WriteFile(path.Join(conf.Paths.OverlayProvisiondir(), "n1", "__SYSTEM__.img"), []byte("system overlay"), 0600))
|
||||
assert.NoError(t, os.WriteFile(path.Join(conf.Paths.OverlayProvisiondir(), "n1", "__RUNTIME__.img"), []byte("runtime overlay"), 0600))
|
||||
|
||||
Reference in New Issue
Block a user