diff --git a/internal/app/wwctl/group/add/main.go b/internal/app/wwctl/group/add/main.go index d5badf5b..ab092f67 100644 --- a/internal/app/wwctl/group/add/main.go +++ b/internal/app/wwctl/group/add/main.go @@ -14,8 +14,13 @@ func CobraRunE(cmd *cobra.Command, args []string) error { os.Exit(1) } - nodeDB.AddGroup(args[0]) - + for _, g := range args { + err = nodeDB.AddGroup(g) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + } nodeDB.Persist() diff --git a/internal/app/wwctl/group/add/root.go b/internal/app/wwctl/group/add/root.go index 114c5ba5..35070791 100644 --- a/internal/app/wwctl/group/add/root.go +++ b/internal/app/wwctl/group/add/root.go @@ -8,10 +8,10 @@ var ( Short: "Add a new node group", Long: "Add a new node group ", RunE: CobraRunE, + Args: cobra.MinimumNArgs(1), } SetVnfs string SetKernel string - // SetGroupLevel bool ) func init() { diff --git a/internal/app/wwctl/group/list/main.go b/internal/app/wwctl/group/list/main.go index 019d241e..177ff841 100644 --- a/internal/app/wwctl/group/list/main.go +++ b/internal/app/wwctl/group/list/main.go @@ -33,9 +33,9 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } } } else { - fmt.Printf("%-22s %-20s %-30s %-30s %-16s\n", "GROUP NAME", "DOMAINNAME", "KERNEL VERSION", "VNFS IMAGE", "RUNTIME OVERLAY") + fmt.Printf("%-22s %-16s %-30s %-30s %-16s\n", "GROUP NAME", "DOMAINNAME", "KERNEL VERSION", "VNFS IMAGE", "RUNTIME OVERLAY") for _, g := range groups { - fmt.Printf("%-22s %-20s %-30s %-30s %-16s\n", g.GroupName, g.DomainName, g.KernelVersion, g.Vnfs, g.RuntimeOverlay) + fmt.Printf("%-22s %-16s %-30s %-30s %-16s\n", g.GroupName, g.DomainName, g.KernelVersion, g.Vnfs, g.RuntimeOverlay) } } diff --git a/internal/app/wwctl/group/set/main.go b/internal/app/wwctl/group/set/main.go index 7e60806c..7bab0c7a 100644 --- a/internal/app/wwctl/group/set/main.go +++ b/internal/app/wwctl/group/set/main.go @@ -1,8 +1,188 @@ package set -import "github.com/spf13/cobra" +import ( + "fmt" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/manifoldco/promptui" + "github.com/spf13/cobra" + "os" +) func CobraRunE(cmd *cobra.Command, args []string) error { + var err error + var groups []node.GroupInfo + + nodeDB, err := node.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err) + os.Exit(1) + } + + if err != nil { + wwlog.Printf(wwlog.ERROR, "Cloud not get nodeList: %s\n", err) + os.Exit(1) + } + + if len(args) > 0 { + var tmp []node.GroupInfo + tmp, err = nodeDB.FindAllGroups() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + + for _, a := range args { + for _, g := range tmp { + if g.Id == a { + groups = append(groups, g) + } + } + } + + } else { + cmd.Usage() + os.Exit(1) + } + + for _, g := range groups { + if SetVnfs != "" { + wwlog.Printf(wwlog.VERBOSE, "Group: %s, Setting vnfs to: %s\n", g.Id, SetVnfs) + err := nodeDB.SetGroupVal(g.Id, "vnfs", SetVnfs) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + + if SetClearNodes == true { + nodes, err := nodeDB.FindAllNodes() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + for _, n := range nodes { + _ = nodeDB.SetNodeVal(g.Id, n.Id, "vnfs", "") + } + } + } + + if SetKernel != "" { + wwlog.Printf(wwlog.VERBOSE, "Group: %s, Setting kernel to: %s\n", g.Id, SetVnfs) + err := nodeDB.SetGroupVal(g.Id, "kernel", SetKernel) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + + if SetClearNodes == true { + nodes, err := nodeDB.FindAllNodes() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + for _, n := range nodes { + _ = nodeDB.SetNodeVal(g.Id, n.Id, "kernel", "") + } + } + } + + if SetDomainName != "" { + wwlog.Printf(wwlog.VERBOSE, "Group: %s, Setting domain name to: %s\n", g.Id, SetDomainName) + err := nodeDB.SetGroupVal(g.Id, "domain", SetDomainName) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + + if SetClearNodes == true { + nodes, err := nodeDB.FindAllNodes() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + for _, n := range nodes { + _ = nodeDB.SetNodeVal(g.Id, n.Id, "domain", "") + } + } + } + + if SetIpxe != "" { + wwlog.Printf(wwlog.VERBOSE, "Group: %s, Setting iPXE template to: %s\n", g.Id, SetIpxe) + err := nodeDB.SetGroupVal(g.Id, "ipxe", SetIpxe) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + + if SetClearNodes == true { + nodes, err := nodeDB.FindAllNodes() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + for _, n := range nodes { + _ = nodeDB.SetNodeVal(g.Id, n.Id, "ipxe", "") + } + } + } + + if SetRuntimeOverlay != "" { + wwlog.Printf(wwlog.VERBOSE, "Group: %s, Setting runtime overlay to: %s\n", g.Id, SetRuntimeOverlay) + err := nodeDB.SetGroupVal(g.Id, "runtimeoverlay", SetRuntimeOverlay) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + if SetClearNodes == true { + nodes, err := nodeDB.FindAllNodes() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + for _, n := range nodes { + _ = nodeDB.SetNodeVal(g.Id, n.Id, "runtimeoverlay", "") + } + } + } + + if SetSystemOverlay != "" { + wwlog.Printf(wwlog.VERBOSE, "Group: %s, Setting system overlay to: %s\n", g.Id, SetSystemOverlay) + err := nodeDB.SetGroupVal(g.Id, "systemoverlay", SetSystemOverlay) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + if SetClearNodes == true { + nodes, err := nodeDB.FindAllNodes() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + for _, n := range nodes { + _ = nodeDB.SetNodeVal(g.Id, n.Id, "systemoverlay", "") + } + } + } + + } + + if len(groups) > 0 { + q := fmt.Sprintf("Are you sure you want to modify %d group(s)", len(groups)) + + prompt := promptui.Prompt{ + Label: q, + IsConfirm: true, + } + + result, _ := prompt.Run() + + if result == "y" || result == "yes" { + nodeDB.Persist() + } + + } else { + fmt.Printf("No groups found\n") + } return nil } diff --git a/internal/app/wwctl/group/set/root.go b/internal/app/wwctl/group/set/root.go index 47d33f64..5b7c0178 100644 --- a/internal/app/wwctl/group/set/root.go +++ b/internal/app/wwctl/group/set/root.go @@ -11,12 +11,22 @@ var ( } SetVnfs string SetKernel string - // SetGroupLevel bool + SetDomainName string + SetIpxe string + SetRuntimeOverlay string + SetSystemOverlay string + SetHostname string + SetClearNodes bool ) func init() { baseCmd.PersistentFlags().StringVarP(&SetVnfs, "vnfs", "V", "", "Set node Virtual Node File System (VNFS)") baseCmd.PersistentFlags().StringVarP(&SetKernel, "kernel", "K", "", "Set Kernel version for nodes") + baseCmd.PersistentFlags().StringVarP(&SetDomainName, "domain", "D", "", "Set the node's domain name") + baseCmd.PersistentFlags().StringVarP(&SetIpxe, "ipxe", "P", "", "Set the node's iPXE template name") + baseCmd.PersistentFlags().StringVarP(&SetRuntimeOverlay, "runtime", "R", "", "Set the node's runtime overlay") + baseCmd.PersistentFlags().StringVarP(&SetSystemOverlay, "system", "S", "", "Set the node's system overlay") + baseCmd.PersistentFlags().BoolVarP(&SetClearNodes, "clear", "c", false, "Clear node configurations when setting parent group") } // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/app/wwctl/node/add/main.go b/internal/app/wwctl/node/add/main.go index 53ee5b29..e52bf95b 100644 --- a/internal/app/wwctl/node/add/main.go +++ b/internal/app/wwctl/node/add/main.go @@ -14,7 +14,13 @@ func CobraRunE(cmd *cobra.Command, args []string) error { os.Exit(1) } - nodeDB.AddNode(args[0], args[1]) + for _, a := range args { + err = nodeDB.AddNode(SetGroup, a) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + } nodeDB.Persist() diff --git a/internal/app/wwctl/node/add/root.go b/internal/app/wwctl/node/add/root.go index 73ac0b37..b95a8b90 100644 --- a/internal/app/wwctl/node/add/root.go +++ b/internal/app/wwctl/node/add/root.go @@ -8,6 +8,7 @@ var ( Short: "Add new node", Long: "Add new node ", RunE: CobraRunE, + Args: cobra.MinimumNArgs(1), } SetGroup string ) diff --git a/internal/app/wwctl/node/delete/main.go b/internal/app/wwctl/node/delete/main.go index bf84f2af..23b566f4 100644 --- a/internal/app/wwctl/node/delete/main.go +++ b/internal/app/wwctl/node/delete/main.go @@ -48,7 +48,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } } else { - wwlog.Printf(wwlog.INFO, "No nodes found\n") + fmt.Printf("No nodes found\n") } return nil diff --git a/internal/app/wwctl/node/delete/root.go b/internal/app/wwctl/node/delete/root.go index eb520fc0..ded732bf 100644 --- a/internal/app/wwctl/node/delete/root.go +++ b/internal/app/wwctl/node/delete/root.go @@ -15,7 +15,7 @@ var ( func init() { baseCmd.PersistentFlags().StringVarP(&SetForce, "force", "f", "", "Force node delete") - baseCmd.PersistentFlags().StringVarP(&SetGroup, "group", "g", "", "Set group to delete nodes from") + baseCmd.PersistentFlags().StringVarP(&SetGroup, "group", "g", "default", "Set group to delete nodes from") } diff --git a/internal/app/wwctl/node/list/main.go b/internal/app/wwctl/node/list/main.go index 3202e445..95ebec14 100644 --- a/internal/app/wwctl/node/list/main.go +++ b/internal/app/wwctl/node/list/main.go @@ -59,10 +59,10 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } } else { - fmt.Printf("%-22s %-30s %-30s %-16s %-16s\n", "NODE NAME", "KERNEL VERSION", "VNFS IMAGE", "SYSTEM OVERLAY", "RUNTIME OVERLAY") + fmt.Printf("%-22s %-16s %-30s %-30s %-16s\n", "NODE NAME", "GROUP NAME", "KERNEL VERSION", "VNFS IMAGE", "RUNTIME OVERLAY") for _, node := range nodes { - fmt.Printf("%-22s %-30s %-30s %-16s %-16s\n", node.Fqdn, node.KernelVersion, node.Vnfs, node.SystemOverlay, node.RuntimeOverlay) + fmt.Printf("%-22s %-16s %-30s %-30s %-16s\n", node.Fqdn, node.GroupName, node.KernelVersion, node.Vnfs, node.RuntimeOverlay) } } diff --git a/internal/app/wwctl/node/list/root.go b/internal/app/wwctl/node/list/root.go index 8073ffa4..59c1c9cc 100644 --- a/internal/app/wwctl/node/list/root.go +++ b/internal/app/wwctl/node/list/root.go @@ -12,7 +12,6 @@ var ( ShowNet bool ShowIpmi bool ShowAll bool - ) func init() { diff --git a/internal/app/wwctl/node/set/main.go b/internal/app/wwctl/node/set/main.go index 53cd2524..65af4929 100644 --- a/internal/app/wwctl/node/set/main.go +++ b/internal/app/wwctl/node/set/main.go @@ -4,13 +4,13 @@ import ( "fmt" "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/manifoldco/promptui" "github.com/spf13/cobra" "os" ) func CobraRunE(cmd *cobra.Command, args []string) error { var err error - var count int var nodes []node.NodeInfo nodeDB, err := node.New() @@ -26,33 +26,157 @@ func CobraRunE(cmd *cobra.Command, args []string) error { if len(args) > 0 { nodes, err = nodeDB.SearchByNameList(args) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Cloud not get nodeList: %s\n", err) + os.Exit(1) + } + } else { cmd.Usage() os.Exit(1) } - if err != nil { - wwlog.Printf(wwlog.ERROR, "Cloud not get nodeList: %s\n", err) - os.Exit(1) - } + for _, n := range nodes { + wwlog.Printf(wwlog.VERBOSE, "Evaluating node: %s\n", n.Fqdn) if SetVnfs != "" { - fmt.Printf("Setting vnfs to: %s\n", SetVnfs) - c, _ := nodeDB.SetNodeVal(n.Id, "vnfs", SetVnfs) - count += c + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting vnfs to: %s\n", n.Fqdn, SetVnfs) + err := nodeDB.SetNodeVal(n.Gid, n.Id, "vnfs", SetVnfs) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } } if SetKernel != "" { - fmt.Printf("Setting kernel to: %s\n", SetVnfs) - c, _ := nodeDB.SetNodeVal(n.Id, "kernel", SetKernel) - count += c + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting kernel to: %s\n", n.Fqdn, SetVnfs) + err := nodeDB.SetNodeVal(n.Gid, n.Id, "kernel", SetKernel) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + } + if SetDomainName != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting domain name to: %s\n", n.Fqdn, SetDomainName) + err := nodeDB.SetNodeVal(n.Gid, n.Id, "domain", SetDomainName) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + } + if SetIpxe != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting iPXE template to: %s\n", n.Fqdn, SetIpxe) + err := nodeDB.SetNodeVal(n.Gid, n.Id, "ipxe", SetIpxe) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + } + if SetRuntimeOverlay != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting runtime overlay to: %s\n", n.Fqdn, SetRuntimeOverlay) + err := nodeDB.SetNodeVal(n.Gid, n.Id, "runtimeoverlay", SetRuntimeOverlay) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + } + if SetSystemOverlay != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting system overlay to: %s\n", n.Fqdn, SetSystemOverlay) + err := nodeDB.SetNodeVal(n.Gid, n.Id, "systemoverlay", SetSystemOverlay) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + } + if SetHostname != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting hostname to: %s\n", n.Fqdn, SetHostname) + err := nodeDB.SetNodeVal(n.Gid, n.Id, "hostname", SetHostname) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + } + + + + if SetNetDevDel == true { + if SetNetDev == "" { + wwlog.Printf(wwlog.ERROR, "You must include the '--netdev' option\n") + os.Exit(1) + } + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Deleting network device: %s\n", n.Fqdn, SetNetDev) + err := nodeDB.DelNodeNet(n.Gid, n.Id, SetNetDev) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + + } + if SetIpaddr != "" { + if SetNetDev == "" { + wwlog.Printf(wwlog.ERROR, "You must include the '--netdev' option\n") + os.Exit(1) + } + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting Ipaddr to: %s\n", n.Fqdn, SetNetDev, SetIpaddr) + err := nodeDB.SetNodeNet(n.Gid, n.Id, SetNetDev, "ipaddr", SetIpaddr) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + } + if SetNetmask != "" { + if SetNetDev == "" { + wwlog.Printf(wwlog.ERROR, "You must include the '--netdev' option\n") + os.Exit(1) + } + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting netmask to: %s\n", n.Fqdn, SetNetDev, SetNetmask) + err := nodeDB.SetNodeNet(n.Gid, n.Id, SetNetDev, "netmask", SetNetmask) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + } + if SetGateway != "" { + if SetNetDev == "" { + wwlog.Printf(wwlog.ERROR, "You must include the '--netdev' option\n") + os.Exit(1) + } + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting gateway to: %s\n", n.Fqdn, SetNetDev, SetGateway) + err := nodeDB.SetNodeNet(n.Gid, n.Id, SetNetDev, "gateway", SetGateway) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + } + if SetHwaddr != "" { + if SetNetDev == "" { + wwlog.Printf(wwlog.ERROR, "You must include the '--netdev' option\n") + os.Exit(1) + } + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting HW address to: %s\n", n.Fqdn, SetNetDev, SetHwaddr) + err := nodeDB.SetNodeNet(n.Gid, n.Id, SetNetDev, "hwaddr", SetHwaddr) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } } } - fmt.Printf("set count: %d\n", count) + if len(nodes) > 0 { + q := fmt.Sprintf("Are you sure you want to modify %d nodes(s)", len(nodes)) -// nodeDB.AddGroup("moo") - nodeDB.AddNode("moo", "node01") + prompt := promptui.Prompt{ + Label: q, + IsConfirm: true, + } - nodeDB.Persist() + result, _ := prompt.Run() + + if result == "y" || result == "yes" { + nodeDB.Persist() + } + + } else { + fmt.Printf("No nodes found\n") + } return nil } \ No newline at end of file diff --git a/internal/app/wwctl/node/set/root.go b/internal/app/wwctl/node/set/root.go index d2ab3d3d..0e9b0119 100644 --- a/internal/app/wwctl/node/set/root.go +++ b/internal/app/wwctl/node/set/root.go @@ -11,12 +11,35 @@ var ( } SetVnfs string SetKernel string -// SetGroupLevel bool + SetNetDev string + SetIpaddr string + SetNetmask string + SetGateway string + SetHwaddr string + SetNetDevDel bool + SetDomainName string + SetIpxe string + SetRuntimeOverlay string + SetSystemOverlay string + SetHostname string ) func init() { baseCmd.PersistentFlags().StringVarP(&SetVnfs, "vnfs", "V", "", "Set node Virtual Node File System (VNFS)") baseCmd.PersistentFlags().StringVarP(&SetKernel, "kernel", "K", "", "Set Kernel version for nodes") + baseCmd.PersistentFlags().StringVarP(&SetDomainName, "domain", "D", "", "Set the node's domain name") + baseCmd.PersistentFlags().StringVarP(&SetIpxe, "ipxe", "P", "", "Set the node's iPXE template name") + baseCmd.PersistentFlags().StringVarP(&SetRuntimeOverlay, "runtime", "R", "", "Set the node's runtime overlay") + baseCmd.PersistentFlags().StringVarP(&SetSystemOverlay, "system", "S", "", "Set the node's system overlay") + baseCmd.PersistentFlags().StringVarP(&SetHostname, "hostname", "H", "", "Set the node's hostname") + + baseCmd.PersistentFlags().StringVarP(&SetNetDev, "netdev", "n", "", "Define the network device to configure") + baseCmd.PersistentFlags().StringVarP(&SetIpaddr, "ipaddr", "I", "", "Set the node's network device IP address") + baseCmd.PersistentFlags().StringVarP(&SetNetmask, "netmask", "M", "", "Set the node's network device netmask") + baseCmd.PersistentFlags().StringVarP(&SetGateway, "gateway", "G", "", "Set the node's network device gateway") + baseCmd.PersistentFlags().StringVarP(&SetHwaddr, "hwaddr", "N", "", "Set the node's network device HW address") + baseCmd.PersistentFlags().BoolVar(&SetNetDevDel, "delete", false, "Delete the node's network device") + } // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index 5f3f6a35..5c95def5 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -57,10 +57,10 @@ func init() { c.Editor = "vi" } - util.ValidateOrDie("warewulf.conf", "warewulfd ipaddr", c.Ipaddr, "^[0-9]+.[0-9]+.[0-9]+.[0-9]+$") - util.ValidateOrDie("warewulf.conf", "system config dir", c.SysConfDir, "^[a-zA-Z0-9-._:/]+$") - util.ValidateOrDie("warewulf.conf", "local state dir", c.LocalStateDir, "^[a-zA-Z0-9-._:/]+$") - util.ValidateOrDie("warewulf.conf", "default editor", c.LocalStateDir, "^[a-zA-Z0-9-._:/]+$") + util.ValidateOrDie("warewulfd ipaddr", c.Ipaddr, "^[0-9]+.[0-9]+.[0-9]+.[0-9]+$") + util.ValidateOrDie("system config dir", c.SysConfDir, "^[a-zA-Z0-9-._:/]+$") + util.ValidateOrDie("local state dir", c.LocalStateDir, "^[a-zA-Z0-9-._:/]+$") + util.ValidateOrDie("default editor", c.LocalStateDir, "^[a-zA-Z0-9-._:/]+$") } diff --git a/internal/pkg/node/node.go b/internal/pkg/node/node.go index 80e366de..a21700eb 100644 --- a/internal/pkg/node/node.go +++ b/internal/pkg/node/node.go @@ -7,7 +7,9 @@ import ( "github.com/hpcng/warewulf/internal/pkg/wwlog" "gopkg.in/yaml.v2" "io/ioutil" + "os" "regexp" + "strings" ) const ConfigFile = "/etc/warewulf/nodes.conf" @@ -48,7 +50,7 @@ type nodeEntry struct { IpmiIpaddr string `yaml:"ipmi ipaddr,omitempty"` IpmiUserName string `yaml:"ipmi username,omitempty"` IpmiPassword string `yaml:"ipmi password,omitempty"` - NetDevs map[string]netDevs + NetDevs map[string]*netDevs } type netDevs struct { @@ -61,6 +63,7 @@ type netDevs struct { type NodeInfo struct { Id string + Gid string Uid string GroupName string HostName string @@ -75,7 +78,7 @@ type NodeInfo struct { IpmiIpaddr string IpmiUserName string IpmiPassword string - NetDevs map[string]netDevs + NetDevs map[string]*netDevs } type GroupInfo struct { @@ -110,40 +113,75 @@ func New() (nodeYaml, error) { return ret, nil } -func (self *nodeYaml) AddGroup(groupname string) error { +func (self *nodeYaml) AddGroup(groupID string) error { var group nodeGroup - self.NodeGroups[groupname] = &group - self.NodeGroups[groupname].DomainSuffix = groupname - return nil -} - -func (self *nodeYaml) AddNode(groupname string, nodename string) error { - var node nodeEntry - - self.NodeGroups[groupname].Nodes[nodename] = &node - self.NodeGroups[groupname].Nodes[nodename].Hostname = nodename - - return nil -} - -func (self *nodeYaml) DelNode(groupname string, nodename string) error { - - if _, ok := self.NodeGroups[groupname]; ok { - if _, ok := self.NodeGroups[groupname].Nodes[nodename]; ok { - delete(self.NodeGroups[groupname].Nodes, nodename) - wwlog.Printf(wwlog.VERBOSE, "Deleting node: %s/%s\n", groupname, nodename) - } else { - return errors.New("Node '"+nodename+"' was not found in group '"+groupname+"'") - } - } else { - return errors.New("Group '"+groupname+"' was not found") + if _, ok := self.NodeGroups[groupID]; ok { + return errors.New("Group name already exists: " + groupID) } + self.NodeGroups[groupID] = &group + self.NodeGroups[groupID].DomainSuffix = groupID return nil } +func (self *nodeYaml) AddNode(groupID string, nodeID string) error { + var node nodeEntry + + wwlog.Printf(wwlog.VERBOSE, "Adding new node: %s/%s\n", groupID, nodeID) + + if _, ok := self.NodeGroups[groupID]; ok { + if _, ok := self.NodeGroups[groupID].Nodes[nodeID]; ok { + return errors.New("Nodename already exists in group: " + nodeID) + } + } else { + return errors.New("Group does not exist: "+groupID) + } + + self.NodeGroups[groupID].Nodes[nodeID] = &node + self.NodeGroups[groupID].Nodes[nodeID].Hostname = nodeID + + return nil +} + +func (self *nodeYaml) DelNode(groupID string, nodeID string) error { + + if _, ok := self.NodeGroups[groupID]; ok { + if _, ok := self.NodeGroups[groupID].Nodes[nodeID]; ok { + delete(self.NodeGroups[groupID].Nodes, nodeID) + wwlog.Printf(wwlog.VERBOSE, "Deleting node: %s/%s\n", groupID, nodeID) + } else { + return errors.New("Node '"+nodeID+"' was not found in group '"+groupID+"'") + } + } else { + return errors.New("Group '"+groupID+"' was not found") + } + + return nil +} + +func (self *nodeYaml) DelNodeNet(groupID string, nodeID string, netDev string) error { + + if _, ok := self.NodeGroups[groupID]; ok { + if _, ok := self.NodeGroups[groupID].Nodes[nodeID]; ok { + if _, ok := self.NodeGroups[groupID].Nodes[nodeID].NetDevs[netDev]; ok { + wwlog.Printf(wwlog.VERBOSE, "Deleting node network device: %s/%s:%s\n", groupID, nodeID, netDev) + delete(self.NodeGroups[groupID].Nodes[nodeID].NetDevs, netDev) + } else { + return errors.New("Network device '"+netDev+"' was not found in node '"+groupID+"/"+nodeID+"'") + } + } else { + return errors.New("Node '"+nodeID+"' was not found in group '"+groupID+"'") + } + } else { + return errors.New("Group '"+groupID+"' was not found") + } + + return nil +} + + func (self *nodeYaml) DelGroup(groupname string) error { if _, ok := self.NodeGroups[groupname]; ok { delete(self.NodeGroups, groupname) @@ -154,80 +192,122 @@ func (self *nodeYaml) DelGroup(groupname string) error { return nil } -func (self *nodeYaml) SetGroupVal(groupID string, entry string, value string) (int, error) { - var count int - - for gid, group := range self.NodeGroups { - for nid := range group.Nodes { - if groupID == gid { - if entry == "vnfs" { - self.NodeGroups[gid].Vnfs = value - self.NodeGroups[gid].Nodes[nid].Vnfs = "" - count++ - } else if entry == "kernel" { - self.NodeGroups[gid].KernelVersion = value - self.NodeGroups[gid].Nodes[nid].KernelVersion = "" - count++ - } else if entry == "domainsuffix" { - self.NodeGroups[gid].DomainSuffix = value - self.NodeGroups[gid].Nodes[nid].DomainSuffix = "" - count++ - } else if entry == "ipxe" { - self.NodeGroups[gid].Ipxe = value - self.NodeGroups[gid].Nodes[nid].Ipxe = "" - count++ - } else if entry == "systemoverlay" { - self.NodeGroups[gid].SystemOverlay = value - self.NodeGroups[gid].Nodes[nid].SystemOverlay = "" - count++ - } else if entry == "runtimeoverlay" { - self.NodeGroups[gid].RuntimeOverlay = value - self.NodeGroups[gid].Nodes[nid].RuntimeOverlay = "" - count++ - } else if entry == "hostname" { - self.NodeGroups[gid].Nodes[nid].Hostname = value - count++ - } - } - } +func (self *nodeYaml) SetGroupVal(groupID string, entry string, value string) error { + if strings.ToUpper(value) == "UNDEF" || strings.ToUpper(value) == "NIL" || strings.ToUpper(value) == "DEL" { + value = "" } - return count, nil + if _, ok := self.NodeGroups[groupID]; ok { + wwlog.Printf(wwlog.VERBOSE, "Setting group %s to: %s = '%s'\n", groupID, entry, value ) + + switch strings.ToUpper(entry) { + case "VNFS": + util.ValidateOrDie("VNFS", entry, "^[a-zA-Z0-9-._]*$") + self.NodeGroups[groupID].Vnfs = value + case "KERNEL": + util.ValidateOrDie("Kernel Version", entry, "^[a-zA-Z0-9-._]*$") + self.NodeGroups[groupID].KernelVersion = value + case "DOMAINSUFFIX": + util.ValidateOrDie("Domain", entry, "^[a-zA-Z0-9-._]*$") + self.NodeGroups[groupID].DomainSuffix = value + case "IPXE": + util.ValidateOrDie("iPXE Template", entry, "^[a-zA-Z0-9-._]*$") + self.NodeGroups[groupID].Ipxe = value + case "SYSTEMOVERLAY": + util.ValidateOrDie("System Overlay", entry, "^[a-zA-Z0-9-._]*$") + self.NodeGroups[groupID].SystemOverlay = value + case "RUNTIMEOVERLAY": + util.ValidateOrDie("Runtime Overlay", entry, "^[a-zA-Z0-9-._]*$") + self.NodeGroups[groupID].RuntimeOverlay = value + } + } else { + return errors.New("Group does not exist: " +groupID) + } + + return nil } -func (self *nodeYaml) SetNodeVal(nodeID string, entry string, value string) (int, error) { - var count int +func (self *nodeYaml) SetNodeVal(groupID string, nodeID string, entry string, value string) error { + if strings.ToUpper(value) == "UNDEF" || strings.ToUpper(value) == "NIL" || strings.ToUpper(value) == "DEL" { + value = "" + } + if _, ok := self.NodeGroups[groupID]; ok { + if _, ok := self.NodeGroups[groupID].Nodes[nodeID]; ok { + wwlog.Printf(wwlog.VERBOSE, "Setting node %s/%s to: %s = '%s'\n", groupID, nodeID, entry, value ) - for gid, group := range self.NodeGroups { - for nid := range group.Nodes { - if nodeID == gid+":"+nid { - if entry == "vnfs" { - self.NodeGroups[gid].Nodes[nid].Vnfs = value - count++ - } else if entry == "kernel" { - self.NodeGroups[gid].Nodes[nid].KernelVersion = value - count++ - } else if entry == "domainsuffix" { - self.NodeGroups[gid].Nodes[nid].DomainSuffix = value - count++ - } else if entry == "ipxe" { - self.NodeGroups[gid].Nodes[nid].Ipxe = value - count++ - } else if entry == "systemoverlay" { - self.NodeGroups[gid].Nodes[nid].SystemOverlay = value - count++ - } else if entry == "runtimeoverlay" { - self.NodeGroups[gid].Nodes[nid].RuntimeOverlay = value - count++ - } else if entry == "hostname" { - self.NodeGroups[gid].Nodes[nid].Hostname = value - count++ - } + switch strings.ToUpper(entry) { + case "VNFS": + util.ValidateOrDie("VNFS", entry, "^[a-zA-Z0-9-._]*$") + self.NodeGroups[groupID].Nodes[nodeID].Vnfs = value + case "KERNEL": + util.ValidateOrDie("Kernel Version", entry, "^[a-zA-Z0-9-._]*$") + self.NodeGroups[groupID].Nodes[nodeID].KernelVersion = value + case "DOMAINSUFFIX": + util.ValidateOrDie("Domain", entry, "^[a-zA-Z0-9-._]*$") + self.NodeGroups[groupID].Nodes[nodeID].DomainSuffix = value + case "IPXE": + util.ValidateOrDie("iPXE Template", entry, "^[a-zA-Z0-9-._]*$") + self.NodeGroups[groupID].Nodes[nodeID].Ipxe = value + case "SYSTEMOVERLAY": + util.ValidateOrDie("System Overlay", entry, "^[a-zA-Z0-9-._]*$") + self.NodeGroups[groupID].Nodes[nodeID].SystemOverlay = value + case "RUNTIMEOVERLAY": + util.ValidateOrDie("Runtime Overlay", entry, "^[a-zA-Z0-9-._]*$") + self.NodeGroups[groupID].Nodes[nodeID].RuntimeOverlay = value + case "HOSTNAME": + util.ValidateOrDie("Hostname", entry, "^[a-zA-Z0-9-._]*$") + self.NodeGroups[groupID].Nodes[nodeID].Hostname = value } + } else { + return errors.New("Node does not exist: " +groupID+ "/" +nodeID) } + } else { + return errors.New("Group does not exist: " +groupID) } - return count, nil + return nil +} + +func (self *nodeYaml) SetNodeNet(groupID string, nodeID string, netDev string, entry string, value string) error { + if strings.ToUpper(value) == "UNDEF" || strings.ToUpper(value) == "NIL" || strings.ToUpper(value) == "DEL" { + value = "" + } + + if _, ok := self.NodeGroups[groupID]; ok { + if _, ok := self.NodeGroups[groupID].Nodes[nodeID]; ok { + if _, ok := self.NodeGroups[groupID].Nodes[nodeID].NetDevs[netDev]; ok { + wwlog.Printf(wwlog.VERBOSE, "Editing existing node NetDev entry for node: %s/%s\n", groupID, nodeID) + } else { + var nd netDevs + self.NodeGroups[groupID].Nodes[nodeID].NetDevs[netDev] = &nd + } + } else { + return errors.New("Node does not exist: "+groupID+"/"+nodeID) + } + } else { + return errors.New("Group does not exist: "+groupID) + } + + switch strings.ToUpper(entry) { + case "IPADDR": + util.ValidateOrDie("IP address", value, "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$") + self.NodeGroups[groupID].Nodes[nodeID].NetDevs[netDev].Ipaddr = value + case "NETMASK": + util.ValidateOrDie("Netmask", value, "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$") + self.NodeGroups[groupID].Nodes[nodeID].NetDevs[netDev].Netmask = value + case "GATEWAY": + util.ValidateOrDie("Gateway", value, "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$") + self.NodeGroups[groupID].Nodes[nodeID].NetDevs[netDev].Gateway = value + case "TYPE": + util.ValidateOrDie("Network device type", value, "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$") + self.NodeGroups[groupID].Nodes[nodeID].NetDevs[netDev].Type = value + case "HWADDR": + util.ValidateOrDie("HW address", value, "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$") + self.NodeGroups[groupID].Nodes[nodeID].NetDevs[netDev].Hwaddr = value + + } + + return nil } func (self *nodeYaml) Persist() error { @@ -237,7 +317,18 @@ func (self *nodeYaml) Persist() error { return err } - fmt.Println(string(out)) + file, err := os.OpenFile(ConfigFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + + defer file.Close() + + _, err = file.WriteString(string(out)) + if err != nil { + return err + } return nil } @@ -277,7 +368,8 @@ func (self *nodeYaml) FindAllNodes() ([]NodeInfo, error) { var n NodeInfo n.Id = nodename - n.Uid = groupname + ":" + nodename + n.Gid = groupname +// n.Uid = groupname + ":" + nodename n.GroupName = groupname n.HostName = node.Hostname n.IpmiIpaddr = node.IpmiIpaddr @@ -338,13 +430,13 @@ func (self *nodeYaml) FindAllNodes() ([]NodeInfo, error) { n.Fqdn = node.Hostname } - util.ValidateOrDie(n.Fqdn, "group name", n.GroupName, "^[a-zA-Z0-9-._]+$") - util.ValidateOrDie(n.Fqdn, "vnfs", n.Vnfs, "^[a-zA-Z0-9-._:/]+$") - util.ValidateOrDie(n.Fqdn, "system overlay", n.SystemOverlay, "^[a-zA-Z0-9-._]+$") - util.ValidateOrDie(n.Fqdn, "runtime overlay", n.RuntimeOverlay, "^[a-zA-Z0-9-._]+$") - util.ValidateOrDie(n.Fqdn, "domain suffix", n.DomainName, "^[a-zA-Z0-9-._]+$") - util.ValidateOrDie(n.Fqdn, "hostname", n.HostName, "^[a-zA-Z0-9-_]+$") - util.ValidateOrDie(n.Fqdn, "kernel version", n.KernelVersion, "^[a-zA-Z0-9-._]+$") + util.ValidateOrDie(n.Fqdn +":group name", n.GroupName, "^[a-zA-Z0-9-._]*$") + util.ValidateOrDie(n.Fqdn +":vnfs", n.Vnfs, "^[a-zA-Z0-9-._:/]*$") + util.ValidateOrDie(n.Fqdn +":system overlay", n.SystemOverlay, "^[a-zA-Z0-9-._]*$") + util.ValidateOrDie(n.Fqdn +":runtime overlay", n.RuntimeOverlay, "^[a-zA-Z0-9-._]*$") + util.ValidateOrDie(n.Fqdn +":domain suffix", n.DomainName, "^[a-zA-Z0-9-._]*$") + util.ValidateOrDie(n.Fqdn +":hostname", n.HostName, "^[a-zA-Z0-9-_]*$") + util.ValidateOrDie(n.Fqdn +":kernel version", n.KernelVersion, "^[a-zA-Z0-9-._]*$") ret = append(ret, n) } diff --git a/internal/pkg/overlay/runtime.go b/internal/pkg/overlay/runtime.go index 00774a7d..e95cdbdd 100644 --- a/internal/pkg/overlay/runtime.go +++ b/internal/pkg/overlay/runtime.go @@ -75,7 +75,7 @@ func RuntimeBuild(nodeList []node.NodeInfo, force bool) error { } if util.IsDir(OverlayDir) == false { - wwlog.Printf(wwlog.WARN, "%-35s: Skipped (unknown runtime overlay)\n", node.Fqdn) + wwlog.Printf(wwlog.WARN, "%-35s: Skipped (runtime overlay template not found)\n", node.Fqdn) continue } diff --git a/internal/pkg/overlay/system.go b/internal/pkg/overlay/system.go index c570e5d9..89453e62 100644 --- a/internal/pkg/overlay/system.go +++ b/internal/pkg/overlay/system.go @@ -75,7 +75,7 @@ func SystemBuild(nodeList []node.NodeInfo, force bool) error { } if util.IsDir(OverlayDir) == false { - wwlog.Printf(wwlog.WARN, "%-35s: Skipped (unknown system overlay)\n", node.Fqdn) + wwlog.Printf(wwlog.WARN, "%-35s: Skipped (system overlay template not found)\n", node.Fqdn) continue } diff --git a/internal/pkg/util/util.go b/internal/pkg/util/util.go index 75854737..e7889073 100644 --- a/internal/pkg/util/util.go +++ b/internal/pkg/util/util.go @@ -111,9 +111,9 @@ func TaintCheck(pattern string, expr string) bool { return false } -func ValidateOrDie(hostname string, name string, pattern string, expr string) { +func ValidateOrDie(message string, pattern string, expr string) { if TaintCheck(pattern, expr) == false { - wwlog.Printf(wwlog.ERROR, "Entry '%s:%s' contains illegal characters: '%s'\n", hostname, name, pattern) + wwlog.Printf(wwlog.ERROR, "%s does not validate: '%s'\n", message, pattern) os.Exit(1) } }