Merge pull request #3 from gmkurtzer/node_discovery

I've been testing this over and over, lots of good features and it is working so I'm merging. :)
This commit is contained in:
Gregory M. Kurtzer
2020-12-13 22:46:38 -08:00
committed by GitHub
55 changed files with 730 additions and 245 deletions

View File

@@ -40,7 +40,7 @@ lint:
@echo Running golangci-lint...
@$(GOLANGCI_LINT) run --build-tags "$(WW_BUILD_GO_BUILD_TAGS)" ./...
all: vendor warewulfd wwctl wwclient
all: vendor wwctl wwclient
files: all
install -d -m 0755 /var/warewulf/
@@ -60,9 +60,6 @@ services: files
# sudo systemctl enable tftp
# sudo systemctl restart tftp
warewulfd:
cd cmd/warewulfd; go build -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../warewulfd
wwctl:
cd cmd/wwctl; go build -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../wwctl
@@ -70,7 +67,6 @@ wwclient:
cd cmd/wwclient; CGO_ENABLED=0 GOOS=linux go build -mod vendor -a -ldflags '-extldflags -static' -o ../../wwclient
clean:
rm -f warewulfd
rm -f wwclient
rm -f wwctl

View File

@@ -1,12 +0,0 @@
package main
import (
"github.com/hpcng/warewulf/internal/app/warewulfd"
)
func main() {
root := warewulfd.GetRootCommand()
root.Execute()
}

View File

@@ -0,0 +1,12 @@
#!ipxe
echo
echo ================================================================================
echo Warewulf v4
echo
echo MESSAGE: This node is unconfigured. Please have your system administrator add a
echo configuration for this node with HW address: {{$.Hwaddr}}
echo
echo Rebooting in 1 minute...
sleep 60
reboot

View File

@@ -1,8 +0,0 @@
nodeprofiles:
default:
comment: "This profile is automatically included for each node"
container name: ""
kernel version: ""
kernel args: crashkernel=no quiet
nodes: {}

1
go.mod
View File

@@ -42,6 +42,7 @@ require (
github.com/prometheus/common v0.6.0 // indirect
github.com/prometheus/procfs v0.0.3 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/sevlyar/go-daemon v0.1.5 // indirect
github.com/sirupsen/logrus v1.7.0 // indirect
github.com/spf13/cobra v1.1.1
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 // indirect

4
go.sum
View File

@@ -201,6 +201,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU=
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
@@ -340,6 +342,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sevlyar/go-daemon v0.1.5 h1:Zy/6jLbM8CfqJ4x4RPr7MJlSKt90f00kNM1D401C+Qk=
github.com/sevlyar/go-daemon v0.1.5/go.mod h1:6dJpPatBT9eUwM5VCw9Bt6CdX9Tk6UWvhW3MebLDRKE=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=

View File

@@ -1,90 +0,0 @@
package response
import (
"fmt"
"github.com/hpcng/warewulf/internal/pkg/node"
"github.com/hpcng/warewulf/internal/pkg/warewulfconf"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"log"
"net/http"
"strconv"
"strings"
"text/template"
)
type iPxeTemplate struct {
Hostname string
Fqdn string
ContainerName string
Hwaddr string
Ipaddr string
Port string
KernelArgs string
KernelVersion string
}
func IpxeSend(w http.ResponseWriter, req *http.Request) {
url := strings.Split(req.URL.Path, "/")
nodes, 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)
return
}
hwaddr := strings.ReplaceAll(url[2], "-", ":")
node, err := nodes.FindByHwaddr(hwaddr)
if err != nil {
log.Printf("Could not find HW Addr: %s: %s\n", hwaddr, err)
w.WriteHeader(404)
return
}
if node.Id.Defined() == true {
conf, err := warewulfconf.New()
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
return
}
log.Printf("IPXE: %15s: %s\n", node.Id.Get(), req.URL.Path)
// TODO: Fix template path to use config package
ipxeTemplate := fmt.Sprintf("/etc/warewulf/ipxe/%s.ipxe", node.Ipxe.Get())
tmpl, err := template.ParseFiles(ipxeTemplate)
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
return
}
var replace iPxeTemplate
replace.Fqdn = node.Id.Get()
replace.Ipaddr = conf.Ipaddr
replace.Port = strconv.Itoa(conf.Warewulf.Port)
replace.Hostname = node.Id.Get()
replace.Hwaddr = url[2]
replace.ContainerName = node.ContainerName.Get()
replace.KernelArgs = node.KernelArgs.Get()
replace.KernelVersion = node.KernelVersion.Get()
err = tmpl.Execute(w, replace)
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
return
}
log.Printf("SEND: %15s: %s\n", node.Id.Get(), ipxeTemplate)
} else {
log.Printf("ERROR: iPXE request from unknown Node (hwaddr=%s)\n", url[2])
}
return
}

View File

