From 0c9bf78cd0e285b3fdeca65417e35f6b3facd7bd Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Thu, 27 Jul 2023 15:55:25 +0200 Subject: [PATCH] using ProvisionSend instean of GrubSend Signed-off-by: Christian Goll --- etc/grub/grub.cfg.ww | 9 +- internal/pkg/container/grub.go | 3 + internal/pkg/warewulfd/parser.go | 26 +++- internal/pkg/warewulfd/provision.go | 191 ++++++++-------------------- internal/pkg/warewulfd/util.go | 2 +- internal/pkg/warewulfd/warewulfd.go | 50 ++++++-- 6 files changed, 117 insertions(+), 164 deletions(-) diff --git a/etc/grub/grub.cfg.ww b/etc/grub/grub.cfg.ww index cf7c8165..add45e64 100644 --- a/etc/grub/grub.cfg.ww +++ b/etc/grub/grub.cfg.ww @@ -1,7 +1,3 @@ -# This file is autogenerated by warewulf -# Host: {{.BuildHost}} -# Time: {{.BuildTime}} -# Source: {{.BuildSource}} echo "================================================================================" echo "Warewulf v4 now booting with grub" echo @@ -11,10 +7,10 @@ container="${uri}&stage=container&compress=gz" system="${uri}&stage=system&compress=gz" echo "Warewulf Controller: {{.Ipaddr}}" echo "Trying to load a kernel... " -linux $kernel wwid=${net_default_mac} +linux $kernel wwid="${net_default_mac}" "{{.KernelArgs}}" if [ x$? = x0 ] ; then echo "Loading initrd..." -initrd $system $container +initrd $container $system echo "Booting..." boot else @@ -26,3 +22,4 @@ sleep 60 reboot fi + diff --git a/internal/pkg/container/grub.go b/internal/pkg/container/grub.go index 9b6d97b4..ab6ba5c3 100644 --- a/internal/pkg/container/grub.go +++ b/internal/pkg/container/grub.go @@ -20,6 +20,9 @@ func grubNames() []string { `grub-tpm.efi`, `grub.efi`, `grubx64.efi`, + `grubia32.efi`, + `grubaa64.efi`, + `grubarm.efi`, } } diff --git a/internal/pkg/warewulfd/parser.go b/internal/pkg/warewulfd/parser.go index 1e62dd0f..5923bf25 100644 --- a/internal/pkg/warewulfd/parser.go +++ b/internal/pkg/warewulfd/parser.go @@ -5,6 +5,7 @@ import ( "strconv" "strings" + "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/pkg/errors" ) @@ -16,6 +17,7 @@ type parserInfo struct { uuid string stage string overlay string + efifile string compress string } @@ -25,16 +27,22 @@ func parseReq(req *http.Request) (parserInfo, error) { url := strings.Split(req.URL.Path, "?")[0] path_parts := strings.Split(url, "/") - if len(path_parts) != 3 { + if len(path_parts) < 3 { return ret, errors.New("unknown path components in GET") } // handle when stage was passed in the url path /[stage]/hwaddr stage := path_parts[1] - hwaddr := path_parts[2] - hwaddr = strings.ReplaceAll(hwaddr, "-", ":") - hwaddr = strings.ToLower(hwaddr) - + hwaddr := "" + if stage != "efiboot" { + hwaddr = path_parts[2] + hwaddr = strings.ReplaceAll(hwaddr, "-", ":") + hwaddr = strings.ToLower(hwaddr) + } else if len(path_parts) > 3 { + ret.efifile = strings.Join(path_parts[2:], "/") + } else { + ret.efifile = path_parts[2] + } ret.hwaddr = hwaddr ret.ipaddr = strings.Split(req.RemoteAddr, ":")[0] ret.remoteport, _ = strconv.Atoi(strings.Split(req.RemoteAddr, ":")[1]) @@ -63,6 +71,8 @@ func parseReq(req *http.Request) (parserInfo, error) { ret.stage = "system" } else if stage == "overlay-runtime" { ret.stage = "runtime" + } else if stage == "efiboot" { + ret.stage = "efiboot" } } @@ -76,7 +86,11 @@ func parseReq(req *http.Request) (parserInfo, error) { return ret, errors.New("no stage encoded in GET") } if ret.hwaddr == "" { - return ret, errors.New("no hwaddr encoded in GET") + ret.hwaddr = ArpFind(ret.ipaddr) + wwlog.Verbose("node mac encoded, arp cache got %s for %s", ret.hwaddr, ret.ipaddr) + if ret.hwaddr == "" { + return ret, errors.New("no hwaddr encoded in GET") + } } if ret.ipaddr == "" { return ret, errors.New("could not obtain ipaddr from HTTP request") diff --git a/internal/pkg/warewulfd/provision.go b/internal/pkg/warewulfd/provision.go index e301ac77..bdca8a34 100644 --- a/internal/pkg/warewulfd/provision.go +++ b/internal/pkg/warewulfd/provision.go @@ -8,24 +8,17 @@ import ( "path" "strconv" "strings" - "sync" "text/template" warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/container" "github.com/hpcng/warewulf/internal/pkg/kernel" - "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/overlay" "github.com/hpcng/warewulf/internal/pkg/util" "github.com/hpcng/warewulf/internal/pkg/wwlog" ) -var sentMap = make(map[string]string) - -// define a mutex lock -var mu sync.Mutex - -type iPxeTemplate struct { +type templateVars struct { Message string WaitTime string Hostname string @@ -40,20 +33,13 @@ type iPxeTemplate struct { KernelOverride string } -var status_stages = map[string]string{ - "ipxe": "IPXE", - "kernel": "KERNEL", - "kmods": "KMODS_OVERLAY", - "system": "SYSTEM_OVERLAY", - "runtime": "RUNTIME_OVERLAY"} - func ProvisionSend(w http.ResponseWriter, req *http.Request) { + wwlog.Debug("Requested URL: %s", req.URL.String()) conf := warewulfconf.Get() - rinfo, err := parseReq(req) if err != nil { w.WriteHeader(http.StatusBadRequest) - wwlog.ErrorExc(err, "") + wwlog.ErrorExc(err, "Bad status") return } @@ -67,6 +53,14 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { } } + status_stages := map[string]string{ + "efiboot": "EFI", + "ipxe": "IPXE", + "kernel": "KERNEL", + "kmods": "KMODS_OVERLAY", + "system": "SYSTEM_OVERLAY", + "runtime": "RUNTIME_OVERLAY"} + status_stage := status_stages[rinfo.stage] var stage_file string @@ -91,26 +85,13 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { wwlog.Error("%s (unknown/unconfigured node)", rinfo.hwaddr) if rinfo.stage == "ipxe" { stage_file = path.Join(conf.Paths.Sysconfdir, "/warewulf/ipxe/unconfigured.ipxe") - tmpl_data = iPxeTemplate{ + tmpl_data = templateVars{ Hwaddr: rinfo.hwaddr} } } else if rinfo.stage == "ipxe" { stage_file = path.Join(conf.Paths.Sysconfdir, "warewulf/ipxe/"+node.Ipxe.Get()+".ipxe") - tmpl_data = iPxeTemplate{ - Id: node.Id.Get(), - Cluster: node.ClusterName.Get(), - Fqdn: node.Id.Get(), - Ipaddr: conf.Ipaddr, - Port: strconv.Itoa(conf.Warewulf.Port), - Hostname: node.Id.Get(), - Hwaddr: rinfo.hwaddr, - ContainerName: node.ContainerName.Get(), - KernelArgs: node.Kernel.Args.Get(), - KernelOverride: node.Kernel.Override.Get()} - } else if rinfo.stage == "grub.cfg" { - stage_file = path.Join(conf.Paths.Sysconfdir, "warewulf/grub/"+node.Grub.Get()+".ipxe") - tmpl_data = iPxeTemplate{ + tmpl_data = templateVars{ Id: node.Id.Get(), Cluster: node.ClusterName.Get(), Fqdn: node.Id.Get(), @@ -157,7 +138,6 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { } else { context = rinfo.stage } - stage_file, err = getOverlayFile( node, context, @@ -174,6 +154,45 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { wwlog.ErrorExc(err, "") return } + } else if rinfo.stage == "efiboot" { + wwlog.Debug("requested method: %s", req.Method) + containerName := node.ContainerName.Get() + switch rinfo.efifile { + case "shim.efi": + stage_file = container.ShimFind(containerName) + if stage_file == "" { + wwlog.ErrorExc(fmt.Errorf("could't find shim.efi"), containerName) + w.WriteHeader(http.StatusNotFound) + return + } + case "grub.efi", "grub-tpm.efi", "grubx64.efi", "grubia32.efi", "grubaa64.efi", "grubarm.efi": + stage_file = container.GrubFind(containerName) + if stage_file == "" { + wwlog.ErrorExc(fmt.Errorf("could't find grub.efi"), containerName) + w.WriteHeader(http.StatusNotFound) + return + } + case "grub.cfg": + stage_file = path.Join(conf.Paths.Sysconfdir, "warewulf/grub/grub.cfg.ww") + tmpl_data = templateVars{ + Id: node.Id.Get(), + Cluster: node.ClusterName.Get(), + Fqdn: node.Id.Get(), + Ipaddr: conf.Ipaddr, + Port: strconv.Itoa(conf.Warewulf.Port), + Hostname: node.Id.Get(), + Hwaddr: rinfo.hwaddr, + ContainerName: node.ContainerName.Get(), + KernelArgs: node.Kernel.Args.Get(), + KernelOverride: node.Kernel.Override.Get()} + if stage_file == "" { + wwlog.ErrorExc(fmt.Errorf("could't find grub.cfg template"), containerName) + w.WriteHeader(http.StatusNotFound) + return + } + default: + wwlog.ErrorExc(fmt.Errorf("could't find efiboot file: %s", rinfo.efifile), "") + } } else if rinfo.stage == "shim" { if node.ContainerName.Defined() { stage_file = container.ShimFind(node.ContainerName.Get()) @@ -270,109 +289,3 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { } } - -/* -simple handler for sending only out grub to the nodes, parses the -GET request, so that for "/grub/shim.efi" the "shim.efi" from the -container of the default profile will be sent and for "/grub/shim.efi?$container" the -shim of $container will be sent -*/ -func GrubSend(w http.ResponseWriter, req *http.Request) { - req.Close = true - wwlog.Debug("Grub send called with url: %s host: %s", req.URL, req.Host) - url := strings.Split(req.URL.Path, "?")[0] - path_parts := strings.Split(path.Join(url), "/") - remoteIP := strings.Split(req.RemoteAddr, ":")[0] - mu.Lock() - if last_sent, ok := sentMap[remoteIP]; ok { - wwlog.Debug("last sent is: %s", last_sent) - } - mu.Unlock() - if len(path_parts) != 3 { - w.WriteHeader(http.StatusBadRequest) - wwlog.ErrorExc(fmt.Errorf("unknown path components in GET: %s", req.URL.Path), remoteIP) - return - } - requested_file := path_parts[2] - nodeDB, err := node.New() - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - profiles, _ := nodeDB.MapAllProfiles() - containerName := "" - if profile, ok := profiles["default"]; ok { - containerName = profile.ContainerName.Get() - } - remoteNode, err := nodeDB.FindByIpaddr(remoteIP) - if err == nil { - containerName = remoteNode.ContainerName.Get() - } else { - if mac := ArpFind(remoteIP); mac != "" { - remoteNode, err := nodeDB.FindByHwaddr(mac) - if err != nil { - containerName = remoteNode.ContainerName.Get() - } else if remoteNode, err = GetNodeOrSetDiscoverable(mac); err == nil { - containerName = remoteNode.ContainerName.Get() - } - } - } - - if len(url) == 2 { - containerName = strings.Split(req.URL.Path, "?")[1] - } - mu.Lock() - if last_sent, ok := sentMap[remoteIP]; ok && last_sent == "shim.efi" { - requested_file = "grub.efi" - } - mu.Unlock() - wwlog.Recv("remoteIP: %s, container: %s, filename: %s", remoteIP, containerName, requested_file) - var stage_file string - switch requested_file { - case "shim.efi": - stage_file = container.ShimFind(containerName) - if stage_file == "" { - wwlog.ErrorExc(fmt.Errorf("could't find shim.efi"), containerName) - w.WriteHeader(http.StatusNotFound) - return - } - mu.Lock() - sentMap[remoteIP] = "shim.efi" - mu.Unlock() - case "grub.efi", "grubx86.efi": - stage_file = container.GrubFind(containerName) - if stage_file == "" { - wwlog.ErrorExc(fmt.Errorf("could't find grub.efi"), containerName) - w.WriteHeader(http.StatusNotFound) - return - } - mu.Lock() - sentMap[remoteIP] = "grub.efi" - mu.Unlock() - } - wwlog.Serv("stage_file '%s'", stage_file) - - if util.IsFile(stage_file) { - /* - fd, err := os.Open(stage_file) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - defer fd.Close() - */ - /* - stat, err := fd.Stat() - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - - http.ServeContent(w, req, stage_file, stat.ModTime(), fd) - io.Copy(w, fd) - req.Close = true - */ - http.ServeFile(w, req, stage_file) - wwlog.Send("%15s: %s", remoteIP, stage_file) - } -} diff --git a/internal/pkg/warewulfd/util.go b/internal/pkg/warewulfd/util.go index 8094def9..a3f7439d 100644 --- a/internal/pkg/warewulfd/util.go +++ b/internal/pkg/warewulfd/util.go @@ -40,7 +40,7 @@ func sendFile( fd) wwlog.Send("%15s: %s", sendto, filename) - + req.Body.Close() return nil } diff --git a/internal/pkg/warewulfd/warewulfd.go b/internal/pkg/warewulfd/warewulfd.go index e76826aa..aab8cd80 100644 --- a/internal/pkg/warewulfd/warewulfd.go +++ b/internal/pkg/warewulfd/warewulfd.go @@ -5,6 +5,7 @@ import ( "os" "os/signal" "strconv" + "strings" "syscall" warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" @@ -14,6 +15,21 @@ import ( // TODO: https://github.com/danderson/netboot/blob/master/pixiecore/dhcp.go // TODO: https://github.com/pin/tftp +/* +wrapper type for the server mux as him requests http://efiboot//grub.efi which is filtered out by http to `301 Moved Permanently` which +which shim.fi can't handle. So filter out `//` before they hit http +*/ +type slashFix struct { + mux http.Handler +} + +/* +Filter out the '//' +*/ +func (h *slashFix) ServeHTTP(w http.ResponseWriter, r *http.Request) { + r.URL.Path = strings.Replace(r.URL.Path, "//", "/", -1) + h.mux.ServeHTTP(w, r) +} func RunServer() error { err := DaemonInitLogging() @@ -53,23 +69,33 @@ func RunServer() error { if err != nil { wwlog.Warn("couldn't copy default shim: %s", err) } - - http.HandleFunc("/provision/", ProvisionSend) - http.HandleFunc("/ipxe/", ProvisionSend) - http.HandleFunc("/grub/", GrubSend) - http.HandleFunc("/kernel/", ProvisionSend) - http.HandleFunc("/kmods/", ProvisionSend) - http.HandleFunc("/container/", ProvisionSend) - http.HandleFunc("/overlay-system/", ProvisionSend) - http.HandleFunc("/overlay-runtime/", ProvisionSend) - http.HandleFunc("/status", StatusSend) + var wwHandler http.ServeMux + wwHandler.HandleFunc("/provision/", ProvisionSend) + wwHandler.HandleFunc("/ipxe/", ProvisionSend) + wwHandler.HandleFunc("/efiboot/", ProvisionSend) + wwHandler.HandleFunc("/kernel/", ProvisionSend) + wwHandler.HandleFunc("/kmods/", ProvisionSend) + wwHandler.HandleFunc("/container/", ProvisionSend) + wwHandler.HandleFunc("/overlay-system/", ProvisionSend) + wwHandler.HandleFunc("/overlay-runtime/", ProvisionSend) + wwHandler.HandleFunc("/status", StatusSend) conf := warewulfconf.Get() daemonPort := conf.Warewulf.Port - wwlog.Serv("Starting HTTPD REST service on port %d", daemonPort) + /* + wwlog.Serv("Starting HTTPD REST service on port %d", daemonPort) + s := &http.Server{ + Addr: ":" + strconv.Itoa(daemonPort), + Handler: &slashFix{&wwHandler}, + ReadTimeout: 10 * time.Second, + IdleTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + } + err = s.ListenAndServe() + */ + err = http.ListenAndServe(":"+strconv.Itoa(daemonPort), &slashFix{&wwHandler}) - err = http.ListenAndServe(":"+strconv.Itoa(daemonPort), nil) if err != nil { return errors.Wrap(err, "Could not start listening service") }