diff --git a/internal/app/wwctl/configure/dhcp/main.go b/internal/app/wwctl/configure/dhcp/main.go index f9863584..15b61bd4 100644 --- a/internal/app/wwctl/configure/dhcp/main.go +++ b/internal/app/wwctl/configure/dhcp/main.go @@ -1,144 +1,10 @@ package dhcp import ( - "fmt" - "os" - "path" - "strconv" - "strings" - "text/template" - - "github.com/hpcng/warewulf/internal/pkg/buildconfig" - "github.com/hpcng/warewulf/internal/pkg/node" - "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" - "github.com/hpcng/warewulf/internal/pkg/wwlog" - "github.com/pkg/errors" + "github.com/hpcng/warewulf/internal/pkg/configure" "github.com/spf13/cobra" ) -type dhcpTemplate struct { - Ipaddr string - Port string - RangeStart string - RangeEnd string - Network string - Netmask string - Nodes []node.NodeInfo -} - func CobraRunE(cmd *cobra.Command, args []string) error { - return Configure(SetShow) -} - -func Configure(show bool) error { - var d dhcpTemplate - var templateFile string - - nodeDB, err := node.New() - if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err) - os.Exit(1) - } - - controller, err := warewulfconf.New() - if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - os.Exit(1) - } - - if controller.Ipaddr == "" { - wwlog.Printf(wwlog.ERROR, "The Warewulf IP Address is not properly configured\n") - os.Exit(1) - } - - if controller.Netmask == "" { - wwlog.Printf(wwlog.ERROR, "The Warewulf Netmask is not properly configured\n") - os.Exit(1) - } - - if !controller.Dhcp.Enabled { - wwlog.Printf(wwlog.INFO, "This system is not configured as a Warewulf DHCP controller\n") - os.Exit(1) - } - - if controller.Dhcp.RangeStart == "" { - wwlog.Printf(wwlog.ERROR, "Configuration is not defined: `dhcpd range start`\n") - os.Exit(1) - } - - if controller.Dhcp.RangeEnd == "" { - wwlog.Printf(wwlog.ERROR, "Configuration is not defined: `dhcpd range end`\n") - os.Exit(1) - } - - if controller.Dhcp.ConfigFile == "" { - controller.Dhcp.ConfigFile = "/etc/dhcp/dhcpd.conf" - } - - nodes, err := nodeDB.FindAllNodes() - if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not find all controllers: %s\n", err) - os.Exit(1) - } - - d.Nodes = append(d.Nodes, nodes...) - - templateFile = dhcpTemplateFile(controller.Dhcp.Template) - tmpl, err := template.New(path.Base(templateFile)).ParseFiles(templateFile) - if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - os.Exit(1) - } - - d.Ipaddr = controller.Ipaddr - d.Port = strconv.Itoa(controller.Warewulf.Port) - d.Network = controller.Network - d.Netmask = controller.Netmask - d.RangeStart = controller.Dhcp.RangeStart - d.RangeEnd = controller.Dhcp.RangeEnd - - if !show { - fmt.Printf("Writing the DHCP configuration file\n") - configWriter, err := os.OpenFile(controller.Dhcp.ConfigFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0640) - if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - os.Exit(1) - } - defer configWriter.Close() - err = tmpl.Execute(configWriter, d) - if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - os.Exit(1) - } - - fmt.Printf("Enabling and restarting the DHCP services\n") - err = util.SystemdStart(controller.Dhcp.SystemdName) - if err != nil { - return errors.Wrap(err, "failed to start") - } - } else { - err = tmpl.Execute(os.Stdout, d) - if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - os.Exit(1) - } - - } - - return nil -} - -// dhcpTemplateFile returns the path of the warewulf dhcp template given controller.Dhcp.Template. -func dhcpTemplateFile(controllerDhcpTemplate string) (templateFile string) { - if controllerDhcpTemplate == "" { - templateFile = path.Join(buildconfig.SYSCONFDIR(), "warewulf/dhcp/default-dhcpd.conf") - } else { - if strings.HasPrefix(controllerDhcpTemplate, "/") { - templateFile = controllerDhcpTemplate - } else { - templateFile = path.Join(buildconfig.SYSCONFDIR(), "warewulf/dhcp/"+controllerDhcpTemplate+"-dhcpd.conf") - } - } - return + return configure.Configure("DHCP", setShow) } diff --git a/internal/app/wwctl/configure/dhcp/root.go b/internal/app/wwctl/configure/dhcp/root.go index d27f8ad3..a4a32716 100644 --- a/internal/app/wwctl/configure/dhcp/root.go +++ b/internal/app/wwctl/configure/dhcp/root.go @@ -7,17 +7,17 @@ import ( var ( baseCmd = &cobra.Command{ DisableFlagsInUseLine: true, - Use: "dhcp [OPTIONS]", - Short: "Manage and initialize DHCP", + Use: "dhcp [OPTIONS]", + Short: "Manage and initialize DHCP", Long: "DHCP is a dependent service to Warewulf. This command will configure DHCP as defined\n" + "in the warewulf.conf file.", RunE: CobraRunE, } - SetShow bool + setShow bool ) func init() { - baseCmd.PersistentFlags().BoolVarP(&SetShow, "show", "s", false, "Show configuration (don't update)") + baseCmd.PersistentFlags().BoolVarP(&setShow, "show", "s", false, "Show configuration (don't update)") } // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/app/wwctl/configure/hosts/main.go b/internal/app/wwctl/configure/hosts/main.go index 9b33710b..9e620ffd 100644 --- a/internal/app/wwctl/configure/hosts/main.go +++ b/internal/app/wwctl/configure/hosts/main.go @@ -1,117 +1,10 @@ package hosts import ( - "bytes" - "os" - "path" - "text/template" - - "github.com/hpcng/warewulf/internal/pkg/buildconfig" - "github.com/hpcng/warewulf/internal/pkg/node" - "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" - "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/internal/pkg/configure" "github.com/spf13/cobra" ) -type TemplateStruct struct { - PrevHostFile string - Ipaddr string - Fqdn string - AllNodes []node.NodeInfo -} - func CobraRunE(cmd *cobra.Command, args []string) error { - return Configure(SetShow) -} - -func Configure(show bool) error { - var replace TemplateStruct - - if !util.IsFile(path.Join(buildconfig.SYSCONFDIR(), "warewulf/hosts.tmpl")) { - wwlog.Printf(wwlog.WARN, "Template not found, not updating host file\n") - return nil - } - - controller, err := warewulfconf.New() - if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - os.Exit(1) - } - - n, err := node.New() - if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err) - os.Exit(1) - } - - tmpl, err := template.ParseFiles(path.Join(buildconfig.SYSCONFDIR(), "warewulf/hosts.tmpl")) - if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not parse hosts template: %s\n", err) - os.Exit(1) - } - - replace.PrevHostFile = "" - w, err := os.Open("/etc/hosts") - if err != nil { - wwlog.Printf(wwlog.WARN, "%s\n", err) - } else { - // if /etc/hosts.ww does not exist, backup /etc/hosts to /etc/hosts.wwbackup - if !util.IsFile("/etc/hosts.wwbackup") { - err = util.CopyFile("/etc/hosts", "/etc/hosts.wwbackup") - if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - } - } - - // read all lines before the # warewulf comment and put into PrevHostFile template variable - lines, _ := util.ReadFile("/etc/hosts") - if lines != nil { - var buffer bytes.Buffer - for _, line := range lines { - //wwlog.Printf(wwlog.INFO, "Reading line: %s\n", line) - if util.ValidString(line, "^#.*maintained by warewulf") { - break - } - buffer.WriteString(line) - buffer.WriteString("\n") - } - replace.PrevHostFile = buffer.String() - } - } - - //wwlog.Printf(wwlog.INFO, "PrevHostFile is %s\n", replace.PrevHostFile) - - w.Close() - - nodes, _ := n.FindAllNodes() - - replace.AllNodes = nodes - replace.Ipaddr = controller.Ipaddr - replace.Fqdn = controller.Fqdn - - if !SetShow { - // only open "/etc/hosts" when intended to write, as 'os.O_TRUNC' will empty the file otherwise. - w, err = os.OpenFile("/etc/hosts", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) - if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - os.Exit(1) - } - defer w.Close() - - err = tmpl.Execute(w, replace) - if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - os.Exit(1) - } - } else { - err = tmpl.Execute(os.Stdout, replace) - if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - os.Exit(1) - } - - } - - return nil + return configure.Configure("hosts", setShow) } diff --git a/internal/app/wwctl/configure/hosts/root.go b/internal/app/wwctl/configure/hosts/root.go index 84877a40..e0187947 100644 --- a/internal/app/wwctl/configure/hosts/root.go +++ b/internal/app/wwctl/configure/hosts/root.go @@ -4,18 +4,18 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - DisableFlagsInUseLine: true, - Use: "hosts [OPTIONS]", - Short: "Update the /etc/hosts file", + DisableFlagsInUseLine: true, + Use: "hosts [OPTIONS]", + Short: "Update the /etc/hosts file", Long: "Write out the /etc/hosts file based on the Warewulf template (hosts.tmpl) in the\n" + "Warewulf configuration directory.", RunE: CobraRunE, } - SetShow bool + setShow bool ) func init() { - baseCmd.PersistentFlags().BoolVarP(&SetShow, "show", "s", false, "Show configuration (don't update)") + baseCmd.PersistentFlags().BoolVarP(&setShow, "show", "s", false, "Show configuration (don't update)") } // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/app/wwctl/configure/nfs/main.go b/internal/app/wwctl/configure/nfs/main.go index 548ee8f4..fa5c0df5 100644 --- a/internal/app/wwctl/configure/nfs/main.go +++ b/internal/app/wwctl/configure/nfs/main.go @@ -1,75 +1,10 @@ package nfs import ( - "fmt" - "os" - - "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" - "github.com/hpcng/warewulf/internal/pkg/wwlog" - - "github.com/pkg/errors" + "github.com/hpcng/warewulf/internal/pkg/configure" "github.com/spf13/cobra" ) func CobraRunE(cmd *cobra.Command, args []string) error { - return Configure(SetShow) -} - -func Configure(show bool) error { - - controller, err := warewulfconf.New() - if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - os.Exit(1) - } - - if controller.Network == "" { - wwlog.Printf(wwlog.ERROR, "Network must be defined in warewulf.conf to configure NFS\n") - os.Exit(1) - } - if controller.Netmask == "" { - wwlog.Printf(wwlog.ERROR, "Netmask must be defined in warewulf.conf to configure NFS\n") - os.Exit(1) - } - - if !SetShow { - - if controller.Nfs.Enabled { - exports, err := os.OpenFile("/etc/exports", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) - if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - os.Exit(1) - } - defer exports.Close() - - fmt.Fprintf(exports, "# This file was written by Warewulf (wwctl configure nfs)\n") - - for _, export := range controller.Nfs.ExportsExtended { - fmt.Fprintf(exports, "%s %s/%s(%s)\n", export.Path, controller.Network, controller.Netmask, export.ExportOptions) - } - - fmt.Printf("Enabling and restarting the NFS services\n") - if controller.Nfs.SystemdName == "" { - err := util.SystemdStart("nfs-server") - if err != nil { - return errors.Wrap(err, "failed to start nfs-server") - } - } else { - err := util.SystemdStart(controller.Nfs.SystemdName) - if err != nil { - return errors.Wrap(err, "failed to start") - } - } - } - } else { - fmt.Printf("/etc/exports:\n") - - for _, export := range controller.Nfs.ExportsExtended { - fmt.Printf("%s %s/%s\n", export.Path, controller.Network, controller.Netmask) - } - fmt.Printf("\n") - } - - return nil + return configure.Configure("NFS", setShow) } diff --git a/internal/app/wwctl/configure/nfs/root.go b/internal/app/wwctl/configure/nfs/root.go index 4e074021..ba403a49 100644 --- a/internal/app/wwctl/configure/nfs/root.go +++ b/internal/app/wwctl/configure/nfs/root.go @@ -5,17 +5,17 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ DisableFlagsInUseLine: true, - Use: "nfs [OPTIONS]", - Short: "Manage and initialize NFS", + Use: "nfs [OPTIONS]", + Short: "Manage and initialize NFS", Long: "NFS is an optional dependent service of Warewulf, this tool will automatically\n" + "configure NFS as per the configuration in the warewulf.conf file.", RunE: CobraRunE, } - SetShow bool + setShow bool ) func init() { - baseCmd.PersistentFlags().BoolVarP(&SetShow, "show", "s", false, "Show configuration (don't update)") + baseCmd.PersistentFlags().BoolVarP(&setShow, "show", "s", false, "Show configuration (don't update)") } // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/app/wwctl/configure/root.go b/internal/app/wwctl/configure/root.go index 6d8a2c41..8a0950c2 100644 --- a/internal/app/wwctl/configure/root.go +++ b/internal/app/wwctl/configure/root.go @@ -1,7 +1,6 @@ package configure import ( - "fmt" "os" "github.com/hpcng/warewulf/internal/app/wwctl/configure/dhcp" @@ -9,20 +8,20 @@ import ( "github.com/hpcng/warewulf/internal/app/wwctl/configure/nfs" "github.com/hpcng/warewulf/internal/app/wwctl/configure/ssh" "github.com/hpcng/warewulf/internal/app/wwctl/configure/tftp" - "github.com/pkg/errors" + "github.com/hpcng/warewulf/internal/pkg/configure" "github.com/spf13/cobra" ) var ( baseCmd = &cobra.Command{ DisableFlagsInUseLine: true, - Use: "configure [OPTIONS]", - Short: "Manage system services", + Use: "configure [OPTIONS]", + Short: "Manage system services", Long: "This application allows you to manage and initialize Warewulf dependent system\n" + "services based on the configuration in the warewulf.conf file.", RunE: CobraRunE, } - SetDoAll bool + allFunctions bool ) func init() { @@ -32,7 +31,7 @@ func init() { baseCmd.AddCommand(ssh.GetCommand()) baseCmd.AddCommand(nfs.GetCommand()) - baseCmd.PersistentFlags().BoolVarP(&SetDoAll, "all", "a", false, "Configure all services") + baseCmd.PersistentFlags().BoolVarP(&allFunctions, "all", "a", false, "Configure all services") } // GetRootCommand returns the root cobra.Command for the application. @@ -41,44 +40,17 @@ func GetCommand() *cobra.Command { } func CobraRunE(cmd *cobra.Command, args []string) error { - if SetDoAll { - fmt.Printf("################################################################################\n") - fmt.Printf("Configuring: DHCP\n") - err := dhcp.Configure(false) - if err != nil { - return errors.Wrap(err, "failed to configure dhcp") - } + var err error + if allFunctions { + for _, s := range [5]string{"DHPC", "hosts", "NFS", "SSH", "TFTP"} { + err = configure.Configure(s, false) + if err != nil { + os.Exit(1) + } - fmt.Printf("################################################################################\n") - fmt.Printf("Configuring: TFTP\n") - err = tftp.Configure(false) - if err != nil { - return errors.Wrap(err, "failed to configure tftp") - } - - fmt.Printf("################################################################################\n") - fmt.Printf("Configuring: /etc/hosts\n") - err = hosts.Configure(false) - if err != nil { - return errors.Wrap(err, "failed to configure hosts") - } - - fmt.Printf("################################################################################\n") - fmt.Printf("Configuring: NFS\n") - err = nfs.Configure(false) - if err != nil { - return errors.Wrap(err, "failed to configure nfs") - } - - fmt.Printf("################################################################################\n") - fmt.Printf("Configuring: SSH\n") - err = ssh.Configure(false) - if err != nil { - return errors.Wrap(err, "failed to configure ssh") } } else { - //nolint:errcheck - cmd.Help() + _ = cmd.Help() os.Exit(0) } diff --git a/internal/app/wwctl/configure/ssh/main.go b/internal/app/wwctl/configure/ssh/main.go index 91285287..1d7afb1b 100644 --- a/internal/app/wwctl/configure/ssh/main.go +++ b/internal/app/wwctl/configure/ssh/main.go @@ -1,99 +1,10 @@ package ssh import ( - "fmt" - "os" - "path" - - "github.com/hpcng/warewulf/internal/pkg/buildconfig" - "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/wwlog" - "github.com/pkg/errors" + "github.com/hpcng/warewulf/internal/pkg/configure" "github.com/spf13/cobra" ) func CobraRunE(cmd *cobra.Command, args []string) error { - return Configure(SetShow) -} - -func Configure(show bool) error { - if os.Getuid() == 0 { - fmt.Printf("Updating system keys\n") - - wwkeydir := path.Join(buildconfig.SYSCONFDIR(), "warewulf/keys") + "/" - - err := os.MkdirAll(path.Join(buildconfig.SYSCONFDIR(), "warewulf/keys"), 0755) - if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not create base directory: %s\n", err) - os.Exit(1) - } - - if !util.IsFile(wwkeydir + "ssh_host_rsa_key") { - fmt.Printf("Setting up key: ssh_host_rsa_key\n") - err = util.ExecInteractive("ssh-keygen", "-q", "-t", "rsa", "-f", wwkeydir+"ssh_host_rsa_key", "-C", "", "-N", "") - if err != nil { - return errors.Wrap(err, "failed to exec ssh-keygen command") - } - } else { - fmt.Printf("Skipping, key already exists: ssh_host_rsa_key\n") - } - - if !util.IsFile(wwkeydir + "ssh_host_dsa_key") { - fmt.Printf("Setting up key: ssh_host_dsa_key\n") - err = util.ExecInteractive("ssh-keygen", "-q", "-t", "dsa", "-f", wwkeydir+"ssh_host_dsa_key", "-C", "", "-N", "") - if err != nil { - return errors.Wrap(err, "failed to exec ssh-keygen command") - } - } else { - fmt.Printf("Skipping, key already exists: ssh_host_dsa_key\n") - } - - if !util.IsFile(wwkeydir + "ssh_host_ecdsa_key") { - fmt.Printf("Setting up key: ssh_host_ecdsa_key\n") - err = util.ExecInteractive("ssh-keygen", "-q", "-t", "ecdsa", "-f", wwkeydir+"ssh_host_ecdsa_key", "-C", "", "-N", "") - if err != nil { - return errors.Wrap(err, "failed to exec ssh-keygen command") - } - } else { - fmt.Printf("Skipping, key already exists: ssh_host_ecdsa_key\n") - } - - if !util.IsFile(wwkeydir + "ssh_host_ed25519_key") { - fmt.Printf("Setting up key: ssh_host_ed25519_key\n") - err = util.ExecInteractive("ssh-keygen", "-q", "-t", "ed25519", "-f", wwkeydir+"ssh_host_ed25519_key", "-C", "", "-N", "") - if err != nil { - return errors.Wrap(err, "failed to exec ssh-keygen command") - } - } else { - fmt.Printf("Skipping, key already exists: ssh_host_ed25519_key\n") - } - } else { - fmt.Printf("Updating user's keys\n") - } - - homeDir, err := os.UserHomeDir() - if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not obtain the user's home directory: %s\n", err) - os.Exit(1) - } - - authorizedKeys := path.Join(homeDir, "/.ssh/authorized_keys") - rsaPriv := path.Join(homeDir, "/.ssh/id_rsa") - rsaPub := path.Join(homeDir, "/.ssh/id_rsa.pub") - - if !util.IsFile(authorizedKeys) { - fmt.Printf("Setting up: %s\n", authorizedKeys) - err = util.ExecInteractive("ssh-keygen", "-q", "-t", "rsa", "-f", rsaPriv, "-C", "", "-N", "") - if err != nil { - return errors.Wrap(err, "failed to exec ssh-keygen command") - } - err := util.CopyFile(rsaPub, authorizedKeys) - if err != nil { - return errors.Wrap(err, "failed to copy keys") - } - } else { - fmt.Printf("Skipping, authorized_keys already exists: %s\n", authorizedKeys) - } - - return nil + return configure.Configure("SSH", setShow) } diff --git a/internal/app/wwctl/configure/ssh/root.go b/internal/app/wwctl/configure/ssh/root.go index 2246c0db..a3909e64 100644 --- a/internal/app/wwctl/configure/ssh/root.go +++ b/internal/app/wwctl/configure/ssh/root.go @@ -5,18 +5,18 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ DisableFlagsInUseLine: true, - Use: "ssh [OPTIONS]", - Short: "Manage and initialize SSH", + Use: "ssh [OPTIONS]", + Short: "Manage and initialize SSH", Long: "SSH is an optionally dependent service for Warewulf, this tool will automatically\n" + "setup the ssh keys nodes using the 'default' system overlay as well as user owned\n" + "keys.", RunE: CobraRunE, } - SetShow bool + setShow bool ) func init() { - baseCmd.PersistentFlags().BoolVarP(&SetShow, "show", "s", false, "Show configuration (don't update)") + baseCmd.PersistentFlags().BoolVarP(&setShow, "show", "s", false, "Show configuration (don't update)") } // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/app/wwctl/configure/tftp/main.go b/internal/app/wwctl/configure/tftp/main.go index b161bb28..f122fb28 100644 --- a/internal/app/wwctl/configure/tftp/main.go +++ b/internal/app/wwctl/configure/tftp/main.go @@ -1,48 +1,10 @@ package tftp import ( - "fmt" - "os" - "path" - - "github.com/hpcng/warewulf/internal/pkg/buildconfig" - "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" - "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/internal/pkg/configure" "github.com/spf13/cobra" ) func CobraRunE(cmd *cobra.Command, args []string) error { - return Configure(SetShow) -} - -func Configure(show bool) error { - controller, err := warewulfconf.New() - if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - os.Exit(1) - } - - if buildconfig.TFTPDIR() == "" { - wwlog.Printf(wwlog.ERROR, "Tftp root directory is not configured by build\n") - os.Exit(1) - } - - err = os.MkdirAll(path.Join(buildconfig.TFTPDIR(), "warewulf"), 0755) - if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - os.Exit(1) - } - - if !show { - - fmt.Printf("Enabling and restarting the TFTP services\n") - err = util.SystemdStart(controller.Tftp.SystemdName) - if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - os.Exit(1) - } - } - - return nil + return configure.Configure("TFTP", setShow) } diff --git a/internal/app/wwctl/configure/tftp/root.go b/internal/app/wwctl/configure/tftp/root.go index 7629fa2c..36198f10 100644 --- a/internal/app/wwctl/configure/tftp/root.go +++ b/internal/app/wwctl/configure/tftp/root.go @@ -5,17 +5,17 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ DisableFlagsInUseLine: true, - Use: "tftp [OPTIONS]", - Short: "Manage and initialize TFTP", + Use: "tftp [OPTIONS]", + Short: "Manage and initialize TFTP", Long: "TFTP is a dependent service of Warewulf, this tool will enable the tftp services\n" + "on your Warewulf master.", RunE: CobraRunE, } - SetShow bool + setShow bool ) func init() { - baseCmd.PersistentFlags().BoolVarP(&SetShow, "show", "s", false, "Show configuration (don't update)") + baseCmd.PersistentFlags().BoolVarP(&setShow, "show", "s", false, "Show configuration (don't update)") } // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/pkg/configure/common.go b/internal/pkg/configure/common.go new file mode 100644 index 00000000..713e08be --- /dev/null +++ b/internal/pkg/configure/common.go @@ -0,0 +1,32 @@ +package configure + +import ( + "fmt" + + "github.com/pkg/errors" +) + +func Configure(s string, v bool) error { + if !v { + fmt.Printf("################################################################################\n") + fmt.Printf("Configuring: %s\n", s) + } + + var err error + switch s { + case "DHCP": + err = configureDHCP(v) + case "hosts": + err = configureHosts(v) + case "NFS": + err = configureNFS(v) + case "SSH": + err = configureSSH(v) + case "TFTP": + err = configureTFTP(v) + } + if err != nil { + return errors.Wrap(err, "Failed to configure "+s) + } + return nil +} diff --git a/internal/pkg/configure/dhcp.go b/internal/pkg/configure/dhcp.go new file mode 100644 index 00000000..1b7b2511 --- /dev/null +++ b/internal/pkg/configure/dhcp.go @@ -0,0 +1,139 @@ +package configure + +import ( + "fmt" + "os" + "path" + "strconv" + "strings" + "text/template" + + "github.com/hpcng/warewulf/internal/pkg/buildconfig" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/pkg/errors" +) + +type dhcpTemplate struct { + Ipaddr string + Port string + RangeStart string + RangeEnd string + Network string + Netmask string + Nodes []node.NodeInfo +} + +func configureDHCP(show bool) error { + var d dhcpTemplate + var templateFile string + + nodeDB, err := node.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err) + os.Exit(1) + } + + controller, err := warewulfconf.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + + if controller.Ipaddr == "" { + wwlog.Printf(wwlog.ERROR, "The Warewulf IP Address is not properly configured\n") + os.Exit(1) + } + + if controller.Netmask == "" { + wwlog.Printf(wwlog.ERROR, "The Warewulf Netmask is not properly configured\n") + os.Exit(1) + } + + if !controller.Dhcp.Enabled { + wwlog.Printf(wwlog.INFO, "This system is not configured as a Warewulf DHCP controller\n") + os.Exit(1) + } + + if controller.Dhcp.RangeStart == "" { + wwlog.Printf(wwlog.ERROR, "Configuration is not defined: `dhcpd range start`\n") + os.Exit(1) + } + + if controller.Dhcp.RangeEnd == "" { + wwlog.Printf(wwlog.ERROR, "Configuration is not defined: `dhcpd range end`\n") + os.Exit(1) + } + + if controller.Dhcp.ConfigFile == "" { + controller.Dhcp.ConfigFile = "/etc/dhcp/dhcpd.conf" + } + + nodes, err := nodeDB.FindAllNodes() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not find all controllers: %s\n", err) + os.Exit(1) + } + + d.Nodes = append(d.Nodes, nodes...) + + templateFile = dhcpTemplateFile(controller.Dhcp.Template) + tmpl, err := template.New(path.Base(templateFile)).ParseFiles(templateFile) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + + d.Ipaddr = controller.Ipaddr + d.Port = strconv.Itoa(controller.Warewulf.Port) + d.Network = controller.Network + d.Netmask = controller.Netmask + d.RangeStart = controller.Dhcp.RangeStart + d.RangeEnd = controller.Dhcp.RangeEnd + + if !show { + fmt.Printf("Writing the DHCP configuration file\n") + configWriter, err := os.OpenFile(controller.Dhcp.ConfigFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0640) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + defer configWriter.Close() + err = tmpl.Execute(configWriter, d) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + + fmt.Printf("Enabling and restarting the DHCP services\n") + err = util.SystemdStart(controller.Dhcp.SystemdName) + if err != nil { + return errors.Wrap(err, "failed to start") + } + } else { + err = tmpl.Execute(os.Stdout, d) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + + } + + return nil +} + +// dhcpTemplateFile returns the path of the warewulf dhcp template given controller.Dhcp.Template. +func dhcpTemplateFile(controllerDhcpTemplate string) (templateFile string) { + if controllerDhcpTemplate == "" { + templateFile = path.Join(buildconfig.SYSCONFDIR(), "warewulf/dhcp/default-dhcpd.conf") + } else { + if strings.HasPrefix(controllerDhcpTemplate, "/") { + templateFile = controllerDhcpTemplate + } else { + templateFile = path.Join(buildconfig.SYSCONFDIR(), "warewulf/dhcp/"+controllerDhcpTemplate+"-dhcpd.conf") + } + } + return +} diff --git a/internal/app/wwctl/configure/dhcp/dhcp_main_test.go b/internal/pkg/configure/dhcp_main_test.go similarity index 80% rename from internal/app/wwctl/configure/dhcp/dhcp_main_test.go rename to internal/pkg/configure/dhcp_main_test.go index 49dc32e8..bf265141 100644 --- a/internal/app/wwctl/configure/dhcp/dhcp_main_test.go +++ b/internal/pkg/configure/dhcp_main_test.go @@ -1,4 +1,4 @@ -package dhcp +package configure import ( "path" @@ -8,12 +8,12 @@ import ( ) func TestDhcpTemplateFile(t *testing.T) { - tests := []struct{ + tests := []struct { parameter string - expected string + expected string }{ {"", path.Join(buildconfig.SYSCONFDIR(), "warewulf/dhcp/default-dhcpd.conf")}, - {"default", path.Join(buildconfig.SYSCONFDIR(), "warewulf/dhcp/default-dhcpd.conf")}, + {"default", path.Join(buildconfig.SYSCONFDIR(), "warewulf/dhcp/default-dhcpd.conf")}, {"static", path.Join(buildconfig.SYSCONFDIR(), "warewulf/dhcp/static-dhcpd.conf")}, {"/test/absolute/path.conf", "/test/absolute/path.conf"}, } @@ -25,4 +25,3 @@ func TestDhcpTemplateFile(t *testing.T) { } } } - diff --git a/internal/pkg/configure/hosts.go b/internal/pkg/configure/hosts.go new file mode 100644 index 00000000..d0c918fd --- /dev/null +++ b/internal/pkg/configure/hosts.go @@ -0,0 +1,110 @@ +package configure + +import ( + "bytes" + "os" + "path" + "text/template" + + "github.com/hpcng/warewulf/internal/pkg/buildconfig" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + "github.com/hpcng/warewulf/internal/pkg/wwlog" +) + +type hostsTemplate struct { + PrevHostFile string + Ipaddr string + Fqdn string + AllNodes []node.NodeInfo +} + +func configureHosts(show bool) error { + var replace hostsTemplate + + if !util.IsFile(path.Join(buildconfig.SYSCONFDIR(), "warewulf/hosts.tmpl")) { + wwlog.Printf(wwlog.WARN, "Template not found, not updating host file\n") + return nil + } + + controller, err := warewulfconf.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + + n, err := node.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err) + os.Exit(1) + } + + tmpl, err := template.ParseFiles(path.Join(buildconfig.SYSCONFDIR(), "warewulf/hosts.tmpl")) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not parse hosts template: %s\n", err) + os.Exit(1) + } + + replace.PrevHostFile = "" + w, err := os.Open("/etc/hosts") + if err != nil { + wwlog.Printf(wwlog.WARN, "%s\n", err) + } else { + // if /etc/hosts.ww does not exist, backup /etc/hosts to /etc/hosts.wwbackup + if !util.IsFile("/etc/hosts.wwbackup") { + err = util.CopyFile("/etc/hosts", "/etc/hosts.wwbackup") + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + } + } + + // read all lines before the # warewulf comment and put into PrevHostFile template variable + lines, _ := util.ReadFile("/etc/hosts") + if lines != nil { + var buffer bytes.Buffer + for _, line := range lines { + //wwlog.Printf(wwlog.INFO, "Reading line: %s\n", line) + if util.ValidString(line, "^#.*maintained by warewulf") { + break + } + buffer.WriteString(line) + buffer.WriteString("\n") + } + replace.PrevHostFile = buffer.String() + } + } + + w.Close() + + nodes, _ := n.FindAllNodes() + + replace.AllNodes = nodes + replace.Ipaddr = controller.Ipaddr + replace.Fqdn = controller.Fqdn + + if !show { + // only open "/etc/hosts" when intended to write, as 'os.O_TRUNC' will empty the file otherwise. + w, err = os.OpenFile("/etc/hosts", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + defer w.Close() + + err = tmpl.Execute(w, replace) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + } else { + err = tmpl.Execute(os.Stdout, replace) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + + } + + return nil +} diff --git a/internal/pkg/configure/nfs.go b/internal/pkg/configure/nfs.go new file mode 100644 index 00000000..6025f77b --- /dev/null +++ b/internal/pkg/configure/nfs.go @@ -0,0 +1,69 @@ +package configure + +import ( + "fmt" + "os" + + "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/pkg/errors" +) + +func configureNFS(show bool) error { + + controller, err := warewulfconf.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + + if controller.Network == "" { + wwlog.Printf(wwlog.ERROR, "Network must be defined in warewulf.conf to configure NFS\n") + os.Exit(1) + } + if controller.Netmask == "" { + wwlog.Printf(wwlog.ERROR, "Netmask must be defined in warewulf.conf to configure NFS\n") + os.Exit(1) + } + + if !show { + + if controller.Nfs.Enabled { + exports, err := os.OpenFile("/etc/exports", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + defer exports.Close() + + fmt.Fprintf(exports, "# This file was written by Warewulf (wwctl configure nfs)\n") + + for _, export := range controller.Nfs.ExportsExtended { + fmt.Fprintf(exports, "%s %s/%s(%s)\n", export.Path, controller.Network, controller.Netmask, export.ExportOptions) + } + + fmt.Printf("Enabling and restarting the NFS services\n") + if controller.Nfs.SystemdName == "" { + err := util.SystemdStart("nfs-server") + if err != nil { + return errors.Wrap(err, "failed to start nfs-server") + } + } else { + err := util.SystemdStart(controller.Nfs.SystemdName) + if err != nil { + return errors.Wrap(err, "failed to start") + } + } + } + } else { + fmt.Printf("/etc/exports:\n") + + for _, export := range controller.Nfs.ExportsExtended { + fmt.Printf("%s %s/%s\n", export.Path, controller.Network, controller.Netmask) + } + fmt.Printf("\n") + } + + return nil +} diff --git a/internal/pkg/configure/ssh.go b/internal/pkg/configure/ssh.go new file mode 100644 index 00000000..cf699253 --- /dev/null +++ b/internal/pkg/configure/ssh.go @@ -0,0 +1,72 @@ +package configure + +import ( + "fmt" + "os" + "path" + + "github.com/hpcng/warewulf/internal/pkg/buildconfig" + "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/pkg/errors" +) + +func configureSSH(show bool) error { + if !show { + if os.Getuid() == 0 { + fmt.Printf("Updating system keys\n") + + wwkeydir := path.Join(buildconfig.SYSCONFDIR(), "warewulf/keys") + "/" + + err := os.MkdirAll(path.Join(buildconfig.SYSCONFDIR(), "warewulf/keys"), 0755) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not create base directory: %s\n", err) + os.Exit(1) + } + + for _, k := range [4]string{"rsa", "dsa", "ecdsa", "ed25519"} { + keytype := "ssh_host_" + k + "_key" + if !util.IsFile(path.Join(wwkeydir, keytype)) { + fmt.Printf("Setting up key: %s\n", keytype) + wwlog.Printf(wwlog.DEBUG, "Creating new %s key\n", keytype) + err = util.ExecInteractive("ssh-keygen", "-q", "-t", k, "-f", path.Join(wwkeydir, keytype), "-C", "", "-N", "") + if err != nil { + wwlog.Printf(wwlog.ERROR, "Failed to exec ssh-keygen: %s\n", err) + return errors.Wrap(err, "failed to exec ssh-keygen command") + } + } else { + fmt.Printf("Skipping, key already exists: %s\n", keytype) + } + } + } else { + fmt.Printf("Updating user's keys\n") + } + + homeDir, err := os.UserHomeDir() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not obtain the user's home directory: %s\n", err) + os.Exit(1) + } + + authorizedKeys := path.Join(homeDir, "/.ssh/authorized_keys") + rsaPriv := path.Join(homeDir, "/.ssh/id_rsa") + rsaPub := path.Join(homeDir, "/.ssh/id_rsa.pub") + + if !util.IsFile(authorizedKeys) { + fmt.Printf("Setting up: %s\n", authorizedKeys) + err = util.ExecInteractive("ssh-keygen", "-q", "-t", "rsa", "-f", rsaPriv, "-C", "", "-N", "") + if err != nil { + return errors.Wrap(err, "failed to exec ssh-keygen command") + } + err := util.CopyFile(rsaPub, authorizedKeys) + if err != nil { + return errors.Wrap(err, "failed to copy keys") + } + } else { + fmt.Printf("Skipping, authorized_keys already exists: %s\n", authorizedKeys) + } + } else { + fmt.Printf("'ssh -s' is not yet implemented.\n") + } + return nil +} diff --git a/internal/pkg/configure/tftp.go b/internal/pkg/configure/tftp.go new file mode 100644 index 00000000..9cbbfa31 --- /dev/null +++ b/internal/pkg/configure/tftp.go @@ -0,0 +1,51 @@ +package configure + +import ( + "fmt" + "os" + "path" + + "github.com/hpcng/warewulf/internal/pkg/buildconfig" + "github.com/hpcng/warewulf/internal/pkg/staticfiles" + "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + "github.com/hpcng/warewulf/internal/pkg/wwlog" +) + +var tftpdir string = path.Join(buildconfig.TFTPDIR(), "warewulf") + +func configureTFTP(show bool) error { + if !show { + controller, err := warewulfconf.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + return err + } + + err = os.MkdirAll(tftpdir, 0755) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + return err + } + + fmt.Printf("Writing PXE files to: %s\n", tftpdir) + for _, f := range [4]string{"x86.efi", "i386.efi", "i386.kpxe", "arm64.efi"} { + err = staticfiles.WriteData(path.Join("files/tftp", f), path.Join(tftpdir, f)) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + return err + } + } + + fmt.Printf("Enabling and restarting the TFTP services\n") + err = util.SystemdStart(controller.Tftp.SystemdName) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + return err + } + } else { + fmt.Printf("'tftp -s' is not yet implemented.\n") + } + + return nil +}