From 4a8384dbf3fe39acfbbdd6ec237e28a63ade382a Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Mon, 6 Mar 2023 15:51:34 +0100 Subject: [PATCH] added Changelog Signed-off-by: Christian Goll --- CHANGELOG.md | 11 +++ Makefile | 13 +-- internal/app/wwctl/root.go | 18 ++-- internal/pkg/warewulfconf/constructors.go | 102 ++++++++++++++-------- 4 files changed, 97 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ab78966..bf73cb33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `/etc/hosts` - Added experimental dnsmasq support. +- new subcommand `wwctl genconf` is available with following subcommands: + * `completions` which will create the files used for bash-completion. Also + fish an zsh completions can be generated + * `defaults` which will generate a valid `defaults.conf` + * `man` which will generate the man pages in the specified directory + * `reference` which will generate a reference documentation for the wwctl commands + * `warwulfconf print` which will print the used `warewulf.conf`. If there is no valid + `warewulf.conf` a valid configuration is provided, prefilled with default values + and an IP configuration derived from the network configuration of the host +- All paths can now be configured in `warewulf.conf`, check the paths section of of + `wwctl --emptyconf genconfig warewulfconf print` for the available paths. ## [4.4.0] 2023-01-18 ### Added diff --git a/Makefile b/Makefile index 10212305..397611dd 100644 --- a/Makefile +++ b/Makefile @@ -218,7 +218,9 @@ init: wwctl: $(WWCTL_DEPS) @echo Building "$@" - @cd cmd/wwctl; GOOS=linux go build -mod vendor -tags "$(WW_GO_BUILD_TAGS)" -o ../../wwctl + @cd cmd/wwctl; GOOS=linux go build -mod vendor -tags "$(WW_GO_BUILD_TAGS)" \ + -ldflags "-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=$(SYSCONFDIR)/warewulf/warewulf.conf'" \ + -o ../../wwctl wwclient: $(WWCLIENT_DEPS) @echo Building "$@" @@ -226,10 +228,11 @@ wwclient: $(WWCLIENT_DEPS) -X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=/etc/warewulf/warewulf.conf'" -o ../../wwclient man_pages: wwctl - install -d man_pages - ./wwctl genconfig man man_pages - cp docs/man/man5/*.5 ./man_pages/ - cd man_pages; for i in wwctl*1 *.5; do echo "Compressing manpage: $$i"; gzip --force $$i; done + @install -d man_pages + @./wwctl --emptyconf genconfig man man_pages + @cp docs/man/man5/*.5 ./man_pages/ + @echo -n "Compressing manpage: " + @cd man_pages; for i in wwctl*1 *.5; do gzip --force $$i; echo -n "$$i "; done; echo update_configuration: vendor cmd/update_configuration/update_configuration.go cd cmd/update_configuration && go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=./etc/warewulf.conf'\ diff --git a/internal/app/wwctl/root.go b/internal/app/wwctl/root.go index a59a9739..395c4b4c 100644 --- a/internal/app/wwctl/root.go +++ b/internal/app/wwctl/root.go @@ -34,6 +34,7 @@ var ( DebugFlag bool LogLevel int WarewulfConfArg string + AllowEmptyConf bool ) func init() { @@ -42,9 +43,10 @@ func init() { rootCmd.PersistentFlags().IntVar(&LogLevel, "loglevel", wwlog.INFO, "Set log level to given string") _ = rootCmd.PersistentFlags().MarkHidden("loglevel") rootCmd.PersistentFlags().StringVar(&WarewulfConfArg, "warewulfconf", "", "Set the warewulf configuration file") + rootCmd.PersistentFlags().BoolVar(&AllowEmptyConf, "emptyconf", false, "Allow empty configuration") + _ = rootCmd.PersistentFlags().MarkHidden("emptyconf") rootCmd.SetUsageTemplate(help.UsageTemplate) rootCmd.SetHelpTemplate(help.HelpTemplate) - rootCmd.AddCommand(overlay.GetCommand()) rootCmd.AddCommand(container.GetCommand()) rootCmd.AddCommand(node.GetCommand()) @@ -75,10 +77,16 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) (err error) { wwlog.SetLogLevel(LogLevel) } conf := warewulfconf.New() - if WarewulfConfArg != "" { - err = conf.ReadConf(WarewulfConfArg) - } else if os.Getenv("WAREWULFCONF") != "" { - err = conf.ReadConf(os.Getenv("WAREWULFCONF")) + if !AllowEmptyConf { + if WarewulfConfArg != "" { + err = conf.ReadConf(WarewulfConfArg) + } else if os.Getenv("WAREWULFCONF") != "" { + err = conf.ReadConf(os.Getenv("WAREWULFCONF")) + } else { + err = conf.ReadConf(warewulfconf.ConfigFile) + } + } else { + conf.SetDynamicDefaults() } return } diff --git a/internal/pkg/warewulfconf/constructors.go b/internal/pkg/warewulfconf/constructors.go index f7cfc325..a85d070b 100644 --- a/internal/pkg/warewulfconf/constructors.go +++ b/internal/pkg/warewulfconf/constructors.go @@ -1,12 +1,12 @@ package warewulfconf import ( - "errors" "fmt" "net" "os" - "github.com/brotherpowers/ipsubnet" + "github.com/pkg/errors" + "github.com/creasty/defaults" "github.com/hpcng/warewulf/internal/pkg/wwlog" "gopkg.in/yaml.v2" @@ -20,32 +20,32 @@ var ConfigFile string Creates a new empty ControllerConf object, returns a cached one if called in a nother context. */ -func New() (ret ControllerConf) { +func New() (conf ControllerConf) { // NOTE: This function can be called before any log level is set // so using wwlog.Verbose or wwlog.Debug won't work if !cachedConf.current { - ret.Warewulf = new(WarewulfConf) - ret.Dhcp = new(DhcpConf) - ret.Tftp = new(TftpConf) - ret.Nfs = new(NfsConf) - ret.Paths = new(BuildConfig) - _ = defaults.Set(&ret) - _ = ret.setDynamicDefaults() + conf.Warewulf = new(WarewulfConf) + conf.Dhcp = new(DhcpConf) + conf.Tftp = new(TftpConf) + conf.Nfs = new(NfsConf) + conf.Paths = new(BuildConfig) + _ = defaults.Set(&conf) - cachedConf = ret + cachedConf = conf cachedConf.current = true } else { // If cached struct isn't empty, use it as the return value - ret = cachedConf + conf = cachedConf } - return ret + return conf } /* Populate the configuration with the values from the configuration file. */ func (conf *ControllerConf) ReadConf(confFileName string) (err error) { + wwlog.Debug("Reading warewulf.conf from: %s", confFileName) fileHandle, err := os.ReadFile(confFileName) if err != nil { return err @@ -67,6 +67,10 @@ func (conf *ControllerConf) Read(data []byte) (err error) { if err != nil { return } + err = conf.SetDynamicDefaults() + if err != nil { + return + } if len(conf.Tftp.IpxeBinaries) == 0 { conf.Tftp.IpxeBinaries = defIpxe } @@ -78,38 +82,60 @@ func (conf *ControllerConf) Read(data []byte) (err error) { /* Set the runtime defaults like IP address of running system to the config */ -func (ret *ControllerConf) setDynamicDefaults() (err error) { - if ret.Ipaddr == "" || ret.Netmask == "" { - conn, error := net.Dial("udp", "8.8.8.8:80") - if error != nil { - return err +func (conf *ControllerConf) SetDynamicDefaults() (err error) { + if conf.Ipaddr == "" || conf.Netmask == "" || conf.Network == "" { + var mask net.IPMask + var network *net.IPNet + var ipaddr net.IP + + if conf.Ipaddr == "" { + wwlog.Verbose("Configuration has no valid network, going to dynamic values") + conn, _ := net.Dial("udp", "8.8.8.8:80") + defer conn.Close() + ipaddr = conn.LocalAddr().(*net.UDPAddr).IP + mask = ipaddr.DefaultMask() + sz, _ := mask.Size() + conf.Ipaddr = ipaddr.String() + fmt.Sprintf("/%d", sz) } - defer conn.Close() - localIp := conn.LocalAddr().(*net.UDPAddr) - if ret.Ipaddr == "" { - ret.Ipaddr = localIp.IP.String() - wwlog.Verbose("IP address is not configured in warewulfd.conf, using %s", ret.Ipaddr) + _, network, err = net.ParseCIDR(conf.Ipaddr) + if err == nil { + mask = network.Mask + } else { + return errors.Wrap(err, "Couldn't parse IP address") } - if ret.Netmask == "" { - mask := localIp.IP.DefaultMask() - ret.Netmask = fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]) - wwlog.Verbose("Netmask address is not configured in warewulfd.conf, using %s", ret.Netmask) + if conf.Netmask == "" { + conf.Netmask = fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]) + wwlog.Verbose("Netmask address is not configured in warewulf.conf, using %s", conf.Netmask) + } + if conf.Network == "" { + conf.Network = network.IP.String() + wwlog.Verbose("Network is not configured in warewulf.conf, using %s", conf.Network) } } + if conf.Dhcp.RangeStart == "" && conf.Dhcp.RangeEnd == "" { + start := net.ParseIP(conf.Network).To4() + start[3] += 1 + if start.Equal(net.ParseIP(conf.Ipaddr)) { + start[3] += 1 + } + conf.Dhcp.RangeStart = start.String() + wwlog.Verbose("dhpd start is not configured in warewulf.conf, using %s", conf.Dhcp.RangeStart) + sz, _ := net.IPMask(net.ParseIP(conf.Netmask).To4()).Size() + range_end := (1 << (32 - sz)) / 8 + if range_end > 127 { + range_end = 127 + } + end := net.ParseIP(conf.Network).To4() + end[3] += byte(range_end) + conf.Dhcp.RangeEnd = end.String() + wwlog.Verbose("dhpd end is not configured in warewulf.conf, using %s", conf.Dhcp.RangeEnd) - if ret.Network == "" { - mask := net.IPMask(net.ParseIP(ret.Netmask).To4()) - size, _ := mask.Size() - - sub := ipsubnet.SubnetCalculator(ret.Ipaddr, size) - - ret.Network = sub.GetNetworkPortion() } // check validity of ipv6 net - if ret.Ipaddr6 != "" { - _, ipv6net, err := net.ParseCIDR(ret.Ipaddr6) + if conf.Ipaddr6 != "" { + _, ipv6net, err := net.ParseCIDR(conf.Ipaddr6) if err != nil { - wwlog.Error("Invalid ipv6 address specified, mut be CIDR notation: %s", ret.Ipaddr6) + wwlog.Error("Invalid ipv6 address specified, mut be CIDR notation: %s", conf.Ipaddr6) return errors.New("invalid ipv6 network") } if msize, _ := ipv6net.Mask.Size(); msize > 64 { @@ -117,5 +143,7 @@ func (ret *ControllerConf) setDynamicDefaults() (err error) { return errors.New("invalid ipv6 network size") } } + cachedConf = *conf + cachedConf.current = true return }