@@ -1,40 +0,0 @@
package warewulfd
import (
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"github.com/spf13/cobra"
)
var (
baseCmd = &cobra.Command{
Use: "warewulfd",
Short: "Warewulf Daemon Service",
Long: "This is the primary Warewulf service for provisioning nodes",
RunE: CobraRunE,
PersistentPreRunE: rootPersistentPreRunE,
}
verboseArg bool
debugArg bool
)
func init() {
baseCmd.PersistentFlags().BoolVarP(&verboseArg, "verbose", "v", false, "Run with increased verbosity.")
baseCmd.PersistentFlags().BoolVarP(&debugArg, "debug", "d", false, "Run with debugging messages enabled.")
}
// GetRootCommand returns the root cobra.Command for the application.
func GetRootCommand() *cobra.Command {
return baseCmd
}
func rootPersistentPreRunE(cmd *cobra.Command, args []string) error {
if verboseArg == true {
wwlog.SetLevel(wwlog.VERBOSE)
} else if debugArg == true {
wwlog.SetLevel(wwlog.DEBUG)
} else {
wwlog.SetLevel(wwlog.INFO)
}
return nil
}

View File

@@ -1,24 +0,0 @@
package warewulfd
import (
"github.com/hpcng/warewulf/internal/app/warewulfd/response"
"github.com/spf13/cobra"
"net/http"
)
// TODO: https://github.com/danderson/netboot/blob/master/pixiecore/dhcp.go
// TODO: https://github.com/pin/tftp
func CobraRunE(cmd *cobra.Command, args []string) error {
http.HandleFunc("/ipxe/", response.IpxeSend)
http.HandleFunc("/kernel/", response.KernelSend)
http.HandleFunc("/kmods/", response.KmodsSend)
http.HandleFunc("/container/", response.ContainerSend)
http.HandleFunc("/overlay-system/", response.SystemOverlaySend)
http.HandleFunc("/overlay-runtime", response.RuntimeOverlaySend)
http.ListenAndServe(":9873", nil)
return nil
}

View File

@@ -1,17 +1,17 @@
package service
package configure
import (
"fmt"
"github.com/hpcng/warewulf/internal/app/wwctl/service/dhcp"
"github.com/hpcng/warewulf/internal/app/wwctl/service/tftp"
"github.com/hpcng/warewulf/internal/app/wwctl/configure/dhcp"
"github.com/hpcng/warewulf/internal/app/wwctl/configure/tftp"
"github.com/spf13/cobra"
"os"
)
var (
baseCmd = &cobra.Command{
Use: "service",
Short: "Initialize Warewulf services",
Use: "configure",
Short: "Configure Warewulf services",
Long: "Warewulf Service Initialization",
RunE: CobraRunE,
}

View File

@@ -24,7 +24,13 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
os.Exit(1)
}
container.Build(c, BuildForce)
output, err := container.Build(c, BuildForce)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not build container %s: %s\n", c, err)
os.Exit(1)
} else {
fmt.Printf("%-20s: %s\n", c, output)
}
}
if SetDefault == true {

View File

@@ -0,0 +1,44 @@
package delete
import (
"fmt"
"github.com/hpcng/warewulf/internal/pkg/container"
"github.com/hpcng/warewulf/internal/pkg/node"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"github.com/spf13/cobra"
"os"
)
func CobraRunE(cmd *cobra.Command, args []string) error {
nodeDB, err := node.New()
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not open nodeDB: %s\n", err)
os.Exit(1)
}
nodes, _ := nodeDB.FindAllNodes()
ARG_LOOP:
for _, arg := range args {
for _, n := range nodes {
if n.ContainerName.Get() == arg {
wwlog.Printf(wwlog.ERROR, "Container is configured for nodes, skipping: %s\n", arg)
continue ARG_LOOP
}
}
if container.ValidSource(arg) == false {
wwlog.Printf(wwlog.ERROR, "Container name is not a valid source: %s\n", arg)
continue
}
err := container.DeleteSource(arg)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not remove source: %s\n", arg)
} else {
fmt.Printf("Container has been deleted: %s\n", arg)
}
}
return nil
}

View File

@@ -0,0 +1,25 @@
package delete
import "github.com/spf13/cobra"
var (
baseCmd = &cobra.Command{
Use: "delete",
Short: "Delete Source OCI VNFS images",
Long: "Delete Source OCI VNFS images ",
RunE: CobraRunE,
Args: cobra.MinimumNArgs(1),
}
SetForce bool
SetUpdate bool
SetBuild bool
)
func init() {
}
// GetRootCommand returns the root cobra.Command for the application.
func GetCommand() *cobra.Command {
return baseCmd
}

View File

@@ -4,10 +4,11 @@ import "github.com/spf13/cobra"
var (
baseCmd = &cobra.Command{
Use: "__child",
Hidden: true,
RunE: CobraRunE,
Args: cobra.MinimumNArgs(1),
Use: "__child",
Hidden: true,
RunE: CobraRunE,
Args: cobra.MinimumNArgs(1),
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
}
)

