diff --git a/go.mod b/go.mod index 55abfc57..52406a25 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.15 require ( github.com/brotherpowers/ipsubnet v0.0.0-20170914094241-30bc98f0a5b1 github.com/containers/image/v5 v5.7.0 + github.com/fsnotify/fsnotify v1.4.7 // indirect github.com/manifoldco/promptui v0.8.0 github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 github.com/opencontainers/umoci v0.4.6 diff --git a/go.sum b/go.sum index d9a970e7..8d8980a5 100644 --- a/go.sum +++ b/go.sum @@ -110,6 +110,7 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= diff --git a/internal/app/wwctl/container/imprt/main.go b/internal/app/wwctl/container/imprt/main.go index 309b6824..61fa55bf 100644 --- a/internal/app/wwctl/container/imprt/main.go +++ b/internal/app/wwctl/container/imprt/main.go @@ -5,6 +5,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/container" "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/warewulfd" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/spf13/cobra" "os" @@ -79,6 +80,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } nodeDB.Persist() fmt.Printf("Set default profile to container: %s\n", name) + warewulfd.DaemonReload() } diff --git a/internal/app/wwctl/kernel/imprt/main.go b/internal/app/wwctl/kernel/imprt/main.go index b48ce76b..fbc99997 100644 --- a/internal/app/wwctl/kernel/imprt/main.go +++ b/internal/app/wwctl/kernel/imprt/main.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/hpcng/warewulf/internal/pkg/kernel" "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/warewulfd" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/spf13/cobra" "os" @@ -43,6 +44,8 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } nodeDB.Persist() fmt.Printf("Set default kernel version to: %s\n", args[0]) + + warewulfd.DaemonReload() } } diff --git a/internal/app/wwctl/node/set/main.go b/internal/app/wwctl/node/set/main.go index 2878cbc2..d80f8b79 100644 --- a/internal/app/wwctl/node/set/main.go +++ b/internal/app/wwctl/node/set/main.go @@ -5,6 +5,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/container" "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/warewulfd" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/manifoldco/promptui" "github.com/spf13/cobra" @@ -274,6 +275,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { if result == "y" || result == "yes" { nodeDB.Persist() + warewulfd.DaemonReload() } } diff --git a/internal/app/wwctl/profile/set/main.go b/internal/app/wwctl/profile/set/main.go index 74ec6adc..9f77c9c8 100644 --- a/internal/app/wwctl/profile/set/main.go +++ b/internal/app/wwctl/profile/set/main.go @@ -5,6 +5,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/container" "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/warewulfd" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/manifoldco/promptui" "github.com/spf13/cobra" @@ -251,6 +252,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { if result == "y" || result == "yes" { nodeDB.Persist() + warewulfd.DaemonReload() } } else { diff --git a/internal/app/wwctl/server/reload/main.go b/internal/app/wwctl/server/reload/main.go new file mode 100644 index 00000000..a81f1a85 --- /dev/null +++ b/internal/app/wwctl/server/reload/main.go @@ -0,0 +1,10 @@ +package reload + +import ( + "github.com/hpcng/warewulf/internal/pkg/warewulfd" + "github.com/spf13/cobra" +) + +func CobraRunE(cmd *cobra.Command, args []string) error { + return warewulfd.DaemonReload() +} diff --git a/internal/app/wwctl/server/reload/root.go b/internal/app/wwctl/server/reload/root.go new file mode 100644 index 00000000..b503b746 --- /dev/null +++ b/internal/app/wwctl/server/reload/root.go @@ -0,0 +1,19 @@ +package reload + +import "github.com/spf13/cobra" + +var ( + baseCmd = &cobra.Command{ + Use: "reload", + Short: "Reload the Warewulf server configuration", + RunE: CobraRunE, + } +) + +func init() { +} + +// GetRootCommand returns the root cobra.Command for the application. +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/app/wwctl/server/restart/main.go b/internal/app/wwctl/server/restart/main.go new file mode 100644 index 00000000..264b0794 --- /dev/null +++ b/internal/app/wwctl/server/restart/main.go @@ -0,0 +1,11 @@ +package restart + +import ( + "github.com/hpcng/warewulf/internal/pkg/warewulfd" + "github.com/spf13/cobra" +) + +func CobraRunE(cmd *cobra.Command, args []string) error { + warewulfd.DaemonStop() + return warewulfd.DaemonStart() +} diff --git a/internal/app/wwctl/server/restart/root.go b/internal/app/wwctl/server/restart/root.go new file mode 100644 index 00000000..c1538dcd --- /dev/null +++ b/internal/app/wwctl/server/restart/root.go @@ -0,0 +1,19 @@ +package restart + +import "github.com/spf13/cobra" + +var ( + baseCmd = &cobra.Command{ + Use: "restart", + Short: "Restart the Warewulf server", + RunE: CobraRunE, + } +) + +func init() { +} + +// GetRootCommand returns the root cobra.Command for the application. +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/app/wwctl/server/root.go b/internal/app/wwctl/server/root.go index 0ab1e49a..cf1f3f5d 100644 --- a/internal/app/wwctl/server/root.go +++ b/internal/app/wwctl/server/root.go @@ -1,6 +1,8 @@ package server import ( + "github.com/hpcng/warewulf/internal/app/wwctl/server/reload" + "github.com/hpcng/warewulf/internal/app/wwctl/server/restart" "github.com/hpcng/warewulf/internal/app/wwctl/server/start" "github.com/hpcng/warewulf/internal/app/wwctl/server/status" "github.com/hpcng/warewulf/internal/app/wwctl/server/stop" @@ -20,6 +22,9 @@ func init() { baseCmd.AddCommand(start.GetCommand()) baseCmd.AddCommand(status.GetCommand()) baseCmd.AddCommand(stop.GetCommand()) + baseCmd.AddCommand(restart.GetCommand()) + baseCmd.AddCommand(reload.GetCommand()) + } // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/pkg/warewulfconf/constructors.go b/internal/pkg/warewulfconf/constructors.go index 9d83271f..c38fe30b 100644 --- a/internal/pkg/warewulfconf/constructors.go +++ b/internal/pkg/warewulfconf/constructors.go @@ -15,7 +15,7 @@ func New() (ControllerConf, error) { wwlog.Printf(wwlog.DEBUG, "Opening Warewulf configuration file: %s\n", ConfigFile) data, err := ioutil.ReadFile(ConfigFile) if err != nil { - fmt.Printf("error reading node configuration file\n") + fmt.Printf("error reading Warewulf configuration file\n") return ret, err } diff --git a/internal/pkg/warewulfd/daemon.go b/internal/pkg/warewulfd/daemon.go index f52ffe9f..fbd19c8b 100644 --- a/internal/pkg/warewulfd/daemon.go +++ b/internal/pkg/warewulfd/daemon.go @@ -83,6 +83,34 @@ func DaemonStatus() error { return nil } +func DaemonReload() error { + if util.IsFile(WAREWULFD_PIDFILE) == false { + wwlog.Printf(wwlog.INFO, "Warewulf daemon process not running (%s)\n", WAREWULFD_PIDFILE) + return nil + } + + dat, err := ioutil.ReadFile(WAREWULFD_PIDFILE) + if err != nil { + return err + } + + pid, _ := strconv.Atoi(string(dat)) + process, err := os.FindProcess(pid) + + if err != nil { + fmt.Printf("Failed to find process: %s\n", err) + return err + } else { + err := process.Signal(syscall.Signal(syscall.SIGHUP)) + if err != nil { + fmt.Printf("SIGCONT on pid %d returned: %v\n", pid, err) + return err + } + } + + return nil +} + func DaemonStop() error { if util.IsFile(WAREWULFD_PIDFILE) == false { diff --git a/internal/pkg/warewulfd/ipxe.go b/internal/pkg/warewulfd/ipxe.go index 5fde3bc5..ab87f665 100644 --- a/internal/pkg/warewulfd/ipxe.go +++ b/internal/pkg/warewulfd/ipxe.go @@ -30,19 +30,14 @@ func IpxeSend(w http.ResponseWriter, req *http.Request) { url := strings.Split(req.URL.Path, "/") var unconfiguredNode bool - nodeDB, err := node.New() - if err != nil { - log.Printf("Could not read node configuration file: %s\n", err) - w.WriteHeader(503) - return - } - if url[2] == "" { log.Printf("ERROR: Bad iPXE request from %s\n", req.RemoteAddr) w.WriteHeader(404) return } + hwaddr := strings.ReplaceAll(url[2], "-", ":") + conf, err := warewulfconf.New() if err != nil { wwlog.Printf(wwlog.ERROR, "%s\n", err) @@ -50,16 +45,22 @@ func IpxeSend(w http.ResponseWriter, req *http.Request) { return } - hwaddr := strings.ReplaceAll(url[2], "-", ":") + nodeobj, err := GetNode(hwaddr) - n, err := nodeDB.FindByHwaddr(hwaddr) if err != nil { // If we failed to find a node, let's see if we can add one... var netdev string + nodeDB, err := node.New() + if err != nil { + log.Printf("Could not read node configuration file: %s\n", err) + w.WriteHeader(503) + return + } + wwlog.Printf(wwlog.VERBOSE, "Node was not found, looking for discoverable nodes...\n") - n, netdev, err = nodeDB.FindDiscoverableNode() + n, netdev, err := nodeDB.FindDiscoverableNode() if err != nil { wwlog.Printf(wwlog.WARN, "No nodes are set as discoverable...\n") unconfiguredNode = true @@ -79,6 +80,7 @@ func IpxeSend(w http.ResponseWriter, req *http.Request) { wwlog.Printf(wwlog.ERROR, "Could not persist new node configuration while adding node: %s\n", n.Id.Get()) unconfiguredNode = true } else { + nodeobj = n wwlog.Printf(wwlog.INFO, "Building System Overlay:\n") _ = overlay.BuildSystemOverlay([]node.NodeInfo{n}) wwlog.Printf(wwlog.INFO, "Building Runtime Overlay:\n") @@ -110,9 +112,10 @@ func IpxeSend(w http.ResponseWriter, req *http.Request) { return } else { - log.Printf("IPXE: %15s: %s\n", n.Id.Get(), req.URL.Path) - ipxeTemplate := fmt.Sprintf("/etc/warewulf/ipxe/%s.ipxe", n.Ipxe.Get()) + log.Printf("IPXE: %15s: %s\n", nodeobj.Id.Get(), req.URL.Path) + + ipxeTemplate := fmt.Sprintf("/etc/warewulf/ipxe/%s.ipxe", nodeobj.Ipxe.Get()) tmpl, err := template.ParseFiles(ipxeTemplate) if err != nil { @@ -122,14 +125,14 @@ func IpxeSend(w http.ResponseWriter, req *http.Request) { var replace iPxeTemplate - replace.Fqdn = n.Id.Get() + replace.Fqdn = nodeobj.Id.Get() replace.Ipaddr = conf.Ipaddr replace.Port = strconv.Itoa(conf.Warewulf.Port) - replace.Hostname = n.Id.Get() + replace.Hostname = nodeobj.Id.Get() replace.Hwaddr = url[2] - replace.ContainerName = n.ContainerName.Get() - replace.KernelArgs = n.KernelArgs.Get() - replace.KernelVersion = n.KernelVersion.Get() + replace.ContainerName = nodeobj.ContainerName.Get() + replace.KernelArgs = nodeobj.KernelArgs.Get() + replace.KernelVersion = nodeobj.KernelVersion.Get() err = tmpl.Execute(w, replace) if err != nil { @@ -137,7 +140,7 @@ func IpxeSend(w http.ResponseWriter, req *http.Request) { return } - log.Printf("SEND: %15s: %s\n", n.Id.Get(), ipxeTemplate) + log.Printf("SEND: %15s: %s\n", nodeobj.Id.Get(), ipxeTemplate) } return diff --git a/internal/pkg/warewulfd/nodedb.go b/internal/pkg/warewulfd/nodedb.go new file mode 100644 index 00000000..b2feddab --- /dev/null +++ b/internal/pkg/warewulfd/nodedb.go @@ -0,0 +1,64 @@ +package warewulfd + +import ( + "github.com/hpcng/warewulf/internal/pkg/errors" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "sync" +) + +type nodeDB struct { + lock sync.RWMutex + NodeInfo map[string]node.NodeInfo +} + +var ( + db nodeDB +) + +func LoadNodeDB() error { + + var TmpMap map[string]node.NodeInfo + TmpMap = make(map[string]node.NodeInfo) + + wwlog.Printf(wwlog.INFO, "Loading the node Database\n") + + DB, err := node.New() + if err != nil { + return err + } + + nodes, err := DB.FindAllNodes() + if err != nil { + return err + } + + for _, n := range nodes { + for _, netdev := range n.NetDevs { + wwlog.Printf(wwlog.DEBUG, "Caching node entry: '%s' -> %s\n", netdev.Hwaddr.Get(), n.Id.Get()) + + TmpMap[netdev.Hwaddr.Get()] = n + } + } + + db.lock.Lock() + defer db.lock.Unlock() + db.NodeInfo = TmpMap + + return nil +} + +func GetNode(val string) (node.NodeInfo, error) { + db.lock.RLock() + defer db.lock.RUnlock() + + if _, ok := db.NodeInfo[val]; ok { + wwlog.Printf(wwlog.DEBUG, "Found node:\n%+v\n", db.NodeInfo[val]) + + return db.NodeInfo[val], nil + } + + wwlog.Printf(wwlog.VERBOSE, "Node not found in DB: %s\n", val) + var empty node.NodeInfo + return empty, errors.New("No node found") +} diff --git a/internal/pkg/warewulfd/util.go b/internal/pkg/warewulfd/util.go index 3e5403ab..9cdba891 100644 --- a/internal/pkg/warewulfd/util.go +++ b/internal/pkg/warewulfd/util.go @@ -1,7 +1,6 @@ package warewulfd import ( - "fmt" "github.com/hpcng/warewulf/internal/pkg/errors" "github.com/hpcng/warewulf/internal/pkg/node" "io" @@ -14,22 +13,18 @@ import ( func getSanity(req *http.Request) (node.NodeInfo, error) { url := strings.Split(req.URL.Path, "/") - var ret node.NodeInfo - - nodes, err := node.New() - if err != nil { - return ret, errors.New(fmt.Sprintf("%s", err)) - } hwaddr := strings.ReplaceAll(url[2], "-", ":") - ret, err = nodes.FindByHwaddr(hwaddr) + + nodeobj, err := GetNode(hwaddr) if err != nil { + var ret node.NodeInfo return ret, errors.New("Could not find node by HW address") } - log.Printf("REQ: %15s: %s\n", ret.Id.Get(), req.URL.Path) + log.Printf("REQ: %15s: %s\n", nodeobj.Id.Get(), req.URL.Path) - return ret, nil + return nodeobj, nil } func sendFile(w http.ResponseWriter, filename string, sendto string) error { diff --git a/internal/pkg/warewulfd/warewulfd.go b/internal/pkg/warewulfd/warewulfd.go index ba90adcc..10a3e409 100644 --- a/internal/pkg/warewulfd/warewulfd.go +++ b/internal/pkg/warewulfd/warewulfd.go @@ -3,6 +3,9 @@ package warewulfd import ( "github.com/hpcng/warewulf/internal/pkg/wwlog" "net/http" + "os" + "os/signal" + "syscall" ) // TODO: https://github.com/danderson/netboot/blob/master/pixiecore/dhcp.go @@ -10,6 +13,23 @@ import ( func RunServer() error { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGHUP) + + go func() { + for _ = range c { + err := LoadNodeDB() + if err != nil { + wwlog.Printf(wwlog.WARN, "Could not load database: %s\n", err) + } + } + }() + + err := LoadNodeDB() + if err != nil { + wwlog.Printf(wwlog.WARN, "Could not load database: %s\n", err) + } + wwlog.Printf(wwlog.DEBUG, "Registering handlers for the web service\n") http.HandleFunc("/ipxe/", IpxeSend) @@ -21,7 +41,11 @@ func RunServer() error { wwlog.Printf(wwlog.VERBOSE, "Starting HTTPD REST service\n") - http.ListenAndServe(":9873", nil) + err = http.ListenAndServe(":9873", nil) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not start listening service: %s\n", err) + os.Exit(1) + } return nil }