Merge branch 'main' of github.com:ctrliq/warewulf into main

This commit is contained in:
Shannon V. Davidson
2020-12-14 09:14:19 -06:00
57 changed files with 791 additions and 251 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

@@ -97,24 +97,78 @@ There are three major groups of data to provision:
``` ```
sudo ./wwctl container pull docker://warewulf/centos-7 centos-7 sudo ./wwctl container pull docker://warewulf/centos-7 centos-7 --setdefault
sudo ./wwctl container build centos-7 sudo ./wwctl kernel build $(uname -r) --setdefault
sudo ./wwctl kernel build $(uname -r)
``` ```
#### Set up the default node profile #### Set up the default node profile
The `--setdefault` arguments above will automatically set those entries in the default
profile, but if you wanted to set them by hand to something different, you can do the
following:
``` ```
sudo ./wwctl profile set default -K $(uname -r) -C centos-7 sudo ./wwctl profile set default -K $(uname -r) -C centos-7
```
Next we set some default networking configurations for the first ethernet device. On
modern Linux distributions, the name of the device is not critical, as it will be setup
according to the HW address. Because all nodes will share the netmask and gateway, we
can configure them in the default profile as follows:
```
sudo ./wwctl profile set default --netdev eth0 -M 255.255.255.0 -G 192.168.1.1 sudo ./wwctl profile set default --netdev eth0 -M 255.255.255.0 -G 192.168.1.1
sudo ./wwctl profile list sudo ./wwctl profile list
``` ```
#### Add a node and build node specific overlays #### Add a node and build node specific overlays
Adding nodes can be done while setting configurations in one command. Here we are setting
the IP address of `eth0` and setting this node to be discoverable, which will then
automatically have the HW address added to the configuration as the node boots.
Node names must be unique. If you have node groups and/or multiple clusters, designate
them using dot notation.
Note that the full node configuration comes from both cascading profiles and node
configurations which always supersede profile configurations.
``` ```
sudo ./wwctl node add n0000.cluster --netdev eth0 -I 192.168.1.100 -H 00:0c:29:23:8b:48 sudo ./wwctl node add n0000.cluster --netdev eth0 -I 192.168.1.100 --discoverable
sudo ./wwctl node list -a n0000 sudo ./wwctl node list -a n0000
```
### Warewulf Overlays
There are two types of overlays: system and runtime overlays.
System overlays are provisioned to the node before `/sbin/init` is called. This enables us
to prepopulate node configurations with content that is node specific like networking and
service configurations. When using the overlay subsystem, system overlays are never shown
by default. So when running `overlay` commands, you are always looking at runtime overlays
unless the `-s` option is passed.
Runtime overlays are provisioned after the node has booted and periodically during the
normal runtime of the node. Runtime overlays are also obtained using privileged source
ports such that non-root users can not obtain this from the Warewulf service (note: there
are other ways to secure the provisioned files like a provisioning VLan). Because these
overlays are provisioned at periodic intervals, they are very useful for content that
changes, like users and groups.
Overlays are generated from a template structure that is viewed using the `wwctl overlay`
commands. Files that end in the `.ww` suffix are templates and abide by standard
text/template rules. This supports loops, arrays, variables, and functions making overlays
extremely flexible.
All overlays are compiled before being provisioned. This accelerates the provisioning
process because there is less to do when nodes are being managed at scale.
Here are some of the common `overlay` commands:
```
sudo ./wwctl overlay list -l
sudo ./wwctl overlay list -ls
sudo EDITOR=vim ./wwctl overlay edit default /etc/hello_world.ww
sudo ./wwctl overlay build -a sudo ./wwctl overlay build -a
``` ```
@@ -125,7 +179,8 @@ and then begin booting nodes.
``` ```
sudo ./wwctl ready sudo ./wwctl ready
./warewulfd sudo ./wwctl server start
sudo ./wwctl server status
``` ```
#### Boot your compute node and watch it boot #### Boot your compute node and watch it boot

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

@@ -195,6 +195,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

@@ -33,6 +33,8 @@ var (
SetDelProfile []string SetDelProfile []string
SetForce bool SetForce bool
SetInit string SetInit string
SetDiscoverable bool
SetUndiscoverable bool
) )
func init() { func init() {
@@ -63,6 +65,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

@@ -60,7 +60,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
fmt.Fprintf(w, "# when the overlay is rendered for the individual nodes. Here are some examples\n") fmt.Fprintf(w, "# when the overlay is rendered for the individual nodes. Here are some examples\n")
fmt.Fprintf(w, "# of macros and logic which can be used within this file:\n") fmt.Fprintf(w, "# of macros and logic which can be used within this file:\n")
fmt.Fprintf(w, "#\n") fmt.Fprintf(w, "#\n")
fmt.Fprintf(w, "# Node FQDN = {{.Fqdn}}\n") fmt.Fprintf(w, "# Node FQDN = {{.Id}}\n")
fmt.Fprintf(w, "# Node Group = {{.GroupName}}\n") fmt.Fprintf(w, "# Node Group = {{.GroupName}}\n")
fmt.Fprintf(w, "# Network Config = {{.NetDevs.eth0.Ipaddr}}, {{.NetDevs.eth0.Hwaddr}}, etc.\n") fmt.Fprintf(w, "# Network Config = {{.NetDevs.eth0.Ipaddr}}, {{.NetDevs.eth0.Hwaddr}}, etc.\n")
fmt.Fprintf(w, "#\n") fmt.Fprintf(w, "#\n")

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"
) )
@@ -74,6 +75,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
@@ -113,6 +117,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
@@ -157,6 +164,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
@@ -177,9 +187,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"
@@ -31,6 +32,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"`
} }
@@ -73,6 +75,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
@@ -91,7 +95,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

@@ -67,6 +67,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)
@@ -140,6 +144,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

@@ -119,9 +119,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
}