View File

@@ -36,5 +36,14 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
os.Exit(1)
}
fmt.Printf("Rebuilding container...\n")
output, err := container.Build(containerName, false)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not build container %s: %s\n", containerName, err)
os.Exit(1)
} else {
fmt.Printf("%s\n", output)
}
return nil
}

View File

@@ -7,11 +7,12 @@ import (
var (
baseCmd = &cobra.Command{
Use: "exec",
Short: "Spawn any command inside a Warewulf container",
Long: "Run a command inside a Warewulf container ",
RunE: CobraRunE,
Args: cobra.MinimumNArgs(1),
Use: "exec",
Short: "Spawn any command inside a Warewulf container",
Long: "Run a command inside a Warewulf container ",
RunE: CobraRunE,
Args: cobra.MinimumNArgs(1),
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
}
)

View File

@@ -18,8 +18,8 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
os.Exit(1)
}
nconfig, _ := node.New()
nodes, _ := nconfig.FindAllNodes()
nodeDB, _ := node.New()
nodes, _ := nodeDB.FindAllNodes()
nodemap := make(map[string]int)
for _, n := range nodes {

View File

@@ -3,6 +3,7 @@ package pull
import (
"fmt"
"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/wwlog"
"github.com/spf13/cobra"
@@ -50,9 +51,35 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
os.Exit(1)
}
if SetBuild == true {
fmt.Printf("Building container: %s\n", name)
container.Build(name, false)
fmt.Printf("Building container: %s\n", name)
output, err := container.Build(name, true)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not build container %s: %s\n", name, err)
os.Exit(1)
} else {
fmt.Printf("%s: %s\n", name, output)
}
if SetDefault == true {
nodeDB, err := node.New()
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err)
os.Exit(1)
}
//TODO: Don't loop through profiles, instead have a nodeDB function that goes directly to the map
profiles, _ := nodeDB.FindAllProfiles()
for _, profile := range profiles {
wwlog.Printf(wwlog.DEBUG, "Looking for profile default: %s\n", profile.Id.Get())
if profile.Id.Get() == "default" {
wwlog.Printf(wwlog.DEBUG, "Found profile default, setting container name to: %s\n", name)
profile.ContainerName.Set(name)
nodeDB.ProfileUpdate(profile)
}
}
nodeDB.Persist()
fmt.Printf("Set default profile to container: %s\n", name)
}
return nil

View File

@@ -10,15 +10,17 @@ var (
RunE: CobraRunE,
Args: cobra.MinimumNArgs(1),
}
SetForce bool
SetUpdate bool
SetBuild bool
SetForce bool
SetUpdate bool
SetBuild bool
SetDefault bool
)
func init() {
baseCmd.PersistentFlags().BoolVarP(&SetForce, "force", "f", false, "Force overwrite of an existing container")
baseCmd.PersistentFlags().BoolVarP(&SetUpdate, "update", "u", false, "Update and overwrite an existing container")
baseCmd.PersistentFlags().BoolVarP(&SetBuild, "build", "b", false, "Build container when after pulling")
baseCmd.PersistentFlags().BoolVar(&SetDefault, "setdefault", false, "Set this container for the default profile")
}

View File

@@ -2,6 +2,7 @@ package container
import (
"github.com/hpcng/warewulf/internal/app/wwctl/container/build"
"github.com/hpcng/warewulf/internal/app/wwctl/container/delete"
"github.com/hpcng/warewulf/internal/app/wwctl/container/exec"
"github.com/hpcng/warewulf/internal/app/wwctl/container/list"
"github.com/hpcng/warewulf/internal/app/wwctl/container/pull"
@@ -22,7 +23,7 @@ func init() {
baseCmd.AddCommand(list.GetCommand())
baseCmd.AddCommand(pull.GetCommand())
baseCmd.AddCommand(exec.GetCommand())
baseCmd.AddCommand(delete.GetCommand())
}
// GetRootCommand returns the root cobra.Command for the application.

View File

@@ -12,10 +12,12 @@ import (
func CobraRunE(cmd *cobra.Command, args []string) error {
for _, arg := range args {
err := kernel.Build(arg)
output, err := kernel.Build(arg)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Failed building kernel: %s\n", err)
os.Exit(1)
} else {
fmt.Printf("%s: %s\n", arg, output)
}
}

View File

@@ -102,6 +102,17 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
}
}
if SetDiscoverable == true {
wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting node to discoverable\n", n.Id.Get())
n.Discoverable.SetB(true)
err := nodeDB.NodeUpdate(n)
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
os.Exit(1)
}
}
}
nodeDB.Persist()

View File

