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... @echo Running golangci-lint...
@$(GOLANGCI_LINT) run --build-tags "$(WW_BUILD_GO_BUILD_TAGS)" ./... @$(GOLANGCI_LINT) run --build-tags "$(WW_BUILD_GO_BUILD_TAGS)" ./...
all: vendor warewulfd wwctl wwclient all: vendor wwctl wwclient
files: all files: all
install -d -m 0755 /var/warewulf/ install -d -m 0755 /var/warewulf/
@@ -60,9 +60,6 @@ services: files
# sudo systemctl enable tftp # sudo systemctl enable tftp
# sudo systemctl restart tftp # sudo systemctl restart tftp
warewulfd:
cd cmd/warewulfd; go build -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../warewulfd
wwctl: wwctl:
cd cmd/wwctl; go build -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../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 cd cmd/wwclient; CGO_ENABLED=0 GOOS=linux go build -mod vendor -a -ldflags '-extldflags -static' -o ../../wwclient
clean: clean:
rm -f warewulfd
rm -f wwclient rm -f wwclient
rm -f wwctl 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/common v0.6.0 // indirect
github.com/prometheus/procfs v0.0.3 // indirect github.com/prometheus/procfs v0.0.3 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // 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/sirupsen/logrus v1.7.0 // indirect
github.com/spf13/cobra v1.1.1 github.com/spf13/cobra v1.1.1
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 // indirect 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 h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU=
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= 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/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 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 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/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/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/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 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 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= 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 ( import (
"fmt" "fmt"
"github.com/hpcng/warewulf/internal/app/wwctl/service/dhcp" "github.com/hpcng/warewulf/internal/app/wwctl/configure/dhcp"
"github.com/hpcng/warewulf/internal/app/wwctl/service/tftp" "github.com/hpcng/warewulf/internal/app/wwctl/configure/tftp"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"os" "os"
) )
var ( var (
baseCmd = &cobra.Command{ baseCmd = &cobra.Command{
Use: "service", Use: "configure",
Short: "Initialize Warewulf services", Short: "Configure Warewulf services",
Long: "Warewulf Service Initialization", Long: "Warewulf Service Initialization",
RunE: CobraRunE, RunE: CobraRunE,
} }

View File

@@ -24,7 +24,13 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
os.Exit(1) 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 { 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 ( var (
baseCmd = &cobra.Command{ baseCmd = &cobra.Command{
Use: "__child", Use: "__child",
Hidden: true, Hidden: true,
RunE: CobraRunE, RunE: CobraRunE,
Args: cobra.MinimumNArgs(1), 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) 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 return nil
} }

View File

@@ -7,11 +7,12 @@ import (
var ( var (
baseCmd = &cobra.Command{ baseCmd = &cobra.Command{
Use: "exec", Use: "exec",
Short: "Spawn any command inside a Warewulf container", Short: "Spawn any command inside a Warewulf container",
Long: "Run a command inside a Warewulf container ", Long: "Run a command inside a Warewulf container ",
RunE: CobraRunE, RunE: CobraRunE,
Args: cobra.MinimumNArgs(1), 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) os.Exit(1)
} }
nconfig, _ := node.New() nodeDB, _ := node.New()
nodes, _ := nconfig.FindAllNodes() nodes, _ := nodeDB.FindAllNodes()
nodemap := make(map[string]int) nodemap := make(map[string]int)
for _, n := range nodes { for _, n := range nodes {

View File

@@ -3,6 +3,7 @@ package pull
import ( import (
"fmt" "fmt"
"github.com/hpcng/warewulf/internal/pkg/container" "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/util"
"github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/hpcng/warewulf/internal/pkg/wwlog"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -50,9 +51,35 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
os.Exit(1) os.Exit(1)
} }
if SetBuild == true { fmt.Printf("Building container: %s\n", name)
fmt.Printf("Building container: %s\n", name) output, err := container.Build(name, true)
container.Build(name, false) 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 return nil

View File

@@ -10,15 +10,17 @@ var (
RunE: CobraRunE, RunE: CobraRunE,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
} }
SetForce bool SetForce bool
SetUpdate bool SetUpdate bool
SetBuild bool SetBuild bool
SetDefault bool
) )
func init() { func init() {
baseCmd.PersistentFlags().BoolVarP(&SetForce, "force", "f", false, "Force overwrite of an existing container") 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(&SetUpdate, "update", "u", false, "Update and overwrite an existing container")
baseCmd.PersistentFlags().BoolVarP(&SetBuild, "build", "b", false, "Build container when after pulling") 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 ( import (
"github.com/hpcng/warewulf/internal/app/wwctl/container/build" "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/exec"
"github.com/hpcng/warewulf/internal/app/wwctl/container/list" "github.com/hpcng/warewulf/internal/app/wwctl/container/list"
"github.com/hpcng/warewulf/internal/app/wwctl/container/pull" "github.com/hpcng/warewulf/internal/app/wwctl/container/pull"
@@ -22,7 +23,7 @@ func init() {
baseCmd.AddCommand(list.GetCommand()) baseCmd.AddCommand(list.GetCommand())
baseCmd.AddCommand(pull.GetCommand()) baseCmd.AddCommand(pull.GetCommand())
baseCmd.AddCommand(exec.GetCommand()) baseCmd.AddCommand(exec.GetCommand())
baseCmd.AddCommand(delete.GetCommand())
} }
// GetRootCommand returns the root cobra.Command for the application. // GetRootCommand returns the root cobra.Command for the application.

View File

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

View File

@@ -10,13 +10,14 @@ var (
RunE: CobraRunE, RunE: CobraRunE,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
} }
SetGroup string SetGroup string
SetController string SetController string
SetNetDev string SetNetDev string
SetIpaddr string SetIpaddr string
SetNetmask string SetNetmask string
SetGateway string SetGateway string
SetHwaddr string SetHwaddr string
SetDiscoverable bool
) )
func init() { func init() {
@@ -27,6 +28,7 @@ func init() {
baseCmd.PersistentFlags().StringVarP(&SetNetmask, "netmask", "M", "", "Set the node's network device netmask") 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(&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().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(), "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 %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(), "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(), "KernelVersion", node.KernelVersion.Source(), node.KernelVersion.Print())
fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "KernelArgs", node.KernelArgs.Source(), node.KernelArgs.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 { if len(SetAddProfile) > 0 {
for _, p := range SetAddProfile { for _, p := range SetAddProfile {
wwlog.Printf(wwlog.VERBOSE, "Node: %s, adding profile to '%s'\n", n.Id.Get(), p) wwlog.Printf(wwlog.VERBOSE, "Node: %s, adding profile to '%s'\n", n.Id.Get(), p)

View File

@@ -32,6 +32,8 @@ var (
SetDelProfile []string SetDelProfile []string
SetForce bool SetForce bool
SetInit string SetInit string
SetDiscoverable bool
SetUndiscoverable bool
) )
func init() { func init() {
@@ -61,6 +63,8 @@ func init() {
baseCmd.PersistentFlags().BoolVarP(&SetYes, "yes", "y", false, "Set 'yes' to all questions asked") 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().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 package wwctl
import ( import (
"github.com/hpcng/warewulf/internal/app/wwctl/configure"
"github.com/hpcng/warewulf/internal/app/wwctl/container" "github.com/hpcng/warewulf/internal/app/wwctl/container"
"github.com/hpcng/warewulf/internal/app/wwctl/kernel" "github.com/hpcng/warewulf/internal/app/wwctl/kernel"
"github.com/hpcng/warewulf/internal/app/wwctl/node" "github.com/hpcng/warewulf/internal/app/wwctl/node"
"github.com/hpcng/warewulf/internal/app/wwctl/overlay" "github.com/hpcng/warewulf/internal/app/wwctl/overlay"
"github.com/hpcng/warewulf/internal/app/wwctl/profile" "github.com/hpcng/warewulf/internal/app/wwctl/profile"
"github.com/hpcng/warewulf/internal/app/wwctl/ready" "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/hpcng/warewulf/internal/pkg/wwlog"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -37,8 +38,9 @@ func init() {
rootCmd.AddCommand(kernel.GetCommand()) rootCmd.AddCommand(kernel.GetCommand())
// rootCmd.AddCommand(group.GetCommand()) // rootCmd.AddCommand(group.GetCommand())
rootCmd.AddCommand(profile.GetCommand()) rootCmd.AddCommand(profile.GetCommand())
rootCmd.AddCommand(service.GetCommand()) rootCmd.AddCommand(configure.GetCommand())
rootCmd.AddCommand(ready.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 ( import (
"fmt" "fmt"
"github.com/hpcng/warewulf/internal/pkg/errors"
"github.com/hpcng/warewulf/internal/pkg/util" "github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/hpcng/warewulf/internal/pkg/wwlog"
"os" "os"
@@ -9,46 +10,40 @@ import (
"path" "path"
) )
func Build(name string, buildForce bool) { func Build(name string, buildForce bool) (string, error) {
rootfsPath := RootFsDir(name) rootfsPath := RootFsDir(name)
imagePath := ImageFile(name) imagePath := ImageFile(name)
if ValidSource(name) == false { if ValidSource(name) == false {
wwlog.Printf(wwlog.INFO, "%-35s: Skipping (bad path)\n", name) return "", errors.New("Container does not exist")
return
} }
if buildForce == false { if buildForce == false {
wwlog.Printf(wwlog.DEBUG, "Checking if there have been any updates to the VNFS directory\n") wwlog.Printf(wwlog.DEBUG, "Checking if there have been any updates to the VNFS directory\n")
if util.PathIsNewer(rootfsPath, imagePath) { if util.PathIsNewer(rootfsPath, imagePath) {
wwlog.Printf(wwlog.INFO, "%-35s: Skipping, VNFS is current\n", name) return "Skipping (VNFS is current)", nil
return
} }
} }
wwlog.Printf(wwlog.DEBUG, "Making parent directory for: %s\n", name) wwlog.Printf(wwlog.DEBUG, "Making parent directory for: %s\n", name)
err := os.MkdirAll(path.Dir(imagePath), 0755) err := os.MkdirAll(path.Dir(imagePath), 0755)
if err != nil { if err != nil {
fmt.Printf("ERROR: %s\n", err) return "Failed creating directory", err
return
} }
wwlog.Printf(wwlog.DEBUG, "Making parent directory for: %s\n", rootfsPath) wwlog.Printf(wwlog.DEBUG, "Making parent directory for: %s\n", rootfsPath)
err = os.MkdirAll(path.Dir(rootfsPath), 0755) err = os.MkdirAll(path.Dir(rootfsPath), 0755)
if err != nil { if err != nil {
fmt.Printf("ERROR: %s\n", err) return "Failed creating directory", err
return
} }
wwlog.Printf(wwlog.DEBUG, "Building VNFS image: '%s' -> '%s'\n", rootfsPath, imagePath) 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) cmd := fmt.Sprintf("cd %s; find . | cpio --quiet -o -H newc | gzip -c > \"%s\"", rootfsPath, imagePath)
err = exec.Command("/bin/sh", "-c", cmd).Run() err = exec.Command("/bin/sh", "-c", cmd).Run()
if err != nil { if err != nil {
wwlog.Printf(wwlog.ERROR, "Failed building VNFS: %s\n", err) return "Failed building VNFS", err
return
} }
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 { 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 false
} }
return true 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 return ret, nil
} }
func Build(kernelVersion string) error { func Build(kernelVersion string) (string, error) {
kernelImage := "/boot/vmlinuz-" + kernelVersion kernelImage := "/boot/vmlinuz-" + kernelVersion
kernelDrivers := "/lib/modules/" + kernelVersion kernelDrivers := "/lib/modules/" + kernelVersion
@@ -81,20 +81,19 @@ func Build(kernelVersion string) error {
os.MkdirAll(path.Dir(driversDestination), 0755) os.MkdirAll(path.Dir(driversDestination), 0755)
if util.IsFile(kernelImage) == false { 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 { 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") wwlog.Printf(wwlog.VERBOSE, "Setting up Kernel\n")
if _, err := os.Stat(kernelImage); err == nil { if _, err := os.Stat(kernelImage); err == nil {
err := util.CopyFile(kernelImage, kernelDestination) err := util.CopyFile(kernelImage, kernelDestination)
if err != nil { 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") 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) cmd := fmt.Sprintf("cd /; find .%s | cpio --quiet -o -H newc -F \"%s\"", kernelDrivers, driversDestination)
err := exec.Command("/bin/sh", "-c", cmd).Run() err := exec.Command("/bin/sh", "-c", cmd).Run()
if err != nil { 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" "gopkg.in/yaml.v2"
"io/ioutil" "io/ioutil"
"regexp" "regexp"
"sort"
"strings" "strings"
) )
@@ -73,6 +74,9 @@ func (self *nodeYaml) FindAllNodes() ([]NodeInfo, error) {
n.SystemOverlay.Set(node.SystemOverlay) n.SystemOverlay.Set(node.SystemOverlay)
n.RuntimeOverlay.Set(node.RuntimeOverlay) n.RuntimeOverlay.Set(node.RuntimeOverlay)
n.Discoverable.SetB(node.Discoverable)
n.Disabled.SetB(node.Disabled)
for devname, netdev := range node.NetDevs { for devname, netdev := range node.NetDevs {
if _, ok := n.NetDevs[devname]; !ok { if _, ok := n.NetDevs[devname]; !ok {
var netdev NetDevEntry var netdev NetDevEntry
@@ -111,6 +115,9 @@ func (self *nodeYaml) FindAllNodes() ([]NodeInfo, error) {
n.SystemOverlay.SetAlt(self.NodeProfiles[p].SystemOverlay, pstring) n.SystemOverlay.SetAlt(self.NodeProfiles[p].SystemOverlay, pstring)
n.RuntimeOverlay.SetAlt(self.NodeProfiles[p].RuntimeOverlay, 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 { for devname, netdev := range self.NodeProfiles[p].NetDevs {
if _, ok := n.NetDevs[devname]; !ok { if _, ok := n.NetDevs[devname]; !ok {
var netdev NetDevEntry var netdev NetDevEntry
@@ -154,6 +161,9 @@ func (self *nodeYaml) FindAllProfiles() ([]NodeInfo, error) {
p.RuntimeOverlay.Set(profile.RuntimeOverlay) p.RuntimeOverlay.Set(profile.RuntimeOverlay)
p.SystemOverlay.Set(profile.SystemOverlay) p.SystemOverlay.Set(profile.SystemOverlay)
p.Disabled.SetB(profile.Disabled)
p.Discoverable.SetB(profile.Discoverable)
for devname, netdev := range profile.NetDevs { for devname, netdev := range profile.NetDevs {
if _, ok := p.NetDevs[devname]; !ok { if _, ok := p.NetDevs[devname]; !ok {
var netdev NetDevEntry var netdev NetDevEntry
@@ -174,9 +184,40 @@ func (self *nodeYaml) FindAllProfiles() ([]NodeInfo, error) {
ret = append(ret, p) 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 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) { func (self *nodeYaml) FindByHwaddr(hwa string) (NodeInfo, error) {
var ret NodeInfo var ret NodeInfo

View File

@@ -1,6 +1,7 @@
package node package node
import ( import (
"fmt"
"github.com/hpcng/warewulf/internal/pkg/util" "github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/hpcng/warewulf/internal/pkg/wwlog"
"os" "os"
@@ -30,6 +31,7 @@ type NodeConf struct {
RuntimeOverlay string `yaml:"runtime overlay files,omitempty"` RuntimeOverlay string `yaml:"runtime overlay files,omitempty"`
SystemOverlay string `yaml:"system overlay files,omitempty"` SystemOverlay string `yaml:"system overlay files,omitempty"`
Init string `yaml:"init,omitempty"` Init string `yaml:"init,omitempty"`
Discoverable bool `yaml:"discoverable,omitempty"`
Profiles []string `yaml:"profiles,omitempty"` Profiles []string `yaml:"profiles,omitempty"`
NetDevs map[string]*NetDevs `yaml:"network devices,omitempty"` NetDevs map[string]*NetDevs `yaml:"network devices,omitempty"`
} }
@@ -71,6 +73,8 @@ type NodeInfo struct {
IpmiPassword Entry IpmiPassword Entry
RuntimeOverlay Entry RuntimeOverlay Entry
SystemOverlay Entry SystemOverlay Entry
Discoverable Entry
Disabled Entry
Init Entry //TODO: Finish adding this... Init Entry //TODO: Finish adding this...
Profiles []string Profiles []string
GroupProfiles []string GroupProfiles []string
@@ -89,7 +93,20 @@ type NetDevEntry struct {
func init() { func init() {
//TODO: Check to make sure nodes.conf is found //TODO: Check to make sure nodes.conf is found
if util.IsFile(ConfigFile) == false { if util.IsFile(ConfigFile) == false {
wwlog.Printf(wwlog.ERROR, "Configuration file not found: %s\n", ConfigFile) c, err := os.OpenFile(ConfigFile, os.O_RDWR|os.O_CREATE, 0644)
os.Exit(1) 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) { func (self *Entry) SetAltB(val bool, from string) {
self.altbool = val if val == true {
self.from = from self.altbool = val
self.from = from
}
return return
} }
@@ -106,6 +108,13 @@ func (self *Entry) Print() string {
return "--" return "--"
} }
func (self *Entry) PrintB() bool {
if self.from == "" {
return self.bool
}
return self.altbool
}
func (self *Entry) Source() string { func (self *Entry) Source() string {
if self.value != "" && self.altvalue != "" { if self.value != "" && self.altvalue != "" {
return "SUPERSEDED" 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].IpmiPassword = node.IpmiPassword.GetReal()
self.Nodes[nodeID].RuntimeOverlay = node.RuntimeOverlay.GetReal() self.Nodes[nodeID].RuntimeOverlay = node.RuntimeOverlay.GetReal()
self.Nodes[nodeID].SystemOverlay = node.SystemOverlay.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].Profiles = node.Profiles
self.Nodes[nodeID].NetDevs = make(map[string]*NetDevs) 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].IpmiPassword = profile.IpmiPassword.GetReal()
self.NodeProfiles[profileID].RuntimeOverlay = profile.RuntimeOverlay.GetReal() self.NodeProfiles[profileID].RuntimeOverlay = profile.RuntimeOverlay.GetReal()
self.NodeProfiles[profileID].SystemOverlay = profile.SystemOverlay.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].Profiles = profile.Profiles
self.NodeProfiles[profileID].NetDevs = make(map[string]*NetDevs) 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 var OverlayFile string
if overlayType == "runtime" { if overlayType == "runtime" {
wwlog.Printf(wwlog.VERBOSE, "Building runtime overlay for: %s\n", n.Id.Get())
OverlayDir = config.RuntimeOverlaySource(n.RuntimeOverlay.Get()) OverlayDir = config.RuntimeOverlaySource(n.RuntimeOverlay.Get())
OverlayFile = config.RuntimeOverlayImage(n.Id.Get()) OverlayFile = config.RuntimeOverlayImage(n.Id.Get())
} else if overlayType == "system" { } else if overlayType == "system" {
wwlog.Printf(wwlog.VERBOSE, "Building system overlay for: %s\n", n.Id.Get())
OverlayDir = config.SystemOverlaySource(n.RuntimeOverlay.Get()) OverlayDir = config.SystemOverlaySource(n.RuntimeOverlay.Get())
OverlayFile = config.SystemOverlayImage(n.Id.Get()) OverlayFile = config.SystemOverlayImage(n.Id.Get())
} else { } else {

View File

@@ -1,4 +1,4 @@
package response package warewulfd
import ( import (
"github.com/hpcng/warewulf/internal/pkg/container" "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 ( import (
"github.com/hpcng/warewulf/internal/pkg/kernel" "github.com/hpcng/warewulf/internal/pkg/kernel"

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
package response package warewulfd
import ( import (
"fmt" "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
}