diff --git a/etc/examples/grub.cfg.ww b/etc/grub/grub.cfg.ww similarity index 100% rename from etc/examples/grub.cfg.ww rename to etc/grub/grub.cfg.ww diff --git a/internal/pkg/config/warewulf.go b/internal/pkg/config/warewulf.go index e8f08ac5..a793e074 100644 --- a/internal/pkg/config/warewulf.go +++ b/internal/pkg/config/warewulf.go @@ -10,4 +10,5 @@ type WarewulfConf struct { EnableHostOverlay bool `yaml:"host overlay" default:"true"` Syslog bool `yaml:"syslog" default:"false"` DataStore string `yaml:"datastore" default:"/var/lib/warewulf"` + DefaultBoot string `yaml:"boot method" default:"ipxe"` } diff --git a/internal/pkg/container/grub.go b/internal/pkg/container/grub.go new file mode 100644 index 00000000..b1f81b84 --- /dev/null +++ b/internal/pkg/container/grub.go @@ -0,0 +1,60 @@ +package container + +import ( + "path" + "path/filepath" + "sort" + + "github.com/hpcng/warewulf/internal/pkg/wwlog" +) + +func grubDirs() []string { + return []string{ + `/usr/share/grub2/x86_64-efi`, + `/usr/share/efi/x86_64/`, + } +} +func grubNames() []string { + return []string{ + `grub-tpm.efi`, + `grub.efi`, + } +} + +/* +Tries to find a grub.efi in the used container +*/ +func GrubFind(container string) string { + wwlog.Debug("Finding grub") + container_path := RootFsDir(container) + if container_path == "" { + return "" + } + for _, grubdir := range grubDirs() { + wwlog.Debug("Checking grub directory: %s", grubdir) + for _, shimname := range shimNames() { + wwlog.Debug("Checking for shim name: %s", shimname) + grubPaths, err := filepath.Glob(path.Join(container_path, grubdir, shimname)) + if err != nil { + return "" + } + if len(grubPaths) == 0 { + continue + } + sort.Slice(grubPaths, func(i, j int) bool { + return grubPaths[i] > grubPaths[j] + }) + for _, grubPath := range grubPaths { + wwlog.Debug("Checking for grub path: %s", grubPath) + // Only succeeds if shimPath exists and, if a + // symlink, links to a path that also exists + grubPath, err = filepath.EvalSymlinks(grubPath) + if err == nil { + wwlog.Debug("found grub: %s", grubPath) + return grubPath + } + } + } + } + return "" +} diff --git a/internal/pkg/container/shim.go b/internal/pkg/container/shim.go new file mode 100644 index 00000000..575d04fd --- /dev/null +++ b/internal/pkg/container/shim.go @@ -0,0 +1,57 @@ +package container + +import ( + "path" + "path/filepath" + "sort" + + "github.com/hpcng/warewulf/internal/pkg/wwlog" +) + +func shimDirs() []string { + return []string{ + `/usr/share/efi/x86_64/`, + `/usr/lib64/`, + ``} +} +func shimNames() []string { + return []string{ + `shim.efi`, + `shim-sles.efi`, + } +} + +func ShimFind(container string) string { + wwlog.Debug("Finding shim") + container_path := RootFsDir(container) + if container_path == "" { + return "" + } + for _, shimdir := range shimDirs() { + wwlog.Debug("Checking shim directory: %s", shimdir) + for _, shimname := range shimNames() { + wwlog.Debug("Checking for shim name: %s", shimname) + shimPaths, err := filepath.Glob(path.Join(container_path, shimdir, shimname)) + if err != nil { + return "" + } + if len(shimPaths) == 0 { + continue + } + sort.Slice(shimPaths, func(i, j int) bool { + return shimPaths[i] > shimPaths[j] + }) + for _, shimPath := range shimPaths { + wwlog.Debug("Checking for shim path: %s", shimPath) + // Only succeeds if shimPath exists and, if a + // symlink, links to a path that also exists + shimPath, err = filepath.EvalSymlinks(shimPath) + if err == nil { + wwlog.Debug("found shim: %s", shimPath) + return shimPath + } + } + } + } + return "" +} diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index fe7eb87a..f0e59c1b 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -31,6 +31,8 @@ defaultnode: init: /sbin/init root: initramfs ipxe template: default + grub template: grub.cfg.ww + boot method: ipxe profiles: - default network devices: diff --git a/internal/pkg/node/datastructure.go b/internal/pkg/node/datastructure.go index 989c007a..aa49ce64 100644 --- a/internal/pkg/node/datastructure.go +++ b/internal/pkg/node/datastructure.go @@ -20,6 +20,7 @@ type NodeConf struct { ClusterName string `yaml:"cluster name,omitempty" lopt:"cluster" sopt:"c" comment:"Set cluster group"` ContainerName string `yaml:"container name,omitempty" lopt:"container" sopt:"C" comment:"Set container name"` Ipxe string `yaml:"ipxe template,omitempty" lopt:"ipxe" comment:"Set the iPXE template name"` + Grub string `yaml:"grub template,omitempty" lopt:"grub" comment:"Set the grub template name"` // Deprecated start // Kernel settings here are deprecated and here for backward compatibility KernelVersion string `yaml:"kernel version,omitempty"` @@ -52,6 +53,7 @@ type NodeConf struct { PrimaryNetDev string `yaml:"primary network,omitempty" lopt:"primarynet" sopt:"p" comment:"Set the primary network interface"` Disks map[string]*Disk `yaml:"disks,omitempty"` FileSystems map[string]*FileSystem `yaml:"filesystems,omitempty"` + BootMethod string `yaml:"boot method,omitempty" lopt:"bootmethod" comment:" boot method, can be grub or ipxe"` } type IpmiConf struct { @@ -154,6 +156,7 @@ type NodeInfo struct { ClusterName Entry ContainerName Entry Ipxe Entry + Grub Entry RuntimeOverlay Entry SystemOverlay Entry Root Entry @@ -164,6 +167,7 @@ type NodeInfo struct { Ipmi *IpmiEntry Profiles Entry PrimaryNetDev Entry + BootMethod Entry NetDevs map[string]*NetDevEntry Tags map[string]*Entry Disks map[string]*DiskEntry diff --git a/internal/pkg/warewulfd/provision.go b/internal/pkg/warewulfd/provision.go index f2851a26..0cfc2aa7 100644 --- a/internal/pkg/warewulfd/provision.go +++ b/internal/pkg/warewulfd/provision.go @@ -100,7 +100,19 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { 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{ + 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 == "kernel" { if node.Kernel.Override.Defined() { stage_file = kernel.KernelImage(node.Kernel.Override.Get()) @@ -154,6 +166,25 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { wwlog.ErrorExc(err, "") return } + } else if rinfo.stage == "shim" { + if node.ContainerName.Defined() { + stage_file = container.ShimFind(node.ContainerName.Get()) + + if stage_file == "" { + wwlog.Error("No kernel found for container %s", node.ContainerName.Get()) + } + } else { + wwlog.Warn("No container set for this %s", node.Id.Get()) + } + } else if rinfo.stage == "grub" { + if node.ContainerName.Defined() { + stage_file = container.GrubFind(node.ContainerName.Get()) + if stage_file == "" { + wwlog.Error("No grub found for container %s", node.ContainerName.Get()) + } + } else { + wwlog.Warn("No conainer set for node %s", node.Id.Get()) + } } wwlog.Serv("stage_file '%s'", stage_file) diff --git a/overlays/host/etc/dhcp/dhcpd.conf.ww b/overlays/host/etc/dhcp/dhcpd.conf.ww index 4a9637f7..794c31e4 100644 --- a/overlays/host/etc/dhcp/dhcpd.conf.ww +++ b/overlays/host/etc/dhcp/dhcpd.conf.ww @@ -20,11 +20,15 @@ option architecture-type code 93 = unsigned integer 16; if exists user-class and option user-class = "iPXE" { filename "http://{{$.Ipaddr}}:{{$.Warewulf.Port}}/ipxe/${mac:hexhyp}"; } else { +{{- if .BoothMethpd == "ipxe" }} {{range $type,$name := $.Tftp.IpxeBinaries }} if option architecture-type = {{ $type }} { filename "/warewulf/{{ basename $name }}"; } -{{ end }} +{{- end }}{{/* range IpxeBinaries */}} +{{ else }} + filename {{ .DefaultShim }} +{{- end }}{{/* BootMethod */}} } {{if eq .Dhcp.Template "static" -}}