@@ -10,13 +10,14 @@ var (
RunE: CobraRunE,
Args: cobra.MinimumNArgs(1),
}
SetGroup string
SetController string
SetNetDev string
SetIpaddr string
SetNetmask string
SetGateway string
SetHwaddr string
SetGroup string
SetController string
SetNetDev string
SetIpaddr string
SetNetmask string
SetGateway string
SetHwaddr string
SetDiscoverable bool
)
func init() {
@@ -27,6 +28,7 @@ func init() {
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", "H", "", "Set the node's network device HW address")
baseCmd.PersistentFlags().BoolVar(&SetDiscoverable, "discoverable", false, "Make this node discoverable")
}

View File

@@ -50,6 +50,9 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "ClusterName", node.ClusterName.Source(), node.ClusterName.Print())
fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "Profiles", "--", strings.Join(node.Profiles, ","))
fmt.Printf("%-20s %-18s %-12s %t\n", node.Id.Get(), "Disabled", node.Disabled.Source(), node.Disabled.PrintB())
fmt.Printf("%-20s %-18s %-12s %t\n", node.Id.Get(), "Discoverable", node.Discoverable.Source(), node.Discoverable.PrintB())
fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "ContainerName", node.ContainerName.Source(), node.ContainerName.Print())
fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "KernelVersion", node.KernelVersion.Source(), node.KernelVersion.Print())
fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "KernelArgs", node.KernelArgs.Source(), node.KernelArgs.Print())

View File

