From b9cf0a703d96485bb4fc346c2ef12187b380648a045c27e538939a77b2418c3f Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Wed, 17 Jan 2024 13:10:53 +0000 Subject: [PATCH] Accepting request 1139432 from home:mslacken:pr - added documentation for replacing dhcpd and tftp with dnsmasq as README.dnsmasq (jira#HPC-65) - added following patches: * clean-warewulf-conf.patch * dnsmasq-template-move.patch OBS-URL: https://build.opensuse.org/request/show/1139432 OBS-URL: https://build.opensuse.org/package/show/network:cluster/warewulf4?expand=0&rev=50 --- README.dnsmasq | 52 ++++ clean-warewulf-conf.patch | 238 +++++++++++++++ dnsmasq-template-move.patch | 53 ++++ grub-boot.patch | 582 +++++++++++++----------------------- warewulf4-v4.5.0rc0.tar.gz | 4 +- warewulf4.changes | 9 + warewulf4.spec | 9 +- 7 files changed, 573 insertions(+), 374 deletions(-) create mode 100644 README.dnsmasq create mode 100644 clean-warewulf-conf.patch create mode 100644 dnsmasq-template-move.patch diff --git a/README.dnsmasq b/README.dnsmasq new file mode 100644 index 0000000..ef5f83b --- /dev/null +++ b/README.dnsmasq @@ -0,0 +1,52 @@ +Replace dhcpd and tftp with dnsmasq +=================================== + +The isc `dhcpd` server and `tftp` service can be replaced by `dnsmasq` as +singe service, which has also the benefit that dns entries for the +wwarewulf cluster will then also be provided. + +Installation +----------- + +Before the installation, make sure that `dhcpd` and `tftp` are disabled. +You can do that with the commands: +``` +systemctl disable dhcpd +systemctl stop dhcpd +systemctl disable tftp +systemctl stop tftp +``` + +Now you can install `dnsmasq` with command +``` +zypper install dnsmasq +``` + +After the installation you have to instruct `warewulf` to use `dnsmasq` as +`dhcpd` and `tftp` service, `dnsmasq` has to be specified in the configuration +file `/etc/warewulf/warewulf.conf`. There you have to change the two following +values: +``` +tftp: + systemd name: dnsmasq +dhcp: + systemd name: dnsmasq +``` + +The configuration of `dnsmasq` doesn't need to be changed, as the default configuration +includes all files with following pattern `/etc/dnsmasq.d/*conf` into its configuration. +This configuration is created by the template `overlays/host/etc/dnsmasq.d/ww4-hosts.conf.ww`. +In order to build this template run +``` +wwctl overlay build -H +``` +After that the `dnsmasq` service has to be enabled with either +``` +systemctl enable --now dnsmasq +``` +or by (re)configuring warewulf with +``` +wwctl configure dhcp +wwctl configure tftp +``` + diff --git a/clean-warewulf-conf.patch b/clean-warewulf-conf.patch new file mode 100644 index 0000000..9cc66e4 --- /dev/null +++ b/clean-warewulf-conf.patch @@ -0,0 +1,238 @@ +diff --git a/Makefile b/Makefile +index e85ac05b..466faf19 100644 +--- a/Makefile ++++ b/Makefile +@@ -106,7 +106,8 @@ install: build docs + install -d -m 0755 $(DESTDIR)$(SYSTEMDDIR) + install -d -m 0755 $(DESTDIR)$(IPXESOURCE) + install -d -m 0755 $(DESTDIR)$(DATADIR)/warewulf +- test -f $(DESTDIR)$(WWCONFIGDIR)/warewulf.conf || install -m 0644 etc/warewulf.conf $(DESTDIR)$(WWCONFIGDIR) ++ # wwctl genconfig to get the compiled in paths to warewulf.conf ++ test -f $(DESTDIR)$(WWCONFIGDIR)/warewulf.conf || ./wwctl --warewulfconf etc/warewulf.conf genconfig warewulfconf print> $(DESTDIR)$(WWCONFIGDIR)/warewulf.conf + test -f $(DESTDIR)$(WWCONFIGDIR)/nodes.conf || install -m 0644 etc/nodes.conf $(DESTDIR)$(WWCONFIGDIR) + test -f $(DESTDIR)$(WWCONFIGDIR)/wwapic.conf || install -m 0644 etc/wwapic.conf $(DESTDIR)$(WWCONFIGDIR) + test -f $(DESTDIR)$(WWCONFIGDIR)/wwapid.conf || install -m 0644 etc/wwapid.conf $(DESTDIR)$(WWCONFIGDIR) +diff --git a/internal/pkg/config/buildconfig.go.in b/internal/pkg/config/buildconfig.go.in +index 125f192f..6f761cc1 100644 +--- a/internal/pkg/config/buildconfig.go.in ++++ b/internal/pkg/config/buildconfig.go.in +@@ -5,17 +5,44 @@ var ConfigFile = "@SYSCONFDIR@/warewulf/warewulf.conf" + type BuildConfig struct { + Bindir string `default:"@BINDIR@"` + Sysconfdir string `default:"@SYSCONFDIR@"` +- Datadir string `default:"@DATADIR@"` + Localstatedir string `default:"@LOCALSTATEDIR@"` + Ipxesource string `default:"@IPXESOURCE@"` + Srvdir string `default:"@SRVDIR@"` +- Tftpdir string `default:"@TFTPDIR@"` + Firewallddir string `default:"@FIREWALLDDIR@"` + Systemddir string `default:"@SYSTEMDDIR@"` + WWOverlaydir string `default:"@WWOVERLAYDIR@"` + WWChrootdir string `default:"@WWCHROOTDIR@"` + WWProvisiondir string `default:"@WWPROVISIONDIR@"` +- Version string `default:"@VERSION@"` +- Release string `default:"@RELEASE@"` + WWClientdir string `default:"@WWCLIENTDIR@"` ++ version string `default:"@VERSION@"` ++ release string `default:"@RELEASE@"` ++} ++ ++func (conf BuildConfig) Version() string { ++ return conf.version ++} ++ ++func (conf BuildConfig) Release() string { ++ return conf.release ++} ++ ++type TFTPConf struct { ++ Enabled bool `yaml:"enabled" default:"true"` ++ TftpRoot string `yaml:"tftproot" default:"@TFTPDIR@"` ++ SystemdName string `yaml:"systemd name" default:"tftp"` ++ ++ IpxeBinaries map[string]string `yaml:"ipxe" default:"{\"00:09\": \"ipxe-snponly-x86_64.efi\",\"00:00\": \"undionly.kpxe\",\"00:0B\": \"arm64-efi/snponly.efi\",\"00:07\": \"ipxe-snponly-x86_64.efi\"}"` ++} ++ ++// WarewulfConf adds additional Warewulf-specific configuration to ++// BaseConf. ++type WarewulfConf struct { ++ Port int `yaml:"port" default:"9983"` ++ Secure bool `yaml:"secure" default:"true"` ++ UpdateInterval int `yaml:"update interval" default:"60"` ++ AutobuildOverlays bool `yaml:"autobuild overlays" default:"true"` ++ EnableHostOverlay bool `yaml:"host overlay" default:"true"` ++ Syslog bool `yaml:"syslog" default:"false"` ++ DataStore string `yaml:"datastore" default:"@DATADIR@"` ++ GrubBoot bool `yaml:"grubboot" default:"false"` + } +diff --git a/internal/pkg/config/mounts.go b/internal/pkg/config/mounts.go +index daf38c0a..2eb5060b 100644 +--- a/internal/pkg/config/mounts.go ++++ b/internal/pkg/config/mounts.go +@@ -3,8 +3,8 @@ package config + // A MountEntry represents a bind mount that is applied to a container + // during exec and shell. + type MountEntry struct { +- Source string `yaml:"source" default:"/etc/resolv.conf"` +- Dest string `yaml:"dest,omitempty" default:"/etc/resolv.conf"` +- ReadOnly bool `yaml:"readonly,omitempty" default:"false"` ++ Source string `yaml:"source"` ++ Dest string `yaml:"dest,omitempty"` ++ ReadOnly bool `yaml:"readonly,omitempty"` + Options string `yaml:"options,omitempty"` // ignored at the moment + } +diff --git a/internal/pkg/config/root_test.go b/internal/pkg/config/root_test.go +index f8c9a697..d57d1557 100644 +--- a/internal/pkg/config/root_test.go ++++ b/internal/pkg/config/root_test.go +@@ -43,10 +43,9 @@ func TestDefaultRootConf(t *testing.T) { + + assert.NotEmpty(t, conf.Paths.Bindir) + assert.NotEmpty(t, conf.Paths.Sysconfdir) +- assert.NotEmpty(t, conf.Paths.Datadir) ++ assert.NotEmpty(t, conf.Warewulf.DataStore) + assert.NotEmpty(t, conf.Paths.Localstatedir) + assert.NotEmpty(t, conf.Paths.Srvdir) +- assert.NotEmpty(t, conf.Paths.Tftpdir) + assert.NotEmpty(t, conf.Paths.Firewallddir) + assert.NotEmpty(t, conf.Paths.Systemddir) + assert.NotEmpty(t, conf.Paths.WWOverlaydir) +diff --git a/internal/pkg/config/tftp.go b/internal/pkg/config/tftp.go +deleted file mode 100644 +index cd5260df..00000000 +--- a/internal/pkg/config/tftp.go ++++ /dev/null +@@ -1,11 +0,0 @@ +-package config +- +-// TFTPConf represents that configuration for the TFTP service that +-// Warewulf will configure. +-type TFTPConf struct { +- Enabled bool `yaml:"enabled" default:"true"` +- TftpRoot string `yaml:"tftproot" default:"/var/lib/tftpboot"` +- SystemdName string `yaml:"systemd name" default:"tftp"` +- +- IpxeBinaries map[string]string `yaml:"ipxe" default:"{\"00:09\": \"ipxe-snponly-x86_64.efi\",\"00:00\": \"undionly.kpxe\",\"00:0B\": \"arm64-efi/snponly.efi\",\"00:07\": \"ipxe-snponly-x86_64.efi\"}"` +-} +diff --git a/internal/pkg/config/warewulf.go b/internal/pkg/config/warewulf.go +deleted file mode 100644 +index 9a0dc3f4..00000000 +--- a/internal/pkg/config/warewulf.go ++++ /dev/null +@@ -1,14 +0,0 @@ +-package config +- +-// WarewulfConf adds additional Warewulf-specific configuration to +-// BaseConf. +-type WarewulfConf struct { +- Port int `yaml:"port" default:"9983"` +- Secure bool `yaml:"secure" default:"true"` +- UpdateInterval int `yaml:"update interval" default:"60"` +- AutobuildOverlays bool `yaml:"autobuild overlays" default:"true"` +- EnableHostOverlay bool `yaml:"host overlay" default:"true"` +- Syslog bool `yaml:"syslog" default:"false"` +- DataStore string `yaml:"datastore" default:"/var/lib/warewulf"` +- GrubBoot bool `yaml:"grubboot" default:"false"` +-} +diff --git a/internal/pkg/configure/tftp.go b/internal/pkg/configure/tftp.go +index 058ca059..ab71429d 100644 +--- a/internal/pkg/configure/tftp.go ++++ b/internal/pkg/configure/tftp.go +@@ -12,7 +12,7 @@ import ( + + func TFTP() error { + controller := warewulfconf.Get() +- var tftpdir string = path.Join(controller.Paths.Tftpdir, "warewulf") ++ var tftpdir string = path.Join(controller.TFTP.TftpRoot, "warewulf") + + err := os.MkdirAll(tftpdir, 0755) + if err != nil { +diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go +index 857f93cf..1ddf9274 100644 +--- a/internal/pkg/node/constructors.go ++++ b/internal/pkg/node/constructors.go +@@ -49,7 +49,7 @@ func New() (NodeYaml, error) { + ConfigFile = path.Join(conf.Paths.Sysconfdir, "warewulf/nodes.conf") + } + if DefaultConfig == "" { +- DefaultConfig = path.Join(conf.Paths.Datadir, "warewulf/defaults.conf") ++ DefaultConfig = path.Join(conf.Warewulf.DataStore, "warewulf/defaults.conf") + } + wwlog.Verbose("Opening node configuration file: %s", ConfigFile) + data, err := os.ReadFile(ConfigFile) +diff --git a/internal/pkg/testenv/testenv.go b/internal/pkg/testenv/testenv.go +index 7303d326..6ddbeeca 100644 +--- a/internal/pkg/testenv/testenv.go ++++ b/internal/pkg/testenv/testenv.go +@@ -68,10 +68,10 @@ func New(t *testing.T) (env *TestEnv) { + + conf.Paths.Sysconfdir = env.GetPath(Sysconfdir) + conf.Paths.Bindir = env.GetPath(Bindir) +- conf.Paths.Datadir = env.GetPath(Datadir) ++ conf.Warewulf.DataStore = env.GetPath(Datadir) + conf.Paths.Localstatedir = env.GetPath(Localstatedir) + conf.Paths.Srvdir = env.GetPath(Srvdir) +- conf.Paths.Tftpdir = env.GetPath(Tftpdir) ++ conf.TFTP.TftpRoot = env.GetPath(Tftpdir) + conf.Paths.Firewallddir = env.GetPath(Firewallddir) + conf.Paths.Systemddir = env.GetPath(Systemddir) + conf.Paths.WWOverlaydir = env.GetPath(WWOverlaydir) +@@ -82,10 +82,10 @@ func New(t *testing.T) (env *TestEnv) { + for _, confPath := range []string{ + conf.Paths.Sysconfdir, + conf.Paths.Bindir, +- conf.Paths.Datadir, ++ conf.Warewulf.DataStore, + conf.Paths.Localstatedir, + conf.Paths.Srvdir, +- conf.Paths.Tftpdir, ++ conf.TFTP.TftpRoot, + conf.Paths.Firewallddir, + conf.Paths.Systemddir, + conf.Paths.WWOverlaydir, +diff --git a/internal/pkg/version/version.go b/internal/pkg/version/version.go +index d4b5fb85..6231e4fc 100644 +--- a/internal/pkg/version/version.go ++++ b/internal/pkg/version/version.go +@@ -12,7 +12,7 @@ Return the version of wwctl + */ + func GetVersion() string { + conf := warewulfconf.Get() +- return fmt.Sprintf("%s-%s", conf.Paths.Version, conf.Paths.Release) ++ return fmt.Sprintf("%s-%s", conf.Paths.Version(), conf.Paths.Release()) + } + + /* +diff --git a/internal/pkg/warewulfd/copyshim.go b/internal/pkg/warewulfd/copyshim.go +index 44ea45f4..0b295175 100644 +--- a/internal/pkg/warewulfd/copyshim.go ++++ b/internal/pkg/warewulfd/copyshim.go +@@ -24,22 +24,22 @@ func CopyShimGrub() (err error) { + if shimPath == "" { + return fmt.Errorf("no shim found on the host os") + } +- err = util.CopyFile(shimPath, path.Join(conf.Paths.Tftpdir, "warewulf", "shim.efi")) ++ err = util.CopyFile(shimPath, path.Join(conf.TFTP.TftpRoot, "warewulf", "shim.efi")) + if err != nil { + return err + } +- _ = os.Chmod(path.Join(conf.Paths.Tftpdir, "warewulf", "shim.efi"), 0o755) ++ _ = os.Chmod(path.Join(conf.TFTP.TftpRoot, "warewulf", "shim.efi"), 0o755) + grubPath := container.GrubFind("") + if grubPath == "" { + return fmt.Errorf("no grub found on host os") + } +- err = util.CopyFile(grubPath, path.Join(conf.Paths.Tftpdir, "warewulf", "grub.efi")) ++ err = util.CopyFile(grubPath, path.Join(conf.TFTP.TftpRoot, "warewulf", "grub.efi")) + if err != nil { + return err + } +- _ = os.Chmod(path.Join(conf.Paths.Tftpdir, "warewulf", "grub.efi"), 0o755) +- err = util.CopyFile(grubPath, path.Join(conf.Paths.Tftpdir, "warewulf", "grubx64.efi")) +- _ = os.Chmod(path.Join(conf.Paths.Tftpdir, "warewulf", "grubx64.efi"), 0o755) ++ _ = os.Chmod(path.Join(conf.TFTP.TftpRoot, "warewulf", "grub.efi"), 0o755) ++ err = util.CopyFile(grubPath, path.Join(conf.TFTP.TftpRoot, "warewulf", "grubx64.efi")) ++ _ = os.Chmod(path.Join(conf.TFTP.TftpRoot, "warewulf", "grubx64.efi"), 0o755) + + return + } diff --git a/dnsmasq-template-move.patch b/dnsmasq-template-move.patch new file mode 100644 index 0000000..47502e0 --- /dev/null +++ b/dnsmasq-template-move.patch @@ -0,0 +1,53 @@ +diff --git a/overlays/host/dnsmasq.d/ww4-hosts.conf.ww b/overlays/host/etc/dnsmasq.d/ww4-hosts.conf.ww +similarity index 57% +rename from overlays/host/dnsmasq.d/ww4-hosts.conf.ww +rename to overlays/host/etc/dnsmasq.d/ww4-hosts.conf.ww +index a9d6d66f..951a1596 100644 +--- a/overlays/host/dnsmasq.d/ww4-hosts.conf.ww ++++ b/overlays/host/etc/dnsmasq.d/ww4-hosts.conf.ww +@@ -4,14 +4,30 @@ + # Source {{.BuildSource}} + {{ nobackup }} + # select the x86 hosts which will get the iXPE binary ++dhcp-match=set:bios,option:client-arch,0 #legacy boot + dhcp-match=set:x86PC,option:client-arch, 7 #EFI x86-64 + dhcp-match=set:x86PC,option:client-arch, 6 #EFI x86-64 + dhcp-match=set:x86PC,option:client-arch, 9 #EFI x86-64 +-{{ with (index $.Tftp.IpxeBinaries "00:07" ) }}dhcp-boot=tag:x86PC,"/warewulf/{{ index $.Tftp.IpxeBinaries "00:07" }}"{{ end }} +-dhcp-no-override +-# iPXE binary will get the following configuration file ++dhcp-match=set:aarch64,option:client-arch, 11 #EFI aarch64 ++dhcp-match=set:iPXE,77,"iPXE" + dhcp-userclass=set:iPXE,iPXE ++dhcp-vendorclass=set:efi-http,HTTPClient:Arch:00016 ++dhcp-option-force=tag:efi-http,60,HTTPClient ++# for http boot always use shim/grub ++dhcp-boot=tag:efi-http,"http://{{$.Ipaddr}}:{{$.Warewulf.Port}}/efiboot/shim.efi" ++# iPXE binary will get the following configuration file + dhcp-boot=tag:iPXE,"http://{{$.Ipaddr}}:{{$.Warewulf.Port}}/ipxe/${mac:hexhyp}" ++{{- if $.Warewulf.GrubBoot }} ++dhcp-boot=tag:x86PC,"warewulf/shim.efi" ++{{- else }} ++{{- with (index $.Tftp.IpxeBinaries "00:07" ) }} ++dhcp-boot=tag:x86PC,"/warewulf/{{ index $.Tftp.IpxeBinaries "00:07" }}" ++{{- end }} ++{{- with (index $.Tftp.IpxeBinaries "00:0B" ) }} ++dhcp-boot=tag:aarch64,"/warewulf/{{ index $.Tftp.IpxeBinaries "00:0B" }}" ++{{- end }} ++{{- end }} ++dhcp-no-override + {{- if $.Tftp.Enabled }} + # also act as tftp server + tftp-root={{ $.Tftp.TftpRoot }} +@@ -24,7 +40,7 @@ dhcp-range={{$.Dhcp.RangeStart}},{{$.Dhcp.RangeEnd}},{{$.Netmask}},6h + {{- if $netdev.Ipaddr.Defined}} {{/* if we have an ip address on this network device */}} + {{- if $netdev.Hwaddr.Defined }} + dhcp-host={{$netdev.Hwaddr.Get}},set:warewulf,{{$node.Id.Get}},{{$netdev.Ipaddr.Get}},infinite +-{{- end}} {{/* end if Hwaddr */}} +-{{- end}} {{/* end if ip */}} +-{{- end}} {{/* end for each network device */}} +-{{- end}} {{/* end for each node */}} ++{{- end}}{{/* end if Hwaddr */}} ++{{- end}}{{/* end if ip */}} ++{{- end}}{{/* end for each network device */}} ++{{- end}}{{/* end for each node */}} diff --git a/grub-boot.patch b/grub-boot.patch index 9f5e8da..d601c19 100644 --- a/grub-boot.patch +++ b/grub-boot.patch @@ -1,24 +1,3 @@ -diff --git a/CHANGELOG.md b/CHANGELOG.md -index 70280717..93304050 100644 ---- a/CHANGELOG.md -+++ b/CHANGELOG.md -@@ -124,6 +124,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - Support importing containers with symlinked `/bin/sh` #797 - - Don't panic on malformed passwd #527 - -+- first container imported container is added to the default profile -+- grub in combination can now be set as boot method with `warewulf.grubboot: true` in -+ `warewulf.conf`. For unknown nodes `grub.efi` and `shim.efi` will be extracted from -+ the host running warewulf. If node has container it will get these binaries from the -+ container image. -+ -+- Added support for booting nodes with grub. Enable this behavior using -+ warewulf.grubboot: true in warewulf.conf. For unknown nodes, grub.efi -+ and shim.efi are extracted from the Warewulf host. If the booted node -+ has a container these binaries are extracted from the container image. - ## [4.4.0] 2023-01-18 - - ### Added diff --git a/Makefile b/Makefile index 4ef5a5bf..e85ac05b 100644 --- a/Makefile @@ -45,63 +24,96 @@ index 4ef5a5bf..e85ac05b 100644 (cd overlays && find * -type l -exec cp -av {} $(DESTDIR)$(WWOVERLAYDIR)/{} \;) diff --git a/etc/grub/chainload.ww b/etc/grub/chainload.ww new file mode 100644 -index 00000000..916aff84 +index 00000000..dc3c51d8 --- /dev/null +++ b/etc/grub/chainload.ww -@@ -0,0 +1,18 @@ +@@ -0,0 +1,28 @@ +# This file is autogenerated by warewulf +# Host: {{ .BuildHost }} +# Time: {{ .BuildTime }} +# Source: {{ .BuildSource }} +echo "================================================================================" -+echo "Warewulf v4 now booting with grub" -+echo -+echo "Warewulf Controller: {{.Ipaddr}}" -+echo "Chain loading specific grub.cfg" -+uri="(http,{{.Ipaddr}}:{{.Warewulf.Port}})/efiboot/grub.cfg" -+echo $uri -+configfile $uri -+echo "MESSAGE: This node is unconfigured. Please have your system administrator add a" -+echo " configuration for this node with HW address: ${net_default_mac}" -+echo "" -+echo "Rebooting in 1 minute..." -+sleep 60 -+reboot ++echo "Warewulf v4 now iXPE booting with grub" ++echo "================================================================================" ++set timeout=2 ++# Must chainload in order to get kernel args for specific node ++menuentry "Load specific configfile" { ++ conf="(http,{{.Ipaddr}}:{{.Warewulf.Port}})/efiboot/grub.cfg" ++ configfile $conf ++} ++menuentry "Chainload shim of container" { ++ shim="(http,{{.Ipaddr}}:{{.Warewulf.Port}})/efiboot/shim.efi" ++ chainloader ${shim} ++} ++menuentry "UEFI Firmware Settings" --id "uefi-firmware" { ++ fwsetup ++} ++menuentry "System restart" { ++ echo "System rebooting..." ++ reboot ++} ++menuentry "System shutdown" { ++ echo "System shutting down..." ++ halt ++} diff --git a/etc/grub/grub.cfg.ww b/etc/grub/grub.cfg.ww new file mode 100644 -index 00000000..79357fae +index 00000000..7efbbe0a --- /dev/null +++ b/etc/grub/grub.cfg.ww -@@ -0,0 +1,29 @@ +@@ -0,0 +1,51 @@ +echo "================================================================================" -+echo "Warewulf v4 now booting with grub: {{.Fqdn}} ({{.Hwaddr}})" ++echo "Warewulf v4 now http booting grub: {{.Fqdn}} ({{.Hwaddr}})" +echo "================================================================================" -+uri="(http,{{.Ipaddr}}:9873)/provision/${net_default_mac}?assetkey=" ++echo ++echo "Warewulf Controller: {{.Ipaddr}}" ++echo ++sleep 1 ++smbios --type1 --get-string 8 --set assetkey ++uri="(http,{{.Ipaddr}}:{{.Port}})/provision/${net_default_mac}?assetkey=${assetkey}" +kernel="${uri}&stage=kernel" +container="${uri}&stage=container&compress=gz" +system="${uri}&stage=system&compress=gz" +runtime="${uri}&stage=runtime&compress=gz" -+echo "Warewulf Controller: {{.Ipaddr}}" -+{{if .KernelOverride }} -+echo "Kernel: {{.KernelOverride}}" -+{{else}} -+echo "Kernel: {{.ContainerName}} (container default)" -+{{end}} -+echo "KernelArgs: {{.KernelArgs}}" -+linux $kernel wwid=${net_default_mac} {{.KernelArgs}} -+if [ x$? = x0 ] ; then -+echo "Loading Container: {{.ContainerName}}" -+initrd $container $system $runtime -+echo -+boot -+else -+echo "MESSAGE: This node is unconfigured. Please have your system administrator add a" -+echo " configuration for this node with HW address: ${net_default_mac}" -+echo "" -+echo "Rebooting in 1 minute..." -+sleep 60 -+reboot -+fi ++set default=ww4 ++set timeout=5 ++menuentry "Network boot node: {{.Id}}" --id ww4 { ++ {{if .KernelOverride }} ++ echo "Kernel: {{.KernelOverride}}" ++ {{else}} ++ echo "Kernel: {{.ContainerName}} (container default)" ++ {{end}} ++ echo "KernelArgs: {{.KernelArgs}}" ++ linux $kernel wwid=${net_default_mac} {{.KernelArgs}} ++ if [ x$? = x0 ] ; then ++ echo "Loading Container: {{.ContainerName}}" ++ initrd $container $system $runtime ++ boot ++ else ++ echo "MESSAGE: This node seems to be unconfigured. Please have your system administrator add a" ++ echo " configuration for this node with HW address: ${net_default_mac}" ++ echo "" ++ echo "Rebooting in 1 minute..." ++ sleep 60 ++ reboot ++ fi ++} ++menuentry "Chainload specific configfile" { ++ conf="(http,{{.Ipaddr}}:{{.Port}})/efiboot/grub.cfg" ++ configfile $conf ++} ++menuentry "UEFI Firmware Settings" --id "uefi-firmware" { ++ fwsetup ++} ++menuentry "System restart" { ++ echo "System rebooting..." ++ reboot ++} ++menuentry "System shutdown" { ++ echo "System shutting down..." ++ halt ++} +\ No newline at end of file diff --git a/go.mod b/go.mod index c9e07d29..4d304414 100644 --- a/go.mod @@ -407,7 +419,7 @@ index 00000000..a590690f + return "" +} diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go -index 08c27b19..053d3a63 100644 +index fe7eb87a..857f93cf 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -31,6 +31,7 @@ defaultnode: @@ -418,7 +430,7 @@ index 08c27b19..053d3a63 100644 profiles: - default network devices: -@@ -306,6 +307,36 @@ func (config *NodeYaml) ListAllProfiles() []string { +@@ -305,6 +306,36 @@ func (config *NodeYaml) ListAllProfiles() []string { return ret } @@ -640,20 +652,18 @@ index 1e62dd0f..063d10b4 100644 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 f2851a26..b04a8a91 100644 +index f2851a26..bdca8a34 100644 --- a/internal/pkg/warewulfd/provision.go +++ b/internal/pkg/warewulfd/provision.go -@@ -3,7 +3,9 @@ package warewulfd +@@ -3,6 +3,7 @@ package warewulfd import ( "bytes" "errors" + "fmt" "net/http" -+ "os" "path" "strconv" - "strings" -@@ -17,7 +19,7 @@ import ( +@@ -17,7 +18,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/wwlog" ) @@ -662,7 +672,7 @@ index f2851a26..b04a8a91 100644 Message string WaitTime string Hostname string -@@ -32,13 +34,10 @@ type iPxeTemplate struct { +@@ -32,20 +33,13 @@ type iPxeTemplate struct { KernelOverride string } @@ -673,14 +683,19 @@ index f2851a26..b04a8a91 100644 - "system": "SYSTEM_OVERLAY", - "runtime": "RUNTIME_OVERLAY"} - -+/* -+Handles all the http request for warewulfd, different stages are encoded -+in the GET request. -+*/ 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 + } -@@ -59,8 +58,17 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { +@@ -59,6 +53,14 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { } } @@ -693,13 +708,9 @@ index f2851a26..b04a8a91 100644 + "runtime": "RUNTIME_OVERLAY"} + status_stage := status_stages[rinfo.stage] -- var stage_file string -+ var stage_file string = "" -+ updateSentDB := true + var stage_file string - // TODO: when module version is upgraded to go1.18, should be 'any' type - var tmpl_data interface{} -@@ -83,13 +91,13 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { +@@ -83,13 +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") @@ -715,29 +726,15 @@ index f2851a26..b04a8a91 100644 Id: node.Id.Get(), Cluster: node.ClusterName.Get(), Fqdn: node.Id.Get(), -@@ -100,8 +108,20 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { +@@ -100,7 +102,6 @@ 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 == "kernel" { -+ if DBGetWWinit(node.Id.Get()) { -+ DBReset(node.Id.Get()) -+ } -+ if DBSize(node.Id.Get()) == 0 { -+ fd, err := os.Open(path.Join(path.Join(conf.Paths.Tftpdir, "warewulf", "grub.cfg"))) -+ if err != nil { -+ wwlog.Warn("no grub.cfg detected for potential tftp boot node: %s", node) -+ } else { -+ defer fd.Close() -+ DBAddImage(node.Id.Get(), "grub.cfg", fd) -+ } -+ -+ } if node.Kernel.Override.Defined() { stage_file = kernel.KernelImage(node.Kernel.Override.Get()) - } else if node.ContainerName.Defined() { -@@ -137,7 +157,6 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { +@@ -137,7 +138,6 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { } else { context = rinfo.stage } @@ -745,7 +742,7 @@ index f2851a26..b04a8a91 100644 stage_file, err = getOverlayFile( node, context, -@@ -154,6 +173,64 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { +@@ -154,6 +154,64 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { wwlog.ErrorExc(err, "") return } @@ -810,29 +807,6 @@ index f2851a26..b04a8a91 100644 } wwlog.Serv("stage_file '%s'", stage_file) -@@ -187,11 +264,12 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { - - w.Header().Set("Content-Type", "text") - w.Header().Set("Content-Length", strconv.Itoa(buf.Len())) -+ reader := bytes.NewReader(buf.Bytes()) -+ DBAddImage(node.Id.Get(), stage_file, reader) - _, err = buf.WriteTo(w) - if err != nil { - wwlog.ErrorExc(err, "") - } -- - wwlog.Send("%15s: %s", node.Id.Get(), stage_file) - - } else { -@@ -210,7 +288,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(http.StatusNotFound) - } - -- err = sendFile(w, req, stage_file, node.Id.Get()) -+ err = sendFile(w, req, stage_file, node.Id.Get(), updateSentDB) - if err != nil { - wwlog.ErrorExc(err, "") - return diff --git a/internal/pkg/warewulfd/provision_test.go b/internal/pkg/warewulfd/provision_test.go index 870e1de0..5ec8d72e 100644 --- a/internal/pkg/warewulfd/provision_test.go @@ -965,267 +939,31 @@ index 870e1de0..5ec8d72e 100644 assert.NoError(t, readErr) assert.Equal(t, tt.body, string(data)) assert.Equal(t, tt.status, res.StatusCode) -diff --git a/internal/pkg/warewulfd/sentDB.go b/internal/pkg/warewulfd/sentDB.go -new file mode 100644 -index 00000000..ee64e4c5 ---- /dev/null -+++ b/internal/pkg/warewulfd/sentDB.go -@@ -0,0 +1,144 @@ -+package warewulfd -+ -+import ( -+ "crypto/sha256" -+ "fmt" -+ "io" -+ "path" -+ "sync" -+ -+ "github.com/hpcng/warewulf/internal/pkg/wwlog" -+) -+ -+/* -+store the sent files name and its checksum -+*/ -+type SentFiles struct { -+ Files []File `json:"files:"` -+ sha256sum [32]byte -+ Sha256hex string `json:"sha256"` -+ wwinit bool -+} -+ -+type File struct { -+ FileName string `json:"file name"` -+ sha256sum [32]byte -+ Sha256hex string `json:"sha256"` -+} -+ -+/* -+Database for the checksum of sent files, this -+values can be used for measured boot in combination with -+TPM devicces -+*/ -+var sentDB map[string]*SentFiles -+ -+// mutex for locking the map -+var mu sync.Mutex -+ -+func init() { -+ sentDB = map[string]*SentFiles{} -+} -+ -+/* -+Adds the image with the name to the database -+*/ -+func DBAddImage(node string, fileName string, content io.ReadSeeker) { -+ wwlog.Debug("adding file %s for node %s to sentDB", node, fileName) -+ hasher := sha256.New() -+ sent := File{ -+ FileName: path.Base(fileName), -+ } -+ if _, err := io.Copy(hasher, content); err != nil { -+ wwlog.SecWarn("couldn't create hash of %s for %s", fileName, node) -+ return -+ } -+ copy(sent.sha256sum[:], hasher.Sum(nil)) -+ sent.Sha256hex = fmt.Sprintf("%x", sent.sha256sum) -+ mu.Lock() -+ if _, ok := sentDB[node]; !ok { -+ sentDB[node] = new(SentFiles) -+ } -+ sentDB[node].Files = append(sentDB[node].Files, sent) -+ for i := 0; i < sha256.Size; i++ { -+ sentDB[node].sha256sum[i] = 1 -+ } -+ for _, sntFile := range sentDB[node].Files { -+ sentDB[node].sha256sum = sha256.Sum256(append(sentDB[node].sha256sum[:], sntFile.sha256sum[:]...)) -+ } -+ sentDB[node].Sha256hex = fmt.Sprintf("%x", sentDB[node].sha256sum) -+ mu.Unlock() -+ hasher.Reset() -+} -+ -+/* -+Get the final sum of all the hashed files -+*/ -+func DBGetSum(node string) (ret [sha256.Size]byte) { -+ mu.Lock() -+ defer mu.Unlock() -+ if sentNode, ok := sentDB[node]; ok { -+ ret = sentNode.sha256sum -+ return -+ } -+ ret = [sha256.Size]byte{0} -+ return -+} -+ -+/* -+Reset the database for a single node -+*/ -+func DBReset(node string) { -+ mu.Lock() -+ if _, ok := sentDB[node]; !ok { -+ sentDB[node] = new(SentFiles) -+ } -+ sentDB[node] = new(SentFiles) -+ mu.Unlock() -+} -+ -+/* -+Reset the database -+*/ -+func DBResetAll() { -+ sentDB = make(map[string]*SentFiles) -+} -+ -+/* -+Get the size of the DB -+*/ -+func DBSize(node string) int { -+ mu.Lock() -+ if _, ok := sentDB[node]; !ok { -+ sentDB[node] = new(SentFiles) -+ } -+ size := len(sentDB[node].Files) -+ mu.Unlock() -+ return size -+} -+ -+/* -+Check if wwinit was sent -+*/ -+func DBGetWWinit(node string) bool { -+ mu.Lock() -+ if _, ok := sentDB[node]; !ok { -+ sentDB[node] = new(SentFiles) -+ } -+ ret := sentDB[node].wwinit -+ mu.Unlock() -+ return ret -+} -+ -+/* -+Mark that wwinit was sent -+*/ -+ -+func DBWWinitSent(node string) { -+ mu.Lock() -+ if _, ok := sentDB[node]; !ok { -+ sentDB[node] = new(SentFiles) -+ } -+ sentDB[node].wwinit = true -+ mu.Unlock() -+} -diff --git a/internal/pkg/warewulfd/sentstatus.go b/internal/pkg/warewulfd/sentstatus.go -new file mode 100644 -index 00000000..36350367 ---- /dev/null -+++ b/internal/pkg/warewulfd/sentstatus.go -@@ -0,0 +1,34 @@ -+package warewulfd -+ -+import ( -+ "encoding/json" -+ "net/http" -+ -+ "github.com/hpcng/warewulf/internal/pkg/wwlog" -+ "github.com/pkg/errors" -+) -+ -+func sentStatusJSON() ([]byte, error) { -+ wwlog.Debug("Request for node sent status data...") -+ -+ ret, err := json.MarshalIndent(sentDB, "", " ") -+ if err != nil { -+ return ret, errors.Wrap(err, "could not marshal JSON data from status structure") -+ } -+ -+ return ret, nil -+ -+} -+ -+func SentStatus(w http.ResponseWriter, req *http.Request) { -+ status, err := sentStatusJSON() -+ if err != nil { -+ w.WriteHeader(http.StatusInternalServerError) -+ return -+ } -+ -+ _, err = w.Write(status) -+ if err != nil { -+ wwlog.Warn("Could not send sent status JSON: %s", err) -+ } -+} -diff --git a/internal/pkg/warewulfd/sentsums_test.go b/internal/pkg/warewulfd/sentsums_test.go -new file mode 100644 -index 00000000..41fcfe00 ---- /dev/null -+++ b/internal/pkg/warewulfd/sentsums_test.go -@@ -0,0 +1,23 @@ -+package warewulfd -+ -+import ( -+ "bytes" -+ "testing" -+) -+ -+func Test_SumOne(t *testing.T) { -+ firstText := `Scalable. Flexible. Today, Warewulf unites the ecosystem with the ability to provision containers directly to the bare metal hardware at massive scale, simplistically while retaining massive flexibility.` -+ secondText := `Being open source for over two-decades, and pioneering the concept of stateless node management, Warewulf is among the most successful HPC cluster platforms in the industry with support from OpenHPC, contributors around the world, and usage from every industry.` -+ DBAddImage("n01", "firstText", bytes.NewReader([]byte(firstText))) -+ DBAddImage("n01", "secondText", bytes.NewReader([]byte(secondText))) -+ if sum_n02 := DBGetSum("n02"); sum_n02 != [32]byte{} { -+ t.Errorf("Sum of second entry must be zero") -+ } -+ if sum_n01 := DBGetSum("n01"); sum_n01 == [32]byte{} { -+ t.Errorf("Sum of entry must not be zero") -+ } -+ DBResetAll() -+ if sum_n01 := DBGetSum("n01"); sum_n01 != [32]byte{} { -+ t.Errorf("Sum after reset must be zero") -+ } -+} diff --git a/internal/pkg/warewulfd/util.go b/internal/pkg/warewulfd/util.go -index 3290a678..cefa1cab 100644 +index 3290a678..caaf22ae 100644 --- a/internal/pkg/warewulfd/util.go +++ b/internal/pkg/warewulfd/util.go -@@ -1,8 +1,11 @@ +@@ -1,8 +1,10 @@ package warewulfd import ( + "bufio" -+ "io" "net/http" "os" + "strings" "github.com/hpcng/warewulf/internal/pkg/node" nodepkg "github.com/hpcng/warewulf/internal/pkg/node" -@@ -15,7 +18,7 @@ func sendFile( - w http.ResponseWriter, - req *http.Request, - filename string, -- sendto string) error { -+ sendto string, updateSentDB bool) error { - - fd, err := os.Open(filename) - if err != nil { -@@ -36,7 +39,14 @@ func sendFile( - filename, - stat.ModTime(), +@@ -38,7 +40,7 @@ func sendFile( fd) -- -+ // seek back -+ _, err = fd.Seek(0, io.SeekStart) -+ if err != nil { -+ wwlog.Warn("couldn't seek in file: %s", filename) -+ } -+ if updateSentDB { -+ DBAddImage(sendto, filename, fd) -+ } - wwlog.Send("%15s: %s", sendto, filename) + wwlog.Send("%15s: %s", sendto, filename) +- ++ req.Body.Close() return nil -@@ -70,3 +80,34 @@ func getOverlayFile( + } + +@@ -70,3 +72,34 @@ func getOverlayFile( return } @@ -1261,7 +999,7 @@ index 3290a678..cefa1cab 100644 + return +} diff --git a/internal/pkg/warewulfd/warewulfd.go b/internal/pkg/warewulfd/warewulfd.go -index 796054b0..4c480ebe 100644 +index 796054b0..c9376634 100644 --- a/internal/pkg/warewulfd/warewulfd.go +++ b/internal/pkg/warewulfd/warewulfd.go @@ -5,6 +5,7 @@ import ( @@ -1296,7 +1034,7 @@ index 796054b0..4c480ebe 100644 func RunServer() error { err := DaemonInitLogging() -@@ -49,21 +67,27 @@ func RunServer() error { +@@ -49,21 +67,36 @@ func RunServer() error { wwlog.Error("Could not prepopulate node status DB: %s", err) } @@ -1321,18 +1059,122 @@ index 796054b0..4c480ebe 100644 + wwHandler.HandleFunc("/overlay-system/", ProvisionSend) + wwHandler.HandleFunc("/overlay-runtime/", ProvisionSend) + wwHandler.HandleFunc("/status", StatusSend) -+ wwHandler.HandleFunc("/sentstatus", SentStatus) 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) ++ /* ++ 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) -+ err = http.ListenAndServe(":"+strconv.Itoa(daemonPort), &slashFix{&wwHandler}) if err != nil { return errors.Wrap(err, "Could not start listening service") } +diff --git a/internal/pkg/wwlog/wwlog.go b/internal/pkg/wwlog/wwlog.go +index 98f67f2a..0b8ee5ca 100644 +--- a/internal/pkg/wwlog/wwlog.go ++++ b/internal/pkg/wwlog/wwlog.go +@@ -36,9 +36,11 @@ var ( + ERROR = SetLevelName(40, "ERROR") + SECWARN = SetLevelName(31, "SECWARN") + WARN = SetLevelName(30, "WARN") ++ ERROUT = SetLevelName(29, "ERROUT") + SEND = SetLevelName(27, "SEND") + RECV = SetLevelName(26, "RECV") + SERV = SetLevelName(25, "SERV") ++ OUT = SetLevelName(22, "OUT") + SECINFO = SetLevelName(21, "SECINFO") + INFO = SetLevelName(20, "INFO") + SECVERBOSE = SetLevelName(16, "SECVERBOSE") +@@ -52,6 +54,7 @@ var ( + levelNames = []string{"NOTSET"} + logLevel = INFO + logErr io.Writer = os.Stderr ++ logOut io.Writer = os.Stdout + logFormatter LogFormatter = DefaultFormatter + ) + +@@ -153,11 +156,30 @@ func GetLogLevel() int { + Set the log output writer + By default they are set to output writer + */ +-func SetLogWriter(err io.Writer) { +- logErr = err ++func SetLogWriter(newOut io.Writer) { ++ logErr = newOut ++ logOut = newOut + } + +-func GetLogWriter() io.Writer { ++/* ++Set the log ofr info only ++*/ ++func SetLogWriterInfo(newOut io.Writer) { ++ logOut = newOut ++} ++ ++/* ++Set the log ofr info only ++*/ ++func SetLogWriterErr(newOut io.Writer) { ++ logOut = newOut ++} ++ ++func GetLogWriterInfo() io.Writer { ++ return logOut ++} ++ ++func GetLogWriterErr() io.Writer { + return logErr + } + +@@ -196,8 +218,12 @@ func LogCaller(level int, skip int, err error, message string, a ...interface{}) + } + + message = logFormatter(logLevel, &rec) ++ if level == INFO || level == RECV || level == SEND || level == OUT { ++ fmt.Fprint(logOut, message) ++ } else { ++ fmt.Fprint(logErr, message) + +- fmt.Fprint(logErr, message) ++ } + } + } + +@@ -249,6 +275,10 @@ func Info(message string, a ...interface{}) { + LogCaller(INFO, 1, nil, message, a...) + } + ++func Output(message string, a ...interface{}) { ++ LogCaller(OUT, 1, nil, message, a...) ++} ++ + func InfoExc(err error, message string, a ...interface{}) { + LogCaller(INFO, 1, err, message, a...) + } +@@ -281,6 +311,10 @@ func SecWarn(message string, a ...interface{}) { + LogCaller(SECWARN, 1, nil, message, a...) + } + ++func ErrOut(message string, a ...interface{}) { ++ LogCaller(ERROUT, 1, nil, message, a...) ++} ++ + func Error(message string, a ...interface{}) { + LogCaller(ERROR, 1, nil, message, a...) + } diff --git a/overlays/host/etc/dhcp/dhcpd.conf.ww b/overlays/host/etc/dhcp/dhcpd.conf.ww index 4a9637f7..03fe54cb 100644 --- a/overlays/host/etc/dhcp/dhcpd.conf.ww diff --git a/warewulf4-v4.5.0rc0.tar.gz b/warewulf4-v4.5.0rc0.tar.gz index a4c0e22..46c52db 100644 --- a/warewulf4-v4.5.0rc0.tar.gz +++ b/warewulf4-v4.5.0rc0.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0bb66935b1e4b11e542ef521284389a6e492fc775adfe8a9384fcc8efff76f2 -size 16927353 +oid sha256:23b80008f251cf1abfa23bede22168d503cb7abc0e71b79703de425e4c835d7f +size 16928203 diff --git a/warewulf4.changes b/warewulf4.changes index 7126ddb..384086d 100644 --- a/warewulf4.changes +++ b/warewulf4.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Wed Jan 17 11:16:59 UTC 2024 - Christian Goll + +- added documentation for replacing dhcpd and tftp with dnsmasq + as README.dnsmasq (jira#HPC-65) +- added following patches: + * clean-warewulf-conf.patch + * dnsmasq-template-move.patch + ------------------------------------------------------------------- Thu Dec 21 14:48:08 UTC 2023 - Christian Goll diff --git a/warewulf4.spec b/warewulf4.spec index 565d613..0f15e1b 100644 --- a/warewulf4.spec +++ b/warewulf4.spec @@ -1,7 +1,7 @@ # # spec file for package warewulf4 # -# Copyright (c) 2023 SUSE LLC +# Copyright (c) 2024 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -33,7 +33,10 @@ URL: https://warewulf.org Source0: https://github.com/hpcng/warewulf/releases/download/nightly/warewulf-4.5.x.tar.gz#/warewulf4-v%{version}.tar.gz Source5: warewulf4-rpmlintrc Source10: config-ww4.sh +Source20: README.dnsmasq Patch10: grub-boot.patch +Patch12: clean-warewulf-conf.patch +Patch15: dnsmasq-template-move.patch # no firewalld in sle12 %if 0%{?sle_version} >= 150000 || 0%{?suse_version} > 1500 @@ -163,6 +166,7 @@ mv -v %{buildroot}%{_sysconfdir}/bash_completion.d/wwctl \ #rm -r %{buildroot}%{_datadir}/doc/warewulf # copy the LICESNSE.md via %%doc rm -f %{buildroot}/usr/share/doc/packages/warewulf/LICENSE.md +cp %{S:20} . # use ipxe-bootimgs images from distribution yq e ' @@ -173,7 +177,7 @@ yq e ' .["container mounts"] += {"source": "/etc/SUSEConnect", "dest": "/etc/SUSEConnect", "readonly": true} | .["container mounts"] += {"source": "/etc/zypp/credentials.d/SCCcredentials", "dest": "/etc/zypp/credentials.d/SCCcredentials", "readonly": true}' \ -i %{buildroot}%{_sysconfdir}/warewulf/warewulf.conf -sed -i 's@\(^\s*\)\(.*:.*\):@\1"\2":@' %{buildroot}%{_sysconfdir}/warewulf/warewulf.conf +#sed -i -e 's@\(^\s*\)\(.*:.*\):@\1"\2":@' %{buildroot}%{_sysconfdir}/warewulf/warewulf.conf # fix dhcp for SUSE mv %{buildroot}%{_localstatedir}/lib/warewulf/overlays/host/etc/dhcp/dhcpd.conf.ww %{buildroot}%{_localstatedir}/lib/warewulf/overlays/host/etc/dhcpd.conf.ww rmdir %{buildroot}%{_localstatedir}/lib/warewulf/overlays/host/etc/dhcp @@ -214,6 +218,7 @@ EOF %files %defattr(-,root,root) %doc README.md +%doc README.dnsmasq %license LICENSE.md %{_datadir}/bash-completion/completions/wwctl %attr(0755, root, warewulf) %dir %{_sysconfdir}/warewulf