@@ -185,6 +185,27 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
}
}
if SetDiscoverable == true {
wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting node to discoverable\n", n.Id.Get())
n.Discoverable.SetB(true)
err := nodeDB.NodeUpdate(n)
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
os.Exit(1)
}
}
if SetUndiscoverable == true {
wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting node to undiscoverable\n", n.Id.Get())
n.Discoverable.SetB(false)
err := nodeDB.NodeUpdate(n)
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
os.Exit(1)
}
}
if len(SetAddProfile) > 0 {
for _, p := range SetAddProfile {
wwlog.Printf(wwlog.VERBOSE, "Node: %s, adding profile to '%s'\n", n.Id.Get(), p)

View File

@@ -32,6 +32,8 @@ var (
SetDelProfile []string
SetForce bool
SetInit string
SetDiscoverable bool
SetUndiscoverable bool
)
func init() {
@@ -61,6 +63,8 @@ func init() {
baseCmd.PersistentFlags().BoolVarP(&SetYes, "yes", "y", false, "Set 'yes' to all questions asked")
baseCmd.PersistentFlags().BoolVarP(&SetForce, "force", "f", false, "Force configuration (even on error)")
baseCmd.PersistentFlags().BoolVar(&SetDiscoverable, "discoverable", false, "Make this node discoverable")
baseCmd.PersistentFlags().BoolVar(&SetUndiscoverable, "undiscoverable", false, "Remove the discoverable flag")
}

View File

@@ -1,13 +1,14 @@
package wwctl
import (
"github.com/hpcng/warewulf/internal/app/wwctl/configure"
"github.com/hpcng/warewulf/internal/app/wwctl/container"
"github.com/hpcng/warewulf/internal/app/wwctl/kernel"
"github.com/hpcng/warewulf/internal/app/wwctl/node"
"github.com/hpcng/warewulf/internal/app/wwctl/overlay"
"github.com/hpcng/warewulf/internal/app/wwctl/profile"
"github.com/hpcng/warewulf/internal/app/wwctl/ready"
"github.com/hpcng/warewulf/internal/app/wwctl/service"
"github.com/hpcng/warewulf/internal/app/wwctl/server"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"github.com/spf13/cobra"
@@ -37,8 +38,9 @@ func init() {
rootCmd.AddCommand(kernel.GetCommand())
// rootCmd.AddCommand(group.GetCommand())
rootCmd.AddCommand(profile.GetCommand())
rootCmd.AddCommand(service.GetCommand())
rootCmd.AddCommand(configure.GetCommand())
rootCmd.AddCommand(ready.GetCommand())
rootCmd.AddCommand(server.GetCommand())
}

View File

@@ -0,0 +1,29 @@
package server
import (
"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"
"github.com/spf13/cobra"
)
var (
baseCmd = &cobra.Command{
Use: "server",
Short: "Warewulf server process commands",
Long: "Warewulf profiles...",
}
test bool
)
func init() {
baseCmd.AddCommand(start.GetCommand())
baseCmd.AddCommand(status.GetCommand())
baseCmd.AddCommand(stop.GetCommand())
}
// GetRootCommand returns the root cobra.Command for the application.
func GetCommand() *cobra.Command {
return baseCmd
}

View File

@@ -0,0 +1,11 @@
package start
import (
"github.com/hpcng/warewulf/internal/pkg/warewulfd"
"github.com/spf13/cobra"
)
func CobraRunE(cmd *cobra.Command, args []string) error {
return warewulfd.DaemonStart()
}

View File

@@ -0,0 +1,20 @@
package start
import "github.com/spf13/cobra"
var (
baseCmd = &cobra.Command{
Use: "start",
Short: "Start Warewulf server",
Long: "Warewulf Server ",
RunE: CobraRunE,
}
)
func init() {
}
// GetRootCommand returns the root cobra.Command for the application.
func GetCommand() *cobra.Command {
return baseCmd
}

View File

@@ -0,0 +1,11 @@
package status
import (
"github.com/hpcng/warewulf/internal/pkg/warewulfd"
"github.com/spf13/cobra"
)
func CobraRunE(cmd *cobra.Command, args []string) error {
warewulfd.DaemonStatus()
return nil
}

View File

@@ -0,0 +1,20 @@
package status
import "github.com/spf13/cobra"
var (
baseCmd = &cobra.Command{
Use: "status",
Short: "Warewulf server status",
Long: "Warewulf Server ",
RunE: CobraRunE,
}
)
func init() {
}
// GetRootCommand returns the root cobra.Command for the application.
func GetCommand() *cobra.Command {
return baseCmd
}

View File

@@ -0,0 +1,11 @@
package stop
import (
"github.com/hpcng/warewulf/internal/pkg/warewulfd"
"github.com/spf13/cobra"
)
func CobraRunE(cmd *cobra.Command, args []string) error {
warewulfd.DaemonStop()
return nil
}

View File

@@ -0,0 +1,20 @@
package stop
import "github.com/spf13/cobra"
var (
baseCmd = &cobra.Command{
Use: "stop",
Short: "Stop Warewulf server",
Long: "Warewulf Server ",
RunE: CobraRunE,
}
)
func init() {
}
// GetRootCommand returns the root cobra.Command for the application.
func GetCommand() *cobra.Command {
return baseCmd
}

View File

@@ -2,6 +2,7 @@ package container
import (
"fmt"
"github.com/hpcng/warewulf/internal/pkg/errors"
"github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"os"
@@ -9,46 +10,40 @@ import (
"path"
)
func Build(name string, buildForce bool) {
func Build(name string, buildForce bool) (string, error) {
rootfsPath := RootFsDir(name)
imagePath := ImageFile(name)
if ValidSource(name) == false {
wwlog.Printf(wwlog.INFO, "%-35s: Skipping (bad path)\n", name)
return
return "", errors.New("Container does not exist")
}
if buildForce == false {
wwlog.Printf(wwlog.DEBUG, "Checking if there have been any updates to the VNFS directory\n")
if util.PathIsNewer(rootfsPath, imagePath) {
wwlog.Printf(wwlog.INFO, "%-35s: Skipping, VNFS is current\n", name)
return
return "Skipping (VNFS is current)", nil
}
}
wwlog.Printf(wwlog.DEBUG, "Making parent directory for: %s\n", name)
err := os.MkdirAll(path.Dir(imagePath), 0755)
if err != nil {
fmt.Printf("ERROR: %s\n", err)
return
return "Failed creating directory", err
}
wwlog.Printf(wwlog.DEBUG, "Making parent directory for: %s\n", rootfsPath)
err = os.MkdirAll(path.Dir(rootfsPath), 0755)
if err != nil {
fmt.Printf("ERROR: %s\n", err)
return
return "Failed creating directory", err
}
wwlog.Printf(wwlog.DEBUG, "Building VNFS image: '%s' -> '%s'\n", rootfsPath, imagePath)
cmd := fmt.Sprintf("cd %s; find . | cpio --quiet -o -H newc | gzip -c > \"%s\"", rootfsPath, imagePath)
err = exec.Command("/bin/sh", "-c", cmd).Run()
if err != nil {
wwlog.Printf(wwlog.ERROR, "Failed building VNFS: %s\n", err)
return
return "Failed building VNFS", err
}
wwlog.Printf(wwlog.INFO, "%-35s: Done\n", name)
return "Done", nil
}

View File

@@ -77,9 +77,15 @@ func ValidSource(name string) bool {
}
if util.IsDir(fullPath) == false {
wwlog.Printf(wwlog.WARN, "Location is not a VNFS source directory: %s\n", name)
wwlog.Printf(wwlog.VERBOSE, "Location is not a VNFS source directory: %s\n", name)
return false
}
return true
}
func DeleteSource(name string) error {
fullPath := SourceDir(name)
return os.RemoveAll(fullPath)
}

View File

@@ -69,7 +69,7 @@ func ListKernels() ([]string, error) {
return ret, nil
}
func Build(kernelVersion string) error {
func Build(kernelVersion string) (string, error) {
kernelImage := "/boot/vmlinuz-" + kernelVersion
kernelDrivers := "/lib/modules/" + kernelVersion
@@ -81,20 +81,19 @@ func Build(kernelVersion string) error {
os.MkdirAll(path.Dir(driversDestination), 0755)
if util.IsFile(kernelImage) == false {
return errors.New("Could not locate kernel image: " + kernelImage)
return "", errors.New("Could not locate kernel image")
}
if util.IsDir(kernelDrivers) == false {
return errors.New("Could not locate kernel drivers: " + kernelDrivers)
return "", errors.New("Could not locate kernel drivers")
}
wwlog.Printf(wwlog.VERBOSE, "Setting up Kernel\n")
if _, err := os.Stat(kernelImage); err == nil {
err := util.CopyFile(kernelImage, kernelDestination)
if err != nil {
return err
return "", err
}
wwlog.Printf(wwlog.INFO, "%-45s: Done\n", "vmlinuz-"+kernelVersion)
}
wwlog.Printf(wwlog.VERBOSE, "Building Kernel driver image\n")
@@ -102,10 +101,9 @@ func Build(kernelVersion string) error {
cmd := fmt.Sprintf("cd /; find .%s | cpio --quiet -o -H newc -F \"%s\"", kernelDrivers, driversDestination)
err := exec.Command("/bin/sh", "-c", cmd).Run()
if err != nil {
return err
return "", err
}
wwlog.Printf(wwlog.INFO, "%-45s: Done\n", "kmods-"+kernelVersion+".img")
}
return nil
return "Done", nil
}

View File

@@ -7,6 +7,7 @@ import (
"gopkg.in/yaml.v2"
"io/ioutil"
"regexp"
"sort"
"strings"
)
@@ -73,6 +74,9 @@ func (self *nodeYaml) FindAllNodes() ([]NodeInfo, error) {
n.SystemOverlay.Set(node.SystemOverlay)
n.RuntimeOverlay.Set(node.RuntimeOverlay)
n.Discoverable.SetB(node.Discoverable)
n.Disabled.SetB(node.Disabled)
for devname, netdev := range node.NetDevs {
if _, ok := n.NetDevs[devname]; !ok {
var netdev NetDevEntry
@@ -111,6 +115,9 @@ func (self *nodeYaml) FindAllNodes() ([]NodeInfo, error) {
n.SystemOverlay.SetAlt(self.NodeProfiles[p].SystemOverlay, pstring)
n.RuntimeOverlay.SetAlt(self.NodeProfiles[p].RuntimeOverlay, pstring)
n.Disabled.SetAltB(self.NodeProfiles[p].Disabled, pstring)
n.Discoverable.SetAltB(self.NodeProfiles[p].Discoverable, pstring)
for devname, netdev := range self.NodeProfiles[p].NetDevs {
if _, ok := n.NetDevs[devname]; !ok {
var netdev NetDevEntry
@@ -154,6 +161,9 @@ func (self *nodeYaml) FindAllProfiles() ([]NodeInfo, error) {
p.RuntimeOverlay.Set(profile.RuntimeOverlay)
p.SystemOverlay.Set(profile.SystemOverlay)
p.Disabled.SetB(profile.Disabled)
p.Discoverable.SetB(profile.Discoverable)
for devname, netdev := range profile.NetDevs {
if _, ok := p.NetDevs[devname]; !ok {
var netdev NetDevEntry
@@ -174,9 +184,40 @@ func (self *nodeYaml) FindAllProfiles() ([]NodeInfo, error) {
ret = append(ret, p)
}
sort.Slice(ret, func(i, j int) bool {
if ret[i].ClusterName.Get() < ret[j].ClusterName.Get() {
return true
} else if ret[i].ClusterName.Get() == ret[j].ClusterName.Get() {
if ret[i].Id.Get() < ret[j].Id.Get() {
return true
}
}
return false
})
return ret, nil
}
func (self *nodeYaml) FindDiscoverableNode() (NodeInfo, string, error) {
var ret NodeInfo
nodes, _ := self.FindAllNodes()
for _, node := range nodes {
if node.Discoverable.GetB() == false {
continue
}
for netdev, dev := range node.NetDevs {
if dev.Hwaddr.Defined() == false {
return node, netdev, nil
}
}
}
return ret, "", errors.New("No unconfigured nodes found")
}
func (self *nodeYaml) FindByHwaddr(hwa string) (NodeInfo, error) {
var ret NodeInfo

View File

@@ -1,6 +1,7 @@
package node
import (
"fmt"
"github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"os"
@@ -30,6 +31,7 @@ type NodeConf struct {
RuntimeOverlay string `yaml:"runtime overlay files,omitempty"`
SystemOverlay string `yaml:"system overlay files,omitempty"`
Init string `yaml:"init,omitempty"`
Discoverable bool `yaml:"discoverable,omitempty"`
Profiles []string `yaml:"profiles,omitempty"`
NetDevs map[string]*NetDevs `yaml:"network devices,omitempty"`
}
@@ -71,6 +73,8 @@ type NodeInfo struct {
IpmiPassword Entry
RuntimeOverlay Entry
SystemOverlay Entry
Discoverable Entry
Disabled Entry
Init Entry //TODO: Finish adding this...
Profiles []string
GroupProfiles []string
@@ -89,7 +93,20 @@ type NetDevEntry struct {
func init() {
//TODO: Check to make sure nodes.conf is found
if util.IsFile(ConfigFile) == false {
wwlog.Printf(wwlog.ERROR, "Configuration file not found: %s\n", ConfigFile)
os.Exit(1)
c, err := os.OpenFile(ConfigFile, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not create new configuration file: %s\n", err)
os.Exit(1)
}
fmt.Fprintf(c, "nodeprofiles:\n")
fmt.Fprintf(c, " default:\n")
fmt.Fprintf(c, " comment: This profile is automatically included for each node\n")
fmt.Fprintf(c, " kernel args: crashkernel=no quiet\n")
fmt.Fprintf(c, "nodes: {}\n")
c.Close()
wwlog.Printf(wwlog.INFO, "Created default node configuration\n")
}
}

View File

@@ -41,8 +41,10 @@ func (self *Entry) SetAlt(val string, from string) {
}
func (self *Entry) SetAltB(val bool, from string) {
self.altbool = val
self.from = from
if val == true {
self.altbool = val
self.from = from
}
return
}
@@ -106,6 +108,13 @@ func (self *Entry) Print() string {
return "--"
}
func (self *Entry) PrintB() bool {
if self.from == "" {
return self.bool
}
return self.altbool
}
func (self *Entry) Source() string {
if self.value != "" && self.altvalue != "" {
return "SUPERSEDED"

View File

@@ -66,6 +66,10 @@ func (self *nodeYaml) NodeUpdate(node NodeInfo) error {
self.Nodes[nodeID].IpmiPassword = node.IpmiPassword.GetReal()
self.Nodes[nodeID].RuntimeOverlay = node.RuntimeOverlay.GetReal()
self.Nodes[nodeID].SystemOverlay = node.SystemOverlay.GetReal()
self.Nodes[nodeID].Disabled = node.Disabled.GetRealB()
self.Nodes[nodeID].Discoverable = node.Discoverable.GetRealB()
self.Nodes[nodeID].Profiles = node.Profiles
self.Nodes[nodeID].NetDevs = make(map[string]*NetDevs)
@@ -138,6 +142,10 @@ func (self *nodeYaml) ProfileUpdate(profile NodeInfo) error {
self.NodeProfiles[profileID].IpmiPassword = profile.IpmiPassword.GetReal()
self.NodeProfiles[profileID].RuntimeOverlay = profile.RuntimeOverlay.GetReal()
self.NodeProfiles[profileID].SystemOverlay = profile.SystemOverlay.GetReal()
self.NodeProfiles[profileID].Disabled = profile.Disabled.GetRealB()
self.NodeProfiles[profileID].Discoverable = profile.Discoverable.GetRealB()
self.NodeProfiles[profileID].Profiles = profile.Profiles
self.NodeProfiles[profileID].NetDevs = make(map[string]*NetDevs)

View File

@@ -118,9 +118,13 @@ func buildOverlay(nodeList []node.NodeInfo, overlayType string) error {
var OverlayFile string
if overlayType == "runtime" {
wwlog.Printf(wwlog.VERBOSE, "Building runtime overlay for: %s\n", n.Id.Get())
OverlayDir = config.RuntimeOverlaySource(n.RuntimeOverlay.Get())
OverlayFile = config.RuntimeOverlayImage(n.Id.Get())
} else if overlayType == "system" {
wwlog.Printf(wwlog.VERBOSE, "Building system overlay for: %s\n", n.Id.Get())
OverlayDir = config.SystemOverlaySource(n.RuntimeOverlay.Get())
OverlayFile = config.SystemOverlayImage(n.Id.Get())
} else {

View File

@@ -1,4 +1,4 @@
package response
package warewulfd
import (
"github.com/hpcng/warewulf/internal/pkg/container"

View File

@@ -0,0 +1,114 @@
package warewulfd
import (
"fmt"
"github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"io/ioutil"
"os"
"os/exec"
"strconv"
"syscall"
"time"
)
const (
WAREWULFD_PIDFILE = "/tmp/warewulfd.pid"
WAREWULFD_LOGFILE = "/tmp/warewulfd.log"
)
func DaemonStart() error {
if os.Getenv("WAREWULFD_BACKGROUND") == "1" {
RunServer()
} else {
os.Setenv("WAREWULFD_BACKGROUND", "1")
f, err := os.OpenFile(WAREWULFD_LOGFILE, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
return err
}
p, err := os.OpenFile(WAREWULFD_PIDFILE, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return err
}
cmd := exec.Command(os.Args[0], "server", "start")
cmd.Stdout = f
cmd.Stderr = f
cmd.Start()
pid := cmd.Process.Pid
fmt.Fprintf(p, "%d", pid)
p.Close()
time.Sleep(1 * time.Second)
DaemonStatus()
}
return nil
}
func DaemonStatus() 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(0))
if err != nil {
fmt.Printf("SIGCONT on pid %d returned: %v\n", pid, err)
return err
} else {
fmt.Printf("Warewulf daemon is running at PID: %d\n", pid)
}
}
return nil
}
func DaemonStop() 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)
} else {
err := process.Signal(syscall.Signal(15))
if err != nil {
fmt.Printf("SIGCONT on pid %d returned: %v\n", pid, err)
} else {
fmt.Printf("Terminated Warewulf process at PID: %d\n", pid)
}
}
os.Remove(WAREWULFD_PIDFILE)
return nil
}

View File

@@ -0,0 +1,144 @@
package warewulfd
import (
"fmt"
"github.com/hpcng/warewulf/internal/pkg/node"
"github.com/hpcng/warewulf/internal/pkg/overlay"
"github.com/hpcng/warewulf/internal/pkg/warewulfconf"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"log"
"net/http"
"strconv"
"strings"
"text/template"
)
type iPxeTemplate struct {
Message string
WaitTime string
Hostname string
Fqdn string
ContainerName string
Hwaddr string
Ipaddr string
Port string
KernelArgs string
KernelVersion string
}
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
}
conf, err := warewulfconf.New()
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
w.WriteHeader(503)
return
}
hwaddr := strings.ReplaceAll(url[2], "-", ":")
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
wwlog.Printf(wwlog.VERBOSE, "Node was not found, looking for discoverable nodes...\n")
n, netdev, err = nodeDB.FindDiscoverableNode()
if err != nil {
wwlog.Printf(wwlog.WARN, "No nodes are set as discoverable...\n")
unconfiguredNode = true
} else {
wwlog.Printf(wwlog.INFO, "Adding new configuration to discoverable node: %s\n", n.Id.Get())
n.NetDevs[netdev].Hwaddr.Set(hwaddr)
n.Discoverable.SetB(false)
err := nodeDB.NodeUpdate(n)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not add discovered configuration for node: %s\n", n.Id.Get())
unconfiguredNode = true
} else {
err := nodeDB.Persist()
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not persist new node configuration while adding node: %s\n", n.Id.Get())
unconfiguredNode = true
} else {
wwlog.Printf(wwlog.INFO, "Building System Overlay:\n")
_ = overlay.BuildSystemOverlay([]node.NodeInfo{n})
wwlog.Printf(wwlog.INFO, "Building Runtime Overlay:\n")
_ = overlay.BuildRuntimeOverlay([]node.NodeInfo{n})
}
}
}
}
if unconfiguredNode == true {
log.Printf("UNCONFIGURED NODE: %15s\n", hwaddr)
tmpl, err := template.ParseFiles("/etc/warewulf/ipxe/unconfigured.ipxe")
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
return
}
var replace iPxeTemplate
replace.Hwaddr = hwaddr
err = tmpl.Execute(w, replace)
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
return
}
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())
tmpl, err := template.ParseFiles(ipxeTemplate)
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
return
}
var replace iPxeTemplate
replace.Fqdn = n.Id.Get()
replace.Ipaddr = conf.Ipaddr
replace.Port = strconv.Itoa(conf.Warewulf.Port)
replace.Hostname = n.Id.Get()
replace.Hwaddr = url[2]
replace.ContainerName = n.ContainerName.Get()
replace.KernelArgs = n.KernelArgs.Get()
replace.KernelVersion = n.KernelVersion.Get()
err = tmpl.Execute(w, replace)
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
return
}
log.Printf("SEND: %15s: %s\n", n.Id.Get(), ipxeTemplate)
}
return
}

View File

@@ -1,4 +1,4 @@
package response
package warewulfd
import (
"github.com/hpcng/warewulf/internal/pkg/kernel"

View File

@@ -1,4 +1,4 @@
package response
package warewulfd
import (
"github.com/hpcng/warewulf/internal/pkg/kernel"

View File

@@ -1,4 +1,4 @@
package response
package warewulfd
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package response
package warewulfd
import (
"github.com/hpcng/warewulf/internal/pkg/config"

View File

@@ -1,4 +1,4 @@
package response
package warewulfd
import (
"fmt"

View File

@@ -0,0 +1,27 @@
package warewulfd
import (
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"net/http"
)
// TODO: https://github.com/danderson/netboot/blob/master/pixiecore/dhcp.go
// TODO: https://github.com/pin/tftp
func RunServer() error {
wwlog.Printf(wwlog.DEBUG, "Registering handlers for the web service\n")
http.HandleFunc("/ipxe/", IpxeSend)
http.HandleFunc("/kernel/", KernelSend)
http.HandleFunc("/kmods/", KmodsSend)
http.HandleFunc("/container/", ContainerSend)
http.HandleFunc("/overlay-system/", SystemOverlaySend)
http.HandleFunc("/overlay-runtime", RuntimeOverlaySend)
wwlog.Printf(wwlog.VERBOSE, "Starting HTTPD REST service\n")
http.ListenAndServe(":9873", nil)
return nil
}